Rails 5.2 の Credentials で diff を復号化してから比較する

先日、こんなブログを書きました。

sinsoku.hatenablog.com

この gem の機能に「diff を平文で表示できる」というものがあるけど、 gem 無くても実現出来ちゃったので紹介する。

1. 復号化するスクリプトを用意する

bin/decrypt を用意する。

#!/usr/bin/env ruby

system("rails r 'puts Rails.application.encrypted(\"#{ARGV[0]}\").read'")

実行権限をつけておく。

$ chmod +x bin/decrypt

2. Git の設定をする

まず、 .gitattributes を用意する。

$ echo config/credentials.yml.enc diff=rails_credentials >> .gitattributes

次に、 git config を設定する。

$ git config diff.rails_credentials.textconv bin/decrypt

3. credentials を変更し、git diff を実行する

$ rails credentials:edit
( change something )
$ git diff
Running via Spring preloader in process 42724
Running via Spring preloader in process 42756
diff --git a/config/credentials.yml.enc b/config/credentials.yml.enc
index 1d31b4b..e701793 100644
--- a/config/credentials.yml.enc
+++ b/config/credentials.yml.enc
@@ -4,3 +4,5 @@
 
 # Used as the base secret for all MessageVerifiers in Rails, including the one protecting cookies.
 secret_key_base: 76804339ec1fcd2243da42662528f60dd5044ccb7f9f2479aa10dca307d12f706517ea22f013170f0dec98c8d6f022a62f54c0f031b88587608f272ce9d18c08
+
+foo: hello

こういう Tips みたいなのって rails/rails にプルリク投げた方が良いものなんだろうか。 Issue じゃないしなぁ...。

Rails の Issue とプルリクを毎日読むと勉強になる

最近やっているけど、これ良い勉強になっているのでブログで紹介する。

読むようになったきっかけ

先月、永和システムマネジメントさんのOSSパッチ会に参加しました。

agile.esm.co.jp

この会の懇親会では Rails の Issue やプルリクの話題が多く出ました。 ただ、私が知らない話題もいくつか出ていて、もっと Rails の更新内容を知りたいと思いました。

その結果、酔った勢いで rails/railsWatching にしてみました。

毎日だけど、雑に読む

Rails は活発に開発されているため、Issue(プルリク)は毎日たくさん増えます。 しかし、これを隅々まで目を通すのはとても大変です。

なので、雑に目を通しています。

  • 興味のないやつは読み飛ばす
    • 自分が使用していない機能(ActionCable, ActiveStorageなど)の Issue やプルリク
  • 英語の長文は Google 翻訳を使って、概要だけ読む
  • 忙しいときはタイトルと description だけ読む
    • コメントを全部読むと時間がかかる

大変だと続かないので、まずは出来る範囲で読んでいます。

雑に Issue やプルリクに目を通すだけであれば、意外と短時間で済みます。

Issue(プルリク) の感想を Twitter に投稿する

  • 便利な機能のプルリクを読んだ
  • 自分の知らなかった Rails の挙動を知った

など、何かあったときは Twitter に投稿してます。

なんとなく、アウトプットしておいた方が記憶に残る気がする。

仕事に役立ちそうな情報は社内に共有する

業務中に Rails の Issue(プルリク) を読む事もあるので、役立ちそうな情報は積極的に社内 Slack に流します。

Rails の最新情報を社内に布教してる」ことを言い訳に、業務中でも Rails の Issue(プルリク)を読みやすくします。*1

2月前半のまとめ

ここ最近で Rails の Issue(プルリク) を読んでいて面白かったのをいくつか紹介する。

リダイレクトの 301, 302 の違い

github.com

  • routes.rbredirect メソッド: 301
  • コントローラの redirect_to メソッド: 302

ステータスコードが違う。

301リダイレクトにはいくつか問題があるので302 を使う方が良いとか、デフォルトの挙動を変えるのはキツいので temporary_redirect を導入するとかの話がされている。

英文が長いので、細かいところは自信ない。

accepts_nested_attributes_for で validates_uniqueness_of が効かない

github.com

一度解決したけど再発したみたい。

Rails 5.2.0.rc1 でも再現する。

