GitHub API経由でコミットを取り出す

[GitHub API](https://developer.github.com/v3/)は大変よくできていて、[Gitのさまざまな操作をAPI経由](https://developer.github.com/v3/git/)で行うことができる。

スマートフォンなどのgitがインストールしにくい環境であっても、GitHubからプログラムをクローンしたりコミットすることが可能になる。これは素晴らしいのでまずはレポジトリのクローンから試してみることにした。

## レポジトリの取得
[ongaeshi/tango: Flashcard for RubyPico](https://github.com/ongaeshi/tango) から。GitHubのAPIは分かりやすくてurlの項目を数珠つなぎに見ていけば目的のデータを取得できる。

```
$ curl https://api.github.com/repos/ongaeshi/tango/git/trees/master?recursive=1
{
  "sha": "f5ef6ef11cba6ad1654e04631395e5758d5ad6f2",
  "url": "https://api.github.com/repos/ongaeshi/tango/git/trees/f5ef6ef11cba6ad1654e04631395e5758d5ad6f2",
  "tree": [
    {
      "path": ".gitignore",
      "mode": "100644",
      "type": "blob",
      "sha": "d045bfa4618473ed28e1fa157d6fda40ccc1059a",
      "size": 9,
      "url": "https://api.github.com/repos/ongaeshi/tango/git/blobs/d045bfa4618473ed28e1fa157d6fda40ccc1059a"
    },
    {
      "path": "README.md",
      "mode": "100644",
      "type": "blob",
      "sha": "17092b70377aec764a6bc3ce9b8bf891ff407737",
      "size": 279,
      "url": "https://api.github.com/repos/ongaeshi/tango/git/blobs/17092b70377aec764a6bc3ce9b8bf891ff407737"
    },
    {
      "path": "cli.rb",
      "mode": "100644",
      "type": "blob",
      "sha": "55a1a4266ddfde1833582afcf7408e045d63ba2d",
      "size": 1075,
      "url": "https://api.github.com/repos/ongaeshi/tango/git/blobs/55a1a4266ddfde1833582afcf7408e045d63ba2d"
    },
  .
  .
  ],
  "truncated": false
}
```

## treeの取得
各ファイルやディレクトリの情報。例として`cli.rb`のtreeを取り出す。

```
$ curl https://api.github.com/repos/ongaeshi/tango/git/blobs/55a1a4266ddfde1833582afcf7408e045d63ba2d
{
  "sha": "55a1a4266ddfde1833582afcf7408e045d63ba2d",
  "size": 1075,
  "url": "https://api.github.com/repos/ongaeshi/tango/git/blobs/55a1a4266ddfde1833582afcf7408e045d63ba2d",
  "content": "cmVxdWlyZSAidGFuZ28vanNvbl9maWxlIgpyZXF1aXJlICJ0YW5nby93b3Jk\ncyIKCm1vZHVsZSBUYW5nbwogIGNsYXNzIENsaQogICAgZGVmIGluaXRpYWxp\nemUKICAgICAgQGpzb24gPSBKc29uRmlsZS5uZXcoIndvcmQuanNvbiIpCiAg\nICAgIEB3b3JkcyA9IFdvcmRzLm5ldyhAanNvbikKICAgIGVuZAogICAgCiAg\nICBkZWYgbWFpbmxvb3AKICAgICAgbG9vcCBkbwogICAgICAgIGNvbW1hbmQg\nPSBwcm9tcHQKCiAgICAgICAgY2FzZSBjb21tYW5kCiAgICAgICAgd2hlbiAv\nXmwvCiAgICAgICAgICBsaXN0KGNvbW1hbmQuc3BsaXRbMS4uLTFdKQogICAg\nICAgIHdoZW4gL15ybS8KICAgICAgICAgIHJtKGNvbW1hbmQuc3BsaXRbMS4u\nLTFdKQogICAgICAgIHdoZW4gInQiCiAgICAgICAgd2hlbiAiaCIKICAgICAg\nICAgIGhlbHAKICAgICAgICBlbHNlCiAgICAgICAgICBhZGQoY29tbWFuZCkK\nICAgICAgICBlbmQKICAgICAgZW5kCiAgICBlbmQKICAgIAogICAgZGVmIGFk\nZChjb21tYW5kKQogICAgICBjbWRzID0gY29tbWFuZC5zcGxpdCgiICIpCgog\nICAgICBAd29yZHMuYWRkX29yX3VwZGF0ZShjbWRzWzBdLCBjbWRzWzFdKQog\nICAgICBAd29yZHMuc2F2ZQogICAgZW5kCiAgICAgIAogICAgZGVmIGxpc3Qo\nYXJncykKICAgICAgd29yZHMgPSBAd29yZHMudG9fYQogICAgICB3b3JkcyA9\nIHdvcmRzLnNvcnRfYnkgeyB8ZXwgZS51cGRhdGVkX2F0IH0ucmV2ZXJzZQog\nICAgICAKICAgICAgd29yZHMuZWFjaCBkbyB8ZXwKICAgICAgICBpZiBhcmdz\nLmVtcHR5PwogICAgICAgICAgcHV0cyBlLmFzdHIKICAgICAgICBlbHNlCiAg\nICAgICAgICBwdXRzIGUuYXN0ciBpZiBlLndvcmQuaW5jbHVkZT8gYXJnc1sw\nXQogICAgICAgIGVuZAogICAgICBlbmQKICAgIGVuZAogICAgCiAgICBkZWYg\ncm0oYXJncykKICAgICAgYXJncy5lYWNoIGRvIHxlfAogICAgICAgIHIgPSBA\nd29yZHMucm0gZQogICAgICAgIHB1dHMgIk5vdCBmb3VuZCAje2V9IiB1bmxl\nc3MgcgogICAgICBlbmQKICAgICAgCiAgICAgIEB3b3Jkcy5zYXZlCiAgICBl\nbmQKICAgIAogICAgZGVmIGhlbHAKICAgIGVuZAogIGVuZAplbmQKCg==\n",
  "encoding": "base64"
}
```

## contentの変換
base64エンコーディングされているのでデコードすれば中身を取り出すことができる。以下は雑なRubyスクリプト。

```ruby
require 'base64'

str = "cmVxdWlyZSAidGFuZ28vanNvbl9maWxlIgpyZXF1aXJlICJ0YW5nby93b3Jk\ncyIKCm1vZHVsZSBUYW5nbwogIGNsYXNzIENsaQogICAgZGVmIGluaXRpYWxp\nemUKICAgICAgQGpzb24gPSBKc29uRmlsZS5uZXcoIndvcmQuanNvbiIpCiAg\nICAgIEB3b3JkcyA9IFdvcmRzLm5ldyhAanNvbikKICAgIGVuZAogICAgCiAg\nICBkZWYgbWFpbmxvb3AKICAgICAgbG9vcCBkbwogICAgICAgIGNvbW1hbmQg\nPSBwcm9tcHQKCiAgICAgICAgY2FzZSBjb21tYW5kCiAgICAgICAgd2hlbiAv\nXmwvCiAgICAgICAgICBsaXN0KGNvbW1hbmQuc3BsaXRbMS4uLTFdKQogICAg\nICAgIHdoZW4gL15ybS8KICAgICAgICAgIHJtKGNvbW1hbmQuc3BsaXRbMS4u\nLTFdKQogICAgICAgIHdoZW4gInQiCiAgICAgICAgd2hlbiAiaCIKICAgICAg\nICAgIGhlbHAKICAgICAgICBlbHNlCiAgICAgICAgICBhZGQoY29tbWFuZCkK\nICAgICAgICBlbmQKICAgICAgZW5kCiAgICBlbmQKICAgIAogICAgZGVmIGFk\nZChjb21tYW5kKQogICAgICBjbWRzID0gY29tbWFuZC5zcGxpdCgiICIpCgog\nICAgICBAd29yZHMuYWRkX29yX3VwZGF0ZShjbWRzWzBdLCBjbWRzWzFdKQog\nICAgICBAd29yZHMuc2F2ZQogICAgZW5kCiAgICAgIAogICAgZGVmIGxpc3Qo\nYXJncykKICAgICAgd29yZHMgPSBAd29yZHMudG9fYQogICAgICB3b3JkcyA9\nIHdvcmRzLnNvcnRfYnkgeyB8ZXwgZS51cGRhdGVkX2F0IH0ucmV2ZXJzZQog\nICAgICAKICAgICAgd29yZHMuZWFjaCBkbyB8ZXwKICAgICAgICBpZiBhcmdz\nLmVtcHR5PwogICAgICAgICAgcHV0cyBlLmFzdHIKICAgICAgICBlbHNlCiAg\nICAgICAgICBwdXRzIGUuYXN0ciBpZiBlLndvcmQuaW5jbHVkZT8gYXJnc1sw\nXQogICAgICAgIGVuZAogICAgICBlbmQKICAgIGVuZAogICAgCiAgICBkZWYg\ncm0oYXJncykKICAgICAgYXJncy5lYWNoIGRvIHxlfAogICAgICAgIHIgPSBA\nd29yZHMucm0gZQogICAgICAgIHB1dHMgIk5vdCBmb3VuZCAje2V9IiB1bmxl\nc3MgcgogICAgICBlbmQKICAgICAgCiAgICAgIEB3b3Jkcy5zYXZlCiAgICBl\nbmQKICAgIAogICAgZGVmIGhlbHAKICAgIGVuZAogIGVuZAplbmQKCg==\n"
puts Base64.decode64 str
```

結果

```ruby
require "tango/json_file"
require "tango/words"

module Tango
  class Cli
    def initialize
      @json = JsonFile.new("word.json")
      @words = Words.new(@json)
    end
    
    def mainloop
      loop do
        command = prompt

        case command
        when /^l/
          list(command.split[1..-1])
        when /^rm/
          rm(command.split[1..-1])
        when "t"
        when "h"
          help
        else
          add(command)
        end
      end
    end
    
    def add(command)
      cmds = command.split(" ")

      @words.add_or_update(cmds[0], cmds[1])
      @words.save
    end
      
    def list(args)
      words = @words.to_a
      words = words.sort_by { |e| e.updated_at }.reverse
      
      words.each do |e|
        if args.empty?
          puts e.astr
        else
          puts e.astr if e.word.include? args[0]
        end
      end
    end
    
    def rm(args)
      args.each do |e|
        r = @words.rm e
        puts "Not found #{e}" unless r
      end
      
      @words.save
    end
    
    def help
    end
  end
end
```

## 残り
- treeをたどる
- treeだったらディレクトリ作成
- blobだったらcontentをbase64デコードして内容をファイルに書き出し

だれかすでに書いてそうだなぁ。探してみよう。

## mrubyで動かすには
mrubyで動くようになれば[RubyPico](http://rubypico.ongaeshi.me)でもできるようになるはず。

GETはすでに`Browser.get`でできるようになっているので、あとはbase64。

[mruby-base64 mgem](https://github.com/mattn/mruby-base64)を追加すればよさそう。今回もmattnさんに足を向けて寝られない結果となりました。