sashimi4’s diary

日々の雑多なメモを書きます

Notify Gradle's build completion to Slack.

Gradleのビルド終了をSlackに通知する

今関わってるプロジェクトのビルドが諸事情により酷く長く、Instant Runが使えないこともしばしばあり、ビルド終了の通知を受けたくなったのでやってみたメモ。

How to

Slack通知を専門に行うtaskを追加する。

[project dir]/gradle/build_notifier.gradle

task pushToSlack << {
  Properties localProperties = new Properties()
  localProperties.load(project.rootProject.file('local.properties').newDataInputStream())
  (["${project.rootDir}/gradle/notify_build_finish.sh", localProperties.getProperty('slackToken')].execute().text)
}

tasks.whenTaskAdded { task ->
  if (task.name ==~ /assemble.*/) {
    task.doLast {
      project.tasks.getByName("pushToSlack").execute()
    }
  }
}

NOTE: task名が assemble で始まるタスクの終了を通知します

通知を行うシェルスクリプトを追加する。 [project dir]/gradle/notify_build_finish.sh

#!/bin/bash
curl -X POST --data-urlencode 'payload={"channel": "#チャンネル名 または @ユーザ名", "username": "Gradle", "text": "Build has just finished.", "icon_emoji": ":gradle:"}' https://hooks.slack.com/services/$1

NOTE: chmod +x notify_build_finish.sh で実行権限の付与を忘れないこと。

SlackのIncomingWebhookのTokenを設定。 [project dir]/local.properties

slackToken=*********/*********/***********************

追加したtaskを読み込む。

[project dir]/app/build.gradle

...
apply from: file('../gradle/build_notifier.gradle')
...

android {...}
...

めも

  • 共同開発には微妙。なにかしらオプトアウトできる手段を提供すべき。
  • Unix/Linux系OSでしか動作しない。WSLで動くだろうか?

おまけ

単純にMac内で通知が見れればいい場合は、 .bashrcalias notify='osascript -e '"'"'display notification "めっせーじ" with title "たいとる" subtitle "さぶたいとる" sound name "Purr"'"'"'' と入れておいて、

$ ./gradlew check ; notify

なんてすると終了時に通知を受け取れますね。

gradlew自体にaliasを張って、

alias gradle='echo $@ ; osascript -e '"'"'display notification "めっせーじ" with title "たいとる" subtitle "さぶたいとる" sound name "Purr"'"'"''

こんな感じにしたかったんだけどうまくいきませんでした。:thinking_face:

以上。

現存するFindBugsErrorを全て除外設定してみる

いろいろな理由から、
「今あるFindBugsのエラーを一旦無視したい!」「だけどFindBugs自体は有効化させたままでいたい…!」
という時があるかもしれません。
(私は既存プロジェクトにFindBugsを途中から導入しようとしてこうなりました…。既に数百件のエラーがあります…。)
(「とりあえず今あるエラーは一旦無視して、これから発生する分は随時対応しようね」ということにしました。)

除外ルールを自動生成してしまおう!

FindBugsのレポートを生成

パースしやすいように、一時的にXMLにします。

task findbugs(type: FindBugs) {
  ...
  ignoreFailures = false
  effort = "max"
  reportLevel = "low"
  excludeFilter = new File("${project.rootDir}/config/findbugs/findbugs_filter.xml")
  classes = files("$project.buildDir/intermediates/javac/")

  source 'src/main'
  include '**/*.java'

  reports {
//    xml.enabled = false
//    html.enabled = !xml.isEnabled()
//    html {
//      destination(new File("${project.rootDir}/build/reports/findbugs_report.html"))
//    }
    // 一時的に形式をXMLに変更
    xml.enabled = true
    html.enabled = !xml.isEnabled()
    html {
      destination(new File("${project.rootDir}/build/reports/findbugs_report.xml"))
    }
  }
  classpath = files()
  ...
}

Findbugsを実行

$ ./gradlew build # FindBugsは成果物を静的解析するので、まだであればbuildする
$ ./gradlew findbugs

除外ルール自動生成

$ cat fb-filter-generator.rb
#! /usr/bin/ruby
require 'active_support'
require 'active_support/core_ext'
require 'open-uri'

