のらねこの気まま暮らし

技術系だけど、Qiita向きではないポエムとかを書くめったに更新されないヤツ

#yapcasia 2013 に参加しました

9/20, 9/21 とYAPC::ASIA 2013に参加してきました。
参加するのは3回目で、初のLTにも挑戦させていただきました。

備忘録はあとでまとめるとして、ざっくり所感とか次の一年に思うトコロとかを書き留めようかなと。
あとブログ書くまでがYAPCなので。あとブログ書くまでがYAPCなので。


今年はPerlの話題が多かったですね。去年は「あれ、これPerlのカンファレンスだよね?!」ってくらい周辺技術の話が多かった気がするんですけれど(笑)


セッションで増えたなって思ったのは、Perlのテスト、とGit周りの開発フロー/環境の話。


Perlのテストは、「50ms or die」とは言わずとも、どこも時間短縮したいようで。
中規模チャットサービスの運用事例で紹介されたApp::Prove::Plugin::MySQLPoolを使って一つのサーバでMySQLを並列化するっていうのは気になった。
http://yapcasia.org/2013/talk/show/767463b0-d8fd-11e2-971a-72936aeab6a4で紹介されてたテストの手法、並列化や実行手順はもろもろ参考にしたいところでした。

また、PhantomJSやSeleniumを使ってUIをテストするという話もちらほら。
この辺りは概要だけ撫でて詳しく全然追っていなかったので、ちゃんと追いたい。


Gitに関しては、開発フロー、ブランチ戦略とか、各所で微妙に異なる方法を採用してる。
git-flowとかの影響もありつつ、それだけじゃ賄えないよね。だからうちはこうしようとおもう、みたいなものがあるっぽい。


今回のベストトークショーは二年連続しての @ さんが受賞。おめでとうございます!

Mojoliciousのトークは(一年半くらいまともに触ってない)僕としてはいろいろ忘れてたコトを思い出せてよかった。
敢えてMojoのPluginや機能をModelとかで避けるのは「Web名前空間以外ではMojoに依存したくない」という思いからそういう規約を作っているとのこと。Mojo本体もバージョンアップ速いし、互換性の無いPluginを多数投入しちゃうと、移植とか大変だよね。管理大変だよね。だから、WebはMojoにしつつビジネスロジックの部分はそこから切り離せることができるようにしたい、とのことだった。

トークの後に雑談させてもらって色々と勉強になりました!


Mojoliciousといえば、WAF、WAFといえば、Amon2!!!
Amon2をテーマにしたセッションは少ない(っていうか無かった?)んですけど、いくつかのセッションの中で「Amon2使って」って言葉が聞こえてました。

