GitHub Issues/Pull requests を高速に整理する “Triage for GitHub”

TL;DR

GitHub issueとpull requestのやるやらないを、すばやく振り分けていくためのデス
クトップアプリ “Triage for GitHub“を作りました。

triage-for-github

GitHubのIssue, Pull Requestを迅速にさばいていくことが出来ます。

Motivation

Dependency Update as a Service – Tachikoma.io, greenkeeper, deppbot – Automated Dependency Updates を使うことで、ガンガンpull requestを飛ばし、CIやカバレッジなど、GitHubのエコシステムに乗ることが出来るようになりました。

(tachikoma.io 使ってね!)

ただし、数が増えるとpull requestを持て余します。見逃すこともしょっちゅう。

too-many-pull-requests

特に定期ping的に空pull requestでciを起動するような場合、ciが通っていることを
確認したらガンガンpull requestを閉じたい。

Plan A: /pulls

https://github.com/pulls

github-pulls

GitHubには自分のpull request一覧を見るページがあり、これは求めるものに近いものがある。他にも、検索を使ってorganizationのpull request一覧を見る方法もある。しかし、現状どうしてもポチポチクリックしていく必要があり、閉じていくのも面倒くさい。

Plan B: Triage for GitHub

triage-for-github

そこで、ガシガシ閉じていく専用のデスクトップアプリを作りました。
ランチャーみたいなイメージで、気になるのあったら’Jump’してGitHubのUIを開いて確認していく、GitHub Issueのトリアージを実現します。

使い方

セットアップ

Latest releaseから自分の環境のzipをダウンロードします。

settings

settingsに、GitHubのpersonal tokenを入れてください。scopeは repo or public_repoで、つまり、private+public repos or public reposのどちらかのトークンを入れます。

トークン未入力の場合、lyrictenor/example-issuesのissues/pull requestsを表示します。issue立てたり、pull requestを送ったり、自由にどうぞ。雰囲気はつかめると思います。

トークン未入力の場合、GitHubのAPIリクエスト回数制限が極端に少ないです。
https://developer.github.com/v3/#rate-limiting

60 requests per hour.

一定時間IP制限かかるので注意です。

トークンを使っていても、人によっては結構な回数APIリクエストをするアプリなので、過剰な巡回・起動には注意してください。

表示

updated atの新しい順に、5日ぶんのissue/pull requestが並びます。日数はひとまず固定です。

実験的機能 自動巡回

autopilotをonにすると、定期的に自動巡回します。
が、コレはまだ動いたり動かなかったりです。

autopiloting flagがonになったままになってしまうバグが有るため、そうなった場合
はdebug paneから force unlockしてください。

issueの詳細

issue, pull requestの詳細は v っぽいアイコンをクリックしてください。

show-details

まとめ

GitHub issues, pull requestsをガンガン振り分けできる “Triage for GitHub”を作ったのでガンガン整理できる。

使ったnpm modules

力作。package.json

  "dependencies": {
    "classnames": "^2.1.3",
    "crossing": "^1.0.1",
    "crypto-js": "^3.1.5",
    "electron-debug": "0.3.0",
    "electron-open-link-in-browser": "^1.0.1",
    "electron-template-menu": "1.0.3",
    "es6-error": "^2.0.0",
    "is-empty-object": "^1.1.0",
    "level-js": "^2.2.2",
    "level-sublevel": "^6.5.2",
    "levelup": "^1.2.1",
    "lodash": "^3.10.1",
    "material-ui": "0.13.1",
    "moment": "^2.10.6",
    "octokat": "^0.4.11",
    "react": "^0.14.2",
    "react-addons-create-fragment": "^0.14.2",
    "react-addons-pure-render-mixin": "^0.14.2",
    "react-addons-transition-group": "^0.14.2",
    "react-addons-update": "^0.14.2",
    "react-dom": "^0.14.2",
    "react-redux": "^4.0.0",
    "react-router": "1.0.0-rc4",
    "react-tap-event-plugin": "0.2.1",
    "redux": "^3.0.0",
    "redux-batched-updates": "^0.1.0",
    "redux-devtools-dock-monitor": "^1.0.0-beta-3",
    "redux-devtools-log-monitor": "^1.0.0-beta-3",
    "redux-form": "^3.0.0-beta-4",
    "redux-thunk": "^1.0.0",
    "reset-storage": "^1.0.1",
    "rndm": "^1.1.0"
  },
  "devDependencies": {
    "archiver": "^0.16.0",
    "babel": "^5.8.23",
    "babel-core": "^5.8.23",
    "babel-eslint": "^4.1.1",
    "babel-loader": "^5.3.2",
    "babel-plugin-react-transform": "^1.0.3",
    "babel-runtime": "^5.8.20",
    "buffered-spawn": "^1.1.2",
    "conventional-changelog": "^0.5.0",
    "cpy": "^3.4.0",
    "css-loader": "^0.21.0",
    "eclint": "^1.1.1",
    "electron-packager": "^5.0.2",
    "electron-prebuilt": "0.34.2",
    "eslint": "^1.3.1",
    "eslint-config-airbnb": "0.1.0",
    "eslint-loader": "^1.0.0",
    "eslint-plugin-babel": "^2.1.1",
    "eslint-plugin-react": "~3.6.3",
    "espower-babel": "^3.3.0",
    "express": "^4.13.3",
    "extract-text-webpack-plugin": "^0.9.0",
    "fixpack": "^2.2.0",
    "globstar": "^1.0.0",
    "html-webpack-plugin": "^1.6.1",
    "is-travis-ci-build-for-tag": "^1.1.1",
    "json-loader": "^0.5.2",
    "mocha": "^2.3.0",
    "npm-check-updates": "^2.2.0",
    "power-assert": "^1.0.0",
    "react-transform-catch-errors": "^1.0.0",
    "react-transform-hmr": "^1.0.1",
    "redbox-react": "^1.0.5",
    "redux-devtools": "^v3.0.0-beta-3",
    "rimraf": "^2.4.3",
    "silence-chromium": "^2.0.0",
    "style-loader": "^0.13.0",
    "touch": "^1.0.0",
    "webfont-dl": "^0.1.2",
    "webpack": "^1.12.0",
    "webpack-dev-middleware": "^1.2.0",
    "webpack-hot-middleware": "^2.1.0",
    "yargs": "^3.23.0"
  },
