Windows 上の Jenkins から curl で TypeTalk API を叩いてメッセージを送信するまでの道のり

Windows 上の Jenkins はとにかく罠が多い…。

Windows 上の Jenkins から「シェルスクリプトの実行」で curl コマンドを実行し、TypeTalk API を叩いて任意のメッセージを投稿してみようと思った。それがかなりつまづいたのでまとめる。

TypeTalk API の叩き方

今回は、シェルスクリプトから TypeTalk API を叩いて、任意のメッセージを投稿させようと思った。

予め TypeTalk のトピックの設定から、Client ID と Client Secret という認証用の文字列を発行しておく。

そして、TypeTalk API の公式リファレンスを基にすれば、以下の2行のコードでメッセージが投稿できるはずだった…。

# アクセストークンを発行し、jq で curl のレスポンスをパースして変数に入れる
access_token=$(curl https://typetalk.com/oauth2/access_token -X POST --data-urlencode "client_id=【Client ID】" -d "client_secret=【Client Secret】" -d "grant_type=client_credentials" -d "scope=topic.post" | jq -r .access_token)

# メッセージ「こんにちは!」を指定のトピックに投稿する
curl https://typetalk.com/api/v1/topics/【トピック ID】 -X POST -H "Authorization:Bearer $access_token" --data-urlencode "message=こんにちは!"

Mac のターミナルで上の2行を実行すると、正常にメッセージが送信できた。しかし、Windows GitBash から実行すると、投稿メッセージが文字化けしてしまっていた。

GitBash の curl で POST すると日本語が文字化けする

最初は Mac の curl と Windows GitBash 付属の curl で仕様が違うのか?と思ったが、どうもそうでもないみたいだ。curl--verbose (or -v) オプションを渡すと通信内容の詳細が表示されるので見てみたが、解決の糸口には繋がらず…。

色々調べてみると、近しいポイントでつまづいていそうな人は見かけるが、そのものズバリな答えがなくて結構困った。

とりあえず、1つ目の文献で触れられていた @file オプションを利用したやり方にしてみた。メッセージはシェルスクリプト内で用意したいので、リダイレクトで msg.txt というファイルに書き込むことにした。

access_token=$(curl https://typetalk.com/oauth2/access_token -X POST --data-urlencode "client_id=【Client ID】" -d "client_secret=【Client Secret】" -d "grant_type=client_credentials" -d "scope=topic.post" | jq -r .access_token)

echo 'message=こんにちは!' > msg.txt
curl https://typetalk.com/api/v1/topics/【トピック ID】 -X POST -H "Authorization:Bearer $access_token" -d @msg.txt

-d じゃなくて --data-urlencode か?とか思ったけどダメ。そこで chcp のことを思い出して msg.txt を Notepad++ で開いてみると、このファイルは Shift-JIS 形式になっていた。どうもリダイレクトのエンコーディングはコードページに左右されるっぽい。

$ chcp.com でコードページを確認してみると、932 ≒ Shift-JIS と返ってきた (英語版の Windows を使ったりしていると 932 ではなく 437 = 英語 が返される)。そこで GitBash 上で $ chcp.com 65001 と実行して、UTF-8 形式に変更してみてから msg.txt を生成してみた。コマンドプロンプトなら拡張子 .com を省略できるが、GitBash では .com を省略できないので chcp ではなく chcp.com と叩く。

chcp.com 65001

access_token=$(curl https://typetalk.com/oauth2/access_token -X POST --data-urlencode "client_id=【Client ID】" -d "client_secret=【Client Secret】" -d "grant_type=client_credentials" -d "scope=topic.post" | jq -r .access_token)

echo 'message=こんにちは!' > msg.txt
curl https://typetalk.com/api/v1/topics/【トピック ID】 -X POST -H "Authorization:Bearer $access_token" -d @msg.txt

こうすると、msg.txt を BOM なし UTF-8 で出力できた。GitBash 上で叩いた限りだとコレで日本語が送信できた。

やった〜じゃあコレを Jenkins のジョブに書き込んでやろう〜と思って実行してみると、なぜか Jenkins を経由して GitBash を使った場合だと文字化けする…。一体何故…。

Jenkins 上で実行する GitBash のリダイレクトは Java の実行環境に左右される

Jenkins の「シェルスクリプトの実行」経由で GitBash を利用すると、なんか環境が変なところがある。今回色々調べてみると、どうも Java 製である Jenkins が、Java の実行環境に影響されて、シェルスクリプトの実行内容にも影響が出ているっぽいことが分かった。

このサイトにならって、Jenkins のシステムプロパティで file.encodingsun.jnu.encoding を見てみると、Shift-JIS (MS932) となっていた。そこでコチラのページを参考に、

システム環境変数で JAVA_TOOL_OPTIONS-Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8 に設定し、Jenkins のサービスを再起動

してみたところ、chcp.com 65001 をせずとも、msg.txt 生成時のリダイレクトが UTF-8 (BOM なし) に切り替わり、文字化けせず curl で POST できた。

access_token=$(curl https://typetalk.com/oauth2/access_token -X POST --data-urlencode "client_id=【Client ID】" -d "client_secret=【Client Secret】" -d "grant_type=client_credentials" -d "scope=topic.post" | jq -r .access_token)

echo 'message=こんにちは!' > msg.txt
curl https://typetalk.com/api/v1/topics/【トピック ID】 -X POST -H "Authorization:Bearer $access_token" -d @msg.txt

シェルスクリプトはコレだけで良くなった。よしよし、じゃあ次はメッセージに改行を入れてみよう…と思ってまたハマった。

投稿メッセージに改行を入れるには

改行をメッセージに入れたくて、message=ほげ\nふがmsg.txt に書き込んでみたが、うまく改行されなかった。ログを見ると、curl の送信時に \ 記号が \\ とエスケープされているようだった。&br; という Backlog 記法も試してみたけどダメだった。

解決法を探ったところ、実際に改行を入れたファイルを用意して、-d ではなく --data-binary オプションで送ると良いそうだ。

改行を含んだテキストファイルを吐くには、cat によるヒアドキュメントを利用する。

access_token=$(curl https://typetalk.com/oauth2/access_token -X POST --data-urlencode "client_id=【Client ID】" -d "client_secret=【Client Secret】" -d "grant_type=client_credentials" -d "scope=topic.post" | jq -r .access_token)

cat << EOF > msg.txt
message=ほげ
ふが
EOF

curl https://typetalk.com/api/v1/topics/【トピック ID】 -X POST -H "Authorization:Bearer $access_token" --data-binary @msg.txt

こうすると、msg.txt 内で実際に改行されている部分が \n としてそのまま送信されるので、投稿メッセージにコメントを含めることができた。

あと、message= 部分のイコール記号の意味合いでバッティングするものと思われるが、本文中に半角イコール = があると、それ以降の文字列が送られない。どうもうまくエスケープできなかったので、本文で使用するイコール記号は全角のみ使用することにした。いいやり方ないだろうか…。

以上

今回やったことは、結局「エンコーディングを UTF-8 に揃える」ということだ。Jenkins 起動時に -Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8 オプションを与え、もし必要なら chcp.com 65001 コマンドを利用して、UTF-8 な msg.txt を用意できれば良い。

あとはこのファイルを curl POST する時に --data-binary を使って渡してやれば上手くいった。

「GitBash on Jenkins on Windows」な環境はエンコーディングとコードページの罠がキツいのう…。