ページ

2015年10月29日木曜日

Git フック - サーバーサイド -

最近gitのhookを触ることがあったのでメモ
git hookとは
特定のタイミング(commitした、pushしたなど)でスクリプトを起動することができる
スクリプトはクライアントとサーバーサイドに分けられれ.git/hooksにスクリプトを配置する

   サーバーサイドフック一覧


サーバーサイドつまりはリモートリポジトリ側でフックする時に対応しているスクリプト一覧
  • pre-receive
    クライアントからpushされた時に最初に実行されるスクリプト
    戻り値を0以外の値で終了させるとpushがなかったことにできる
  • update
    pre-receiveは一度のみ呼ばれるのに対しupdateはブランチ単位でそれぞれ実行される
  • post-receive
    pushが完了したら一度だけ呼ばれる
  • post-update
    最後に呼ばれこのスクリプトがコマンド結果自体に影響は及ぼさない
どんな引数が渡ってくるのか不明だったので、それぞれのスクリプトがどんな引数を受け取るか調査
それぞれのスクリプトを以下に編集
#!/bin/sh
echo "スクリプト名 >>" >> up.log
echo $# >> up.log
echo $@ >> up.log
echo "--------------" >> up.log
↓実行結果
pre-receive >>
0

--------------
update >>
3
refs/heads/master 9848db4829aac05708fcdd369ced6e2e90647322 a3cea1587a51c59b48911c5fadc8e362f2bbaa19
--------------
post-receive >>
0

--------------
post-update >>
1
refs/heads/master
--------------
結果から引数を撮るのはupdatepost-updateのみ
updateには引数として1:参照名, 2:push前のSHA, 3:新しいSHA

   コミッターとメッセージ取得


pushされた全コミットのコミッターとメッセージを取得するスクリプトを書いてみようと思います
スクリプトは扱いやすいrubyを使って書いてみました
  • update
#!/usr/bin/env ruby

refname = ARGV[0]
oldrev  = ARGV[1]
newrev  = ARGV[2]

puts "push info >> \n(#{refname}) (#{oldrev[0,6]}) (#{newrev[0,6]})"

sharevs = `git rev-list #{oldrev[0,6]}..#{newrev[0,6]}`.split("\n")
sharevs.each do |rev|
  message = `git cat-file commit #{rev} | sed '1,/^$/d'`

  committer = "?"
  messages = `git cat-file commit #{rev}`.split("\n")
  messages.each do |msg|
    committer = msg if msg.include?("committer")
  end

  puts committer
  puts message
end
まず、3つの引数の情報を取得し、git rev-listコマンドを使用してpush分の全SHA値を取得しています
例) git rev-list 旧SHA..新SHA
➜  githook git:(master) git rev-list 460477..a3cea15
a3cea1587a51c59b48911c5fadc8e362f2bbaa19
9848db4829aac05708fcdd369ced6e2e90647322
079c9649b95fad9e48288f320f40bf6a4e4a552b
※ 旧SHAは表示されない!!
次にgit cat-fileコマンドを使ってコミットの情報を取得しています
例) git cat-file commit SHA値
➜  githook git:(master) git cat-file commit a3cea1587
tree cb06c6f35f32204a91f96d60051260eb0fe3c06e
parent 9848db4829aac05708fcdd369ced6e2e90647322
author slowhand0309 <slowhand0309@gmail.com> 1445675171 +0900
committer slowhand0309 <slowhand0309@gmail.com> 1445675171 +0900

git message.
git cat-file commit #{rev} | sed '1,/^$/d'では 空白行以下のメッセージ=commitメッセージを取得しています
今回は取得したコミッターやメッセージをputsしているだけ
(※標準出力するとクライアントのpushした時にメッセージ表示されます)ですが
色々な事が出来るので遊んでみようと思います

2015年10月24日土曜日

Android Tips1 - リソース名指定読み込み/ソフトキーボード表示の切替 -


Android開発で役に立つかも?しれないTipsを書いて行こうと思います。
リソース名指定読み込み

例えば・・
  • res_hoge1.png
  • res_hoge2.png
  • res_hoge3.png 
    ・・・
  • res_hoge20.png
