読者です 読者をやめる 読者になる 読者になる

GeekFactory

int128.hatenablog.com

Travis CIでビルドのアウトプットを外部に保存する

GitHubプロジェクトのContinuous IntegrationにはTravis CIが便利ですね。Travis CIはビルド中の標準出力(とエラー出力)をログに残してくれるので、ビルドが失敗した場合も原因を調べられるようになっています。しかし、テスト実行中のデバッグログなど、あらゆるものを標準出力に流してしまうと、ビルド失敗時の調査に大変な労力がかかってしまうため、ビルドレポートを外部に保存する方が便利でしょう。本稿では、Travis CIでビルドのアウトプットを外部に保存する方法を説明します。

方針

アウトプットの保存先として下記のような選択肢があります。

  1. Git Repositoryにpushする。GitHubなど。
  2. Cloud Storageに保存する。S3など。
  3. SSH Serverに保存する。自前のサーバにscpするなど。

いずれにしても、第三者に保存先を勝手に変更されないように、クレデンシャルを適切に管理する必要があります。 リポジトリの設定ファイルに平文でクレデンシャルを書いてしまうと悪用されるおそれがあります。 Travis CIは暗号化をサポートしており、あらかじめ暗号化した文字列を設定ファイルに書いておくと、ビルド実行時に自動的に復号された文字列が環境変数にセットされる仕組みがあります。

ここでは、下記の方針でアウトプットを保存することにしましょう。

  • ビルドのアウトプットはGitHub Pagesにpushします。
  • GitHubにはHTTPSでアクセスします。

実現方法

まず、ビルドレポートを保存するためのリポジトリを作成します。 とりあえずindex.htmlをコミットしておきましょう。

GitHubHTTPSアクセスするためのトークンを取得します。 Settings - Applications - Personal access tokensでトークンを取得できます。

f:id:int128:20131123231758p:plain

念のため、取得したトークンでHTTPSアクセスできるか確認しておきましょう。

git remote add test https://ユーザ名:トークン@github.com/example/build-report.git
git fetch test

そして、ユーザ名と取得したトークンをそれぞれ暗号化します。travis encryptコマンドを使います。表示された文字列はこのあと設定ファイルに書くのでメモしておきます。

travis encrypt GH_LOGIN=ユーザ名
travis encrypt GH_TOKEN=ユーザ名

.travis.ymlを書きます。Gradleの例を挙げますが、他の言語やビルドツールでも似たようなものになると思います。

language: java
jdk:
  - oraclejdk7
script:
  - ./gradlew build

# 下記でビルドレポートを保存する
after_script:
  - ./.travis.sh publish_report

# 先ほど暗号化した環境変数を書く
env:
  global:
    - TERM=dumb
    - GH_BUILD_REPORT=example/build-report
    - secure: "xxx"

.travis.shを書きます。概ね下記のことをやっています。

  • ~/.netrcにクレデンシャルを一時的に書き出す。
  • メールアドレスと名前をconfigに設定する。
  • 保存先のリポジトリをcloneする。下記ではGitHub Pagesのブランチを指定している。
  • ビルドレポートをaddする。下記ではブランチ名ごとにフォルダを分けてビルドレポートを保存している。
  • ブランチ一覧のファイルを生成する。これはビルドレポート一覧ページを生成する時に便利。
  • commit & pushする。
#!/bin/bash -xe
function publish_report () {
    if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then
        set +x
        echo "machine github.com" >> ~/.netrc
        echo "login $GH_LOGIN"    >> ~/.netrc
        echo "password $GH_TOKEN" >> ~/.netrc
        set -x

        git config --global user.email 'travis@travis-ci.org'
        git config --global user.name 'travis'
        git clone --quiet --branch=gh-pages "https://github.com/$GH_BUILD_REPORT.git" gh-pages
        cd gh-pages

        git rm -q -r "$TRAVIS_BRANCH" || true
        mkdir -p "$TRAVIS_BRANCH"

        [ -d ../reports ] && cp -a ../reports "$TRAVIS_BRANCH"
        git add "$TRAVIS_BRANCH"

        grep "$TRAVIS_BRANCH" branch-list || echo "$TRAVIS_BRANCH" >> branch-list
        git add branch-list

        git commit -q -m "Automatically updated by Travis build $TRAVIS_BUILD_NUMBER"
        git push origin gh-pages

        rm -v ~/.netrc
    fi
}

"$@"

以上により、ビルドが実行されたらビルドレポートがGitHub Pagesに保存されるようになりました。

ビルドを試すときは後で消せるように適当なブランチを作ってpushするようにしましょう。gitリポジトリに保存する場合はclone後の状態を考えてaddしないといけないとかハマることが多いので、意外とS3の方が簡単かもしれません。

おまけ:ビルドレポート一覧ページ

先ほどの例ではbranch-listというファイルにブランチ一覧を書き出しているため、これを使えば一覧ページを簡単に生成できます。 ここではJavaScriptでさくっと動的生成してみます。

$(function () {
    var computeTemplate = function (template, branchName) {
      return template.replace(/BRANCH/g, branchName);
    }.bind(null, $('.branch-template').html());
    $('.branch-template').empty();

    $.get('/build-report/branch-list').done(function (branchList) {
      branchList.split(/[\r\n]+/).filter(function (branchName) {
        return branchName.match(/\w+/);
      }).forEach(function (branchName) {
        $(computeTemplate(branchName)).prependTo('.page>ul');
      });
    });
});
<h1 class="page-title">Build Report</h1>
<ul class="branch-template">
<li>BRANCH branch

<ul>
<li>Test Report: <a href="/build-report/BRANCH/reports/tests/">Unit Test</a>, <a href="/build-report/BRANCH/reports/server-integration-tests/">Server Integration Test</a> and <a href="https://github.com/gradle-ssh-plugin/build-report/blob/gh-pages/BRANCH/reports/acceptance-test.log">Acceptance Test</a></li>
<li>Coverage Report: <a href="/build-report/BRANCH/reports/jacoco/test/html/">Unit Test</a> and <a href="/build-report/BRANCH/reports/jacoco/server-integration-tests/html/">Server Integration Test</a></li>
<li><a href="/build-report/BRANCH/docs/groovydoc/">API Document</a></li>
</ul></li>
</ul>

Gradle SSH PluginのContinuous Integrationでは本稿で説明したような仕組みを導入しており、ビルドレポートが簡単に確認できるようになっています。