def create_match(classname:, abbrev:)
  <<~EOS
  <Match>
      <Class name="#{classname}"/>
      <Bug code="#{abbrev}"/>
  </Match>
  EOS
end
hash = Hash.from_xml open('./build/reports/findbugs_report.xml').read

filter_xml = ""
hash["BugCollection"]["BugInstance"].each do |bi|
  if bi["Class"].is_a?(Array)
    bi["Class"].each do |bc|
      filter_xml << create_match(classname: bc["classname"], abbrev: bi["abbrev"])
    end
  else
    filter_xml << create_match(classname: bi["Class"]["classname"], abbrev: bi["abbrev"])
  end
end

File.open('./findbugs_filter.xml','w') do |file|
    file.puts <<~EOS
    <?xml version="1.0" encoding="UTF-8"?>
    <FindBugsFilter>
      #{filter_xml}
    </FindBugsFilter>
  EOS
end

↑のスクリプトをプロジェクト直下で実行します。

$ ruby ./fb-filter-generator.rb

実行したディレクトリに findbugs_filter.xml という除外ルールのファイルができるはずです。

私はFindBugsの除外ファイルを

excludeFilter = new File("${project.rootDir}/config/findbugs/findbugs_filter.xml")

と設定していますので、

$ mv ./findbugs_filter.xml ./config/findbugs/findbugs_filter.xml

と実行して設置しました。

これで、いま存在するFindBugsのエラーが全て除外できます!!!

Android Java から Fluentd にログを送信してみる

環境

手順

FluentdのDockerコンテナを準備

$ docker pull fluent/fluentd

fluent.conf を作成

$ mkdir fluent && cd fluent
$ mkdir etc log
$ cat > "./etc/fluent.conf" <<EOF
# Receive events from 24224/tcp
# This is used by log forwarding and the fluent-cat command
<source>
  @type forward
  port 24224
</source>

# Match events tagged with "app.*"
<match app.*>
  @type file
  path         /fluentd/log/app.*.log
  symlink_path /fluentd/log/app.log
  append       true
  time_slice_format %Y%m%d
  time_slice_wait   1m
  time_format       %Y%m%dT%H%M%S%z
</match>
EOF

configは docs.fluentd.org をベースに雑に書きました。

$ docker run -it --rm \
--name docker-fluent-logger \
-p 24224:24224 -p 24224:24224/udp \
-v $(pwd)/log:/fluentd/log \
-v $(pwd)/etc:/fluentd/etc \
-e FLUENTD_CONF=fluent.conf \
fluent/fluentd:latest

Android

$ grep fluent app/build.gradle
  // https://mvnrepository.com/artifact/org.fluentd/fluent-logger
  api group: 'org.fluentd', name: 'fluent-logger', version: '0.3.2'
$ grep uses-permission app/src/main/AndroidManifest.xml
  <uses-permission android:name="android.permission.INTERNET"/>
package com.example.fluentloggertest;

import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;

import org.fluentd.logger.FluentLogger;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                new Thread() {
                    @Override
                    public void run() {
                        FluentLogger LOG = FluentLogger.getLogger("app", "fluentdのDockerコンテナが動いているマシンIP", 24224);
                        LOG.log("action", "MainActivity", "onClickFab"); // こんな感じ?
                    }
                }.start();
            }
        });
    }
...

プロジェクトテンプレから、デフォルトでFabがあるものを使っています。

logを確認

↑のアプリでFabをクリックすると、、