p = Parent.create
#=> #<Parent id: 1, created_at: "2018-02-11 18:24:53", updated_at: "2018-02-11 18:24:53">
p.update_attributes :children_attributes => [{:name => "Kid"}, {:name => "Kid"}]
#=> true
Child.all.map(&:name)
#=> ["Kid", "Kid"]
c = Child.first
#=> #<Child id: 1, parent_id: 1, name: "Kid", created_at: "2018-02-11 18:24:59", updated_at: "2018-02-11 18:24:59">
c.save
#=> false
c.errors.full_messages
#=> ["Name has already been taken"]

並列テストのプルリク(WIP)

github.com

rails/rails##26703 で System Test のプルリクを投げた方の新作。

Rails 標準で対応されると便利そう。

has_many through で touch が効かない

github.com

touch が効かないという Issue。
コメントに dependent: :destroy で回避策する方法があるけど、それはどうなんだ...って感じがする。

あと、この Issue で through の対象レコードは削除されないことを知った。マジか。

class Post < ActiveRecord::Base
  has_many :taggings
  has_many :tags, through: :taggings
end

class Tagging < ActiveRecord::Base
  belongs_to :post
  belongs_to :tag
end

class Tag < ActiveRecord::Base
end


post = Post.create!
tag = Tag.create!

post.tags << tag
post.tags.count
#=> 1
post.tags.destroy_all

Tag.count
#=> 1

Tag も消えると思っていました。

Relation#pick の追加

github.com

DHHのプルリクで、 pick ってメソッドが追加されている。

Person.where(id: 1).pick(:name)
  # SELECT people.name FROM people WHERE id = 1 LIMIT 1
  # => 'David'

便利そうだけど、戻り値が文字列 or 配列ってのは気をつけて使わないと NoMethodError が出そう。

あと、このプルリクのテストは最初 fail していて、kamipo さんが「Failing the assert will be fixed by #30000 .」とコメントしていた。 それに対するDHHのコメントは「I just changed the test for now」だった。

「30000をマージして、rebaseにしないのかー」とプルリクを見ながら思った。 まぁ、pick のプルリクのためにマージするにしては 30000 は影響範囲が大きいって判断なのかもしれない。

has_many の関連と where! で誤った値が返ってくる

github.com

where! は内部用のメソッドだから where を使って」とコメントされて close されている。

気になったので Rails 5.2.0.rc1 で試したら再現した。

class User < ActiveRecord::Base
  has_many :tasks
end

class Task < ActiveRecord::Base
end


u = User.create!
2.times { u.tasks.create! }

u.tasks.where(user_id: u.id + 1).count
#=> 0
u.tasks.where!(user_id: u.id + 1).count
#=> 0
u.tasks.where!(user_id: u.id + 1).size
#=> 0
u.tasks.tap { |x| x.where!(user_id: u.id + 1) }.count
#=> 0
u.tasks.tap { |x| x.where!(user_id: u.id + 1) }.size
#=> 2

まとめ

Issue(プルリク)を読んでいると勉強になるので、是非 Rails エンジニアは読んでみると良いですよ。

そして、余裕のある方はプルリクを投げてみよう!*2

*1:まぁ業務中にOSS活動していても特に問題にならないけど

*2:私もまだコントリビュート無いので、2018年は頑張りたい

Rails 5.2 の Credentials を各環境でも使えるようにする gem を作りました

Rails 5.2 で追加される Credentials 機能を development, test, staging などで使えるようにする gem を作ったので紹介します。

github.com

Rails 5.2 の Credentials とは?

API token やパスワードなどの情報を暗号化し、リポジトリで管理できるようにする機能です。 暗号化に使用する鍵は config/master.key もしくは RAILS_MASTER_KEY 環境変数を使います。

今までの Rails では秘匿情報を環境変数で管理するのが主流でした。 しかし、この方法では管理する環境変数が多くなってしまったり、バージョン管理できません。

5.2 で導入される Credentials の機能を使うことで、秘匿情報をリポジトリで管理することができるようになり、サーバに設定する環境変数も1つで済むようになります。

Rails 5.1 で追加された encrypted secrets に似ている?

