読者です 読者をやめる 読者になる 読者になる

koni blog

東京のウェブエンジニア koni です!ウェブサービスをガシガシ作っていきます!

macOS SierraにしたらPHPStormがエラーで起動しない問題の解消 (java, git)

PHPStorm 技術メモ

対応バージョンのJavaが入ってないと言われるので、エラーメッセージに従って、Appleのホームページから落としてきて入れる。

Can't start git /usr/bin/git
Probably the path to Git executable is not valid. Fix it.

というエラーが出た。

whichすると、パスはあっている。

$ which git
/usr/bin/git

$ git
xcrun: error: invalid active developer path (/Library/Developer/CommandLineTools), missing xcrun at: /Library/Developer/CommandLineTools/usr/bin/xcrun

コマンドラインツールがinvalidらしいので、macOS Sierra用のCommand Line toolsをダウンロードする。

Command Line toolsは、XCode 8を入れるとついてくるが、XCode要らないのに5GB近くのものを入れたくないので、Command Line toolsだけインストールする。

Apple Developerにログインして、ダウンロードページに行き、Command Line Tools (macOS 10.12) for Xcode 8を入れる。

https://developer.apple.com/download/more/

問題なく使えるようになった。

この問題、OSのアップデートするといつも起きるのだが、自動的にやってほしい気がする。 できるのかな。

Google Cloud SDK 入れたら、Google App Engine の開発サーバー dev_appserver.py でPHPが動かなくなる問題への対処

技術メモ Google App Engine

Google Cloud SDKを入れたら、Google App Engine(GAE)用のローカルの開発サーバー dev_appserver.pyが以下のようなエラーを吐いて、動かなくなってしまいました。

