くりにっき

フルスタックキュアエンジニアです

Itamae v1.9.13をリリースした

CHANGELOGに載ってる修正

https://github.com/itamae-kitchen/itamae/blob/master/CHANGELOG.md#v1913

Fixed. Can not create empty file

github.com

fileリソースで空ファイルを作れないバグの修正。

github.com

元々remote_fileやtemplateで空ファイル作れていたのですが、いつのバージョンからか作れなくなっていたのを前職時代に観測してました。

その時は execute "touch xxx"で雑に回避してたのですが、今回mitamaeと同じ修正を入れて解決しました。

CHANGELOGに載ってない修正

Fixed rspec warning

github.com

travisのweekly buildのログを見てたら

expect {
  subject.event(:name, :arg) { raise "name is failed" }
}.to raise_error

でwarning出ていたのを直しました。

Fixed. test is failing after run test

github.com

これ英語が怪しいんだけど、要はローカルで開発してる時に同じコンテナに対して同じレシピを2回適用するとItamaeがコケるという現象があって直しました。

-execute "mkdir /tmp/link-force-no-dereference1"
+execute "mkdir -p /tmp/link-force-no-dereference1"
 link "link-force-no-dereference" do
   cwd "/tmp"
   to "link-force-no-dereference1"
   force true
 end

Pixelaで自分のツイート数を草化した

こんな感じです。

リポジトリ

github.com

仕組み

Twilogスクレイピングして、CircleCIのcronの仕組を使って定期的(当日分は1時間おき、前日分は1日1回)にツイート数をグラフを更新しています。

スクリプトRubyで書いていますが、定期実行部分に関してはCircleCIとDockerイメージだけで完結しているので実行環境にRubyをインストールする必要はありません。

Twilogを使ってる理由

  • 特定の日時のツイート数だけを取得するだけならTwitter APIよりTwilogからとってくる方が早いし楽
    • Search APIでも日付を絞り込むことはできるのだが、自分の経験上たまに検索結果が取れなくなることがあるのであまり信用してない *1
    • 特定のユーザのツイートを取得するならuser timeline APIを使うのが確実なのだが直近3200件分しかとれない。
      • 当日分と前日分くらいなら問題ないんだが、それでもuser timetimeで特定の日付を絞ってツイートを取得するのは若干面倒
    • 過去分を一括投入しようとするとRateLimitに引っかかってしまう
    • Twilogであれば https://twilog.org/sue445/stats に全期間(自分が登録した以降全部)のツイート数が入ってるので、何回もAPIを叩くよりもhtmlを1枚スクレイピングする方が圧倒的に楽と判断
  • Twitter APIだと自分でアプリを作る必要があってお手軽感がない
  • 過去分投入だけなら公式のツイートダウンロードでもいいんだけど、OSSとして公開しようとすると英語で説明を書くのが面倒だしスクリプト化しづらい

使い方

基本的には https://github.com/sue445/tweet_pixels/blob/master/README.md に書いてる通りにコマンドを実行するだけ。

最初のPixela登録とグラフ作成だけローカルで実行する必要があります。

Rubyでやりたい場合は https://github.com/sue445/tweet_pixels をforkしてclone後に下記のようなコマンドを実行してください。

cp .env.example .env
vi .env
bundle install --path=vendor/bundle

./bin/console
# register
@client.create_user(agree_terms_of_service: true, not_minor: true)

# create graph
@graph.create(name: "Daily tweets", unit: "Tweets", type: "int", color: "sora")

他言語の場合は同じようなことをよしなにやってください。

直近1年分のツイートを一括投入する場合は bundle exec rake update_multi を実行

グラフ作成後はCircleCIのEnvironment Variablesに

  • TWITTER_ID
  • PIXELA_USERNAME
  • PIXELA_TOKEN
  • PIXELA_GRAPH_ID

を登録してください。

f:id:sue445:20181021110424p:plain

Tips