This will eventually replace Rails.application.secrets and the encrypted secrets introduced in Rails 5.1.

引用: https://github.com/rails/rails/blob/v5.2.0.rc1/activesupport/CHANGELOG.md

encrypted secrets は忘れてください。

Credentials の不満点

DHH曰く「productionしか秘匿情報は必要ない」とのこと。

It's only in production (and derivative environments, like exposed betas) where the secret actually needs to be secret.

引用: https://github.com/rails/rails/pull/30067

いや、staging 環境で使う API token とかがあるので他環境でも使いたい...(´・ω・)ッス

別の方が Environment-specific credentials という Issue を作っていますが、 Close されてるし、 Rails では対応されなそう。

各環境の秘匿情報を管理する gem を作った

そんな経緯で RailsEnvCredentials の gem を作りました。

各環境で別々の credentials.yml.enc を使うことができます。

基本的な使い方

Gemfile に gem を追加して、 bundle install してください。

group :development, :test do
  gem 'rails-env-credentials'
end

もし staging 環境で使う場合は group に :staging も追加する必要があります。

development 用の credentials を生成するには env_credentials:edit コマンドを使います。

$ rails env_credentials:edit -e development

development 環境用に credentials-development.yml.enc が生成されます。 鍵は config/master-development.keyRAILS_MASTER_KEY_DEVELOPMENT 環境変数を使います。

秘匿情報の diff を表示する

credentials.yml.enc は暗号化されているため、普通に git diff をしても差分は表示できません。

$ git diff config/credentials.yml.enc
diff --git a/config/credentials.yml.enc b/config/credentials.yml.enc
index 1a69bf0..c0517de 100644
--- a/config/credentials.yml.enc
+++ b/config/credentials.yml.enc
@@ -1 +1 @@
-SN7yXV6C2NZSdp/ij84h6Tox7iJ6VLHWgnhbY7BFe5TTsLa6V9g1D/iI1H5WMCBEECcpL/z096spDpcjXdq1aS2w3Wz9GijvuCiJNA8LaiWzqN9FMDmPB8eJjff+qebG+mN4Wdmc1/LDWV0GvYYQWAzDuYv3Kyv7jrxh/6MQjm2h9+i1AlBj7ofnzppvz9m8yiM2kyoqk/5aFdNiUqCdvzqbxIAXf0qvL/GPezKVOgbLcKL+/ytIFDvQeFc+BJ9d/RHsvOfyy1hSJxAhstJsf9I2WlI8gWYBjNip6nZzEQUCuhJpFEd+tXTzTOt3ZlWHzodQA9W3E+BScDOUgAPM7eJ9eCCDHvSQrvgI9i9KfpuxFP51OTODqYkR1+kfW7g/ZupvAhwMAM504af7zFLgHxad5Q==--UjL1q8veV48t5/oe--pUi30wtwFBJlM5PIJI0U2A==
\ No newline at end of file
+i3rH3HDSUPAwal/Mi8SsFg+UqKfyudpjTxFpYrXxElOvQYo7wsIZ9v5NRe6mew/WnMNiM/B66jJM4KIf1voMRWrTJaGfkWc4ARLjgcgLNu4rj4+LHyZZ2LkkgqZpTHObtLHEH5E/7ZNQVpHJ1CwfMR/SwGgaDPJFVbM3uF3tGSzY0Fyg3jgeyiiU6Vuk+H6CUGFMxDsjnIj1oMrpACX42vDfpy5wZquyzycG6LpmJsFqofirmb9sT1n32mDoA6k138SDLdnUiNpftNGGK8q9+SaT8e5OzZ8CMSeMLD64psRYCL+qZip+ZX0zvdVmSkUaH/y71bkHTChhMNZzHcW69FBNgfirMmWchjePUlw1+LIjrAIQL9JQD5Wv3E5Yl6RRKX0LSsrtUqEmABz9Lxbq9uGh/HEsC2OnrLOGYFvvKYs53Y1lQVF29f3NKSdk--VINiczKtUJKG4hro--oRD2+JF7lbL/wO8d3dP0Mw==
\ No newline at end of file

これは Git の設定を活用することで表示できるようになります。