広告

独自ドメインのGitHub Pagesをhttpsにするのは出来なくはないけど

GitHub Pagesに独自ドメインあててて、httpsではなくhttpで運用している。github.io でそのままやるならhttpsも使える。

https://sanemat.github.io/talks/

で、cloudflareが提供しているflexible SSL や他社のそれと同等のサービスを使えば、ユーザーにはhttpsで提供することが出来る。
cf_ssl_settings
What do the SSL options (Off, Flexible SSL, Full SSL, Full SSL Strict, SSL Only) mean? – CloudFlare Support

設定はこんな感じ。
GitHub Pages Now (Sorta) Supports HTTPS, So Use It

とはいえ、visitorとcloudflare間だけがhttpsになっても、何を保護してるんだろう。アドレスバーの見た目が緑になるのはデカイのと、なんとなく安心感は出るけど、ねえ。無線AP使った時に安心とかはあるか。
GitHub PagesとCDNだとそもそもfastly挟んでて高速だし。

フォームとか個人情報送受信とかそういうのには使えないhttps。うーーん。あんまり誠実に思えなくてためらってる。

GitHubの二段階認証のSMS届かない

2015-06-06には届いていたGitHubからのSMSが今日2015-06-20数回やっても届かなかった。ググると日本はGitHubのSMS認証の対象外らしく、じゃあなんで今まで来てたんだ? +81 と入れてたのか? と不思議になるのだった。これ届かないの困るよな、ということでauthenticator iphoneに変えた。

Cron for GitHub作った

Cron for GitHub gem作った。

TL;DR

場所はどこでもいいけど、たとえばHerokuから、定期的にブランチを切ることで、webhookを起動させて、GitHubエコシステムを強制起動するコマンド及びgem。

使い方

毎日とか毎週とかHeroku Schedulerから定期実行出来る。Heroku Schedulerは10min/1hour/1day しか選べないが、例えばスクリプト側で、起動されたうちの月曜日の場合は、とか書けばいい。Githubが割とローレベルのapiを提供してくれているので、branch作ったりbranch消したりしてるのにgit clone不要。さくさく快適すぎる。

gemの機能は正確にはcreate branch for github, clear branches for github で、cronにするかどうかは、このgemの外側でどうつかうかなんだけど、名前のインパクト重視で、ひとまず cron for github。

appの使いかた面はこっちに書いた。
GitHubとHerokuの組み合わせでCron for GitHub – Saddler – checkstyle to anywhere

背景

ビルドを定期的にできればいいのに、という要望はtravis-ciに昔からあって、実現していない。

Nightly Builds – CircleCIがそれかと思ったら、これは

簡単な話が「外から環境変数を追加した上で、特定のブランチに対するテストを実行させる」機能

CircleCIを使ってbundle updateを定期実行する – Qiita

で、起動時にはcurlで起こしてやる必要があった。curlはどこから叩くんだっていう。

ciを定期的に叩きたい構想はしばらく前から持っていて、その一つの実現がTachikoma.io。でもTachikoma.ioが提供するのは Dependency Update as a Service 一つだけだった。これgithubのwebhook起点にすればいいのでは?という発想に気づいてからさくさくっと出来た。

自画自賛で神gem。更にCron for GitHubにHerokuデプロイボタンを付けたリポジトリも用意。tagsとか消したら死ぬ系のrefは消せないようにしてるけど、考慮漏れてたら追加は必要。

いやーpacksaddleプロダクト便利だなあ。

