メールでJenkinsにコマンドを送りたい(3) 〜Publisher編〜
前日までの実装では、メールの件名(subject)に書かれた内容をCLIコマンドとして認識し、
実行するところまでできました。ですが、このままだと実行結果が分かりません。
もともと思い描いていた構想の2番目は、
2.成功・失敗を問わず、実行結果をコマンド送信元のメールアドレス宛てに送信したい
-
- 標準のメール通知だと成功時にメールが飛ばない
- Publisherを拡張したらできそう?
という感じで、成功や失敗を問わずに結果を知りたいし、コマンド送信を行ったアドレスに対して
結果が通知されてほしいのです。というわけで、Publisherを拡張して専用の通知を作成したいと思います。
今回の設定画面では、チェックだけ付けられればOKなので、以下のような(ほぼ空の)config.jellyを用意しました。
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form"> </j:jelly>
また、リソースとしてhelp_ja.htmlファイルを置いておくと、説明として認識されます。
上で設定したチェックボックスの右側に?アイコンがついて、クリックすると説明が展開される仕組みのようです。
help_ja.htmlの内容は、以下のようなものです。
<div> Mail Commander実行後、コマンドの送信元にジョブの結果をメールします。 </div>
help_ja.html上は日本語圏向けなので、それ以外用としてhelp.htmlも用意しておきます。
Publisherの書き方は、サンプルに無いので分からないのですが、標準で用意されているメール通知自体が
Publisherを継承している(正確にはPublisherを継承しているNotifierをさらにMailerが継承している)ので、
hudson.tasks.Mailerを参考にして作成しました。
public class MailCommandPublisher extends Publisher { @Override public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException { File logfile = build.getLogFile(); StringBuffer logbuf = new StringBuffer(); try { FileReader in = new FileReader(logfile); BufferedReader br = new BufferedReader(in); String line; while ((line = br.readLine()) != null) { logbuf.append(line); logbuf.append("\n"); } br.close(); in.close(); } catch (IOException e) { listener.getLogger().println(e); return false; } File addressFile = new File(build.getRootDir(), "tmp.address"); String to_address = null; if (addressFile.exists()) { try { FileReader in = new FileReader(addressFile); BufferedReader br = new BufferedReader(in); String line; while ((line = br.readLine()) != null) { to_address = line; } br.close(); in.close(); } catch (IOException e) { listener.getLogger().println(e); return false; } if (to_address != null) { try { MimeMessage msg = new MimeMessage(Mailer.descriptor().createSession()); msg.setRecipients(RecipientType.TO, to_address); msg.setFrom(new InternetAddress(Mailer.descriptor().getAdminAddress())); msg.setSubject("This is a result of mail command"); msg.setSentDate(new Date()); msg.setText(logbuf.toString()); Transport.send(msg); } catch (MessagingException mex) { listener.getLogger().println(mex); mex.printStackTrace(); return false; } } } return true; } public BuildStepMonitor getRequiredMonitorService() { return BuildStepMonitor.NONE; } public static DescriptorImpl DESCRIPTOR; public static DescriptorImpl descriptor() { return Hudson.getInstance().getDescriptorByType(MailCommandPublisher.DescriptorImpl.class); } @Extension public static final class DescriptorImpl extends BuildStepDescriptor<Publisher> { public DescriptorImpl() { load(); DESCRIPTOR = this; } public String getDisplayName() { return Messages.MailCommandPublisher_DisplayName(); } @Override public Publisher newInstance(StaplerRequest req, JSONObject formData) { MailCommandPublisher m = new MailCommandPublisher(); return m; } @Override public boolean isApplicable(Class<? extends AbstractProject> jobType) { return true; } } }
<performメソッドについて>
やっていることは大きく2つで、1つ目は、buildのログを取得してStringBufferに溜め込むことです。
2つ目はメール送信元に対して、StringBufferの内容をメールで送信しています。
メールの送信元は、Builderでしか分からないのでBuilderでメールのFromアドレスをファイルに保存し、
そのファイルの内容をPublisherで開いて宛先にセットしています。(←この方法が良いかどうか。。。不明)
JavaMailの送信では(おそらく他の送信プログラムでも)、Fromアドレスは必須となっているため
Jenkinsのシステム設定にある管理者のメールアドレスを利用しました。⇒Mailer.descriptor().getAdminAddress();
ここに適切なアドレスが指定されていないと送信エラーとなります。上のコードではエラーチェックしてませんが。
今回はわざと間違ったコマンドを送信してみたいと思い、件名に「build -s -p aaa=bbb」として送信してみました。
いままで作成したBuilderとPublisherを組み合わせれば、やりたいことが出来ることがわかったのですが、あくまで定期的に実行しないと駄目です。
標準の定期実行でこのジョブを実行すると、該当するメールが無かった場合もビルド履歴として残ってしまいます。
SCMのポーリングというトリガーを利用すると、SCMに変更が無い場合はビルドが実行されず、余計なビルド履歴が残りません。
次回は、Triggerを使用してこの問題をクリアしたいと思います。