Thanks Driven Life

日々是感謝

Jenkins でジョブが失敗した時にだけ実行したい処理があった場合の対応パターン

あるある話題で、すでにいろんな所でまとめ終わってるものばかりですがとりあえずメモ

1. 「シェルの実行」内で分岐

[設定 (Configure)] → [ビルド (Build)] → [シェルの実行 (Execute Shell)] を弄る

#!/bin/sh

ls /tmp/hoge # Not exists

if [ $? -eq 0 ] ; then
    echo "SUCCESS"
else
    echo "FAILURE"
fi

ポイントは Shebang を指定すること

Jenkins Job の「シェルの実行」に入力されたコードは /tmp/hudson**.sh みたいなファイルに落とされてから実行されますが、この時 Shebang が無いと自動的に set -e が挿入されてしまいます。

失敗した時に実行したいのに、set -e によって if に到達できない問題があるため、Shebang を明示的に記載することでそれを回避できます。

2. Post Build Task を使う(文字列フィルタ編)

「この文字列が出力されてたら status code が 0 でもエラーって思いたいんだよ」という時、 前述の「シェルの実行」内で出力をファイルに落として grep してー・・とがんばってもいいのですが Post build task を使う方がてっとりばやいです。

Post build task のインストール後、[設定 (Configure)] → [ビルド後の処理 (Post-build Actions)] → [Post build task] を追加します。

詳しい使い方はここでは省きます。上記リンク先や Google it 。簡単に言うと、指定した文字列を「ジョブの実行」で出力された文字列で検索して、マッチしたら実行する、みたいなことができます。

これまでの方法で解決できないパターンがある

たとえば「シェルの実行」で bundle exec を行うジョブがあったとします。

cd /path/to/project
bundle install --path vendor/bundle
bundle exec rspec

「このジョブが失敗したときだけある処理をしたい」みたいなことを思い浮かべた時、困ることが多かったです。

パターン1 で難しい理由

#!/bin/sh

cd /path/to/project
bundle install --path vendor/bundle
bundle exec rspec

if [ $? -eq 0 ] ; then
    echo "SUCCESS"
else
    echo "FAILURE"
fi

とやりたくなってしまいますが、これは if まで到達しません 。なぜなら

$ head -n 5 `which bundle`
#!/usr/bin/env bash
set -e
[ -n "$RBENV_DEBUG" ] && set -x

program="${0##*/}"

こんな風に bundle 内で set -e が指定されているため RSpec でエラーが出た時の処理を書きたいと思っても bundle 内の set -e に阻まれて if に到達できない。

(もしかして外からこれを解除する方法あるのかな…)

パターン2で難しい理由

RSpec の結果出力を見て「failure が 0 じゃないからー」とかをチェックしてもいいんですが、これもスマートじゃない気がしました。(もちろん時と場合にはよるんですが。さっと見るだけならいいかな?)

Build other projects で難しい理由

Add post-build action (ビルド後の処理)[Build other projects (他のプロジェクトのビルド)] だと

  • 成功した時だけ
  • 不安定の時にも
  • エラーの時にも

という3つの条件しか選べず「エラーの時 だけ」というのが指定できません。

そこで次の方法です。

3. Post Build Task を使う(ジョブステータスをチェック編)

Post Build Task を使うのは変わらないのですが、そこに書くシェルスクリプトをいじります

#!/bin/sh

curl ${BUILD_URL}/api/json?tree=result | grep -q "SUCCESS"

if [ $? -eq 0 ]; then
  echo "SUCCESS!"
else
  echo "FAILURE..."
fi

$BUILD_URL には http://jenkins.server/job/プロジェクト名/ビルド番号 が入ります。こいつに /api/json をつけくわえると、指定した番号のビルドに関する情報が json 形式で得られます (参考: Remote access API - Jenkins - Jenkins Wiki)

さらに ?tree=result と指定すると

{"result":"SUCCESS"}

このようにビルドの状態だけを抜きとれるので、より一層 grep しやすくなりました。

まとめ

シェルが使えるのでどうにでもなる。

もっと楽い方法あるよーという方はご教授願います