まず、下記の内容で .gitattributes を作成します。

config/credentials*.yml.enc diff=env_credentials

次に Git に復号化のコマンドを設定します。

$ git config diff.env_credentials.textconv 'rails env_credentials:show --file'

この状態で git diff を実行すると、復号化した内容で差分が表示されるようになります。

$ git diff config/credentials.yml.enc
diff --git a/config/credentials.yml.enc b/config/credentials.yml.enc
index 1a69bf0..c0517de 100644
--- a/config/credentials.yml.enc
+++ b/config/credentials.yml.enc
@@ -4,3 +4,5 @@
 
 # Used as the base secret for all MessageVerifiers in Rails, including the one protecting cookies.
 secret_key_base: 358e4c7f994ffdba2a8994c00bf902303d1456d1f4d4e3d8e6c6c843628de5bc158f061fd1759b7a4a015395627ad9b34a4e8714167da0387695cb9906bbe1ba
+aws:
+  access_key_id: 123

まとめ

production 以外での秘匿情報の管理って需要ありそうだけど、Rails にプルリクを投げて英語で説得する自信は無かったので、とりあえず gem を作ってみた。

もし gem が結構使われて、需要ありそうってのが分かったらプルリクを投げるかもしれない。

2月末に株式会社groovesを退職します

2018年2月末に株式会社groovesを辞めます。

おそらく、退職エントリってやつです。

株式会社groovesでやってきたこと

Forkwellの開発を担当していました。サービスの詳細はリンク先を読んで頂くとして、私が主に関わっていたのは下記の2つです。

特にPortfolioの「エンジニアの技術力を可視化する」というコンセプトは私の提案から始まっていて、仕様の作成、設計、実装とだいたい全てを担当していました。

技術的にやってきたこと

新しい機能の実装以外で、開発環境を色々と改善してきました。

  • Werckerを導入して、ブランチ毎にテストを実行するようにした
    • Jenkinsをリストラした
  • wheneverでcronの設定をコード化した
  • RuboCopを導入して、少しずつコードを直した
  • Rails 5 のリリース日に最速アップグレードをした
  • 自動で毎日bundle updateのプルリクを作るようにした
  • 独自ログイン機構を改善して、最終的にdeviseに置き換えた
    • Email認証が実現できるようになった
    • ログイン処理のコード複雑度を激減させた
  • 稀に落ちるテスト、Slow Testをたくさん直した
    • CIの実行時間を20分から8分に高速化した
  • Serverspec を導入した
  • Terraformを導入して、インフラをコード化した
    • staging 環境のインフラをコード化し、少しずつリファクタリング
    • production 環境のインフラはまだコード化できていない...

パッと思い出したのだけ書き出したけど、他にもたくさん開発環境を改善していたと思います。

今まで退職しなかった理由

開発環境を改善し過ぎてしまい、快適になってしまったのが1番の理由です。*1

  • Ruby/Railsはすぐ新しいバージョンに上がる
  • テストコードもあるので簡単にアップグレードできる
  • プルリクのコードが綺麗で、コミット数も少なめ
  • ちゃんとレビューしてもらえるし、キツい口調のレビューは無い
  • 新しいツールやサービスを導入することに肯定的
  • Railsで困っても弊社取締役の@amatsudaさんに質問できる

Railsエンジニアにとってはなかなか良い開発環境だと思います。

あと、エンジニア向け自社サービスなので自分達で仕様を考えることもできますし、厳しい納期もないためストレスなく開発できました。

なぜ退職するのか?

細かいのはいくつかあるのですが、主なものは下記の3つです。*2

1. 規則正しい勤務が苦手

これ完全に自分のせいなのですが、朝起きるのが苦手だったり、深夜にコードを書くのが好きだったりして、規則正しい生活がとても苦手です。 groovesの勤務時間は9:55〜18:55とそこまで出社時間は早くないのですが、それでも時々遅刻したり、タクシーで出社していました。

この件は1on1で上司に相談したところ、リモート勤務フレックス制度などの制度が導入されました。これにより、最近は柔軟な働き方も出来つつあります。 ただ、個人的に欲しかったのは「10分遅れてもSlackで連絡すれば問題ない雰囲気」や「深夜に仕事しても良く、翌日は遅く仕事しても良い」とかで、これはまだgroovesで実現されていません。