$ cd /path/to/project_root
$ dev_appserver.py .
INFO     2016-06-15 02:15:02,903 devappserver2.py:769] Skipping SDK update check.
INFO     2016-06-15 02:15:02,964 api_server.py:205] Starting API server at: http://localhost:52471
INFO     2016-06-15 02:15:02,972 dispatcher.py:197] Starting module "default" running at: http://localhost:8080
INFO     2016-06-15 02:15:02,974 admin_server.py:116] Starting admin server at: http://localhost:8000
ERROR    2016-06-15 02:15:03,980 php_runtime.py:348] The PHP runtime is not available
Traceback (most recent call last):
  File "/Users/koni/src/tool/google-cloud-sdk/platform/google_appengine/google/appengine/tools/devappserver2/php_runtime.py", line 344, in new_instance
    self._check_binaries(php_executable_path, gae_extension_path)
  File "/Users/koni/src/tool/google-cloud-sdk/platform/google_appengine/google/appengine/tools/devappserver2/php_runtime.py", line 265, in _check_binaries
    raise _PHPBinaryError('The development server must be started with the '
_PHPBinaryError: The development server must be started with the --php_executable_path flag set to the path of the php-cgi binary.

--php_executable_pathでPHP-CGIのバイナリのパスを指定しろとのこと。 GAEのPHPのバイナリは、GAEのプラグインが入っている専用のものなので、ローカルのPHP使うならエクステンションを入れないといけません。

原因

ではなぜいままで動いていたかというと、dev_appserver.pyはこれまでGoogle Cloud SDKのものではなくて、GoogleAppEngineLauncher.app内蔵のものを使っていたようです。 それがGoogle Cloud SDKを入れた時にそちらのものを使うようになってしまったようです。 GoogleAppEngineLauncher.appには、GAE用のエクステンションが入ったphp-cgiが内蔵されているので、それを使って動いていたようです。

解決方法

dev_appserver.pyは、Google Cloud SDKのものではなく、GoogleAppEngineLauncher.appのものを使う。

私の環境では以下のパスにありました。

/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/dev_appserver.py

こっちを使ったら普通に動きました。

ちょっと逃げですが、ローカル環境だし動いたからこれでいいかと。。

参考

The PHP Development Server  |  PHP  |  Google Cloud Platform

Let's encryptで無料のSSL/TLS証明書を取得して独自ドメイン運用のGAEに入れて使う

Google App Engine 技術メモ

無料でSSL/TLS証明書が発行できるLets' Encryptプロジェクトが、2016年4月、ついにベータ版から正式版になりました。

jp.techcrunch.com

無料といっても、MozillaやGoogle, Facebookなどのウェブ企業や、シスコやアカマイなどのネットワーク関係の大御所も支援しているプロジェクトなので、審査がなく全自動で発行されるタイプの1,000-2,000円くらいの有料のSSL証明書と全く遜色ありません*1

業界的にウェブの暗号化に向けてこのようなプロジェクトを動かしているのは素晴らしいですね。

GAEへのSSL/TLS証明書の導入

さて、独自ドメイン運用をしているGoogle App Engine(GAE)のサイトをSSL/TLS対応をしてみましょう。

Let's Encrypt では、証明書発行の際に、ドメインの所有の確認のため、指定されたURLにファイルをアップロードする必要があります。よくあるプロセスですね。

通常のサーバーの場合は、この確認用のファイルの設定や、Apache/nginxなどの設定もcertbotというプログラムが証明書の取得とあわせてやってくれるのですが、GAEの場合証明書の設定はGUIでしかできません。

そこで、証明書の取得のみ行うmanualモードで行います。なのでこのためにわざわざサーバーを立てる必要はありません。以下では Mac OSX 10.11.4で作業しました。

1. 独自ドメインの設定を終えておく

まずは、独自ドメインがGAEで表示できる状態にしておきます。 あとのステップで、Let's Encrypt から取得しようとする証明書用のサーバーにリクエストが来て所有の確認が行われるので、DNSの設定などは済ませておく必要があります。

2. 証明書の取得

まずは、Let's Encrypt のクライアントのインストールを行います。

cloneします。10MBほどあるので、そこそこ時間かかります。

$ git clone https://github.com/certbot/certbot
$ cd certbot

それでは早速証明書を取得してみましょう。初回は必要なプログラムのインストールが行われるので、時間がかかります。 また環境のrootのPWを求められる場合があります。

初回のみ、メールアドレスを聞かれるのと、利用規約への同意を求められます。 ちなみにこのcert-onlyのonlyというのは、インストールも行うrunに対して、インストールはしない、という意味です。 また以前は、letsencrypt-autoでしたが、2016年5月からcertbot-auto に変更になったそうです。

$ ./certbot-auto certonly --manual
# PWを求められるかも

必要なミドルウェアのインストールが終わると、次のような画面になります。 ここに取得予定のドメインを入力します。カンマ区切りスペース区切りで複数可。

f:id:konisimple:20160602230440p:plain

次にクライアントのIPアドレスがログに記録され公開されることに対する同意確認画面。Yesで進みます。

f:id:konisimple:20160602230413p:plain

次に以下のようなメッセージが表示されます。

Make sure your web server displays the following content at
http://cheetahapp.net/.well-known/acme-challenge/gTIjcnRpydLV5lcRDD-b-2TcTRh4YyZC26jDSZOx2ck before continuing:

gTIjcnRpydLV5lcRDD-b-2TcTRh4YyZC26jDSZOx2ck.5Hvrmt1oBopQ03IDSAdTZRRoXZACheMnLi_Y272bLx4

If you don't have HTTP server configured, you can run the following
command on the target server (as root):

mkdir -p /tmp/certbot/public_html/.well-known/acme-challenge
cd /tmp/certbot/public_html
printf "%s" gTIjcnRpydLV5lcRDD-b-2TcTRh4YyZC26jDSZOx2ck.5Hvrmt1oBopQ03IDSAdTZRRoXZACheMnLi_Y272bLx4 > .well-known/acme-challenge/gTIjcnRpydLV5lcRDD-b-2TcTRh4YyZC26jDSZOx2ck
# run only once per server:
$(command -v python2 || command -v python2.7 || command -v python2.6) -c \
"import BaseHTTPServer, SimpleHTTPServer; \
s = BaseHTTPServer.HTTPServer(('', 80), SimpleHTTPServer.SimpleHTTPRequestHandler); \
s.serve_forever()"
Press ENTER to continue

ここははやる気持ちを抑えてまだENTERは押しません!!

指示どおりに、Let's Encryptからのメッセージの存在確認用URLにテキストを置きます。 上記の場合は、以下のURLで以下が表示されるようにします。

URL: http://cheetahapp.net/.well-known/acme-challenge/gTIjcnRpydLV5lcRDD-b-2TcTRh4YyZC26jDSZOx2ck

内容: gTIjcnRpydLV5lcRDD-b-2TcTRh4YyZC26jDSZOx2ck.5Hvrmt1oBopQ03IDSAdTZRRoXZACheMnLi_Y272bLx4

GAEでは以下の様に設定します。

lets_encrypt.txt:

gTIjcnRpydLV5lcRDD-b-2TcTRh4YyZC26jDSZOx2ck.5Hvrmt1oBopQ03IDSAdTZRRoXZACheMnLi_Y272bLx4

app.yaml:

- url: /.well-known/acme-challenge/gTIjcnRpydLV5lcRDD-b-2TcTRh4YyZC26jDSZOx2ck
  static_files: lets_encrypt.txt
  upload: lets_encrypt.txt

デプロイします。

$ appcfg update .

さっきのURLをクリックして内容が表示されることを確認したら、エンターを押します。

しばらくすると、以下のような内容が表示されて無事証明書が取得できました。

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at
   /etc/letsencrypt/live/cheetahapp.net/fullchain.pem. Your cert will
   expire on 2016-08-31. To obtain a new or tweaked version of this
   certificate in the future, simply run certbot-auto again. To
   non-interactively renew *all* of your ceriticates, run
   "certbot-auto renew"
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

証明書と秘密鍵は/etc/letsencrypt/live/[domain_name]にあります。

$ sudo ls /etc/letsencrypt/live/cheetahapp.net
cert.pem    chain.pem   fullchain.pem   privkey.pem

秘密鍵を復号化します。

$ sudo openssl rsa -in /etc/letsencrypt/live/[domain_name]/privkey.pem -out /etc/letsencrypt/live/[domain_name]/privkey_no_pass.pem

3. GAEへの登録

GCPのコンソールから、GAE→設定→SSL 証明書→新しい証明書をアップロードの順に開きます。 「PEM でエンコードされた X.509 公開鍵証明書」の欄に、fullchain.pemを、 「復号化された PEM でエンコードされた RSA 秘密鍵」の欄にprivkey_no_pass.pemをペーストします。

ここで復号化を忘れると選択した秘密鍵は有効ではないようです。というエラーメッセージが表示されます。これ結構はまりましたw

4. 動作確認

無事に設定できると、以下のようにアドレスバーが緑になります。やった!!

f:id:konisimple:20160602231741p:plain

課題 証明書が3ヶ月で切れてしまう

SSL証明書は3ヶ月で切れてしまいます。通常年単位での更新なのでこれはちょっと面倒です。 サーバー自体にcertbotを入れれば、/etc/letsencrypt/live/[domain_name]に常に最新に証明書のシンボリックリンクが貼られるので手間はないのですが、GAEだとそうはいきません。 証明書の再取得はコマンドできるのですが、GAEへの証明書の送信はAPIなどあるのでしょうか。軽く探したところは見つかりませんでした。

3ヶ月おきにフォーム行くのは地味に面倒なので、そこに1000円払うと割りきって既存のSSL業者を使う、というのもありかもしれません。

まとめ

GAEのSSL対応、これがあれば5分くらいで終わってしまいますね。 いつもSSL対応は、秘密鍵作ってそれでCSR作って会社のサイトのフォームに行って発行依頼して、メールクリックして、証明書DLしてなど複雑な手順だったので、かなり簡単になりました。 しかも全部無料、提供しているのがシスコ・アカマイ、Google、Facebookなので割と安心、ということで、銀行などが使っているアドレスバーに緑の組織名が表示されるEV SSL以外はどんどん移行が進むんじゃないでしょうか。

参考

qiita.com 秘密鍵の復号化で同じはまりをされていた方w

qiita.com 同様のことをGCEのインスタンスで行った事例。

*1:どちらも改ざんと盗聴には効果がありますが、なりすましには無力です。

Twitter API「Read-only application cannot POST」というエラーの解決方法

Twitter API 技術メモ

Twitter API でPostのAPIを叩いたら以下のような見慣れないエラーが。

Read-only application cannot POST

原因は、Twitterのアプリの設定で、Access LevelReadになっていること。

僕の場合は、Read and writeにはしていたのですが、コンシューマーキーを再生成していないことでした。

Access Levelを変えるページにちゃんと書いてありました。 小さくて薄い字で・・

Access Levelを変えたらコンシューマーキーを再生成しましょう。

Note:
Changes to the application permission model will only reflect in access tokens obtained after the permission model change is saved. You will need to re-negotiate existing access tokens to alter the permission level associated with each of your application's users.

Twitterのツイートを分析したワードクラウドをいい感じに調整して表示する

whotwi Twitter API 形態素解析 PHP d3.js

こんにちは。

本日whotwiに、ツイートを分析し、よく使うことばを分析・グラフィカルに表示する機能をリリースしました。

こんな感じでツイートによく含まれるワードをワードクラウド風に表示します。

試しに企業のTwitterアカウントの中の人運営で有名なタニタさんのアカウントの結果で表示してみましょう。

f:id:konisimple:20160414184423p:plain

こんな感じになります。

タニタさんのアカウントの特徴的な言葉が大きく表示され、数回しか言っていないようなものも小さな文字で周囲に表示されています。また「おはよう」などの回数は多いが一般的な語はやや小さめに表示されています。

whotwiは、アカウントの特徴を調べるツールなので、このようにできるだけ人と違う部分が際立って見えるように、また、意味のある語だけが出るように微調整を行いました。

使ったもの

ワードクラウドの処理としては、形態素解析でテキストを単語に分けて、出現回数の多いものから表示すればOKですね。

  • MeCab 最も有名な形態素解析エンジン 文章を単語ごとに分割します
  • mecab-ipadic-neologd mecabで使う辞書です。新語に弱いIPA辞書の弱点を克服するため、wikipediaやはてなキーワードから不足部分を補った辞書です
  • php-mecab mecabのPHPバインディングです
  • d3-cloud D3.jsのワードクラウドがかけるプラグインです。
  • D3.js

上記を入れると、ワードクラウドが作れます。

ワードクラウドの課題

ただ、回数順で多いものから表示するだけだと、以下のような問題が起きてしまい、つまらない結果になってしまいます。

  • 動詞「する」など、一般的すぎる語が大量に発生してしまう
  • 特徴的なワードでも、出現回数だけだと上位に来ないので表示されない
  • 意味のない単語が多数出現してしまう(特に非自立語)
  • 形態素単位で分解すると、接尾語・接頭語も切り離されてしまい、意味不明になる

そこで、表示にあたっていくつかの工夫を行い、いい感じの表示になったのでその方法をかいてみます。

新語が正しく処理できるようにする

mecabの辞書としてはIPA辞書などが一般的ですが、これだと新語が入っておらず、「Apple Watch」などが正しく処理できません。 「Apple」と「Watch」が別々になってしまいます。分けてしまうと意味が変わってしまうので、わけずに処理したいわけです。

新語などのデータとしては、Wikipediaやはてなキーワードなどが使えそうですが、データを取ってきてmecab用の辞書を作りなおして、とかなり面倒です。

そこで、この課題を解決するべく、@overlastさんが公開されたmecab-ipadic-neologdという辞書を使うことにしました。

これで、たいていの語は分割されずに済むようになりました。

単語の細かい表記の違いで集計がずれないようにする

生のテキストで集計すると、全角半角の違い(「Apple」と「APPLE」)や、長音の連続(「スーパー」と「ス~パー」)などの微妙な違いで、同じ単語が別々にカウントされてしまいます。

そこで、解析前に事前に前処理しておきます。 前処理の方法は、上の@overlastさんが辞書を作る際に書かれたドキュメントが大変参考になります。

Regexp.ja · neologd/mecab-ipadic-neologd Wiki · GitHub

品詞ごとに重みをつける

品詞ごとにも、表示する大きさを変えたり、表示しなくしたりします。 例えば助詞、記号、また非自立の動詞・名刺、接尾の動詞、助動詞はそれ単体では意味がわからないので、削除します。

また、場所や人の名前が入りがちな固有名詞の重みを高めにします。

例えば、「東京に行きたい」であれば、以下のように分解されます。

東京   名詞,固有名詞,地域,一般,*,*,東京,トウキョウ,トーキョー
に 助詞,格助詞,一般,*,*,*,に,ニ,ニ
行き  動詞,自立,*,*,五段・カ行促音便,連用形,行く,イキ,イキ
たい  助動詞,*,*,*,特殊・タイ,基本形,たい,タイ,タイ
。 記号,句点,*,*,*,*,。,。,。

「東京」は固有名詞なので高めにします。助詞「に」、助動詞「たい」などは今回は必要ないので捨てます。

単語ごとの特徴量で重みをつける

また、単語の中でも特徴っぽいものと、そうでないものがあります。 ここでの「特徴」は、全体で見たときに出現頻度が低いという意味です。

今回のワードクラウドでは、できるだけ他の人との違いを可視化させたいので、ツイート一般であまり出現しない単語を大きく表示させたいです。

ちょうどmecabでは、「単語生起コスト」というのがあります。mecabの形態素解析の過程で使われる辞書に含まれるパラメータの一つです。単語の出現頻度が高いほど、「単語生起コスト」は低くなります。実際にはこれと「連接コスト」を合計し、合計コストの低い組み合わせが選択されます。

今回は、あまり出現しない単語を強調したいので、単語生起コストの高いものが出やすいようにします。

接尾語・接頭語をくっつける

形態素的には、「山手線」は、「山手/線」になってしまいます。

山手   名詞,固有名詞,地域,一般,*,*,山手,ヤマテ,ヤマテ
線 名詞,接尾,一般,*,*,*,線,セン,セン
に 助詞,格助詞,一般,*,*,*,に,ニ,ニ
乗り  動詞,自立,*,*,五段・ラ行,連用形,乗る,ノリ,ノリ
たい  助動詞,*,*,*,特殊・タイ,基本形,たい,タイ,タイ
。 記号,句点,*,*,*,*,。,。,。

意味的に「山手線」を一つとして扱いたいので、接尾の名詞である「線」は前とくっつけます。 同様に、接頭語も合体させます。

  • (接頭語)+(あとの形態素)
  • (あとの形態素)+(接尾語)

mecabのサイトに品詞ID(pos_id)の定義があるので、それを参考に結合します。

MeCab: 品詞 ID

ワードクラウドの見た目の調整

今回、ワードの表示にはD3.jsのd3-cloudを使いました。

サーバーサイドで画像を合成する方法だとそこそこのリソースが必要なこと、それぞれの語をクリックできるようにしたかったことが理由です。

実装はサンプルを見ながらやればさくっとできます。

フォントはデフォルトだと細めのフォントになるのですが、太めのフォントの方が見栄えがいいので、ヒラギノProN W6の指定にしました。

文字のフォントを変える場合、実際に描画する要素への指定のほか、d3.layout.cloudのオプションとしても渡す必要があるので注意が必要です。 これがずれると、位置の計算は別のフォントで計算されてしまい、表示が少しずれてしまいます。

     d3.layout.cloud()
            .font(function (d) {
                return 'Impact, HiraKakuProN-W6, sans-serif';
            })

他サイトの事例

実はこれ作り終わるまであまり他サイトの事例を見ていませんでした。 二番煎じどころか何番煎じなんだっていうw

テキストマイニングツール(UserLocal)

テキストマイニング 無料ツール by ユーザーローカル

Twitterのツイートか、自由入力テキストからワードクラウドを作れるサービス。

f:id:konisimple:20160414230153p:plain

こちらはwhotwiと同じでd3-cloudでの実装でした。 単語の抽出はおそらく重み付けなしで名詞と動詞のみ表示というシンプルな実装(ぽい)です。

「シロクモ」(白ヤギコーポレーション)

プレスリリースによると2014年10月20日にリニューアル版がリリースされた、白ヤギコーポレーションさんの「シロくも」ですが、現在はつながるURLが見つけられませんでした。

自分のワードクラウドが作れる「シロくも」がパワーアップ! 「シロクモ」「ラブクモ」「マイクモ」の3種類をリリース - 株式会社白ヤギコーポレーションのプレスリリース

「クロクモ」(@lightnet328さん)

クロクモ

f:id:konisimple:20160414230321p:plain

丸ゴシック体でかわいいですね。 このワードクラウドはブラウザではなくサーバーでPNG画像として生成しているものでした。

ソースコードを公開されています。

ワードクラウド画像の生成には、Pythonのamueller/word_cloudを使っているようです。

シロクモと名前似てるし、フォントも似てるので同じ方が作ったのかも(未検証)

あなたが Twitter で、よく使っている言葉は?(vonvon)

あなたが Twitter で、よく使っている言葉は?

f:id:konisimple:20160414231626p:plain

こちらは、単語がTwitter Birdの形になってますね、かわいい。

小さめの文字だけ傾けて、読みやすいようにしてますね。 あと色合いがとてもお洒落!

生成はjpgでサーバーサイドでした。関係ないけど、vonvon、GCP使ってるんですね!今風。

参考サイト

Twitter エラー326 「To protect our users from spam and other malicious activity, this account is temporarily locked.」について

技術メモ Twitter API

whotwiで、登録ユーザーのツイートを更新するバッチを回していたら、全体の0.3%くらいの一部のユーザーのTokenでTwitter APIを叩いた際、以下のエラーが出ていた。

{
    "errors": [
        {
            "code": 326, 
            "message": "To protect our users from spam and other malicious activity, this account is temporarily locked. Please log in to https://twitter.com to unlock your account."
        }
    ]
}

To protect our users from spam and other malicious activity, this account is temporarily locked. Please log in to https://twitter.com to unlock your account.

「スパムや不審な活動からユーザーを守るため、このアカウントは一時的にロックされています」とのこと。

これはアプリのコンシューマキーの問題ではなくて、本人のOAuth Tokenの問題。ユーザーに再ログインさせて電話番号聞くやつだと思います。

Twitterのエラーコード一覧にのってないエラーでした。エラーコード一覧に載せてないのはスパム対策がもれないようにか。

Error Codes & Responses | Twitter Developers

GAEのローカルサーバーが起動できない時の対処法

Google App Engine

GAEのローカルサーバー dev_appserver.py を起動しようとしたらエラーが出た。

$ dev_appserver.py .
INFO     2016-04-01 03:16:14,707 sdk_update_checker.py:229] Checking for updates to the SDK.
WARNING  2016-04-01 03:16:14,948 simple_search_stub.py:1126] Could not read search indexes from /var/folders/0y/yxn2c_d91gx0y3l1132kwd0w0000gn/T/appengine.koni-tech.koni/search_indexes
INFO     2016-04-01 03:16:14,953 api_server.py:205] Starting API server at: http://localhost:59482
INFO     2016-04-01 03:16:14,956 api_server.py:648] Applying all pending transactions and saving the datastore
INFO     2016-04-01 03:16:14,956 api_server.py:651] Saving search indexes
Traceback (most recent call last):
  File "/usr/local/bin/dev_appserver.py", line 83, in <module>
    _run_file(__file__, globals())
  File "/usr/local/bin/dev_appserver.py", line 79, in _run_file
    execfile(_PATHS.script_file(script_name), globals_)
  File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/tools/devappserver2/devappserver2.py", line 1040, in <module>
    main()
  File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/tools/devappserver2/devappserver2.py", line 1033, in main
    dev_server.start(options)
  File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/tools/devappserver2/devappserver2.py", line 824, in start
    self._dispatcher.start(options.api_host, apis.port, request_data)
  File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/tools/devappserver2/dispatcher.py", line 194, in start
    _module.start()
  File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/tools/devappserver2/module.py", line 1176, in start
    self._balanced_module.start()
  File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/tools/devappserver2/wsgi_server.py", line 315, in start
    self._start_all_fixed_port(host_ports)
  File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/tools/devappserver2/wsgi_server.py", line 352, in _start_all_fixed_port
    raise BindError('Unable to bind %s:%s' % self.bind_addr)
google.appengine.tools.devappserver2.wsgi_server.BindError: Unable to bind localhost:8080

エラーっぽいファイルパスが出ていたので、そのディレクトリを削除して起動しなおしたらうまく行った。

$ rm -rf /var/folders/0y/yxn2c_d91gx0y3l1132kwd0w0000gn/T/appengine.koni-tech.koni/

search_indexesって書いてあるけど、search使ってないんだけどな・・