Tachikoma.iostraregy: none相当のことが完全にできる上、スケジューラーで実行間隔指定、プルリクエストを送らないなどTachikoma.ioやtachikoma gemで指定しづらかったところが解消できる、完全上位互換ですね。

参照

GitHub statusを簡単に通知するコマンドを作った

GitHub statusとは、TravisCIとかがビルド中は黄色で、成功失敗を赤緑でPull Request上で表してくれるあれ。それを簡単に通知するコマンドとそのgemを作った

2015-02-28-github-status-notifier-pending

詳しい使い方はこっちに書いた。
CI環境からGitHubのPull Requestのstatusを簡単に通知するコマンド – Saddler – checkstyle to anywhere

readmeにもあるとおり、こう使う。

# before your command
$ github-status-notifier notify --state pending

$ SOME_YOUR_COMMAND

# after your command
$ github-status-notifier notify --exit-status $?

branchとかリポジトリとかsha1とか、TravisCI/CircleCIのビルドのURLの生成とか、それなりにいい感じに動いてくれるのが特長。シンプルに実行したいだけならjoker1007/github-commit-status-updater こっちを使うと良い。

これ系のproduct今ガッと作ってpacksaddle プロダクトとしてまとめてる。もうちょっと作る。

GitHubのorganization のアプリケーションポリシーがちょっとだけ前進した2015

3行:
* organization単位で3rd partyアプリケーションのホワイトリスト作れるようになった。
* admin権限ある人が、そのorganizationで使える3rd partyアプリケーションにGrantする。
* adminじゃない人は使いたいときは、権限承認するところでadminにrequest出せるっぽい。

Organization-approved applications
An integrator’s guide to organization application policies

使う側としては、歓迎する機能で、何が起きるかはentry眺めてわかる。

3rd partyアプリ作る人やること:
質問来たら、この説明できるようになればok。

adminの人やること:
restrictにする
そうすると一律全部拒否になるので、
Settings -> Applications -> Authorized aplications -> View
Screen Shot 2015-02-01 at 21.57.43
で 個別にGrant Access する
memberからrequest来たら承認/無視する

課題:
依然、ユーザーのprivate reposをtachikoma.ioのように使おうとすると「そのユーザーのprivateリポジトリも含めた全リポジトリ全organizationのwrite権限を渡す or Not」の雑なscopeしかないので、会社/プロジェクト用の別githubアカウント作ったり、外のサービスとの連携用のgithubアカウント作ったり、で凌ぐしかないのは変わらない。
まあ雑じゃないscopeにしたら管理しきれないってのはもちろんあるけど。

感想:
自分がorganizationのadminじゃなくて、private repos対象のアプリケーション書いてて、organizationのapi keyじゃなくて個人のapi keyで開発してる時にハマるかもなーと思った。そんな環境があるのかは知らない。
あやしいところ、あるいはtoken盗られた後に気付かなそうなところ、には権限okしないようにしましょう、って言っても難しいもんなあ。だいたいおれだけは大丈夫と思ってるはず。

chromeのextensionのリリースでSPOFが多い

chromeのextension確認もメンドイし俺がspofだ!ってなってしまう。複数人がリリースできるようにするにはどうするんだっけ? リリース用のグーグルグループを作って、そこにリリース権限与えて、そこにgoogle acountを追加するんだっけ? 前調べた時の情報をググるキーワード忘れたので出てこない(Group Publishingと判明!!)。あと、この場合store税はまた払うのだろうか?とか。ちょろっと試作する情報はあふれてるけど運用していく情報が足りてない うまくググれない

今回ちょっと考えたのは、app/ と dist/ の構成にしてて、READMEとCHANGELOGは外側にあるんだけど、それでいいんだっけ? なところ
あとconventional-changelog使ってみようと思ったらconventionsが覚えられそうにない。これmergeするときにcommitログ表記に気をつければいいのかなあ。いまいちまだ馴染めてない。完璧自動生成で本当にリリース前に自動生成するだけにすれば回るのかなあ。デフォルトだとタグからタグまで読むから、バージョンを上げて、そこはコミットせずに、changelog作って、とかやっていくのかな。なんか、うん、飲み込めてない。

Group Publishingだ!! SOナイス!
Allow another developer to manage a Chrome extension/app in the Chrome Web Store – Stack Overflow
About Group publishing – Chrome ウェブストア ヘルプ

Release Do Not Merge WIP for GitHub v1.0.3

tl;dr

Do Not Merge WIP for GitHub v1.0.3 catches up GitHub’s html update.
Do Not Merge WIP for Githubのv1.0.3をリリースした。GitHubのhtmlの更新をキャッチアップした。

Known issue

Do not work well with pjax
pjaxでうまくうごかないところがある。
After pjax:complete, this extension does not work well · Issue #22 · sanemat/do-not-merge-wip-for-github
After add ‘wip’ to title, then I can hit merge button · Issue #21 · sanemat/do-not-merge-wip-for-github