GitHubはAPIが公開されていて、アプリケーションから操作することが可能です。
アプリケーションからAPI操作をするためのライブラリである、Octokitも用意されています。konoOctokithagennzai.NET, Obj-C, Ruby用のライブラリが用意されていて、今回は.NET版のOctokit.netを使用して、C#からGitHubを操作するための触りを紹介したいと思います。
Octokit.netのインストール
NuGetでインストールします。
PM> Install-Package Octokit
アクセスと認証
アプリケーションからgithubにアクセスするにはoctokitのGitHubClientのオブジェクトを作成する必要があります。
var github = new GitHubClient(new ProductHeaderValue("IshisakaSample"));
上が、認証もProxyも無い一番簡単な例で、GitHubClientのコンストラクタ引数にアプリケーション名を入れたProductHeaderValueのオブジェクトを渡してあげます。これで基本的にGitHubへのアクセスが可能になります。
プライベートリポジトリへのアクセスやIssueの投稿などをする場合にはユーザー認証が必要になるので、以下のように、Basic認証のCledentialsオブジェクトをつくり、先ほどのProductHeaderValueのオブジェクトとあわせて、GitHubClientのコンストラクタに渡します。
public async void AuthenticationSample(string password) { // Basic認証のオブジェクトを作成する // OAuthのTokenを引数に与えても良い var credential = new Credentials("ishisaka", password); // 認証付きでGutHUbのクライアントを作成 var github = new GitHubClient( new ProductHeaderValue("IshisakaSample"), new InMemoryCredentialStore(credential)); } }
5行目でBasic認証のCledentialsオブジェクトを作成するためにコンスタ久谷ユーザー名とパスワードを渡しています。OAuthを使ってアクセスしたい場合には、先にOAuthのトークンを所得しておき、Cledentialsのコンストラクタにそのトークンをstringで渡します。
8行目でGitHubClientのオブジェクトを作っていますが、第二引数にICredentialStoreを持ったオブジェクトを与える必要があるので、ここではOctocat.netが持っているInMemoryCredentialStoreを使っています。たとえば、ファイルからであるとか、レジストリに認証情報を保存するような場合には自分でIcredentialStoreを持ったクラスを作成して使用することも出来ると思います。
Proxyを経由したアクセスが必要な場合にはもう少し複雑になります。
先ほどまでのやり方では、GitHubClientがコンストラクタの中で自動生成していたConnectionオブジェクトを外側で作成してからGitHubClientのオブジェクトを作成する必要があります。
protected async void UseProxySample(string password) { var proxy = new WebProxy("Proxy Address"); // IE(System)のProxy設定を使用する場合には上に変わって以下のようにします。 ////var proxy = WebRequest.GetSystemWebProxy(); // 認証Proxyを使用している場合でNTLM認証(システムの認証情報)を使用する場合には以下のように指定する proxy.Credentials = CredentialCache.DefaultCredentials; // Basicユーザー認証を使用するProxyでは上に変わり下のように指定します。 ////proxy.Credentials = new NetworkCredential("User", "password"); var client = new HttpClientAdapter(proxy); var credentialStore = new InMemoryCredentialStore(new Credentials("UserName", password)); var connection = new Connection( new ProductHeaderValue("IshiakaApps"), GitHubClient.GitHubApiUrl, credentialStore, client, new SimpleJsonSerializer()); var github = new GitHubClient(connection); // next something else. }
手順的なところは上のコード内のコメント後確認いただきたいのですが、IProxyを持つオブジェクトを作成し、必要な設定をつくったら、それを引数にOcotkitのHttpClientAdapterのオブジェクトを作成、認証に必要なICredetialStoreオブジェクトを作成して、それらを引数としてConnectionのオブジェクトを作成します。
データアクセス
ここではIssueへのアクセスを試みます。
Octokit.netのデータ取得、投稿、変更の各メソッドは全て非同期になっているので、それを前提にコード書く必要があります。
とは言っても、Linqとasync/awaitが有るので、そんなに難しくはありません。
以下は特定のレポジトリかIssue全体を取得してコンソールに表示、ユーザーのアバタ画像を保存しています。
ソースを見ていただければ特に説明もいらないかと思います。
public async void GetIssues() { // クライアントを作成する。公開レポジトリなら認証は必要なし var github = new GitHubClient(new ProductHeaderValue("IshisakaSample")); // イシューを取得する。オーナー名、レポジトリ名を引数に IReadOnlyListissues = await github.Issue.GetForRepository("ishisaka", "nodeintellisense"); foreach (Issue issue in issues) { Console.WriteLine("Number:\t{0}", issue.Number); Console.WriteLine("Title:\t{0}", issue.Title); Console.WriteLine("Date:\t{0}", issue.CreatedAt); Console.WriteLine("Body: \r\n{0}", issue.Body); Console.WriteLine("User:\t{0}", issue.User.Login); Console.WriteLine("--------"); // ユーザーのアバター画像を取得 var avatorUrl = new Uri(issue.User.AvatarUrl); var client = new WebClient(); string downloadPath = @"d:\temp\" + issue.User.Login + ".png"; await client.DownloadFileTaskAsync(avatorUrl, downloadPath) .ContinueWith(t => Console.WriteLine(downloadPath + " download Complte.")); } }
IreadOnlyListで結果を返してくるのが結構気に入らない感じもしますが、ものすごく簡単です。
また、プロパティ名等でよくわからないときにはGitHub APIのドキュメントを読むのが良いと思います。
では、追加と更新を見てみましょう。
これもコードを見ていただければわかると思います。
public async void PutIssues(string password) { Console.WriteLine("レポジトリにIssueを追加する"); var credential = new Credentials("ishisaka", password); var github = new GitHubClient( new ProductHeaderValue("IhsisakaSample"), new InMemoryCredentialStore(credential)); var newIssue = new NewIssue("サンプルイシュー" + DateTime.Now.ToShortTimeString()) { Body = "にほんごてきすと", // 担当ユーザーの割り当てをする場合にはAssigneeプロパティに有効なユーザー名を指定 Assignee = "ishisaka" }; // Issueを新規追加。追加されたIssueが戻ってくる var ret = await github.Issue.Create("ishisaka", "juzshizuoka", newIssue); Console.WriteLine("追加されたIssue"); Console.WriteLine("Number:\t{0}", ret.Number); Console.WriteLine("Title:\t{0}", ret.Title); Console.WriteLine("Date:\t{0}", ret.CreatedAt); Console.WriteLine("Body: \r\n{0}", ret.Body); Console.WriteLine("User:\t{0}", ret.User.Login); Console.WriteLine("--------"); // 編集 // 編集 正直いちいちIssueUpdate作るのがメンドイ var issuesUpdate = new IssueUpdate { Title = ret.Title, Body = ret.Body + "\r\n編集しました。" }; var retUpdate = await github.Issue.Update("ishisaka", "juzshizuoka", ret.Number, issuesUpdate); // 編集されたIssue Console.WriteLine("編集されたIssue"); Console.WriteLine("Number:\t{0}", retUpdate.Number); Console.WriteLine("Title:\t{0}", retUpdate.Title); Console.WriteLine("Date:\t{0}", retUpdate.CreatedAt); Console.WriteLine("Body: \r\n{0}", retUpdate.Body); Console.WriteLine("User:\t{0}", retUpdate.User.Login); Console.WriteLine("--------"); }
もうおわかりとは思いますが、同じIssueなのに、取得時、作成時、更新時で別々のクラスを使用して、別のインスタンスを作る必要があって面倒です。
ただ、これは元々のGitHub APIのJSONオブジェクトがやはりそれぞれで異なっているため、APIにあわせて作成されているのだろうと考えます。
Reactive!
よく訓練されたC#使いは今までのコードを見てくると、何となくおしりのあたりがむずむずしてくると言うか、そのForEachを.Subscribeに変えたくなってくると思います。
Octokit.netにはReactive Extensionsを使用したライブラリがOctokit.Reactiveの名前空間で用意されており、オブザーバーパターンでというか、Rxできます。
以下は、Reactiveでデータ取得する場合のコードです。
public void GetIssues() { // クライアントを作成する。公開レポジトリなら認証は必要なし var github = new GitHubClient(new ProductHeaderValue("IshisakaSample")); // ObservableIssuesClientを作成する var issueObserver = new ObservableIssuesClient(github); // レポジトリのIssueを取得して、表示する。Rxを使えばこんなに簡潔に書けます。 issueObserver.GetForRepository("ishisaka", "nodeintellisense").Subscribe( i => { Console.WriteLine("Number:\t{0}", i.Number); Console.WriteLine("Title:\t{0}", i.Title); Console.WriteLine("Date:\t{0}", i.CreatedAt); Console.WriteLine("Body: \r\n{0}", i.Body); Console.WriteLine("User:\t{0}", i.User.Login); Console.WriteLine("--------"); }); }
だいぶすっきりしました。
Rxするには、データのアクセスに目的に合わせたObservableで始まるクラスのコンストラクタにGitHubClientのインスタンスを渡し、目的の操作を呼んでSubscribeです。
以下は追加の例です。
public void PutIssue(string password) { Console.WriteLine("レポジトリにIssueを追加する"); var credential = new Credentials("ishisaka", password); var github = new GitHubClient( new ProductHeaderValue("IshisakaSample"), new InMemoryCredentialStore(credential)); var newIssue = new NewIssue("サンプルイシュー" + DateTime.Now.ToShortTimeString()) { Body = "にほんごてきすと", // 担当ユーザーの割り当てをする場合にはAssigneeプロパティに有効なユーザー名を指定 Assignee = "ishisaka" }; var issueObserver = new ObservableIssuesClient(github); issueObserver.Create("ishisaka", "juzshizuoka", newIssue).Subscribe( ret => { Console.WriteLine("追加されたIssue"); Console.WriteLine("Number:\t{0}", ret.Number); Console.WriteLine("Title:\t{0}", ret.Title); Console.WriteLine("Date:\t{0}", ret.CreatedAt); Console.WriteLine("Body: \r\n{0}", ret.Body); Console.WriteLine("User:\t{0}", ret.User.Login); Console.WriteLine("--------"); }); }
いやぁいいですねぇ。
##まとめ
- GitHubが.NET向けにOctokit.netと言うGitHub API用のアクセスライブラリを用意していて、Nugetでインストールできます.
- 非同期のアクセスライブラリと、Rxのアクセスライブラリが用意されていて、使用方法自体はGitHub APIを参照すればそれほど難しくはありません。Reactiveの方を使えばいいんじゃないかな。
- ここまで書けば誰かVSのアドインを作ってくれるかな? |д゚)チラッ
全体コードはGitHubで公開しています。
コメント
RT @ishisaka: http://t.co/o1yoMwZ7GxによるGitHubへのアクセス http://t.co/ioqId51rmQ