ジョギング
- 移動の為, お休み
- 左足踵の痛みが続く...
日課
- お休み
- そろそろ, 再開する... まじで
奥阿蘇
黒川温泉を更に山奥に進んでやまなみハイウェイを少し下ったところに良い温泉宿を見つけた.
43 歳の誕生日に奥さんが予約してくれたお宿で秘境感満載でお湯もさることながら, 夕食で出してもらった阿蘇赤牛ステーキが最高だった.
初老丸 Advent Calendar 2018 第 2 日目の記事になる予定です.
先日, 作成した furikake について, 実際にギョームで使ってみて, 自分が欲しいなあと思った機能の追加や発見したバグ等をコツコツ修正しています.
furikake | RubyGems.org | your community gem host
詳細は README をご一読下さい.
カレントディレクトリに addons
ディレクトリを作成して, addons
ディレクトリ以下に Ruby スクリプトを放り込むと, Wiki に載せたいドキュメントを簡単に追加出来るようになってます. 尚, 以下のように furikake setup
を実行した際に addons
ディレクトリを作成するかどうか確認されますので, 合わせてご確認下さい.
$ bundle exec furikake setup [setup] creating .furikake.yml... [setup] .furikake.yml already exists. [setup] create `addons` directory? [y/n] y [setup] creating `addons` directory. [setup] `addons` directory created.
以下 Ruby スクリプトの簡単なサンプルです. 変数 values
の [['value1', 'value2'], ['value3', 'value4']]
と変数 contents
のハッシュの構造だけを守っていただければ, お好きな情報を投稿することが可能になります.
module Furikake::Resources module Addons class Example def self.report(format = nil) values = [['value1', 'value2'], ['value3', 'value4']] contents = { title: 'Example', resources: [ { subtitle: '', header: ['Title1', 'Title2'], resource: values } ] } Furikake::Formatter.shaping(format, contents) end def self.get_resources # ここは頑張って実装する必要があります end end end end
addons
ディレクトリにスクリプトを放り込むことで, 従来は AWS のリソースのみに対応していたところを, Azure や GCP 等の他のクラウドリソース等も Wiki に一覧として投稿することが可能になったはずです.
以下のようにコマンドラインから実行することしか出来ませんでした.
bundle exec furikake publish
cron や Fargate のタスク機能を利用することで自動的に定期投稿は可能でした. (実際には試せていませんが...)
以下のように実行することで, furikake publish
がバックグラウンドで実行されるようになります.
bundle exec furikake monitor --detach
定期的に (デフォルトは 3600 秒毎) リソースを一覧を取得して, 指定した wiki に情報を投稿します. この機能により, cron の設定等を行うことなくドキュメントの自動更新が可能となります.
デーモン化に伴い, 以下のコードをスーパー参考にさせて頂きました.
Ruby script testing to daemonize a ruby script. · GitHub
この場を借りまして, お礼を申し上げます. 有難うございます.
docker-compose を介して利用出来るように, 超簡単に Dockerfile を書き, docker-compose.yml を揃えました.
よろしければ, ご利用下さいませ.
awspec でも利用させて頂いて, いい感じでリリースページを作ってもらえるので利用してみました.
以下のブログ記事が参考になります.
furikake の更新情報でした. よろしければ, furikake を使って頂きましてフィードバック頂けると嬉しいです.
新しい Mac を新調することになり, 幾つかのレビューサイトをチェックしていて気づいたのは, 自分は Mac のことを何も知らないこと.
最新の macOS の名前くらいは判るが, Intel CPU のなんちゃらかんちゃらとか GPU がどーだこーだとか, このアプリは手放せない神アプリだとか, 検索結果に踊る文言を読んでいるとチンプンカンプンなので, どうしたもんだか... 久しぶりに MacFan とか雑誌を買ってみようかと思う.
とりあえず, メモリは大盛りの方が幸せになれるということは解っているものの, 後は, 今の自分の Mac の使い方やライフスタイルを鑑みて検討する必要があるなあ...くらいしか想像が及ばない.
何を基準に選べば良いのやら.
プルリクエストは静かにクローズされました. 丁寧にコメント頂いて嬉しかったです.
初老丸 Advent Calendar 2018 第 1 日目の記事になる予定です.
octorelease という Gem をリリースする際に過去のプルリクエストを git log から拾ってリリースノートを自動生成するツールを利用しようとしたら, それに依存する hub でハマってしまったのでメモしておきます.
octorelease (厳密に言うと, hub) を利用するにあたって, 事前に Github API を利用する為に ${HOME}/.config/hub
に以下のような設定を YAML で設定しておきます.
--- github.com: - user: xxxxxx oauth_token: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
この YAML の書き方が実は問題だったのです... (後述)
rake octorelease
を実行すると以下のような例外が発生しました.
rake aborted! NoMethodError: undefined method `[]=' for nil:NilClass /path/to/vendor/bundle/ruby/2.5.0/gems/hub-1.12.4/lib/hub/github_api.rb:511:in `block in yaml_load' /path/to/vendor/bundle/ruby/2.5.0/gems/hub-1.12.4/lib/hub/github_api.rb:502:in `each' /path/to/vendor/bundle/ruby/2.5.0/gems/hub-1.12.4/lib/hub/github_api.rb:502:in `yaml_load' /path/to/vendor/bundle/ruby/2.5.0/gems/hub-1.12.4/lib/hub/github_api.rb:483:in `load' /path/to/vendor/bundle/ruby/2.5.0/gems/hub-1.12.4/lib/hub/github_api.rb:456:in `initialize' /path/to/vendor/bundle/ruby/2.5.0/gems/octorelease-0.0.6/lib/octorelease.rb:9:in `new' /path/to/vendor/bundle/ruby/2.5.0/gems/octorelease-0.0.6/lib/octorelease.rb:9:in `block in <top (required)>' ... Tasks: TOP => octorelease
例外メッセージを見る限りだと, octorelease が依存している hub というライブラリで問題が発生しているようです.
hub というコマンドラインツールで, ライブラリとしても利用が可能なようです. あまり詳しく見ていないので, 間違っていたら指摘をお願い致します.
ちなみに, 現在は Golang で書き直されているようです.
例外のメッセージを見てみると, hub の github_api.rb というコードの 511 行目で何か起きているようです. 以下, 前後のコードの抜粋です.
... def yaml_load(string) hash = {} host = nil string.split("\n").each do |line| case line when /^---\s*$/, /^\s*(?:#|$)/ # ignore when /^(.+):\s*$/ host = hash[$1] = [] when /^([- ]) (.+?): (.+)/ key, value = $2, $3 host << {} if $1 == '-' host.last[key] = value.gsub(/^'|'$/, '') else raise "unsupported YAML line: #{line}" end end hash end ..
メソッド名を見ると, YAML ファイル (${HOME}/,config/hub) を読み込んで解析するようなメソッドのようですので, irb を起動してこのメソッドをデバッグしてみたいと思います. 用意する YAML ファイルは, 以下のような内容となります.
github.com: - user: foobar oauth_token: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
試しに Ruby 標準の YAML ライブラリを利用して解析してみます. 尚, 検証に利用する Ruby の環境は以下の通りです.
$ ruby --version ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-darwin17
以下のように, Ruby 標準 YAML ライブラリの load_file メソッドを利用してファイルから YAML を読み込みます.
irb(main):001:0> require 'yaml' => true irb(main):003:0> YAML.load_file('sample.yml') => {"github.com"=>[{"user"=>"foobar", "oauth_token"=>"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}]}
ちゃんと解析されて Ruby のハッシュオブジェクトとして読み込まれました. 次に hub で YAML を読み込んでみます. octrelease のソースコードを見る限りだと, hub の Hub::GitHubAPI::FileStore というクラスをインスタンス化する際に YAML ファイルを引数として渡してあげると良さそうです.
irb(main):004:0> require 'hub' => true irb(main):006:0> Hub::GitHubAPI::FileStore.new 'sample.yml' Traceback (most recent call last): ... 5: from /path/to/vendor/bundle/ruby/2.5.0/gems/hub-1.12.4/lib/hub/github_api.rb:456:in `initialize' 4: from /path/to/vendor/bundle/ruby/2.5.0/gems/hub-1.12.4/lib/hub/github_api.rb:483:in `load' 3: from /path/to/vendor/bundle/ruby/2.5.0/gems/hub-1.12.4/lib/hub/github_api.rb:502:in `yaml_load' 2: from /path/to/vendor/bundle/ruby/2.5.0/gems/hub-1.12.4/lib/hub/github_api.rb:502:in `each' 1: from /path/to/vendor/bundle/ruby/2.5.0/gems/hub-1.12.4/lib/hub/github_api.rb:511:in `block in yaml_load' NoMethodError (undefined method `[]=' for nil:NilClass)
冒頭の例外と同じ内容の例外が発生しました. この例外を回避する為, 試行錯誤した結果, 以下のような YAML ファイルの中身にすることで例外を回避することを確認しました. (github.com:
以下に半角スペースのインデントが無い状態です.)
github.com: - user: xxxxxx oauth_token: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
この YAML を一行にすると以下のようになります.
---\ngithub.com:\n- user: foobar\n oauth_key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n
これを, 標準の YAML ライブラリで読み込んでみます.
irb(main):001:0> require 'yaml' => true irb(main):002:0> YAML.load("---\ngithub.com:\n- user: foobar\n oauth_key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n") => {"github.com"=>[{"user"=>"foobar", "oauth_key"=>"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}]}
一応, YAML として読み込むことが出来るようですが, こちら をざっくりと読んでみたところ, YAML 自体は半角スペースによるインデントを使って構造化しているので, 半角スペースでインデントは必須なんではなかろうかと考えていますが, github.com:
以下に半角スペースのインデントが無い状態でも問題は無いようです.
インデントが無い YAML でも問題は無かったのですが, 少々納得がいかなかったので, 引き続きデバッグを進めてみたいと思います.
hub の github_api.rb, 511 行目前後にフォーカスしてみます.
... when /^([- ]) (.+?): (.+)/ key, value = $2, $3 host << {} if $1 == '-' host.last[key] = value.gsub(/^'|'$/, '') ...
文字列として読み込まれた YAML を 1 行毎に正規表現を使ってキャプチャして特殊変数の $1
〜 $3
に放り込んでいるようです. そして, $1
が -
であれば, host
という変数に空の {}
(ハッシュ) を追加して, $2
をハッシュのキー (変数 key
) として, $3
を key
に対する値 (変数 value
) としてハッシュを生成していくことを意図しているようです.
では, このコードを含む yaml_load メソッドだけを切り出して, 以下のような小さなコードでデバッグしてみたいと思います. デバッグには byebug という Gem で配布しているデバッガを利用します.
require 'minitest/autorun' class HubYamlLoadTest < Minitest::Test def test_yaml_load_my_pattern yaml = "---\ngithub.com:\n - user: foobar\n oauth_key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" expect = {"github.com"=>[{"user"=>"foobar", "oauth_key"=>"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}]} assert_equal yaml_load(yaml), expect end end def yaml_load(string) hash = {} host = nil string.split("\n").each do |line| case line when /^---\s*$/, /^\s*(?:#|$)/ # ignore when /^(.+):\s*$/ host = hash[$1] = [] when /(^[- ]) (.+?): (.+)/ key, value = $2, $3 host << {} if $1 == '-' require 'byebug'; byebug # ここにデバッガを差し込む host.last[key] = value.gsub(/^'|'$/, '') else raise "unsupported YAML line: #{line}" end end hash end
byebug の詳しい使い方については, 他の書籍やサイトをご覧下さい.
このテストコードを実行してみます.
$ bundle exec ruby test.rb Run options: --seed 54106 # Running: [25, 34] in /Users/kawahara/sandboxies/octorelease/test.rb 25: host = hash[$1] = [] 26: when /(^[- ]) (.+?): (.+)/ 27: key, value = $2, $3 28: host << {} if $1 == '-' 29: require 'byebug'; byebug => 30: host.last[key] = value.gsub(/^'|'$/, '') 31: else 32: raise "unsupported YAML line: #{line}" 33: end 34: end (byebug)
byebug のプロンプトが現れ, デバッガを差し込んだ次の行で処理が一時停止しています. この状態で, 各変数にどのような値が格納されているのか確認しています.
... (byebug) key "- user" (byebug) value "foobar" (byebug) host []
変数 host
が []
と空の配列になっており, 変数 host
はハッシュである前提で host.last[key]
が実行されるので, キーに変数 key
が代入することが出来ずに例外が発生してしまっているようです.
以下のような変更を加えてみます.
$ diff -u test.rb fix.rb --- test.rb 2018-11-22 07:09:51.000000000 +0900 +++ fix.rb 2018-11-22 07:09:42.000000000 +0900 @@ -25,7 +25,8 @@ host = hash[$1] = [] when /(^[- ]) (.+?): (.+)/ key, value = $2, $3 - host << {} if $1 == '-' + host << {} if $1 == '-' or $2 =~ /^\s*-\s*/ + key.gsub!(/^\s*-\s*|^\s*/, '') require 'byebug'; byebug host.last[key] = value.gsub(/^'|'$/, '') else
この状態で, 変更したコード (fix.rb) を実行してみます.
$ bundle exec ruby fix.rb Run options: --seed 49205 # Running: [26, 35] in /Users/kawahara/sandboxies/octorelease/fix.rb 26: when /(^[- ]) (.+?): (.+)/ 27: key, value = $2, $3 28: host << {} if $1 == '-' or $2 =~ /^\s*-\s*/ 29: key.gsub!(/^\s*-\s*|^\s*/, '') 30: require 'byebug'; byebug => 31: host.last[key] = value.gsub(/^'|'$/, '') 32: else 33: raise "unsupported YAML line: #{line}" 34: end 35: end (byebug) key "user" (byebug) value "foobar" (byebug) host [{}]
変数 host
に空のハッシュ {}
が格納されていることを確認しました. では, デバッガを外して実行してみます.
$ bundle exec ruby fix.rb Run options: --seed 17705 # Running: . Finished in 0.001235s, 809.7167 runs/s, 809.7167 assertions/s. 1 runs, 1 assertions, 0 failures, 0 errors, 0 skips
いい感じですね. また, github.com:
以下に半角スペースのインデントが無い YAML でも正しく解析されるかも検証しておきたいので, 以下のようにテストを書きました.
require 'minitest/autorun' class HubYamlLoadTest < Minitest::Test def test_yaml_load_ok_pattern yaml = "---\ngithub.com:\n- user: foobar\n oauth_key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" expect = {"github.com"=>[{"user"=>"foobar", "oauth_key"=>"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}]} assert_equal yaml_load(yaml), expect end def test_yaml_load_my_pattern yaml = "---\ngithub.com:\n - user: foobar\n oauth_key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" expect = {"github.com"=>[{"user"=>"foobar", "oauth_key"=>"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}]} assert_equal yaml_load(yaml), expect end end def yaml_load(string) hash = {} host = nil string.split("\n").each do |line| case line when /^---\s*$/, /^\s*(?:#|$)/ # ignore when /^(.+):\s*$/ host = hash[$1] = [] when /(^[- ]) (.+?): (.+)/ key, value = $2, $3 host << {} if $1 == '-' or $2 =~ /^\s*-\s*/ key.gsub!(/^\s*-\s*|^\s*/, '') # require 'byebug'; byebug host.last[key] = value.gsub(/^'|'$/, '') else raise "unsupported YAML line: #{line}" end end hash end
これを実行してみます.
$ bundle exec ruby fix.rb Run options: --seed 30867 # Running: .. Finished in 0.003929s, 509.0354 runs/s, 509.0354 assertions/s. 2 runs, 2 assertions, 0 failures, 0 errors, 0 skips
いい感じです. 2 つの YAML パターンが, この yaml_load メソッドで解析出来るようになったはずです.
hub のリポジトリを見ると, Ruby で実装された hub は既にサポートされていないのではと思ってしまう程, master ブランチには Golang のコードが並んでいます. ブランチを github:1.12-stable を切り替えると様子が一変して Ruby で実装された hub を拝むことが出来ます. さらに, .travis.yml を見ると, 以下のようにテスト対象にはいにしへの Ruby バージョンが並んでいます.
sudo: false language: ruby before_install: - script/bootstrap - export PATH=~/bin:"$PATH" script: script/test bundler_args: --without development --deployment --jobs=3 --retry=3 cache: bundler rvm: - 1.8.7 - 1.9.2 - 1.9.3 - 2.0.0 - 2.1.5 - 2.2.0-preview2 notifications: email: false
どうやら, Ruby 版の hub はメンテナンスされていないようです. また, #1591 を見ろということでリンクが張っていますが, このリンクを踏んでも 404 となりページが存在していません. このページが無いことすらメンテナンスされていないということは本当にメンテナンスされていないと思って間違いないようです.
ですが, 一応, 以下のようなプルリクエストを作成してみました.
きっとマージされること無く, ひっそりとクローズされることでしょう.
既にサポートされていないであろう Ruby 版 hub の .travis.yml を見ながら考えました. そもそも Ruby には YAML を読み込んだり, 書き出したりするライブラリがあるのに, なぜ hub にはオレオレ YAML パーサーが実装されているのか. 答えのようなコミットを発見しました.
2013 年の 12 月くらいまでは標準の YAML パーサーを利用していたようですが, YAML を解析する速度が思ったよりも遅かった為, オレオレ YAML パーサーに書き換えたとのことでした.
なるほどです. 実際に標準の YAML とオレオレパーサーでどのくらいの速度の違いがあるかについては別の機会に検証してみたいと思います.
octorelease から始まって, それが依存する hub のソースコードを見ながら色々と学びがありました. きっとプルリクエストはマージされずにまぼろしとなってしまうとは思いますが, これを糧に引き続き Ruby 道に精進して参る所存です.
有難うございました.
ハンズオン資料や以下のコードも良く出来ていたと思うし, 何より, 講師やサポートメンバーの方のケアが最高だった. こんなホスピタリティが高いハンズオンは初めてかもしれない.
Golang 自体については, 自分の理解度の低さに辛くなったので, 改めて Golang について勉強するぞ!という強い思いに至った次第.
久しぶりに美人居酒屋で. 鮭のホイル焼きと湯豆腐を食べる. 今日でキープしていたボトルが無くなった. 次の来店時にキープしよう.
先日, 作成した furikake について, 色々と修正した上で gem 化してリリースしました.
furikake | RubyGems.org | your community gem host
README をご一読下さい.
gem 化に伴い, gem コマンドや bundler を利用してインストールすることが出来ます. また, 従来は .furikake.yml を手動で生成していましたが, 以下のように setup
コマンドを実行することで生成することが出来ます.
$ bundle exec furikake setup
以下のように .furikake.yml が出力されます.
resources: aws: - clb - vpc_endpoint - security_group - ec2 - kinesis - lambda - alb - directory_service - elasticsearch_service - vpc - rds backlog: projects: - space_id: 'your-backlog-space-id' api_key: 'your-backlog-api-key' top_level_domain: 'your-backlog-top-level-domain' wiki_id: your-wiki-id wiki_name: 'your-wiki-name' header: > # Test Header [toc] ## Sub Header footer: > ## Test Footer ### Sub Footer
あとは, 環境に応じて space_id
や api_key
等の情報を修正して下さい. また, resources
キー以下の属性値はドキュメント化する AWS リソースの一覧となります. 将来的には, この属性値を削除することで, 指定したリソースのみドキュメント化出来るようにする予定です.
Markdown だけでなく, CSV や Google SpreadSheet 等の異なるフォーマットにも対応する為, 内部処理を少し修正しています. それに伴い, 以下のように, リソースタイプの実装が少しだけシンプルにしたつもりです.
module Furikake module Resources module Ec2 def report(format = nil) instance = get_resources contents = { title: 'EC2', resources: [ { subtitle: '', header: ['Name', 'Instance ID', 'Instance Type', 'Availability Zone', 'Private IP Address', 'Public IP Address', 'State'], resource: instance } ] } Furikake::Formatter.shaping(format, contents) end def get_resources ec2 = Aws::EC2::Client.new params = {} instances = [] loop do res = ec2.describe_instances(params) ...
get_resources
メソッドは従来から変更はありませんが, report
メソッドには以下のようなハッシュオブジェクトと Furikake::Formatter
クラスのクラスメソッド shaping
だけを呼び出すようにしています.
... contents = { title: 'EC2', resources: [ { subtitle: '', header: ['Name', 'Instance ID', 'Instance Type', 'Availability Zone', 'Private IP Address', 'Public IP Address', 'State'], resource: instance } ] } Furikake::Formatter.shaping(format, contents) ...
↑ ここの部分だけを AWS リソースに応じて修正する感じです. (get_resources
の実装は従来どおり必要です.)
まずは気になるところからテストを追加しています. 個々のリソースタイプについても随時テストを追加していく予定です.
furikake の更新情報でした. よろしければ, furikake を使って頂きましてフィードバック頂けると嬉しいです.
久しぶりの香椎, 俺たちの「よしもと」にて正三おじさんの退職祝いを盛大に執り行った (笑).
多美子おばさんも参加してくれて, とても盛り上がったと思う. おじさん, おばさん共に楽しんで頂けたようで本当に良かった.