CircleCIでボットを運用する時のノウハウとして、Chat Notificationsで普段使いのチャット(自分の場合Slack)を登録しておくとエラー時にすぐに気付けるので便利です。

f:id:sue445:20181021110928p:plain

1時間に1回にSlackに通知がくると逆にウザいので、自分はFixed/Failed Only(ビルドが失敗から成功に戻った時と、ビルドが失敗した時のみ通知)をよく使っています。

所感

今回のに限ったことじゃないんだけど、CircleCIの設定ファイルに実行環境(利用するDockerイメージ)やcronの設定も含めることができるので、GitHubリポジトリでボットの自動実行環境もセットで配布できるのすっごい便利だ。。。

*1: 過去に実際、一時期自分のツイートだけがSearch APIで取れなくなる事象がありました https://twitter.com/sue445/status/897968605567926272

Pixela v0.2.0を出した

github.com

https://github.com/sue445/pixela/blob/master/CHANGELOG.md#v020

大きな変更点

Pixelaのエンドポイントをリソースっぽく扱うためのメソッドを作りました

~v0.1.1

# create graph
client.create_graph(graph_id: "test-graph", name: "graph-name", unit: "commit", type: "int", color: "shibafu")

# register value
require "date" 
client.create_pixel(graph_id: "test-graph", date: Date.today, quantity: 5) 

v0.2.0~

# create graph
client.graph("test-graph").create(name: "graph-name", unit: "commit", type: "int", color: "shibafu")

# register value
client.graph("test-graph").pixel(Date.today).create(quantity: 5)

経緯

sue445.hatenablog.com

先日作った時は最速(サービスリリース24時間以内?)で全APIを網羅したクライアントライブラリを出せたことに全く不満はなかったのですが、実際に使ってみるとPixelaのAPIを愚直にRubyのメソッドにマッピングしただけなので冗長な感は否めませんでした。

そこで、aws-sdk に対する aws-sdk-resources みたいな感じで使えるようにしました

何が嬉しいの?

上の例だけだといまいちメリットが分かりづらいですが、メソッドチェーンを分解して変数抽出することによりAPIをたくさん呼ぶ場合でもなるべく同じ引数を何回も書く手間を減らしてます

graph = client.graph("test-graph")

graph.create(name: "graph-name", unit: "commit", type: "int", color: "shibafu")
graph.pixel(Date.today).create(quantity: 5)

v0.1系までのメソッドとv0.2系以降のメソッドでどっちが使いやすいかはケースバイケースだと思うので、古い方のメソッドを消すつもりは今の所ありません。(実際APIのテストコードを書く時は前者の方が書きやすいので)

詳しい使い方はリファレンスを見てください。

https://www.rubydoc.info/gems/pixela/0.2.0/Pixela/Client

Itamae v1.9.12を出した

メンテナ業の報告です

リリースノート

https://github.com/itamae-kitchen/itamae/blob/master/CHANGELOG.md#v1912

v1.9.12の概要

PRを眺めてCIが通れば即マージできそうなPRに対して「遅くなってごめん。LGTMだけどCIがコケてるので最新のmasterを取り込んだらマージするよ(意訳)」ってコメントして、反応があったPRをマージしてリリースしました

リリースノートに書いてる変更内容

jail backend: add support of FreeBSD Jail (itamae jail)

github.com

FreeBSD Jailサポート

docker backend: Fixed edit action of file resource doesn't work with docker backend

github.com

ItamaeのCIを直す過程で見つけたバグの修正。(後述)

itamae docker でfile editできなかったのを修正

github.com

dry run実行時に (dry-run) がつくようになった

リリースノートに書いてない変更内容

WerckerからTravisCIに移行