$ tail -f ./log/app/*
==> ./log/app.foo-bar.log <==
2018-09-20T01:34:26+00:00   app.action  {"MainActivity":"onClickFab"}
2018-09-20T01:34:28+00:00   app.action  {"MainActivity":"onClickFab"}

こんな感じでログが保存されます! (あー、この app.log のシンボリックリンクはコンテナ内のpathですね…)

超雑ですが、以上です。m(_ _)m

Androidデバイスでディスプレイ解像度変更

前回↓のような記事を書いた通り(?)、私はディスプレイを広く使いたくてしょうがない人間です。 sashimi4.hatenablog.com

そしてAndroidバイスに於いても解像度を変更する手段を発見したので記します。

Android開発環境(adb環境)をセットアップ

省きます。 For geek とつけたのはそういう意味です。 なお、 adbさえあればいいミニマルな環境構築方法は他の方もよく紹介されていますので、ググっていただければ簡単に見つかるかと思います。

adbコマンド実行

規定(≒現在)の解像度を確認。

$ adb shell wm size
Physical size: 720x1280

好みの解像度を設定。 & 任意解像度でオーバーライド出来ているか確認。

$ adb shell wm size 1080x1920

~

$ adb shell wm size
Physical size: 720x1280
Override size: 1080x1920

この端末は4Kは無理でした 😇

$ adb shell wm size 2160x3840

~

$ adb shell wm size
Physical size: 720x1280
Override size: 1440x2560

こんな感じで弄れます。

使ってみたメモ

  • 本変更は端末を再起動しても持続されるようです。
  • IME設定でキーの高さを最大にすると操作性が上がります。
  • 画面の隅の方をタッチすると、タッチ補正により意図しない座標がタッチされることがありました。おそらくdpiの変更により補正条件に悪影響が出ているのでしょうかね…

とりあえず以上です。

コマンドラインからMacOSの画面解像度を変更する

DisplayMenuとかQuickResといったGUIアプリも有るし持っているんだけど、コマンドラインからいじりたくなったのでメモ。

Installation

$   brew install homebrew/cask/cscreen
==> brew cask install homebrew/cask/cscreen
==> Satisfying dependencies
==> Downloading http://www.pyehouse.com/wp-content/uploads/2012/09/cscreenIntel.dmg
######################################################################## 100.0%
==> Verifying checksum for Cask cscreen
==> Installing Cask cscreen
==> Linking Binary 'cscreen' to '/usr/local/bin/cscreen'.
🍺  cscreen was successfully installed!

Usage

Show display list

$ cscreen -l
DisplayID  Index     Depth     Width     Height  Refresh
 4280a40       1        32      2560       1600     0
use -h to display all usage options

Show available settings for display

$ cscreen -s1 -v
DisplayID  Index     Depth     Width     Height  Refresh
 4280a40       1        32      2560       1600     0
 4280a40       2        32      1280        800     0
 4280a40       3        32      2048       1280     0
 4280a40       4        32      1650       1050     0
 4280a40       5        32      1440        900     0
 4280a40       6        32      1152        720     0
 4280a40       7        32       840        524     0
 4280a40       8        32      1024        768     0
 4280a40       9        32       800        600     0
 4280a40      10        32       640        480     0
use -h to display all usage options

※ ディスプレイが1つしか無いので -s1 は省略可。

MacBook Pro内蔵のRetinaディスプレイなんですが、リフレッシュレートが見えませんね... :thinking_face:

Set display as you like!

$ cscreen -s1 -x 2560 -y 1600

私はメインでMacBook Pro (Retina, 13-inch, Early 2015)を使っているので、上記のコマンドでDotByDot表示できます。

その他は -d オプションでDepthや -r オプションでRefresh rateを設定できるようです。 詳しくは -h オプションでヘルプを見ると良いと思います。

比率が合うわけない 1920x1080 とか、その他中途半端な値等を指定すると、 cscreen -s1 -v で表示された中の近似値が選ばれるみたい? ちなみにドット数を超えた解像度は設定できない模様。他のGUIアプリだと設定できるんですけどね…。

以上です。

さくらのクラウドでVPN(L2TP/IPsec PSK)サーバを簡易構築

利用ツール/技術

手順

# 注意
- 本手順を適用すると、サービス利用料が発生します。
- 一身上の都合により英語版コントロールパネルを利用しています。
- 本スクリプトの利用により損害が発生しても、私は一切の責任を負いかねます。

※ 会員登録方法等については省略します。詳しくは下記リンク先をご覧ください。 cloud.sakura.ad.jp

今回は、設定を自動化するためのスクリプトを用意しました。
このスクリプトさくらのクラウド の "スタートアップスクリプト" 機能に通すことで設定を自動化することができます。

スタートアップスクリプト機能とは、サーバ起動時に任意スクリプトを実行し、 パッケージのインストールや各種設定作業の自動化などを簡単に実現できる機能です。 本手順では、さくらのクラウド側で用意されているものではなく、VPNサーバのセットアップ用のカスタムスクリプトを登録して使います。

さくらのクラウドは時間課金ですので、 設定を自動化 して、 使いたいときに立て使わないときは削除 すればコスト面もうれしいですねっ。

スタートアップスクリプトを登録

さくらのクラウド コントロールパネルにログインします。

f:id:sashimi4:20180423204927p:plain IaaSを選択します。

f:id:sashimi4:20180423205551p:plain 設定 > Script の順に開きます。

f:id:sashimi4:20180423210247p:plain ↓のスクリプトを↑の画像のようにコピペします。


以上でスクリプトの登録は完了です。

サーバを作成(スタートアップスクリプト実行)

f:id:sashimi4:20180423210514p:plain サーバを作成します。

f:id:sashimi4:20180423210558p:plain デフォルトでSimple modeにチェックが入っていますが、 必ず外します

スペックは好みに設定して大丈夫です。 f:id:sashimi4:20180423210804p:plain Disk sourceArchive を選択し、 Ubuntu 16.04 を選択します。

f:id:sashimi4:20180423211123p:plain 次に、 Modify disk にて、先程追加したスクリプトを選択します。 すると、VPN接続の認証情報の入力フォームが表示されるので、任意のものを入力します。

後は作成ボタンを押すだけで、スクリプトが自動で設定を行います。

動作確認

f:id:sashimi4:20180423211641p:plain サーバ一覧を開き、サーバが起動していることを確認します。 (起動していなかったら起動します。)

今回はMacで動作確認をします。

f:id:sashimi4:20180423212420p:plain ネットワーク設定からVPN設定を追加します。

f:id:sashimi4:20180423212507p:plain 次にサーバのIPアドレスと、サーバ作成時に設定したユーザ名を入力します。

続いて認証設定を開き、 f:id:sashimi4:20180423212601p:plain 同じく、サーバ作成時に設定したパスワードと事前共有鍵(PSK)を入力・設定します。

後は接続ボタンを押せば繋がるはずです。

f:id:sashimi4:20180423212707p:plain 送受信できていることがわかります。

なお、好み・環境によっては、 f:id:sashimi4:20180423212310p:plain f:id:sashimi4:20180423212903p:plain Advanced から、すべてのトラフィックVPNを通すように設定しても良いでしょう。

f:id:sashimi4:20180423212932p:plain これでCMANさんなんかで確認すると、ゲートウェイIPがさくらのクラウドIPアドレスになっていることがわかります。

手順は以上です。

Have a nice VPN life!

Ubuntuのiptablesを永続化する

configファイルを作成

デフォルト設定に加えたいものがある場合

e.g.

$ sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE # 任意の設定の例
$ sudo sh -c "iptables-save > /etc/iptables.rules"

希望の設定がある場合

e.g.

$ sudo bash -c "cat >/etc/iptables.rules" <<EOF
# 以下、任意の設定の例
*filter
:INPUT ACCEPT [2426:480164]
:FORWARD ACCEPT [1902:649674]
:OUTPUT ACCEPT [2166:1094891]
:f2b-sshd - [0:0]
-A INPUT -p tcp -m multiport --dports 22 -j f2b-sshd
-A INPUT -p tcp -m multiport --dports 22 -j f2b-sshd
-A f2b-sshd -j RETURN
-A f2b-sshd -j RETURN
COMMIT
# Completed on Thu Apr 19 16:00:35 2018
# Generated by iptables-save v1.6.0 on Thu Apr 19 16:00:35 2018
*nat
:PREROUTING ACCEPT [810:66915]
:INPUT ACCEPT [58:8731]
:OUTPUT ACCEPT [19:1213]
:POSTROUTING ACCEPT [0:0]
-A POSTROUTING -o eth0 -j MASQUERADE
COMMIT
EOF

起動時の読み込み設定

$ sudo sed -ie "/^iface eth0.*$/a \  pre-up iptables-restore < /etc/iptables.rules" /etc/network/interfaces

これで eth0がupするときに設定が読み込まれます。 if名は適宜変更してください。

参考 : IptablesHowTo - Community Help Wiki