量産型エンジニアの憂鬱

きっと僕は何物にもなれない。

Nginx の Webdav でデプロイ時のみパスワードが必要な Mavenリポジトリ を立てる

前回の記事で JitPack を使えば Mavenリポジトリ を立てる必要すらないっていう話を書いたのですが、 クローズドな環境では外部サービスの利用が難しい場合があります。

そこで、今回は Nginx を使ってサクッと Mavenリポジトリ を立ててみます。
さらにリポジトリは誰でも見られるようにして、デプロイ時はパスワード認証をかけます。

CentOS 7系でやってみましょう。
ここでは、使い捨てのDockerコンテナを立ち上げて試します。

docker run --rm -it -p 80:80 centos:7 bash 

Nginxのインストール

CentOS 7 にインストールするためには、yumリポジトリを追加する必要があります。

vi /etc/yum.repos.d/nginx.repo
[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/mainline/centos/7/$basearch/
gpgcheck=0
enabled=1

リポジトリを追加したら yum コマンドでインストールしましょう。

yum install -y nginx

インストールをしたら起動します。 nginx  コマンドはバックグラウンドで実行されます。

nginx 

Dockerコンテナでなく実際のサーバーで実行する場合は

systemctl start nginx

で開始することができます。

起動した状態で、ブラウザで以下にアクセスしてみましょう。

http://localhost/

以下のような画面が表示されれば Nginx が起動しています。

f:id:duck8823:20171224095744p:plain

確認が済んだらプロセスを終了します。
コンテナで起動している場合は pid を取得して kill します。

ps x | grep [n]ginx | awk '{print $1}' | xargs kill

実際のサーバーの場合は以下で終了することができます。

systemctl stop nginx

Basic認証できるようにする

ライブラリの利用時は認証せず、デプロイ時にはユーザー名とパスワードで認証するようにします。
Nginx でBasic認証を利用する場合ために httpd-tools をインストールします。

yum install -y httpd-tools

Basic認証のユーザーファイルを作成しましょう。

htpasswd -c /etc/nginx/.htpasswd ユーザー名

画面の指示に従ってパスワードを入力します。

作成された /etc/nginx/.htpasswdBasic認証用のファイルとなります。

Mavenリポジトリの設定を追加する

Maven用の Webdav 設定を追加します。

vi /etc/nginx/conf.d/maven.conf
server {
  # localhost でアクセスすると default.conf が優先される。
  # 以下の設定で 127.0.0.1 にアクセスするとこちらが優先される。 
  listen 80 default;
  server_name _;

  # アクセスする際のパス 以下の場合 http://サーバー名/maven/
  location /maven/ {
    
    # この場合 /var + /mavan/ で Mavenリポジトリの root は /var/maven
    root /var;

    # デプロイ時の一時ディレクトリ
    client_body_temp_path /tmp/maven;
    # ディレクトリを作成するか
    create_full_put_path on;

    # ブラウザでディレクトリにアクセスした際に一覧が表示されるようにする
    autoindex on;
    autoindex_exact_size off;
    autoindex_localtime on;

    # webav を有効にする
    dav_access group:r all:r;
    dav_methods PUT DELETE MKCOL COPY MOVE;

    # GET 以外に制限(Basic認証)をかける
    limit_except GET {
      auth_basic "Maven Repo";
      auth_basic_user_file "/etc/nginx/.htpasswd";
    }
  }
}

設定を作成したら、必要なディレクトリを作成しましょう。
プロセスは nginx ユーザーで実行されるため、作成したディレクトリのオーナーを nginx に変更しておきます。

mkdir -p /var/maven /tmp/maven
chown nginx:nginx /var/maven /tmp/maven
nginx

/etc/nginx/conf.d/default.confserver_name localhost; が設定されているため、 http://localhost/maven/ にアクセスしても default.conf が適用されます。 よって、 404 エラーが返ってきてしまいます。

http://127.0.0.1/maven/ にアクセスすることで /etc/nginx/conf.d/macen.conf が適用され、 /var/maven/ 以下を参照することができます。

ライブラリをデプロイする

Maven

デプロイしたいプロジェクトの pom.xml を編集します。

distributionManagementリポジトリ情報を追記します。
Dockerホストからデプロイする場合、 http://127.0.0.1/maven/ です。
webdav を利用してアップロードするので、 dav:http://127.0.0.1/maven/ となります。

<project>
    ...
    <distributionManagement>
        <repository>
            <id>docker.maven</id>
            <name>Maven Repo</name>
            <url>dav:http://127.0.0.1/maven/</url>
        </repository>
    </distributionManagement>
    ...
    <build>
        <extensions>
            <extension>
                <groupId>org.apache.maven.wagon</groupId>
                <artifactId>wagon-webdav-jackrabbit</artifactId>
                <version>3.0.0</version>
            </extension>
        </extensions>
        ...
    </build>
    ...
</project>

webadv で GET 以外のメソッドにはBasic認証をかけているので、 この状態では ` 401 Unauthorized デとなりプロイできません。

認証情報は $HOME/.m2/settings.xml に記述します。

<settings>
    <servers>
        <server>
            <!-- pom.xml の distributionManagement/repositoryの id と一致させる -->
            <id>docker.maven</id>
            <username>ユーザー名</username>
            <password>パスワード</password>
        </server>
    </servers>
</settings>

下記のコマンドでデプロイすることができます。

mvn deploy

Gradle

build.gradle を編集します。

apply plugin: 'maven'

configurations {
    deployerJars
}

repositories {
    mavenCentral()
}

dependencies {
    deployerJars 'org.apache.maven.wagon:wagon-webdav-jackrabbit:3.0.0'
}

uploadArchives {
    repositories.mavenDeployer {
        configuration = configurations.deployerJars
        repository(url: "http://127.0.0.1/maven/") {
            authentication(userName: "ユーザー名", password: "パスワード")
        }
    }
}

コマンドは以下の通りです。

./gradlew uploadArchives

これによりJarファイル や pom.xml も自動的に生成され、サーバーにアップロードされます。

ライブラリの利用

デプロイ後にブラウザでアクセスしてみると、ディレクトリが増えていると思います。

f:id:duck8823:20171224100130p:plain

Mavenリポジトリは URL を指定するだけで追加できます。
リポジトリを追加したら、通常のライブラリと同じように dependency を記述します。

Maven

<project>
    <repositories>
        ...
        <repository>
            <id>maven.docker</id>
            <name>Maven Repo</name>
            <url>http://127.0.0.1/maven/</url>
        </repository>
    </repositories>
    <dependencies>
        <dependency>
            <groupId>com.example</groupId>
            <artifactId>example-maven</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
    </dependencies>
</project>

Gradle

repositories {
    maven { url 'http://127.0.0.1/maven/' }
}
dependenciea {
    implementation 'com.example:example-maven:0.0.1-SNAPSHOT'
}

おわり

Nginx で webdav を利用して Mavenリポジトリ を立てて、Basic認証でデプロイ(アップロード)を制限する方法を記述しました。
社内ネットワークでライブラリを配布したいけど、webdavでなんでもデプロイされるのはちょっと...ていう場合には使えるかもしれないです。かなりニッチ...

お試しが終わったらコンテナを終了させましょう。
最初の docker コマンドで --rm オプションを指定しているので、コンテナから抜ければ自動的に削除されます。
まっさらな環境で試す場合に Docker 非常に便利ですね。

exit

Mavenライブラリ配布の自動化をやめた話

この記事は モバイル 自動化/自動テスト Advent Calendar 2017 の19日目です。

皆さんは、JavaやKotlinで作成したライブラリ、加えてGradleプラグインの配布について悩んだことはないでしょうか。
クロスプラットフォームな開発環境も増えましたが、Android開発といえば Java や Kotlin といった JVM言語が主流です。
もちろんライブラリもこれらの言語で作成されます。 

一般的に、JavaやKotlinで作成されたライブラリの配布はMavenリポジトリを介して行われます。

Maven公式のリポジトリMaven Central です。 その他、 Bintray jcenter も有名なリポジトリです。 よってライブラリを作成して公開したい場合は上記サーバーにデプロイすることになります。 しかし、Maven Central にライブラリを登録するまでにはアカウント作成など面倒な手順を踏むことになります。

今は21世紀なのでサクッとライブラリを作成して公開したいですね。

Mavenリポジトリを立てて公開する

Mavenリポジトリは、誰でも立てることができます。 http(s) でアクセスできるサーバーを立てるだけです。

自分はこれまで、GitHubソースコードを管理し、レンタルサーバーリポジトリを立てて公開していました。 また、ソースコードの更新からリポジトリへの公開は自動化していました。

自分の場合、MavenリポジトリとJenkinsサーバーを同一にしています。 GitHubで master ブランチが更新された場合、 Jenkinsが mvn deploy を実行するだけです。 このコマンドではローカルのMavenリポジトリに配置されますが、 HTTPサーバーの静的コンテンツを配置するディレクトリ以下にMavenリポジトリを指定すると、 サーバーを介してライブラリを利用することができます。

<distributionManagement>
    <repository>
        <id>local</id>
        <name>local</name>
        <url>file://localhost/path/to/DocumentRoot</url>
    </repository>
</distributionManagement>

この方法ではライブラリを作るたびにJenkinsのジョブを追加しなければなりません。 また、公開できるサーバーとドメインを所有している必要があります。

そこで、GitHubのみを利用して公開する方法を探していました。

GitHub Pagesを利用して公開する

GitHubの ユーザー名.github.io リポジトリあるいは特定のブランチを maven リポジトリとして公開しちゃう方法です。 「GitHub Maven リポジトリ」で検索とこの方法がヒットします。

今回は ユーザー名.github.io リポジトリを利用して公開する方法をご紹介します。

  1. GitHub上に、ユーザー名.github.io リポジトリを作成する

  2. public_repo と user:email の権限を付与した Personal Access Token を作成する

  3. ローカルマシンの settings.xml でサーバーを設定する
    通常は $HOME/.m2/settings.xml にあります。

<settings ...>
  <servers>
    <server>
     <id>github.com</id>
     <password>取得した Personal Access Token</password>
    </server>
  </servers>
</settings>
  1. プロジェクトの pom.xml を設定する
    今回利用する site-maven-pluginMaven Plugin として提供されているので、pom.xml で設定しています。
  <profiles>
    ...
    <profile>
      <id>github</id>
      <properties>
        <!-- settings.xml で記述したサーバーの id -->
        <github.global.server>github.com</github.global.server>
      </properties>
      <distributionManagement>
        <repository>
          <id>internal.repos</id>
          <name>Temporary Repository</name>
          <!-- mvn deploy コマンドで ビルドディレクトリ/mvn 以下にデプロイするようにする -->
          <url>file://${project.build.directory}/mvn</url>
        </repository>
      </distributionManagement>
      <build>
        <plugins>
          <plugin>
            <groupId>com.github.github</groupId>
            <artifactId>site-maven-plugin</artifactId>
            <version>0.12</version>
            <configuration>
              <message>Maven artifacts for ${project.version}</message>
              <!-- GitHubにプッシュするディレクトリを ビルドディレクトリ に指定する -->
              <outputDirectory>${project.build.directory></outputDirectory>
              <!-- ビルドディレクトリには classファイル なども作成されるのでディレクトリを指定する -->
              <includes>
                <!-- mvn deploy コマンドで作成されるローカルリポジトリ -->
                <include>mvn/**/*</include>
              </includes>
              <repositoryOwner>GitHubのユーザー名</repositoryOwner>
              <repositoryName>GitHubのユーザー名.github.io</repositoryName>
              <branch>refs/heads/master</branch>
              <!-- デフォルトだと最新版の artifact で上書きしてしまうので、マージする -->
              <merge>true</merge>
            </configuration>
            <executions>
              <execution>
                <goals>
                  <goal>site</goal>
                </goals>
                <!-- maven deploy 後に実行されるようにする -->
                <phase>deploy</phase>
              </execution>
            </executions>
          </plugin>
        </plugins>
      </build>
    </profile>
  </profiles>
  1. 実行する
    実行時は上述の pom.xml で設定したプロファイルを指定します。
mvn deploy -P github
  1. 自動的に実行されるようにする
    あとはお好みの CI/CD で上述のコマンドが実行されるようにするだけです。

これによりライブラリの配布もGitHub上でできるようになりました。 ライブラリを利用する場合はリポジトリのURLとして https://ユーザー名.github.io/mvn/ を指定します。

maven-site-plugin を利用しているため、Gradleの場合は pom.xml の生成など手間が増えます。

JitPackを利用する

JitPack は少し変わったMavenリポジトリです。 Spockなどがこのリポジトリを介して配布されています。

JitPack を利用する場合、配布側でビルドする必要がありません。 JitPack上のライブラリをインポートしたタイミングで JitPack上でビルドされるのです。

GitHub上のライブラリを利用する

  1. Mavenリポジトリとして https://jitpack.io を指定する

  2. ライブラリの指定

    1. groupId を com.github.リポジトリオーナー名 にする
    2. artifactId を リポジトリ名 にする
    3. バージョンに リリースタグ または コミットハッシュ を指定する

この方法では特別ビルドを行う必要がありません。 しかし、JitPack が groupIdartifactId を書き換えてしまうので、混乱するかもしれません。 ライブラリの groupIdartifactId を それぞれ com.github.リポジトリオーナー名リポジトリ名 にしてしまうのが最も簡単でしょう。 しかし、groupId は独自のドメインを指定したい場合もあります。

JitPackでは DNS TXTレコード を利用してドメイン名を指定する方法を提供しています。 https://jitpack.io/docs/#custom-domain-name

まとめ

最近までレンタルサーバーMaven リポジトリを立てていたのですが、 できる限り管理するサーバーは減らしたいですよね。

Spockを利用する際に指定されていた Mavenリポジトリ が今まで利用したことがなかったものなので調べてみると、 とても便利だったので久しぶりにテンションがあがりました。

これまでGolangJavaScriptRubyなど比較して Javaのライブラリ配布は面倒な印象がありました。 しかし、JitPack を利用すれば配布するためのビルドする必要すらなくなりました。 また、GitHubにソースが公開されているけど配布されていないライブラリについても利用できる可能性があります。

Publicなライブラリの場合はこういったサービスを無料で利用することができて非常に便利ですね。

iOSDCで当日スタッフ & LTトークしてきました

i will blog.


こちらのiOSDCの写真をみてたんです。
…ぼくめっちゃデブやん…

鏡って2割ほど痩せて見えるんですね。気づきがありました。


さて iOSDC 2017 も終わり、日々の業務を粛々と進めております。
僕は 当日スタッフ と、 LTトーク枠で参加してきました。

LTトーク内容は 所属グループのブログ で少し紹介しているのでそちらをご覧ください。
発表前日に資料について気になって、調査したり資料を直したりしていたら朝になっていました。
発表内容に興味を持っていただいて、懇親会時に話かけていただいたりもして、やはり発表するのはいいなと思いました。
…笑いはとれなかったけど。


当日スタッフとしては、トラックDの担当でした。 トラックDはあまりセッションもなかったので、受けつけのヘルプをしていた時間の方が長かったかもしれないです。 あとは仕事ありそうなところ手伝ったり、LT前は緊張しすぎてウロウロしていました。

スタッフのみなさん本当に出来る方々ばかりで、頭があがりませんでした。 何よりみなさんが本当に楽しんでいて、それがよかった。

f:id:duck8823:20170922021214j:plain


是非来年もスタッフとして参加させていただきたいです。 トークも応募したいと思います。 それまでにダイエットします。 痩せてイケメン枠で登壇&スタッフしたいです。


普段は iOS Test Night、 Android Test Night などで運営のお手伝いをしています。こちらもどうぞ参加ください。

testnight.connpass.com

俺コンでも発表します。

orecon.connpass.com

GitHubと各種CIサービスを連携してみる

普段GitHubを使っていて、Travis CIと連携してバッヂをつけたりしています。 Pull Requestが来たときなんかも自動でビルド・テストをしてくれるので便利ですよね。

今回は、自分でサーバーを用意せず利用できるCIとGitHubを連携してみました。 今回ご紹介するのは以下のCIサービスです。


Travis CI

GitHubとの連携

https://travis-ci.org/

トップページ右上の Sign in with GitHub からログインすることができます。

f:id:duck8823:20170716225944p:plain

ログイン後、 https://travis-ci.org/profile/GitHubのアカウント名/ にアクセスすると、Publicなリポジトリ一覧が表示されます。

f:id:duck8823:20170716233025p:plain

連携したいリポジトリf:id:duck8823:20170716233225p:plain に設定すると連携できます。

リポジトリの設定

リポジトリの設定は、リポジトリ名横の歯車マークをクリックします。 f:id:duck8823:20170716234907p:plain

以下のような設定項目が表示されます。 f:id:duck8823:20170717001102p:plain

.travis.yml の記述

詳しくは https://docs.travis-ci.com/user/customizing-the-build/ を見ながら設定しましょう。

以下は ruby プロジェクト(https://github.com/duck8823/danger-slack)の例

language: ruby
cache:
  directories:
    - bundle

rvm:
  - 2.3.1

before_script:
    - bundle exec danger

script:
    - bundle exec rake spec

Travis CIはOS X のイメージも無料で利用することができます。
設定項目も必要最低限で1画面でまとまっていて、
スケジュールも設定できる。
個人のOSSなんかは Travis CI を使うと便利そうですね。



Circle CI

GitHubとの連携

Circle CI のサインアップページから Start with GitHub をクリックし、ログインします。

ログイン後に Settings > Projects に進むとリポジトリ一覧が表示されます。

f:id:duck8823:20170717015852p:plain

連携したいリポジトリの歯車マークを押し、 表示されたページで Follow Project をクリックしましょう。

f:id:duck8823:20170717020024p:plain

リポジトリの設定

follow すると、様々な設定が可能になります。 Settings > Projects > 設定したいプロジェクトの歯車マーク

f:id:duck8823:20170717020604p:plain

環境変数は BUILD SETTINGS の Environment Variables で設定できます。

circle.yml の設定

以下は ruby プロジェクト(https://github.com/duck8823/danger-slack)の例

machine:
  ruby:
    version: 2.3.1

test:
  pre:
    - bundle exec danger
  override:
    - bundle exec rake spec

circle ci では osx を利用するには有料プランを選択する必要があります。



Wercker

GitHub との連携

https://app.wercker.com にアクセスするとログインページに遷移します。
LOG IN WITH GITHUB をクリックしてログインしましょう。

ログイン後、ヘッダーに表示されている Create をクリックし、 Application を選択します。
f:id:duck8823:20170717025058p:plain

リポジトリ一覧が表示されるので、連携したいプロジェクトを選択肢し、 Use selected repo をクリックします。 f:id:duck8823:20170717025259p:plain

続いてアクセス方法を選択して Next step をクリックします。 f:id:duck8823:20170717025428p:plain

リポジトリの設定

ヘッダーメニューの Applications をクリックし、一覧から設定したいリポジトリを選択します。 Environment を選択すると環境変数を設定することができます。 TokenなどはProtectedにチェックをつけるといいでしょう。

f:id:duck8823:20170717030053p:plain

werker.yml

box: ruby
build:
    steps:
        - bundle-install
        - script:
            name: test
            code: bundle exec rake spec

Werkerは、パイプラインを作成することも可能です。
プライベートリポジトリも無料で利用できます。
残念ながら今のところ Danger は利用できません。

Dangerプラグインを作ろう

Danger

最近流行りつつある? Danger です。 Pull Request に対して色々チェックできるようになります。

先月末にポツポツと紹介記事が出ました。

Dangerとても便利です。 他のプロジェクトでも使えそうなルールなどはプラグインにして使い回ししたいですね。 なのでプラグインを作ってみます。

プラグイン作成も公式ページで詳しく乗ってます。 http://danger.systems/guides/creating_your_first_plugin.html

Dangerのインストー

$ gem install danger

プラグインの初期化

プラグイン名にハイフン(-)は推奨されていません。 単語はアンダースコア(_)で区切りましょう。

$ danger plugins create <plugin_name>

上記コマンドを実行すると、 git config からユーザ名とメールアドレスを取得してよしなに初期化してくれます。

danger-plugin_name/
├── Gemfile
├── Guardfile
├── LICENSE.txt
├── README.md
├── Rakefile
├── danger-plugin_name.gemspec
├── lib
│   ├── danger_plugin.rb
│   ├── danger_plugin_name.rb
│   └── plugin_name
│       ├── gem_version.rb
│       └── plugin.rb
└── spec
    ├── plugin_name_spec.rb
    └── spec_helper.rb

3 directories, 12 files

作成されたディレクトリへ移動します。 danger-<plugin_name> というディレクトリが作成されています。

$ cd danger-plugin_name

テストなどに必要なGemをインストールしておきます。

$ bundle install --path vendor/bundle

また、テストを実行する際にリポジトリの upstream の情報が必要になります。リポジトリを作成して upstream を登録しておきます。

$ git remote add origin <上流のリポジトリURL>
$ git push -u origin master

メソッドを作る

簡単なプラグインを作る場合は lib/<plugin_name>/plugin.rb を変更するだけでできます。

say というメソッドを追加してみます。

lib/<plugin_name>/plugin.rb

diff --git a/lib/plugin_name/plugin.rb b/lib/plugin_name/plugin.rb
index eeda801..cea9600 100644
--- a/lib/plugin_name/plugin.rb
+++ b/lib/plugin_name/plugin.rb
@@ -29,5 +29,12 @@ module Danger
     def warn_on_mondays
       warn 'Trying to merge code on a Monday' if Date.today.wday == 1
     end
+
+    # A method that messages 'Hello Danger.'
+    # @return [Array<String>]
+    #
+    def say
+      message 'Hello Danger.'
+    end
   end
 end

テストを書く

プラグインを作ったらテストをかきましょう。

spec/<plugin_name>_spec.rb

diff --git a/spec/plugin_name_spec.rb b/spec/plugin_name_spec.rb
index 484958f..a3ec8ad 100644
--- a/spec/plugin_name_spec.rb
+++ b/spec/plugin_name_spec.rb
@@ -36,6 +36,11 @@ module Danger
         expect(@dangerfile.status_report[:warnings]).to eq([])
       end

+      it "Messages" do
+        @my_plugin.say
+
+        expect(@dangerfile.status_report[:messages]).to eq(["Hello Danger."])
+      end
     end
   end
 end

テストを書いたら実行します。 以下のコマンドで rspec と RuboCop が実行されます。 RuboCopでは色々指摘されると思うので必要あれば修正してください。

$ bundle exec rake spec
....

Finished in 0.06292 seconds (files took 0.4549 seconds to load)
4 examples, 0 failures

Running RuboCop...
Inspecting 6 files
...CCC

Offenses:

lib/plugin_name/plugin.rb:20:1: C: Extra empty line detected at class body beginning.
lib/plugin_name/plugin.rb:30:12: C: Prefer double-quoted strings unless you need single quotes to avoid extra backslashes for escaping.
      warn 'Trying to merge code on a Monday' if Date.today.wday == 1
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
lib/plugin_name/plugin.rb:37:15: C: Prefer double-quoted strings unless you need single quotes to avoid extra backslashes for escaping.
      message 'Hello Danger.'
              ^^^^^^^^^^^^^^^
spec/plugin_name_spec.rb:42:1: C: Tab detected.
        expect(@dangerfile.status_report[:messages]).to eq(["Hello Danger."])
^^
spec/plugin_name_spec.rb:42:3: C: Inconsistent indentation detected.
        expect(@dangerfile.status_report[:messages]).to eq(["Hello Danger."])
  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
spec/spec_helper.rb:12:23: C: Prefer double-quoted strings unless you need single quotes to avoid extra backslashes for escaping.
if `git remote -v` == ''
                      ^^

6 files inspected, 6 offenses detected
RuboCop failed!

動作確認したい

これがとてもめんどくさいです。 Pull Requestを用意する必要があります。 確認の場合、既にCloseされている Pull Request でも問題ありません。 個人で開発しているライブラリのリポジトリで確認してみました。

$ git clone https://github.com/duck8823/Slack-RTM-Bot.git
$ cd Slack-RTM-Bot

Dangerとプラグインを使えるようにGemfileを用意してやります。 プラグイン:path を指定することで作成途中のもので試すことができます。

Gemfile

source "https://rubygems.org"

gem 'danger'
gem 'danger-plugin_name', :path => 'path/to/danger-plugin_name'

bundleインストー

$ bundle install --path vendor/bundle

これで準備ができました。

続いて Dangerfile を用意しましょう。 プロジェクト直下に置く必要があります。

Dangerfile

plugin_name.say

Dangerfile ではプラグインインスタンスが利用できます。 インスタンス名は以下の命名規則に従います。 http://danger.systems/guides/creating_your_first_plugin.html#tech-specs

確認は、

$ bundle exec danger local

で可能です。

また、

$ bundle exec danger pr https://github.com/duck8823/Slack-RTM-Bot/pull/20

のようにすることで特定の Pull Request に対して確認することも可能です。

local または pr で実行した場合、以下のような内容が表示されます。

Info:

+------------------+----------------------------------------------------------------------------------------------+
|                                                  Danger v5.3.3                                                  |
|                                                 DSL Attributes                                                  |
+------------------+----------------------------------------------------------------------------------------------+
|    status_report |                                                                                              |
| violation_report |                                                                                              |
|              --- | ---                                                                                          |
|     scm_provider | github                                                                                       |
|             diff | diff --git a/lib/Slack/RTM/Bot/Client.pm b/lib/Slack/RTM/Bot/Client.pm                       |
|                  | index 9b86bc6..4c8ff0d 100644                                                                |
|                  | --- a/lib/Slack/RTM/Bot/Client.pm                                                            |
|                  | +++ b/lib/Slack/RTM/Bot/Client.pm                                                            |
|                  | @@ -248,7 +248,7 @@ sub _listen {                                                            |
|                   |       $channel = $self->find_channel_or_group_name($buffer_obj->{channel});
                        |   $channel ||= $self->_refetch_channel_name($buffer_obj->{channel});
                          | $channel ||= $self->_refetch_group_name($buffer_obj->{channel});
       |           | -      die "There are no channels or groups of such id: $buffer_obj->{user}" unless $user;
 |                 | +      die "There are no channels or groups of such id: $buffer_obj->{channel}" unless $channel;
                                                                                          |
                                             |RTM::Bot::Response->new(
                                                                   |
|                  | @@ -273,4 +273,4 @@ ACTION: for my $action(@{$self->{actions}}){                             |
                                                                                          |
                                                                                          |
                                                                                            |
|                  | -1;                                                                                          |
|                  | \ No newline at end of file                                                                  |
|                  | +1;                                                                                          |
|      added_files | #<Danger::FileList:0x007fe123a9d678>                                                         |
|    deleted_files | #<Danger::FileList:0x007fe123a9cea8>                                                         |
|   modified_files | lib/Slack/RTM/Bot/Client.pm                                                                  |
|    renamed_files | []                                                                                           |
|    lines_of_code | 4                                                                                            |
|        deletions | 2                                                                                            |
|       insertions | 2                                                                                            |
|          commits | b4e3b9fd228752e3c7fffd7c9cb5258959f281a2                                                     |
|              api | Octokit::Client                                                                              |
|          pr_json | [Skipped JSON]                                                                               |
|          mr_json | [Skipped JSON]                                                                               |
|          pr_diff | [Skipped Diff]                                                                               |
|           review | #<Danger::RequestSources::GitHubSource::Review:0x007fe12398a498>                             |
|         pr_title | bugfix channel or group not found (was checking for user instead)                            |
|          pr_body |                                                                                              |
|        pr_author | dada                                                                                         |
|        pr_labels | []                                                                                           |
|  branch_for_base | master                                                                                       |
|  branch_for_head | master                                                                                       |
|      base_commit | 947aeeaf5443494d3e7895292662b617b369bf05                                                     |
|      head_commit | b4e3b9fd228752e3c7fffd7c9cb5258959f281a2                                                     |
|         mr_title | bugfix channel or group not found (was checking for user instead)                            |
|          mr_body |                                                                                              |
|        mr_author | dada                                                                                         |
|        mr_labels | []                                                                                           |
|              say | Violation Hello Danger. { file: , line:  }                                                   |
|     my_attribute |                                                                                              |
|  warn_on_mondays |                                                                                              |
|              --- | ---                                                                                          |
|              SCM | Danger::GitRepo                                                                              |
|           Source | Danger::LocalGitRepo                                                                         |
|         Requests | Danger::RequestSources::GitHub                                                               |
|      Base Commit | commit 947aeeaf5443494d3e7895292662b617b369bf05                                              |
|                  | Author: shunsuke maeda <duck8823@gmail.com>                                                  |
|                  | Date:   Sat Apr 8 12:30:31 2017 +0900                                                        |
|                  |                                                                                              |
|                  |     Checking in changes prior to tagging of version 1.04.                                    |
|                  |                                                                                              |
|                  |     Changelog diff is:                                                                       |
|                  |                                                                                              |
|                  |     diff --git a/Changes b/Changes                                                           |
|                  |     index 311b411..e8e2578 100644                                                            |
|                  |     --- a/Changes                                                                            |
|                  |     +++ b/Changes                                                                            |
|                  |     @@ -2,6 +2,10 @@ Revision history for Perl extension Slack-RTM-Bot                       |
|                  |                                                                                              |
|                  |      {{$NEXT}}                                                                               |
|                  |                                                                                              |
|                  |     +1.04 2017-04-08T03:30:26Z                                                               |
|                  |     +                                                                                        |
|                  |     +   - Fix dying when response to new channel/group                                       |
|                  |     +                                                                                        |
|                  |      1.03 2017-04-05T23:11:07Z                                                               |
|                  |                                                                                              |
|                  |         - Fix a bug that zombies spawn when stop_RTM                                         |
|      Head Commit | commit b4e3b9fd228752e3c7fffd7c9cb5258959f281a2                                              |
|                  | Author: dada <dada@perl.it>                                                                  |
|                  | Date:   Fri May 19 18:07:20 2017 +0200                                                       |
|                  |                                                                                              |
|                  |     bugfix channel or group not found (was checking for user instead)                        |
+------------------+----------------------------------------------------------------------------------------------+

Results:

Messages:
- [ ] Hello Danger.
- [ ] Hello Danger.

Info: ではDangerが取得した Git や GitHub などの情報を閲覧することができます。また、プラグイン内の 引数がないメソッドを自動的に実行し 、結果を表示してくれます。

Results: では messagefail などの DSL を実行した結果を表示してくれます。

上記の例で Hello Danger. が重複して表示されているのは、

  • Dangerfileでの呼び出し
  • Info: で表示するための呼び出し

で二回実行されたからです。

引数なしのメソッドでPull Requestをクローズするなどの処理を実装した場合、local または pr モードで意図しないメソッド呼び出しが起こる場合があるので気をつけましょう。 (記事を書いた時点でDangerのバージョンは 5.3.3 )

また、実行時に

Octokit::TooManyRequests: GET https://api.github.com/repos/duck8823/Slack-RTM-Bot/issues/20: 403 - API rate limit exceeded for

というようなエラーが出た場合は、 環境変数DANGER_GITHUB_API_TOKEN を設定してやることで回避できる場合があります。

利用するトークンは https://github.com/settings/tokens で作成することができます。

READMEの自動生成

Dangerは README を自動的に生成するコマンドも用意してくれています。

$ bundle exec danger plugins readme
### plugin_name

This is your plugin class. Any attributes or methods you expose here will
be available from within your Dangerfile.

To be published on the Danger plugins site, you will need to have
the public interface documented. Danger uses [YARD](http://yardoc.org/)
for generating documentation from your plugin source, and you can verify
by running `danger plugins lint` or `bundle exec rake spec`.

You should replace these comments with a public description of your library.

<blockquote>Ensure people are well warned about merging on Mondays
  <pre>
my_plugin.warn_on_mondays</pre>
</blockquote>



#### Attributes

`my_attribute` - An attribute that you can read/write from your Dangerfile




#### Methods

`warn_on_mondays` - A method that you can call from your Dangerfile

`say` - A method that messages 'Hello Danger.'

plugins readme コマンドは lib/<plugin_name>/plugin.rb のドキュメント(YARD形式)を元にREADMEを作成します。

正しくドキュメントが記述されているかどうかは、

$ bundle exec danger plugins lint

で調べることができます。 例えば、追加したメソッドにドキュメントが存在しない場合には以下のようなエラーが出ます。

[!] Failed


Errors
  - Description - say (method):
    - You should include a description for your method.
    - @see - https://github.com/dbgrandi/danger-prose/blob/v2.0.0/lib/danger_plugin.rb#L40#-L41
    - /private/tmp/danger-plugin_name/lib/plugin_name/plugin.rb:33


Warnings
  - Return Type - say (method):
    - If the function has no useful return value, use ` @return  [void]` - this will be ignored by documentation generators.
    - @see - https://github.com/dbgrandi/danger-prose/blob/v2.0.0/lib/danger_plugin.rb#L46
    - /private/tmp/danger-plugin_name/lib/plugin_name/plugin.rb:33

Failing due to errors

記述に従ってドキュメントを整備しましょう。

公開しましょう

良いプラグインができたら公開しましょう。

公開前に、 danger-<plugin_name>.gemspec を確認しましょう。

danger-<plugin_name>.gemspec

# coding: utf-8
lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'plugin_name/gem_version.rb'

Gem::Specification.new do |spec|
  spec.name          = 'danger-plugin_name'
  spec.version       = PluginName::VERSION
  spec.authors       = ['shunsuke maeda']
  spec.email         = ['duck8823@gmail.com']
  spec.description   = %q{A short description of danger-plugin_name.}
  spec.summary       = %q{A longer description of danger-plugin_name.}
  spec.homepage      = 'https://github.com/duck8823/danger-plugin_name'
  spec.license       = 'MIT'
.
.
.

githubアカウント名と git config の user.name が異なる場合などは適宜変更してください。 spec.homepage のアカウント名部分に user.name が使われるのでスペースがある場合などは gem に登録できません。

修正が完了したら

$ rake release

RubyGems に登録することができます。

さらに Danger本家サイト にプラグインとして紹介してもらいましょう。 以下にプラグインを追加して Merge Request を出せば、光速でマージしてくれます。

https://gitlab.com/danger-systems/danger.systems/blob/master/plugins.json

その後、サイトはそのうち更新されます。

plugins コマンドでも確認できます。

$ danger plugins
Downloading Plugins list...

Available Danger Plugins:

-> danger-prose
     A danger plugin for working with bodies of markdown prose.
     - Gem:     danger-prose
     - URL:     https://github.com/dbgrandi/danger-prose

-> danger-android_lint
     Lint files of a gradle based Android project. This is done using the Android's Lint tool. Results are passed out as tables in markdown.
     - Gem:     danger-android_lint
     - URL:     https://github.com/loadsmart/danger-android_lint
・
・
・

公開したプラグイン

僕が作ったプラグインも掲載されています。 http://danger.systems/plugins/slack.html

message や fail などの内容をSlackに通知するプラグインです。 DangerはGitHub上に HTML 形式でコメントしてくれるので、 GitHub の Slack 連携では HTML は表現されません。 対策としてHTMLコメントでそれぞれの数を表示してくれているのですが微妙な感じ。

f:id:duck8823:20170711164521p:plain

プラグインでは内容も表示し、色によって error などわかるようにしました。

f:id:duck8823:20170711164536p:plain

地味にチャンネルやメンバー一覧を取得するメソッドも用意しています。 サクッと指摘内容をSlackに通知したい場合は使ってみてください。 https://github.com/duck8823/danger-slack