github.com

  • CircleCIと違ってWerckerはワークフロー設定はymlだけで完結しない(メンテナが設定画面をいじる必要がある)ためPR送る側からすると不便
  • マトリクステストする場合はTravisCI使うのが圧倒的に楽
    • CircleCIでもできないことはないが記述量がTravisCIに比べて多くなる
  • DigitalOceanで立てたVMに対して itamae ssh するのがつらい
    • ssh越しだとCIが安定しない(ローカルのDockerコンテナに対してitamae実行する方がまだ安定する)
    • ssh越しのitamae実行なのでローカルに対してitamae実行するよりも確実にオーバーヘッドがある
    • VirtualBoxのtrusty(ローカル用)とDIgitalOceanのtrusty(CI用)は完全に同一じゃないので、ローカルとCIで挙動が違うのは罠
      • ローカル実行時にもDIgitalOcean使えばいい話なんだけど、アカウント登録して課金しないとVM立てられないのでハードルが高い
      • DockerイメージならローカルとCIで環境を完全に同一にできる
  • 最近の流行だとDockerでCIするのがモダン

みたいな思いを込めて英語でPRのdescriptionを書きました。

かつ、このCIを通すために既存のテストやバグも直しました。

控えめに言って今年一番頑張ったPRでした。

関連エントリ

blog.unasuke.com

マージ後にTravisCIを設定したり、PRをずっと放置してた関係で落ちてたテストを直してようやくmasterブランチのテストが全部通るようになりました。

gemspec更新

github.com

せっかくなので新メンテナの名前をgemspecに追加

今後もマージ後なるはやでリリースしていきたいと思うのでよろしくおねがいします

Pixelaのクライアントgemを作った

Pixelaとは?

blog.a-know.me

pixe.la

このビッグウェーブに乗るしかないと思ってまずはクライアントgemを作りました。ご査収ください

github.com

一応現時点で存在するAPIは全部対応してます。

Packer with mitamaeこぼれ話 #技術書典

Packer with mitamae のおまけというか付録みたいなやつ

sue445.hatenablog.com

副産物

sue445/vagrant-aws

vagrantvagrant-awsvagrant-serverspecがインストールされてるDockerイメージです

https://hub.docker.com/r/sue445/vagrant-aws/

github.com

CircleCI上でVagrantvagrant-awsを使おうとするとvagrant-awsのインストールにそこそこ時間がかかって*1つらかったので、Dockerイメージを自分で作りました。

https://github.com/sue445/dockerfile-vagrant-aws/blob/master/.circleci/config.yml を見てもらえれば分かると思いますが、Vagrantの最新版が出ると自動でDockerfileを更新してtag push&Dockerイメージの自動アップデートをするようにしています。

特定のミドルウェアのDockerイメージを自動更新したい場合に参考になるかと思います。

この辺の最新のバージョンを取得する部分だけ変えてやれば他のDockerfileリポジトリでも流用できるはず。*2

https://github.com/sue445/dockerfile-vagrant-aws/blob/1c73ea65415a649635d36d727519f8441445676e/.circleci/config.yml#L63-L66

packer-provisioner-serverspec

packerでserverspecを使うためのprovisionerです

github.com

https://github.com/unifio/packer-provisioner-serverspec をforkしてるだけなんですが、本家でバイナリを提供してなくて不便だったので自分でビルドしたバイナリをReleasesに置いています。

https://github.com/sue445/packer-provisioner-serverspec/releases

fork元とfork先でコードレベルでの差分はありません。

執筆中に気づいたのですが、Packerはgolang製のバイナリで実行環境に依存しないのがウリなのに、packer-provisioner-serverspecはserverspecを実行するために実行元にRubyがインストールされている必要があるのがイケてないんですよね。。。

provisioner内でDockerのコンテナを起動してその中でserverspecを実行すればまだマシになるとは思ってるのでそのうちなんとかしたいという思いはあります。(思いだけ)

Packer以外の事例だと https://www.npmjs.com/package/serverless-python-requirementsdockerizePip がDockerコンテナ内で pip install しているので同じようなことをやればいいはず。