なんてリソースファイルがある場合、真面目にやると
Drawable d1 = getResources().getDrawable(R.drawable.res_hoge1);
Drawable d2 = getResources().getDrawable(R.drawable.res_hoge2);
Drawable d3 = getResources().getDrawable(R.drawable.res_hoge3);
・・・
Drawable d20 = getResources().getDrawable(R.drawable.res_hoge20);
かなり面倒くさい。できればListや配列でまとめたい所。。
そういう時には以下の方法が便利です(エラー等は考慮してません)
List<Drawable> drawableList = new ArrayList<Drawable>();
for (int i = 0; i < 20; i++) {
  int id = getResources().getIdentifier("res_hoge" + i, "drawable", getPackageName());
  Drawable d = getResources().getDrawable(id);
  drawableList.add(d);
}
詳細
R.javaファイルのパスを指定し、読み込みたいリソースファイルのある内部クラス※1 を指定するとResouresIDが取得できます。
※1 drawablestringlayoutなどここを参照
getResources().getIdentifier("リソースファイル名", "Rファイルでのクラス名", "パッケージ名");
読み込みに失敗した場合はid=0を返す

ソフトキーボード表示の切り替え


    • 特定のウィジェットに対して
    例えばEditTextにフォーカスが移ったとき、フォーカスが外れたときに ソフトキーボードを表示、非表示したいことがあると思います。
    その際にはEditTextのオブジェクトに対してsetOnFocusChangeListenerを 設定してやることでフォーカスの移動で動作を制御してやることができます。 このときInputMethodManagerオブジェクトを利用することで表示・非表示を行います。
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        EditText ed = (EditText)findViewById(R.id.ed);
        ed.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View v, boolean hasFocus) {
                InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
                // フォーカスを受け取ったとき
                if(hasFocus){
                    // ソフトキーボードを表示する
                    inputMethodManager.showSoftInput(v, InputMethodManager.SHOW_FORCED);
                }
                // フォーカスが外れたとき
                else{
                    // ソフトキーボードを閉じる
                    inputMethodManager.hideSoftInputFromWindow(v.getWindowToken(),0);
                }
            }
        });
    }
    • Activity起動時に非表示にする場合
    これは簡単でonCreateの中で以下メソッドを呼び出してやる
    getWindow().setSoftInputMode(
            WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);

    2015年10月21日水曜日

    Elixir/Phoenixで遊ぶ 11 - ElixirでRedis -

    今回は趣向を変えて、ElixirでRedisを操作してみたいと思います
    ElixirのRedisを扱うライブラリとして今回はexredisを使用

    Redisの設定
    インストール方法は以前の記事を参照
    今回は扱いやすいようにシンボリックリンクを張って.bash_profile
    ちょこっと修正
    • シンボリックリンクの作成
    $ ln -s インストールディレクトリ/redis-X.X.X /usr/bin/redis
    • .bash_profileに以下を追加
    PATH=$PATH:/usr/bin/redis/src
    
    export PATH
    
    alias rediss='redis-server'
    alias redisc='redis-cli'
    redissでRedisサーバー起動, rediscでクライアント起動

    プロジェクト作成 & exredisインストール
    rediscliの名前でプロジェクト作成
    $ mix new rediscli
    * creating README.md
    * creating .gitignore
    * creating mix.exs
    * creating config
    * creating config/config.exs
    * creating lib
    * creating lib/rediscli.ex
    * creating test
    * creating test/test_helper.exs
    * creating test/rediscli_test.exs
    
    Your mix project was created successfully.
    You can use mix to compile it, test it, and more:
    
        cd rediscli
        mix test
    
    Run `mix help` for more commands.
    mix.exsにexredisをプロジェクトに取り込むように編集
    defmodule Rediscli.Mixfile do
      use Mix.Project
      〜 省略 〜
      defp deps do
        [{:exredis, ">= 0.2.1"}]
      end
    end
    プロジェクトに適用
    $mix deps.get
    iexで確認してみる
    $ iex -S mix
    
    iex(1)> client = Exredis.start
    #PID<0.174.0>
    iex(2)> {:ok, client} = Exredis.start_link —> パターンマッチング {:OKの場合しかclientにインスタンスが入らない
    {:ok, #PID<0.178.0>}
    iex(3)> client |> Exredis.query ["SET", "name", "slowhand"]
    "OK"
    iex(4)> client |> Exredis.query ["GET", "name"]
    "slowhand"
    iex(5)> client |> Exredis.stop
    :ok
    ↑ではデフォルトの設定で起動(host:127.0.0.1, port:6379)
    起動したのちpipe operator|>を使ってExredis.queryにclientを渡している
    queryではredisのコマンドを実行している
    現時点でkeynameslowhandが格納されている
    今度は実際にredisのクライアントから確認
    $ redisc
    127.0.0.1:6379> get name
    "slowhand"
    ちゃんとslowhandが取れています 今度は逆に、redisのクライアントから設定した値をelixirから参照
    $ redisc
    127.0.0.1:6379> set name fuge
    OK
    127.0.0.1:6379> get name
    "fuge"
    127.0.0.1:6379> exit
    
    $ iex -S mix
    iex(1)> {:ok, client} = Exredis.start_link
    {:ok, #PID<0.114.0>}
    iex(2)> client |> Exredis.query ["GET", "name"]
    "fuge"
    iex(3)> client |> Exredis.stop
    :ok
    iex(4)>
    ちゃんとredisのクライアントから設定した値をexredisでみれています
    とりあえず今回はここまで

    2015年10月18日日曜日

    おすすめのプログラミング書籍紹介3 - ネットワーク/インフラ, デザイン -


    今回紹介する書籍はネットワーク/インフラとデザインです。
    今回紹介分に関しては全然専門では無いのですが・・・
    読んで分かりやすかった書籍を紹介しています。



    ・ ネットワーク/インフラ


    ネットワークに関しては開発の中で必要を迫られる知識になるかと思います。
    スタンドアローンでネットワークを介さない開発なんて聞いた事無いし。。
    インフラに関しても知っておいて損は無いと思います。

    ・基礎からわかる TCP/IP ネットワークコンピューティング入門

    基礎は大事です(笑)
    OSI参照モデルからルーティングやDNS, DHCP, NATやUDPについても
    結構多岐にわたって解説してます。
    付録に各プロトコルのヘッダフォーマットまで載ってます。


    ・絵で見てわかるITインフラの仕組み

    インフラアーキテクチャからサーバーの中身(CPUやメモリ、I/O)や
    キャッシュ、ポーリング、ジャーナリング、レプリケーションなどの仕組み性能まで
    こちらも幅広く解説しています。
    各所にイラストや写真が載せられていて分かりやすいと思います。


    ・ デザイン


    デザインに関しては、ずぶの素人なのですが、
    仕事で画面のデザインも考える事があって(デザインが苦手なんですが・・)
    その時に参考にした書籍です。

    ・デザイン入門教室

    こちらも基礎が大事という事で・・
    この書籍はデザイン素人の自分からしたら目から鱗もんの内容でした。
    デザインに関してのセオリーを使用前と使用後で丁寧に解説してあります。


    ・インタフェースデザインの心理学


    人がアプリの画面を見た時にどう見るのか、どう感じるのかを解説しています。
    例えば「人は過去の経験と予測に基づいて画面を見る」など、普段意識はしてないですが
    iPhoneを使っていてリスト上に「>」マークがあるとそこをタップしたら詳細が見れる事を
    無意識の内に予測して行動しています。
    そういう潜んでる心理を解説している書籍です。
    読んでいて参考になりそうな事ばかりでした。


    2015年10月16日金曜日

    GoogleAnalyticsの内容をSlackへGASで通知するBotの作成 2


    前回に続いてBotを作成していきます 

    Google AnalyticsのProfileIDを取得

    GoogleAnalyticsの指定したビューのProfileIDを取得する
    GoogleAnalyticsの構成については公式ページを参照
    流れとしては、GASでProfileIDをログ出力するスクリプトを作成し
    スクリプトを実行しProfileIDを確認する
    スクリプトは公式ページのものを使用
    以下のスクリプトをコピーする
    function listAccounts() {
      var accounts = Analytics.Management.Accounts.list();
      if (accounts.items && accounts.items.length) {
        for (var i = 0; i < accounts.items.length; i++) {
          var account = accounts.items[i];
          Logger.log('Account: name "%s", id "%s".', account.name, account.id);
    
          // List web properties in the account.
          listWebProperties(account.id);
        }
      } else {
        Logger.log('No accounts found.');
      }
    }
    
    function listWebProperties(accountId) {
      var webProperties = Analytics.Management.Webproperties.list(accountId);
      if (webProperties.items && webProperties.items.length) {
        for (var i = 0; i < webProperties.items.length; i++) {
          var webProperty = webProperties.items[i];
          Logger.log('\tWeb Property: name "%s", id "%s".', webProperty.name,
              webProperty.id);
    
          // List profiles in the web property.
          listProfiles(accountId, webProperty.id);
          }
      } else {
        Logger.log('\tNo web properties found.');
      }
    }
    
    function listProfiles(accountId, webPropertyId) {
      var profiles = Analytics.Management.Profiles.list(accountId,
          webPropertyId);
      if (profiles.items && profiles.items.length) {
        for (var i = 0; i < profiles.items.length; i++) {
          var profile = profiles.items[i];
          Logger.log('\t\tProfile: name "%s", id "%s".', profile.name,
              profile.id);
        }
      } else {
        Logger.log('\t\tNo web properties found.');
      }
    }
    「listAccounts」を選択し実行する
    初回実行時は認証を求められます
    実行が完了したらスクリプトエディッタ上で「表示」>「ログ」を選択し
    ProfileIDを確認する
    一番下のProfileのIDを確認する
    ※ビューを複数設定している場合は対象のProfile IDを確認する

    Slackに通知するスクリプトの作成

    GoogleAnalyticsから解析データを取得する Analytics.Data.Ga.getメソッドを使用する
    このメソッドはRESTfulエンドポイントとしてクエリすることも可能
    例)
    https://www.googleapis.com/analytics/v3/data/ga
      ?ids=ga:12345
      &start-date=2008-10-01
      &end-date=2008-10-31
      &metrics=ga:sessions,ga:bounces
    
    クエリパラメータ
    • ids・・・ga:XXXX形式、XXXXにはProfile IDを設定する
    • start-date・・・取得する解析データの開始日
    • end-date・・・取得する解析データの終了日
    • metrics・・・指標のリスト
    上のパラメータは必須、他はオプションとして指定可能
    詳しくは公式ページを参照
    ↓スクリプト
    /**
     * メイン実行関数
     */
    function reportAnalytics() {
      var profileId = "XXXXXXXXX"; // 確認したProfileID
    
      reportSummary(profileId);
    }
    
    /**
     * 現時点の解析結果をSlackへポスト
     *
     * @param profileId プロファイルID
     */
    function reportSummary(profileId) {
    
      // 
      var today = new Date();
      var toDate = Utilities.formatDate(today, Session.getTimeZone(), 'yyyy-MM-dd');  
      var tableId = "ga:" + profileId;
      var metric = "ga:users,ga:sessions,ga:pageviews";
      var options = {
        "max-results" : 25
      }
    
      // 
      var text = "";
      var report = Analytics.Data.Ga.get(tableId, toDate, toDate, metric, options);
      if (report.rows) {
        var rows = report.getRows();
        text = "現在のブログ状況をお知らせします\n";
        // index:0 ユーザー数
        text += "ユーザー数 : " + rows[0][0] + "\n";
        Logger.log(rows[0][0]);
        // index:1 セッション数
        text += "セッション数 : " + rows[0][1] + "\n";
        Logger.log(rows[0][1]);
        // index:2 ページビュー数
        text += "ページビュー数 : " + rows[0][2] + "\n";
        Logger.log(rows[0][2]);
      }
    
      // 
      if (text != "") {
         var payload = {
         "text" : "ピンポンパンポーン:musical_note:\n" + text + "\n",
         "channel" : "#general",
         "username" : "USERNAME",
         "icon_url" : "http://XXXXXXXXXXXXXXXXXXXXX"
         }
         postSlack(payload);
       }
    }
    
    /**
     * Slackポスト関数
     *
     * @param payload ポスト詳細
     */
    function postSlack(payload) {
    
      var options = {
        "method" : "POST",
        "payload" : JSON.stringify(payload)
      }
    
      var url = "https://XXXXXXXX"; // SlackのWebHooks URL
      var response = UrlFetchApp.fetch(url, options);
      var content = response.getContentText("UTF-8");
    }
    大まかな流れとしては、
    解析データの取得パラメータ作成
    解析データを取得しSlackへ通知する文章を作成
    Slackへ通知
    になります
    ③で投稿するチャンネルを指定できます

    定期的に投稿するように設定

    最後にトリガーを設定し定期的に実行するように設定してやります。
    スクリプトエディッタを開き「リソース」>「現在のプロジェクトのトリガー」を選択し
    実行を「reportAnalytics」に、イベントを「時間主導型」「時タイマー」「※好きな時間」
    を設定する

    あとは指定した時間に投稿されとけばOK
    通知Botのプロジェクトはこちらで公開しています。