弊社も使ってます( ー`дー´)キリッ
MojoもいいWAFだけどAmon2もいいWAFなのです。


前回から楽しみにしているPerl6系の話題。
それをPerl5に取り込むモジュールの紹介、今回もいろいろ発掘があったので楽しみたいトコロ。
特にhttp://search.cpan.org/~barefoot/Method-Signatures-20130505/lib/Method/Signatures.pm:Method::Signatures←個人的にはこれがアツい。
他の言語と同じような表記を持ちつつ、しかしPerlの柔軟な引数の受け渡しを残せる。

method/func とで振る舞いが異なるので、明示的にそれらの宣言を示すことができる。
funcとして期待されてるものをうっかりmethod呼び出ししちゃったりその逆だったりが、ソースをぱっと見するだけでわかるんだよ便利!

いや、`$self = shift`してるところで気づけって説はあるんだけども。
明示的にしてくれるのは嬉しいよね。


そんなこんなで語り尽くせないくらい面白い話題がありました。
あ、これメモ見ず思いつきで書いてるんで、メモみたら1周間分くらいに記事になりそうw



@こと僕は初日に初LTしてきました。
・・・まあ、なんだ。うん、次は準備をちゃんとしよう。

反省はもろもろあるけれど、改めて言いたいことは

Web APIのデータアクセスのI/Fを共通化するために書かれたモジュール

TengのI/F(とコード)を参考にしている

なのでDSLでごりごりAPIの仕様的なのを定義できる

データを利用するためのもろもろな厄介事を隠蔽できる

ビジネスロジックには本質的に「データの扱い方」を書くことができる

ってことが言いたかった。言えれば満足できた。言えなかった、後悔している。
突発的自体が発生しても穏やかに、余裕を持って対応できる大人になりたい。そう思った。

https://github.com/rymizuki/p5-Spica

bug潰したらCPANに上げる。予定。



YAPC::ASIA楽しかったです!!!
I Love Perl Communityヽ(^o^)丿ヽ(^o^)丿

お疲れ様でした!

Spicaのドキュメントの下書き

開発二部と布教用の為に下書きしてみた。

Spicaについては以下。

https://github.com/rymizuki/p5-Spica

めざしたもの

WEB APIにまつわる困ったこと

  • 構造や命名規則やフォーマットが異なる
  • リクエスト方法が異なる
  • 受け取ったデータを加工しないと再利用できない

構造や命名規則やフォーマットが異なる

  • CameCaseだったりsnake\_caseだったり、入り交じってたり
  • dateという名前が提供もとによって時間を含んだり含まなかったり
  • ミリ秒単位まであったりepochだったり
  • そもそもパラメータ名が意味不明だったり
  • JSONだったりXMLだったり


=> 自分のルールに合わせたい

  • 時間を含むなら`_at`、含まないなら`_on`
  • DateTimeモジュールで利用可能な形式で使いたい
  • パラメータ名は仕様書読まなくてもなんとなくわかるようにしたい


リクエスト方法が異なる

  • データ取得するだけなのにPOSTリクエスト
  • データをXMLシリアライズ
  • URLにパラメータ埋め込む
  • ヘッダーにはコレ入れてね


=> どれが何を要求してるのかわからなくなる

  • APIや提供元毎に同じ書式で記述したい

受け取ったデータを加工しないと再利用できない

  • XMLを解析
  • 正規表現で不要な文字列を削除
  • ミリ秒に0.001掛けて秒に直す
  • base64をデコードする


=> Controllerとかビジネスロジックにそういうのは書きたくない

  • APIのクライアント側で利用可能なデータに整形して欲しい
  • DB系モジュールのinflateの機能が欲しい
  • RowClassで拡張して複雑な処理も柔軟に対応できるようにしたい

いままでの自作APIクライアント達

  • FurlやLWPでフェッチしてくる
  • XML::SimpleやJSONモジュールでパース
  • HashRefやArrayRefをそれぞれそのままモデルやControllerへ受け渡し
  • 必要に応じて受け取り側で解析
  • どこで何やってるのかわからなくなる
  • 何がどんなデータ持ってるのかわからなくなる
  • 複数のAPIの仕様が混線して仕様書から漁ってくる
  • APIってなんだ(ゲシュタルト崩壊)

Spica

異なるリクエスト方法をまとめて定義


Spica::SpecでAPIのリクエスト方法や受け取り方を指定できる

package Servers::Spec;
use Spica::Spec::Declare;
 
client {
    name 'service';
    endpoint 'list' => '/servers', [];
    endpoint 'single' => '/service/{service_id}' => ['service_id'];
    endpoint 'update' => +{
        method   => 'PUT',
        path     => '/service/{service_id}',
        requires => ['service_id'],
    };
    columns qw(id ip hostname name environment port description roles config updateed_at created_at);
};


Spicaからclientやendpoinを指定してAPIを叩く

my $spica = Spica->new(host => 'api.servers.dev.ry-m.com');
$spica->spec('Servers::Spec');
$spica->parser('Spica::Parser::JSON');
 
my $services = $spica->fetch(service => 'list', +{}); # iterator
 
my $serivce = $spica->fetch(service => 'single', +{service_id => '0xEE763766EC2C11E2B20E9F176D21D1C2'})->next;
 
$service = $spica->fetch(service => 'update', +{
    service_id  => $serivce->id,
    description => 'Jenkinsのサービスだよ',
});

受け取ったデータのinflate


Tengと同様のI/Fでinflateを定義できる

package Servers::Spec;
use Spica::Spec::Declare;
 
use DateTimeX::Factory;
 
client {
    name 'service';
    endpoint 'single' => '/servers', [];
    columns qw(id ip hostname name environment port description roles config updateed_at created_at);
    inflate qr{_at$} => sub {
        my $value  = shift;
        return if !$value;
        return if $value eq '0000-00-00 00:00:00';
        return DateTimeX::Factory->new(time_zone => 'Asia/Tokyo')->strptime($value => '%F');
    };
};


columnの末尾に`_at`がつくものをDateTimeのオブジェクトに変換している。

my $service = $spica->fetch(service => 'list', +{})->next;
 
say $service->created_at->ymd('/');

受け取ったデータがSpicaの想定しないない構造をしていた


初期値のSpicaはデータの受取をSpica::Receiver::Iteratorが行う。
Spica::Receiver::Iteratorは受け取るデータとして`ArrayRef`を期待するが、
以下のような構造が返ってくることが在る

+{
    data => \@data,
    pager => \%pager_args,
}


そのような場合、以下の二点で対策が可能

  • Spica::Receiver::Iteratorのnewメソッドを拡張する
  • Spica::Clientにfilterを登録する

Spica::Receiver::Iteratorのnewメソッドを拡張する


下記の例では、pagerのデータを捨てているが、もちろんIterator側にpagerのメソッドを用意してもよい。

package Servers::Spec;
use Spica::Spec::Declare;
 
client {
    name 'service';
    endpoint 'list' => '/servers', [];
    columns qw(id ip hostname name environment port description roles config updateed_at created_at);
    receiver 'Servers::Receiver::Iterator';
};
 
package Servers::Receiver::Iterator;
use parent qw(Spica::Receiver::Iterator);
 
use Class::Method::Modifieres;
 
around new => sub {
    my $origin = shift;
    my $class  = shift;
    my %args   = @_;
 
    $args{data} = $args{data}{data};
 
    return $class->$origin(%args);
};
 
1;

Spica::Clientにfilterを登録する


filterは第一引数にフックポイント名、第二引数に実行するCodeRefを期待する。
フックポイントで呼び出されたCodeRefはreceiverクラスに渡すデータを返却しなければならない。
この場合はpagerを切り捨てるしか無い。

package Servers::Spec;
use Spica::Spec::Declare;
 
client {
    name 'service';
    endpoint 'list' => '/servers', [];
    columns qw(id ip hostname name environment port description roles config updateed_at created_at);
    filter before_receive => sub {
        my ($spica, $data) = @_;
        return $data->{data};
    };
};


※ フックポイント名は今後変更する可能性があります。

APIステータスコードがエラーだったら死にたい


Spica::Clientにtrigger、もしくはfilterを登録する。
triggerの登録はfilterと同様に、第一引数にフックポイント名、第二引数にCodeRefを期待する。
triggerは返り値を考慮しない点がfilterと異なる。

package Server::Spec;
use Spica::Spec::Declare;
 
client {
    name 'service';
    endpoint 'list' => '/servers', [];
    columns qw(id ip hostname name environment port description roles config updateed_at created_at);
    trigger before_receive => sub {
        my ($spica, $data) = @_;
 
        if ($data->{status} ne 'success') {
            Carp::croak('API retruned error. reason is '. $data->{reason});
        }
    };
};


※ フックポイント名は今後変更する可能性があります。

perlでWEB APIを複雑に扱いたい人向けのSpicaというモジュールを書いている話

インターネット、便利ですよね。TwitterFacebookニコニコ動画とか、みんないろんなWEBサービス使ってますよね。

一般的に使ってる人や、中にはYoutubeの動画をNoPasteみたくペタペタできる自作のWEBサービスを作ったり、IRCからニコ動の動画検索できるようにしたり。
エンジニアなら日頃の些細な面倒くさいを簡単にしちゃいたくなりますよね。

そう、WEBのエンジニアなら様々なWEB APIを使いこなしてこそです。
自分でAPI書いたり、人が作ったAPIを使ったり。もはや日常茶飯事。息をするようにAPIを叩いているはずです。


さて、PerlにはLWP::UserAgentやFurlといったAPIを叩くためのライブラリがありますね。
とあるAPIをGETで叩いてJSONを受け取りたいなんて時も、次のようにすれば一瞬です。

use Furl;
use JSON;
use Log::Minimal;

my $client = Furl->new(agent => 'Sample');
my $response = $client->get('http://example.com/json');

my $data = JSON->new->utf8->decode($response->body);

infof ddf $data;

ちょっぱや便利!


・・・しかしですね。大規模なプロジェクトやいくつものAPIを一つのプロジェクトで使いわけようとすると、さすがに生でLWPやFurlを叩くことにも限界を感じるようになるんです。
APIから取得したデータを再利用するために加工なんてしようものなら、JSONだったりXMLだったりするデータを解析してDateTimeに変換したり正規表現で書き換えたり。。。
だんだん管理する方も「これなにやってんの?」ってなってくるんですよ。たとえ自分が書いたコードでも。
ちなみに僕はこの一年の間で、8種類くらいのWEB APIをそれぞれ実装したわけです。FurlだったりLWPだったりを使って。そのたびに、独自のラッパーを用意したり、プロジェクト毎に違う書き方だったり、僕が手をいれる前の人の歴史的経緯に従属してたりして、もはやカオスなんです。
管理しきれるかこんなの\(^o^)/


ところで、みなさんORMapper使ってますか? 使っていないのであれば、Tengをおすすめします。TengはDBIx::Skinnyの良いところを残しつつ、よりシンプルにしかし柔軟に利用できるORMapperです。TengのRowクラスは非常に拡張しやすく、しかし所在が明確なので(よほど奇特なことをしない限り)直感的に実装できるスグレモノなんです。


僕はこのTengさんを愛用しながら、「あーAPIもTeng::Rowみたいに拡張しやすければ使いやすいしこんな複雑なクラスを毎度毎度毎度毎度書かなくて済むのにー」と思ったわけです。


・・・それだ(゚∀゚)


というわけで、書きました↓


p5-Spica


いや正しくは、『書いてます』。まだ開発段階で煮詰め切れてないところが多々ありますので気になる方はこっそり使ってみてもいいけど動作は保証しないよ。テスト書いてないから←


Tengをモロパクリさせてもらって、TengみたいにAPIを扱えるライブラリ、を目指そうとしたんですが、とあるかるぱちゅぱちゅぱ先生のアドバイスもあって、ちょっと方向転換中です。

基幹にMouseを使ってますが、とりあえず手っ取り早く動かしたかったから大好きなMouseを使っているだけで、それ以上に意図は無かったので、MooにするかMouse辞めるかするかもしれません。


使い方としては、READMEにある通り、適度にSchemaを書いてそいつをSpicaに食べさせて上げるとお手軽にfetchしてくれて、かつRowオブジェクトを生成してくれます。IteratorとかRowクラスはまじパクリです。

package MySchema;
use Spica::Schema::Declare;

client {
  name 'profile';
  endpoint 'single', '/profile', [qw(user_id)];
  endpoint 'search', '/users', [];
  columns qw(user_id name text);
  inflate text => \&inflate_text;
};

package main;
use Spica;

my $spica = Spica->new(
  host         => 'example.com',
  schema_class => 'MySchema',
  parser       => Spica::Parser::JSON->new,
);

my $profile = $spica->fetch('profile', 'single', +{user_id => $user_id});
isa_ok $profile => 'Row::Profile';

can $profile => qw(user_id name text);

という感じで書けます。
`endpoint`は第一引数にendpoint名、第二引数に対象APIのパス、第三引数に必須パラメータのキーを指定できます。第一引数は省略可能で、

    endpoint '/profile', [qw(user_id)]

という書き方もできます。この場合は最後に実行されたendpointしか保持されないので注意が必要です。

最近はやりのRESTFulなURLにも対応したいという場合には以下の様にしていするとよいです。

    endpoint '/profile/{user_id}', [qw(user_id)];

こう書くと、

$spica->fetch('profile', +{user => 'hogehoge'});

と実行したタイミングで、↓のようにパスが設定されます

http://example.com/profile/hogehoge

`{}`でくくったワードをfetchメソッドのパラメータのキーで置換しているのです。便利でしょ!(自画自賛


というわけで、複雑にWEB APIを扱うためのSpicaというモジュールを書いているというお話でした。意見どしどしお待ちしてます!←

PhoneGap/cordova 3.0.0rc1でpluginsを利用する

先日、「Ubuntu12.4にphonegap(cordova)をインストールしてカジュアルにネイティブアプリを作成する・・・ための環境を作る呪文」という記事を書きましたが一つ困ったことがある。

$ cordova -v
3.0.0rc1

現在zipで配布されているPhoneGapの最新バージョンは"2.9.0"なのに対し、今回インストールしたのは、"3.0.0"なのだ。
ドキュメントやアップグレードガイドもまだ整っておらず、いくつかネットの情報を漁っても2.Xとか1.Xとかの情報ばかりで、3.0を挙げているものがほとんど無かった。

……いや、僕もこのタイミングでまさかメジャーバージョンアップが入るとは思ってなかった。少し前に入れたMacbook Airのversionは2.9だったんだ。情報をちゃんと追ってなくて申し訳ない。


ともあれ、いざアプリを作ろうとして、pluginsの使い方がわからずに困っていた。
いろいろ試行錯誤した末に、ようやく光明が見えたので、記録しとく。

今回は例としてFile APIを利用するとする。

前提

  • Ubuntu 12.4 上
  • PhoneGap/cordova 3.0.0rc1
  • platfrom android(SDK, ANT設定済み)

CLIからpluginsを追加する

PhoneGap/cordova APIリポジトリは以下においてあった(これだと分かるまでに相当の時間を費やした……)ので、そこからインストールする。

https://github.com/apache:github.com/apache

$ cordova plugin add https://github.com/apache/cordova-plugin-file
$ cordova plugin add https://github.com/apache/cordova-plugin-file-transfer

config.xmlにpluginの利用を明記する

platform毎にpluginの設定をconfig.xmlに記述する必要がある。androidは以下のファイル

platforms/android/res/xml/config.xml

の、"plugins"のセクションに以下を追加。

<plugin name="File" value="org.apache.cordova.FileUtils" /> 
<plugin name="FileTransfer" value="org.apache.cordova.FileTransfer" />

AndroidManifest.xmlにpermissionを追加

外部ストレージへのアクセス権が必要なので、permissionを追加する

platforms/android/AndroidManifest.xml

"manifest"のセクションに"uses-permission"を追加する。詳しくはAndroidのドキュメントを参照してくれたまえ。

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

アプリケーションのJSからファイルを呼び出す。

あとは必要なAPIのドキュメントを参照したり他のサイトを参考にしても動くと思う。

var ft = new FileTransfer();

ひとまず、これでAPIのアクセスができるようになる。

まとめ

apacheリポジトリをplaginに指定し、platformのconfig.xmlからpluginを呼び出してやればいい。至ってシンプル。


余談だが、rippleというエミュレートのツールがあるのだが、いくつかの記事にあるように動かしても、localhost:8000へのアクセスでどうやらJSがループに陥りエミュレートする前にブラウザが沈黙してしまう。これも3.0.0の影響なのだろうか・・・・? Build前のリソースをWEB開発と同じようにデバッグできるのであれば、それは相当に便利なんだが・・・。ひとまずはbuildとadb -r installで乗り切るしかなさそうだ。

Ubuntu12.4にphonegap(cordova)をインストールしてカジュアルにネイティブアプリを作成する・・・ための環境を作る呪文

多くのサイトがGUI操作を前提にチュートリアルを書いてたりするので、ドキュメントの隙間を縫ってCLI環境でのphonegap環境について書く。マルチプラットフォームAndroidやらiPhoneやらTizenやら)のアプリを構築できるphoneGapだが、何分Androidくらいしか知識がないので、とりあえずAndroidだけのBuildを目指す。

事前準備: Android環境を構築する

phoneGap内部ではandroidのbuildを行う=antを叩くので、当然ながらandroid環境が必要だ。手前味噌だが以下を参考に構築するとスムーズかもしれない。

CUI(CLI)でAndroidプロジェクトを作成・Build環境を整える呪文

事前準備: nodejsを入れる。

*env系をまとめて管理できるanyenvをインストール

anyenvという**env系の簡易マネージャを作った

@さんが書いた、ndenvとかrbenvとかplenvをまとめて管理してくれる便利ツールがあるのでanyenv経由でndenvをインストールする。便利なのでみんなanyenv使うといいよ!!


riywo/anyenv こちらにanyenvと*envのインストール方法書いてあるのでコピペすればよし。以下コピペ。

$ git clone https://github.com/riywo/anyenv ~/.anyenv
$ echo 'export PATH="$HOME/.anyenv/bin:$PATH"' >> ~/.your_profile
$ echo 'eval "$(anyenv init -)"' >> ~/.your_profile
$ exec $SHELL -l

ndenvをインストール

$ anyenv install ndenv
$ exec $SEHLL -l

nodejsをインストール

phoneGapのCLI版はnode-0.9.9以上を要求するらしいので、情弱な僕は言われた通りに0.9.9を入れる。

$ ndenv install 0.9.9
$ ndenv rehash
$ ndenv global 0.9.9
$ node -v
 v0.9.9

グローバルにcordovaをインストール

cordovaという名前空間にあるので、そいつをグローバル(-g)でインストールする。

$ npm -g install cordova --save 
$ cordova -v
 2.9.0rc1

試しにプロジェクトをつくってみる

cordovaコマンドを打ち込むとヘルプが出てくる。プロジェクトの作り方は以下のようにすればOKらしい。

Global Commands

create [ID] [NAME] ............... creates a cordova project in the specified PATH, with

とのことなので、Helloworldをば。

$ cordova create ~/project/cordova-helloworld helloworld.cordva.ry-m.com helloworld
$ cd cordova-helloworld
$ cordova platform add android
$ cordova build
$ cordova serve android
Static file server running at
  => http://localhost:8000/
CTRL + C to shutdown

ずらっとヘルプにあるコマンドを試打してみたが、上から「プロジェクト作成」「androidをプラットフォームとして追加」「ビルドの実行(androidにおいてはapkファイルの生成)」「アプリのHTMLリソースをlocalhostのドメインでサーバーを立てる」となっている。

cordova自体にサーバーの機構があるので、localhostに立ててあげれば制作して変更のたびにbuildしなくてもおおまかな動作は追えるという便利さである。


……なお、Android環境が構築出来てないと以下のようなエラーになるので、気をつけろ!

$ cordova platform add android                                                                                                                   [~/project/cordova-helloworld]
The command `android` failed. Make sure you have the latest Android SDK installed, and the `android` command (inside the tools/ folder) added to your path. Output: /bin/sh: android: not found

おまけ: JenkinsでBuild環境を用意する

Jenkinsに追加したい場合は、

ビルド > シェルの実行

事前にインストールしとくやつ

  • node v0.9.9
  • cordova 2.9.0rc1

export PATH="$HOME/.anyenv/envs/ndenv/versions/0.9.9/bin:/usr/bin:$PATH"
export PATH="$HOME/.anyenv/envs/ndenv/versions/0.9.9/lib/node_modules/cordova/bin/:/usr/bin:$PATH"

cd ${WORKSPACE}

echo BUILD PROJECT
cordova build

ってすればいい。

jenkins経由でBuildしたAndroidのAPKファイルをDeployGateにPushする呪文

DeployGateについて

deploygate

mixiが提供しているAndroidのアプリ配布ツール。「開発中のアプリを一瞬で手元へ」届けてくれる。
クックパッドやカヤックといった業界おなじみの企業が導入しているので、実績もある。弊社でも使っている。

Webの管理画面やAPI経由でAPKファイルをアップロードすると、DeployGateのAndroidアプリを通して、許可したユーザの端末にアプリをインストールできるようになる。

料金プランはいくつかあるが、無料で利用することもできる。どのプランにせよ、10日間は無料でお試しできるトライアル期間もあるので、Androidの開発をしていてアプリの配布に困ってる方にはおすすめだ。
無料プランは2アプリまで登録が可能で、個人開発であり、逐次削除すればいいし、そんな超速で量産するつもりもないので、ひとまずは無料プランで継続しようと思っている。


前回の記事は、Ubuntuに立てたと言ったが、それはさくらインターネットのVPS上で、AndroidデバッグをするにもいちいちAPKファイルをローカルに持ってきてインストールしないといけない。
それは面倒だし、だれもが「お前はアホか」と言いたくなるような手間である。

しかし、たとえばjenkinsさんでBuildして成功したら手元の端末にUSBなしでアプリが配布されたらなんと便利なことか。USB持ち歩かなくてもいいし、手元のMBAAndroid開発環境を用意しなくてもよい。


もちろん、そんな理想郷ってわけではないのだけれど、勉強も兼ねて、そういう環境を用意して見ることにした。

jenkinsからのdeploy

さて、ゴールは明確だ。
jenkinsのビルド後の処理にDeployGateのAPIを叩くように設定すればいい。

DeployGate Plugin

jenkinsには、DeployGateと連携するためのPluginが幾つか提供されている。
DeployGateの公式ページにも紹介がある。

以下の二点だ。

上のURLは @bols さんが作られたplugin。jenkinsのシステムの設定でAPI Keyとユーザ名を登録してBuild後に送信するファイル名を送信する。こちらは公式Pluginとして提供されているので、jenkinsの管理画面からインストール可能だ。悲しいことに、システムの設定でAPI Keyとユーザー名の登録情報を参照できない(登録してからページをリロードしても、フォームは空欄のままで登録してるのかしてないのかわからない)ということと、プロジェクトごとにAPK Keyとユーザを設定できないので、見送りにした。

僕が登録したのは、下記の @jfsso の配布しているファイル。こちらはjenkinsの設定のPluginの高度な設定から上記リンク(Twitterの発言)からダウンロードできるhpiファイルをアップロードすれば使えるようになる。こちらはプロジェクトごとにAPI Keyとユーザを設定できるので、こちらを採用。

はまったこと。

上記どちらもためして、どちらもうまく行かなかったことがある。指定するファイル名だ。
当初僕は、workspaceからの相対パス

bin/Activity.apk

というように指定してたのだが、どちらのPluginでもうまく動かない。
jenkinsの仕組み的にアクションごとのコマンドは保持されないらしく、ビルド時にcdで移動したワークスペースとは別のところにいるらしい。

なので、下記のように環境変数`WORKSPACE`を指定してやるとjenkins内で適切に処理してくれるようになる。これは、 @jfsso 氏のPluginの管理画面にヒントとして書かれていた。@jfsso++

${WORKSPACE}/bin/Activity.apk

まとめ

ひとまずデプロイゲートによる配布はこれで可能になった。
とはいえまあ・・・使ってて思うんだけど、mixiさん、APKファイルの受付に失敗したら200のステータスではなく401とか失敗を返して欲しいの。200って帰ってきて成功しぜ!って管理画面を見てみても、更新はされておらず、ふぇぇと悩んでしまう。

とはいえ、こういう問題に何時間ハマったのか・・・僕もまだまだ未熟である。


次はPhoneGapのビルドについて書くよ!

Ubuntu12.4にjenkinsを立ててandroidのBuildをできるようにする呪文

AndroidのBuild環境は、できれば統一したい。
複数人で開発していると、それぞれ異なる環境でBuildしているため、他の人の環境だと正しく動かなかったりすることがごくごく稀にある。

Build環境を標準化することとは別に、githubのcommitHook等から自動でBuildできるようにすると楽ちんだったり、ビルドスクリプトを書くのも面倒くさい。

アジャイル開発において自動テストは必須である。

テストが失敗したりビルドや更新の記録を残してプロファイリングしたいなんて話もある。


などなど、CIツールの必要性は今更確認するまでもないので、有名であり弊社でも一度は使われたjenkinsさんを使ってみようとおもう。

jenkinsのインストール

https://wiki.jenkins-ci.org/display/JENKINS/Installing+Jenkins+on+Ubuntu

ここ見れば一発。
とりあえずaptでインストールして、以下の呪文で動作することを確認しましょう。

sudo -H /etc/init.d/jenkins start
curl http://loacalhost:8080

jenkins.myhost.com:80 でJenkinsを待ち受けたい

たぶん80ポートで待ち受けてたりDNSで割り振ったりしてると思うので、jenkinsとは別にロードバランサなりリバースプロキシなりしてる方はそれぞれのツールのドキュメントを参考にしてくださいませ。

ちなむと僕は、Varnishっていうキャッシュサーバーでリバースプロキシしています。
jenkinsのホストにアクセスきたら、`localhost:8091`に振るようにしています。

portの変更

デフォルトの設定は、`/etc/default/jenkins`にあります。suduedit等で下記`JENKINS_PORT`の値を書き換えてください。

JENKINS_PORT="8080"

再起動

sudo -H /etc/init.d/jenkins restart

実際にアクセスしてみよう

URLを叩いて確認。もし動かないようなら各種ドキュメントなり先生に聞くなりしてくれ。


デフォルトでのjenkinsはセキュリティもなにもあったもんじゃないので、[Jenkinsの管理]>[グローバルセキュリティの設定]>[アクセス制御]あたりを設定しておくと良い

※ユーザをサインアップする前に`ログイン済みユーザーに許可`にチェックを入れてしまうと、ログインできずサインアップできずの積み状態になるので、注意。積んだら設定を書き換えれば直せるけれど、それはここでは割愛します。

とりあえずJenkinsのバックエンドはこの設定でコト足りたので完了とする。

jenkinsのPluigins

Pluginのインストール

AndroidのBuildとGithubとの連携で必要なのは以下のplugin

  • GIT plugin
  • Android Emulator Plugin
  • Ant Plugin

それぞれ[Jenkinsの管理]>[プラグインの管理]<[利用可能]の項からインストール可能。
インストール後、Jenkinsを再起動する必要がある。

GITユーザの設定

[Jenkinsの管理]>[システムの設定]>[Git plugin]にgitユーザの情報を入力する

Antの設定

[Jenkinsの管理]>[システムの設定]>[Ant]>[インストール済みAnt]にAntの情報を追加する

Android SDKの設定

[Jenkinsの管理]>[システムの設定]>[Android]>[Android SDK root]にANDROID_HOMEを設定する。
WebIFの作業とは別に、AndroidSDKのインストールが必要となる。

こちらを参考あれ
CUI(CLI)でAndroidプロジェクトを作成・Build環境を整える呪文

今回は、`~/tools/android/sdk`にインストールしたので、そちらを入力。
入力時に不適切なディレクトリだとvalidation errorが表示されるので確認しつつやること。

ANDROID_HOME: ~/tools/android/sdk, PATH: ~/tools/android/sdk/tools

jenkins@ry-m:~$ cd /var/lib/jenkins/tools/
jenkins@ry-m:~/tools$ curl -L -O http://dl.google.com/android/adt/adt-bundle-linux-x86_64-20130522.zip
jenkins@ry-m:~/tools$ unzip adt-bundle-linux-x86_64-20130522.zip
jenkins@ry-m:~/tools$ mv adt-bundle-linux-x86_64-20130522/ android
jenkins@ry-m:~/tools$ android update sdk --no-ui


以上がJenkinsシステム全体の設定。ここからは個々のプロジェクトの設定

ジョブ毎の設定

ビルド手順がようわからなかったのでメモ書き
ビルドの項の先頭に以下のシェルスクリプトを追加する

cd ${WORKSPACE}
export PATH=$PATH:/var/lib/jenkins/tools/android/sdk/tools:/usr/bin

echo UPDATE PROJECT
android update project -p ./ --target android-15

続けて、antの設定を追加する。このとき、デフォルトではなく、インストールしたantの名前を選択すること。

Buildを予約する

問題なくBuildできていればOK