書名がPacker with mitamaeなのにどうしてリポジトリ名がtechbookfest5-itamaeなのか?

github.com

最初はmitamaeじゃなくてItamaeでやろうとしてたのですが、やりたいこと的にmitamaeの方が筋が良さそうということに気づいて方向転換しています。

途中で方向転換したのがこのコミットです。

https://github.com/sue445/techbookfest5-itamae/commit/5307461804342b1e0af2fac7151da7aa95e06506

途中でリポジトリ名を変えるという手もあったのですが、リポジトリ名を変えた時にCircleCIの挙動がどうなる(再設定すれば勝手に同期してくれるのか、作り直しになるのか)か自信がなかったのでそのままにしてます。

書こうと思ってやめたこと

最初にサンプルプロジェクト( https://github.com/sue445/techbookfest5-itamae )を一通り作ってから執筆したのですが、サンプルプロジェクトにあって紙面にない要素があります

rubocop-itamae

github.com

せっかく自分が作ったものだし一応rubocopの静的チェックを入れてはいるのですが、実際に執筆しようとするとPackerでmitamaeを使うという本質から外れてしまうのでオミットしました。

執筆期間

  • サンプルプロジェクト作成:2週間
  • 執筆:4週間
  • レビュー:1ヶ月間

執筆自体は9月頭に終わっていて、技術書典までの残り1ヶ月間をレビューや当日の準備などにあてています

執筆用のリポジトリのコミット頻度はこんな感じです。

f:id:sue445:20181014170513p:plain

被チェック数

f:id:sue445:20181014170855p:plain

マイページの被チェック数を定期的に取得してスプレッドシートに送りつけてグラフ化してたのですが、技術書典の開催中も被チェック数が増えていて面白かったです。

f:id:sue445:20181014170915p:plain

*1:10分くらい

*2: 実際このリポジトリの設定も https://github.com/sue445/dockerfile-heroku-cli/blob/master/.circleci/config.yml をほぼ流用しています

syobocaliteを作った

しょぼいカレンダー のLite(軽量)版APIクライアントを作りました

github.com

モチベーション

元々は https://github.com/sue445/cure-mastodon-botshttps://github.com/xmisao/syobocal を使っていたのですが、以下のような難点がありました

  • 番組の時間をTime.parse でpaeseしてるので *1、HerokuのようにJST以外の環境で動かすと時間がずれる
  • レスポンスが HashArray なので、レスポンスを利用側で拡張したい時にオープンクラスしてモンキーパッチをあてづらい

1年くらいはアプリ側でモンキーパッチをあてて使っていたのですが *2、別のボットを作る時にcure-mastodon-botsと同じことをやるのが嫌だったのでgem化しました

使い方

Ruby本体の TimeactivesupportActiveSupport::TimeWithZone に対応してます。

require "syobocalite"
require "time"

start_at = Time.parse("2018-10-07 08:30:00")
end_at   = Time.parse("2018-10-07 09:00:00")

# or

Time.zone = "Tokyo"
start_at = Time.zone.parse("2018-10-07 08:30:00")
end_at   = Time.zone.parse("2018-10-07 09:00:00")

# Get programs that start between 8:30 and 9:00
Syobocalite.search(start_at: start_at, end_at: end_at)

http://cal.syoboi.jp/cal_chk.php は日付単位でしか取得できないですが、自分の用途だと時間で絞りたいことが多いので時間を引数に渡せるようにしています。*3

*1:https://github.com/xmisao/syobocal/blob/v0.9.1/lib/syobocal/calchk.rb#L27-L28

*2: https://github.com/sue445/cure-mastodon-bots/blob/cd4940a07e2bd38d2671838e44fe6eac9cf799f7/lib/syobocal_ext.rb

*3: https://sites.google.com/site/syobocal/spec/db-php だと時間単位で取得できるのは知ってるのだけど、そっちは番組名とサブタイトルが同時に取得できないので不便