2. 自分の技術力に不安を感じた

転職というイベントは人生に数回しかないため、転職サイトのアクセス数はあまり多くなくて、データ量も少ないです。 勉強会で他社エンジニアの「高負荷の話」や「冗長化の話」などの発表を聞くたびに自分の技術力に不安を感じていました。

もっと色々なサービスのインフラ構成やコードを見たいと思いましたが、それはgroovesにいると難しそうだと感じました。

3. 高い年収や福利厚生の充実している会社が多い

Speeeさんの「個人サーバー代補助制度」やMisocaさんの「勉強会への参加補助制度」など、エンジニア向けの福利厚生が充実している会社はいくつもあります。

groovesはそんなに福利厚生が充実していないので、こういう話を聞くと羨ましいなーと思っていました。 特にRubyKaigi 2017に自費+有給で行ったときは話を聞くたび、「転職しようかな?」ってちょっと心揺れていました。

あと、Forkwell Jobsを見ていたら高い年収帯の求人がいくつもあって、「これ転職したら年収が上がりそう」と時々考えていました。

3月以降の予定

そんな感じで退職意欲が高まってしまったので、辞めることにしました。

ただ、3月以降の予定はまだ決まっていません。

候補1. 週4でgrooves、週1で他社のお手伝い

groovesとの音楽性の違いにより正社員は辞めますが、Forkwellの目指している世界と開発環境はとても好きなのでフリーランス週4で続けられないか打診しました。 上司からも快諾を頂けたので、おそらくフリーランスで継続になりそうです。

週1は「別の会社のお手伝い」か「OSSの活動」が出来たら良いなと思っている。

などを週1でさせて頂ける会社があれば、ご連絡頂けるとありがたいです。

候補2. 良い条件の会社に転職

groovesより良い環境、条件を提示して頂ける会社があれば、これを機に転職するのもアリかなと思っています。

  • 勤務時間にあまり厳しくない(大事)
  • モダンな開発環境が整っている
    • Pull Requestを出して、レビューする文化
    • 新しいRuby/Railsを使っている
    • インフラがコード化されている
  • そこそこコードが綺麗
    • また直し続けるのは避けたい...
  • プロダクトが面白い
  • 福利厚生が充実している

といった会社で、朝弱いダメ社会人Railsエンジニアでも話をしてみたいという採用担当者様がいればご連絡ください。

3月以降の予定まとめ

2月末に株式会社groovesの正社員を辞めて、3月以降は

  1. 何も連絡なければフリーランスになって、groovesで週4勤務
  2. 面白い話があればフリーランスになって、案件を2つくらい掛け持ち
  3. 他社から良い環境・条件を提示されたら他社に転職

のどれかになります。

さいごに

3月以降にgroovesとどのような関係になるかはまだ分かりませんが、Forkwellは良いプロダクトなので今後も何か関われたら嬉しいなぁと思っています。

開発環境・職場環境の改善に尽力されてきた上司の赤川さん、Forkwell開発メンバーやその他groovesでお世話になった皆様に感謝の意を表して、このエントリを締めたいと思います。*3

*1:ただし、インフラを除く

*2:細かいのやWeb上に書けないやつは飲み会で聞いてください

*3:まだ2月末まで1ヶ月くらいありますけどね

git-worktree を使って時間のかかる処理を別の作業ディレクトリで行う

時間のかかる処理の実行中、別の作業を進められるようにする方法。備忘録。

時間のかかる処理の例

  • テストの実行中
  • デプロイの実行中

など。

git-worktree を使う

簡単に作業ディレクトリを増やせる。

$ cd ~/works/app
$ git worktree add ~/works/app_deploy
$ cd ~/works/app_deploy
$ git status
On branch app_deploy
nothing to commit, working tree clean

新しく作成された app_deploy の作業ディレクトリでも普通のリポジトリと同じように作業ができる。

注意点

両方の作業ディレクトリで同じブランチを使えない

作業ディレクトリを2つに増やしても、同じブランチは使えません。 別名のブランチを作る必要があります。

