APIでBitbucketにアクセスしてみる


## はじめに
[「Code for 青空文庫」アイデアソン #1](https://atnd.org/events/66230)というイベントに参加したのをきっかけに[青空文庫](http://www.aozora.gr.jp/)の次世代システムの姿をつらつらと考えるようになっている。同じような興味を持っている人たちが集まって[Aozorahack](https://github.com/aozorahack/aozorahack)という場で意見交換したりプロトタイピングを始めていたりするが、その中で調べたり簡単にコードを書いてみたりした要素技術に関して、できるだけ一般化した形でここに書き出しておこうかと思う。

一つ目はBitbucketのAPIアクセスについて。

## Bitbucket概要

[Bitbucket](https://bitbucket.org/)はwebベースのホスティングサービス。ソースコードをバージョン管理システムで保管でき、Wikiでドキュメントを書いたり、課題やプルリクエストの管理などができる。機能としてはほぼほぼGitHubと同じ。

GitHubと違う部分が大きく二つあって、ひとつはバージョン管理システム。GitHubは名前の通り、Gitだけだけど、BitbucketはGitだけじゃなくてMercurial(hg)も使える。というか、元々はMercurialだけだったんだけど、気がついたらGitもサポートするようになっていた。

もう一つはPrivateリポジトリの扱い。つまり、非公開のリポジトリ。GitHubは有償プランじゃないとPrivateリポジトリを持てないが、Bitbucketは非公開のリポジトリも作れる。しかも数が無制限！Privateリポジトリを共有できるアカウントの数は限られているけど、おひとり様であれば幾らでも作れる。なんという太っ腹！ 

元々は独立したスタートアップが提供していたサービスだったと思うけど、今は[Atlassian](http://www.atlassian.com/)ファミリの一員。AtlassianはJIRAとかConfluenceとかを提供している会社で、私も普段仕事ではとてもお世話になっている。そして、GitHubにGitHub Enterpriseがあるように、Bitbucketにもオンプレミス用のソリューションがあって、それがStash。使ったことないけど、BitbucketのUIそのままに社内で使えるなら嬉しい人は沢山いるはず。

ということで、そんなBitbucketをなんとかプログラムから使ってみたいなと思い、提供されているAPIを叩いてみました。

## Bitbucket API

昨今のWebサービスはだいたいAPIアクセスできることが多い。ご多分にもれず、Bitbucketも同様。
APIドキュメントは[ここ](https://confluence.atlassian.com/display/BITBUCKET/Use+the+Bitbucket+REST+APIs)にあるが、基本は素直なRESTfulなAPI。リソースがURIで定義され、そこにGET/PUT/POST/DELETEの4つのHTTPメソッドでアクセスする。ちなみに、ドキュメントは素敵なことにConfluenceに書かれている。

なお、Bitbucket APIは1.0と2.0の二つのバージョンがサポートされているが、「特に理由がなければ2.0を使って」と書かれているので、ここでは2.0でアクセスしてみる。

### ユーザ認証・ユーザ認可

公開情報にはユーザ認証なしにアクセスできる。これはBitbucketのサイトでログインなしに公開リポジトリにアクセスできるのと同様だ。例えば、こんな風にアクセスすると

```bash
$ curl https://api.bitbucket.org/2.0/repositories/pypy/pypy | python -m json.tool
```

こんな結果が得られる。返されるbodyは基本的にJsonだ。

```javascript
{
    "created_on": "2010-12-13T13:05:45.880068+00:00",
    "description": "PyPy is a very fast and compliant implementation of the Python language.",
    "fork_policy": "allow_forks",
    "full_name": "pypy/pypy",
    "has_issues": true,
    "has_wiki": true,
    "is_private": false,
    "language": "python",
    "links": {
 ...
}
        
```

一方でPrivateリポジトリに関してはユーザ認証が必要で、同じようにアクセスしても、`HTTP 403 FORBIDDEN`が返って来る。そういう場合は、HTTP Basic認証で

```bash
$ curl --user $username:$password https://api.bitbucket.org/2.0/repositories/user/repo
```

とするとアクセスできる。

また、Bitbucketをバックエンドに使ったWebサービスとかを構築するのにOAuth(1.0/2.0)もサポートしている。それを使えばアクセスするユーザのユーザ名・パスワードを直接知ることなく、そのユーザのPrivateリポジトリなどにアクセスできる。それについても書きたいが、長くなるのでまた別の機会に。

### リポジトリへのアクセス

上記で既に書いているようにリポジトリへのアクセスは `https://api.bitbucket.org/2.0/repositories`というエンドポイントに対して行う。主な操作は以下の通り。

| リソース                            | メソッド | 操作              |
|:----------------------------------|:--------|:-----------------|
| /repositories/{owner}　　　　　    | GET     | 該当ユーザのリポジトリのリストを取得 |
| /repositories/{owner}/{repo_slug} | GET     | リポジトリ情報の取得 |
| /repositories/{owner}/{repo_slug} | POST    | リポジトリの作成 |
| /repositories/{owner}/{repo_slug} | DELETE  | リポジトリの削除 |
| /repositories/{owner}/{repo_slug}/watchers | GET  | そのリポジトリをwatchしているユーザのリストの取得 |
| /repositories/{owner}/{repo_slug}/forks | GET  | そのリポジトリをforkしたリポジトリのリストの取得 |

この他のリポジトリに対する操作もできるが、取り敢えず今回はリポジトリの作成、情報取得、削除をやってみた。

なお、Slugは[ここ](https://confluence.atlassian.com/display/BITBUCKET/What+is+a+Slug)に書かれているけど、リポジトリ名のURLフレンドリー版。最初、意味が解らなかったんだけど、Bitbucketのリポジトリ名ってURLでそのまま使えない文字を使うこともできる。例えば "Test Repo #1"とか。で、これに対してBitbucketが勝手に "test-repo-1" とかいう名前(slug)を生成してくれて、それをURLに使う。

## 実践

環境変数 `username`, `password`にそれぞれ、ユーザ名とパスワードが設定されていると仮定します。

### cURLを使った例

まずリポジトリを作成してみる。作成はPOST。Bodyに何も指定しなくてもデフォルト値で作ってくれる。ただ、リポジトリの作成には当たり前といえば当たり前だが、ユーザ認証が必要。ということで、例えばcURLで書くとこんな感じ。

```bash
$ curl -X POST \
	--user $username:$password \
	https://api.bitbucket.org/2.0/repositories/$username/test1
```

で、作られたリポジトリの情報がJsonで返って来る。

```javascript
{
    "created_on": "2015-06-27T00:55:41.727",
    "creator": null,
    "description": "",
    "email_mailinglist": "",
    "email_writers": true,
    "fork_of": null,
    "has_issues": false,
    "has_wiki": false,
    "is_fork": false,
    "is_mq": false,
    "is_private": false,
    "language": "",
    "last_updated": "2015-06-27T00:55:41.754",
    "logo": "https://bitbucket.org/username/test1/avatar/16/?ts=1111111111",
    "mq_of": null,
    "name": "test1",
    "no_forks": false,
    "no_public_forks": false,
    "owner": "username",
    "read_only": false,
    "resource_uri": "/1.0/repositories/username/test1",
    "scm": "git",
    "size": 0,
    "slug": "test1",
    "state": "creating",
    "utc_created_on": "2015-06-26 22:55:41+00:00",
    "utc_last_updated": "2015-06-26 22:55:41+00:00",
    "website": ""
}
```

ここで着目すべきは、`is_private`と`scm`。デフォルトでは公開のGitリポジトリを作ることになっている。公開デフォルトはわかるが、hgじゃなくてgitがデフォルトなんだね。時の流れとは言え、Bitbucketもそうなのかと思うと少しさみしい。

ともかく、POST時にBodyにJsonでパラメータ指定することでこのデフォルト値を変えられる。例えば、こうすると非公開のMercurialリポジトリを作れる。

```bash
$ curl -X POST \
	--user $username:$password \
	--data '{"scm": "hg", "is_private": true}' \
	--header 'Content-type: application/json' \
	https://api.bitbucket.org/2.0/repositories/$username/test2
```


そして、出来たリポジトリの情報を取得するにはGET。`$username/test1`の方は公開で作ってあるのでユーザ認証なしで情報取得できる。

```bash
$ curl https://api.bitbucket.org/2.0/repositories/$username/test1
```

GETではPOSTで作成時よりもさらに多くの情報が返ってくる。各種リソースのURLとかオーナーに関する情報とか。一方で、repo2はPrivateリポジトリで作成したので同様にしても`HTTP 403 FORBIDDEN`が返ってきてしまう。そのときは慌てず騒がず、`--user`でユーザ認証情報を付加すれば良い。

作ったリポジトリの削除はDELETE。これもユーザ認証が必要だが、パラメータの指定は無いので至って簡単。

```bash
$ curl -X DELETE \
	--user $username:$password \
	https://api.bitbucket.org/2.0/repositories/$username/test1
```

綺麗さっぱりリポジトリは消されて、`HTTP 204 NO CONTENT`が返ってくる。

### Node.jsを使った例
同じことを、もう少しプログラム的にNode.js+requestでやってみる。普段はCoffeescriptで書いているのだが、わかりやすいようにJavascriptで頑張ってみる:smile:

まずはリポジトリの作成。

```cr.js
var request = require('request');
var username = process.env.username;
var password = process.env.password;

request.post("https://api.bitbucket.org/2.0/repositories/" + username + "/test2", {
    auth: {
        user: username,
        pass: password
    },
    body: {
        scm: "hg",
        is_private: true
    },
    json: true
}, function(err, resp, body) {
    if (err) {
        return console.log(err);
    } else {
        return console.log(body);
    }
});

```

これを実行してみる。

```bash
$ npm install request
$ node cr.js
```

同様に情報取得と消去。

```get.js
var request = require('request');
var username = process.env.username;
var password = process.env.password;

request.get("https://api.bitbucket.org/2.0/repositories/" + username + "/test2", {
    auth: {
        user: username,
        pass: password
    },
    json: true
}, function(err, resp, body) {
    if (err) {
        return console.log(err);
    } else {
        return console.log(body);
    }
});

```

```del.js
var request = require('request');
var username = process.env.username;
var password = process.env.password;

request.del("https://api.bitbucket.org/2.0/repositories/" + username + "/test2", {
    auth: {
      user: username,
      pass: password
    }
}, function(err, resp, body) {
    if (err) {
        return console.log(err);
    } else {
        return console.log(body);
    }
});

```

```bash
$ node get.js
$ node del.js
```


ほぼ一緒のコードでHTTPのメソッドを変えるだけで別の操作ができる事がわかる。

## さいごに

BitbucketのAPIを使ってみたが、APIはリポジトリの作成・消去だけでなく、ブランチの操作を制限したり、コミットに関する情報を取得したりプルリクエストの操作などができるらしい。

一方、実際に作ったリポジトリに対してファイルを追加したりコミットしたりとかという変更を行いたくなるが、どうやらそれはBitbucketのAPIとしては用意されていないっぽい。作ったリポジトリを手元にCloneして、そこに変更を行い、変更をPushするという手続きになる。Clone元のURLはリポジトリ情報に含まれているので、それを通常のgit/hgコマンドで指定してCloneしてくる。あるいは[libgit2](https://libgit2.github.com/)のようなライブラリを使ってプログラムで操作することになる。

その辺りもAozorahackのプロトタイプとして少し試してみたので次回はその辺りを書いてみようかな。