リポジトリは共通

リポジトリの内容は共通なので、git remotegit config を使う時は注意が必要です。

.gitignore のファイルは複製されない

git 管理されていないので当然なのですが、初めて git worktree を使うとハマるかも。
direnv を使っている場合などはシンボリックリンクを使うと良い。

$ cd ~/works/app_deploy
$ ln -s ~/works/app/.envrc .

Git を少しでも高速、安全に使う方法を紹介

はてブを眺めてたら、なんか Git の記事がバズってたので、便乗して Git の話を書いてみた。

基礎知識

git-reset は危ない

git reset --hard は作業ディレクトリにコミットしていない変更があった場合、全てを吹き飛ばしてしまう可能性があります。

使う前に必ず git status を実行して、作業ディレクトリの状態を確認をした方が良いです。

git-checkout は遅い

checkout コマンドは作業ディレクトリの状態を変更します。このとき ファイルアクセスが発生するため遅い です。

git-branch -f は速くて便利

git branch -f は同じ名前のブランチがあっても無理やりブランチを作成できるコマンドです。

git-reset と異なり、任意のブランチを弄れるため非常に強力です。

少しでも高速、安全に使う事例の紹介

基礎知識を紹介したところで、いくつか事例を紹介します。

間違ってmaster ブランチで作業してしまったとき

よく紹介される方法は下記のコマンド。ちなみに @HEAD と同じです。

$ git branch new-branch
$ git reset --hard @~
$ git checkout new-branch

このコマンドだと2つ問題がある。

  1. reset --hard で未コミットの内容が吹き飛ぶ危険性
  2. checkout を使っていて遅い

少しだけ改善したコマンドは下記の通り。

$ git checkout -b new-branch
$ git branch -f master @~

これなら checkout のファイルアクセスも最小で、未コミットの内容があっても消えません。

master ブランチをリモートに追従したい

よくある方法はこちら。

$ git checkout master
$ git pull origin

checkout してブランチ移動すると作業が止まるし、ファイルアクセスもあって微妙ですよね。 そこで branch -f ですよ。

$ git fetch origin
$ git branch -f master origin/master

どんなに master と origin/master が離れていても一瞬で実行が終わります。

より安全に master ブランチをリモートに追従したい

branch -freset --hard は怖いって人にオススメのコマンドがあります。 rebase です。

$ git fetch origin
$ git rebase origin/master master

これならコミットしていない変更があれば rebase が実行できませんし、コンフリクトが起きたら git rebase --abort で元の状態に戻すことができます。

ブランチから辿れないコミットを探す方法

git reflog を使えば、だいたいなんとかなります。(使い方は適当にググって)

あと、 git reflog show <branch_name> でブランチの履歴が表示できるので、もし git branch -f でミスっても簡単に戻せます。

ブランチや reflog で辿れないオブジェクトを探す

git fsck --lost-found の使い方は昔ブログに書いてあるので、それを参考に頑張って探しましょう。

git add さえしていれば、ギリギリなんとかなる。 git add をしていない作業は諦めて。

sinsoku.hatenablog.com

まとめ

git branch -f は速いし、ミスっても git reflog show <branch_name> で簡単に復旧できるので、みんな使いましょう!

「Randomly Failing Specs」という内容でLTしました #railsdm

資料は Slide Share で公開しています。

www.slideshare.net

話したかったこと

時間切れで全く話せていませんでしたが、本当は「 sleep 使わずに直しましょう」って事が伝えたかった。 資料の作り方や発表内容で完全に失敗してる。

このブログ読んだ方は是非スライドの図を見て、 Capybara のお気持ちを感じ取って欲しい。

他のLT登壇者はだいたい最後の方まで話せていて、流石だなーと思いながら聞いていました。

同人誌

電子版を BOOTH で公開してみました。*1

booth.pm

紙版を購入頂いた方には無料で電子版をお渡ししたいと思っていたので、パスワード付zipを使って用意しました。 パスワードを生成するのが面倒なのですが、他に良い方法が思い浮かびませんでした。申し訳ない。

次回の技術書典では、最初から電子版を用意したいと思います。

*1:というか、これに手間取っていてブログ公開が遅れていた