Rails の issue を解決するまでの手順とOSS初心者でもできること

突然ですが、あなたはRailsのissueとプルリクがいくつあるかご存知でしょうか?

2019年10月17日現在、それぞれ issue 384 / PR 803 になります。

f:id:sinsoku:20191017000221p:plain

多いですよね...。

個人的に、最近このissueを減らすのを少しでも手伝えないものかとissueにコメントしてみたり、パッチを書いたりしてるけど、 なかなか大変なので、コントリビューターの敷居を下げるためにブログ記事を書いてみました。

コントリビュータが増えれば、きっとissueも減るはず!!

Rails への貢献について

Railsガイドに丁寧な説明が記載されているので、読んだ事がない方は一読するのをオススメします。

railsguides.jp

この記事で紹介すること

Rails への貢献方法は色々なものがあります。

  • 新機能の追加
  • バグの報告
  • バグを修正するプルリク作成
  • ドキュメントの追加や修正
  • ...etc

このなかのうち バグの修正 に絞って紹介します。

バグ修正を行うときの手順

  1. 報告されたissueを読んで、問題を手元で再現させる
  2. 原因を突き止める
  3. コードを直す
  4. 動作確認をする
  5. プルリクを投げる

こんな感じでしょうか。 まぁ、仕事でバグを直すときと同じですね。

1. 問題を再現する

Rails の issue には問題を再現させるためのバグテンプレートが存在します。

  • issue に再現手順、再現スクリプトがない場合
    • バグテンプレートを使って書けないか issue の作者に依頼する
    • 例: rails/rails#36413
  • issue に再現スクリプトがある場合
    • 自分の環境でも再現するか確認してみる

2. 原因を調べる

問題が再現できたら、原因であるコードを調べます。

3. コードを直す

問題を再現させるテストコードを書いて、テストが失敗することを確認してから、コードを直します。 (例: rails/rails#37457) 他のテストコードを参考にすれば、意外とテストは書けます。

あと、Rails では minitest が使われているので、普段 RSpec を使っている人はテストの実行方法が分からないかも。

などを参考にしてみてください。

4. 動作確認

ローカルで適当にRailsアプリを作り、修正した Rails のコードを使うように Gemfile を直します。

- gem "rails", "~> 6.0.0"
+ gem "rails", path: "~/.ghq/github.com/rails/rails"

pathディレクトリを指定すると、そのディレクトリの gem を使えます。

あとは rails consolerails server などで動作確認をします。

5. プルリクを投げる

コミットログとプルリクに変更理由をちゃんと書く必要があります。

英語で変更理由を書くのはとても大変なので、いくつか英文を書くテクニックを紹介します。

  • 変更理由を日本語で書いて Google翻訳 でざっくり翻訳する
  • 簡単な英文(=自分が読める英文)になるように調整する
  • 他のバグ修正のプルリクを読んで、似たような英語で書き直せないか考える
  • git log --no-merges --author=kamipo でkamipoさんのコミットログを読んで参考にする

Rails のコミットログやプルリクには参考になる英文がたくさんあるので、うまく探してパクってください。

変更内容に自信がなかったり、不安な場合

都内に住んでいる方であれば、Asakusa.rb や永和さんのOSSパッチ会などに参加して、プルリクの内容について相談するという方法があります。

OSS初心者向け

ここまでで issue を解決する方法を紹介しましたが、「ハードルが高い」と思った人向けにいくつか初心者向けにできることを紹介します。

  1. https://github.com/rails/rails/issues の中から、興味のあるタイトルを読んでみる
    • issue を全部読むのは大変なので、例えば ActiveRecord や ActiveStorage など絞って読むと良い
  2. 自分の環境で問題を再現させてみる
    • 例えば、5.2 で起きる issue を「6.0 でも再現しました」とコメントするのも大事
    • 再現スクリプトのないissueに「この再現スクリプトで再現できました」とコメントするのも良い
  3. Close した issue とプルリクを読んで、コードの直し方を学ぶ
    • 問題と解答例みたいなものなので、読むだけでも勉強になります

最後に Rails Contributors の紹介

Railsで1回でもプルリクがマージされると Rails Contributors に名前が載ります。

contributors.rubyonrails.org

まだランキングに名前が載っていない方は、これに載ることを目標に Rails の issue を眺めてみてはどうでしょう?

意外と簡単に直せるバグが見つかるかもしれませんよ。

*1:過去のバージョンでは動いていて、最新のバージョンでバグっているケース

勉強用にRustのスクリプトをDockerでビルドし、即実行するbashスクリプトを書いた

Rustの勉強をするため、ちょっとしたコードをDockerでビルドして実行するスクリプトを書いたのでメモ。

コード

勉強用のディレクトリに以下の bin/exec というファイルを作り、 $ chomod +x bin/exec で実行権限をつけておく。

#!/bin/sh
set -e

ROOT=$(cd $(dirname $0)/../;pwd)
IMAGE=rust
RUST_CMD="docker run -e USER=$USER -v ${ROOT}:/app -w /app ${IMAGE}"

if [ -n "$1" ]
  then
    $RUST_CMD rustc $1
    EXEC_PATH=`basename $1 .rs`
    $RUST_CMD ./$EXEC_PATH
    rm ./$EXEC_PATH
else
  echo "Usage: bin/exec <source>"
fi

Dockerでビルドし、Docker上で実行して、実行後にバイナリを消すスクリプトです。

試したいファイルを引数に渡すと、実行できる。

$ bin/exec hello_world.rs
Hello, world!

Rustの勉強

昔にもRust入門してたなーと思ってたら、2年前にRustやってたブログが出てきた。

3日坊主にならないように、定期的に Rust 触ってブログに書きたいですね。そんな気持ちはあります。たぶん。

sinsoku.hatenablog.com

持っている技術の棚卸し 2019年

一応フリーランスなので、自分のできることを整理するためにまとめた。

(文章でまとめるの難しい...)

ソフトウェア開発

RubyRails

  • 1人で仕様の調整を行い、実装できる
  • コードレビューを通して、若手エンジニアにRailsを教えられる
  • OSSにプルリクを投げられる
  • RailsのIssue/PRを読んでいる(=最新機能をある程度知っている)
  • 勉強会に参加したり、登壇したことがある

JavaScript

  • ES2015の基本的な文法を知っている
  • jQueryを使った経験がある
  • React.jsでステートレスを意識してコンポーネントを書ける
  • Puppeteerを使ったE2Eテストを書いた経験がある

CSS

  • Bootstrap v3 の基本的な知識がある
  • BEMの基本的な知識がある
  • 他サイト、周りのデザインを参考にしてcssを組める
    • サイト全体のデザイン設計は出来ません

SQL

  • SQLの基本的な知識がある
  • パフォーマンスを意識したSQLを書ける
    • RDBMSの実行計画には詳しくありません
  • リプレイス案件でDB間のデータ移行作業の経験がある

インフラ

  • TerraformとAWSを使ってインフラ環境を構築できる
    • Route53, CF, ALB, ECS(Fargate)の構成を組める
    • GCPは未経験
  • aws-samでサーバレスAPIを組める

開発環境

  • GitHub周りの環境を整えられる
    • ブランチ保護、 Issue/PRテンプレート機能など
    • GitHub Appsを作れる
  • CI環境を構築できる
    • 静的解析(Lint)の設定ができる
    • CircleCIのWorkflow、Orbsを活用できる
  • CD環境を構築できる
    • ローリングアップデートの経験のみ
    • Blue/Greenデプロイ、カナリアデプロイは未経験
  • エラー管理にSentryなどを導入・使用したことがある
  • パフォーマンス改善にDatadogなどを導入・使用したことがある

その他

  • SEO対策の作業をした経験がある
  • ユーザーテストやUX改善について少し勉強したことがある
  • Redashを使ってKPIを表示するダッシューボードを作れる
  • リリースにあわせてGitのブランチ運用を提案できる

興味のあること

  • 開発環境を改善すること
  • お金の話(売上を増やしたり、経費を減らしたり)
  • 読みやすいコードを考えたり、書いたりすること
  • 新しい言語の習得(直近だとRust, Golang
  • エンジニアの評価・採用・組織作り

Rubyのライブラリやアプリケーションで使えるENVの一覧を取得する方法

最近、コンテナ化や The Twelve-Factor App環境変数を使う機会が多くなってきたので、一覧を作る方法が欲しくなった。

gemにする気力は今無いので、とりあえずコード片をブログに書いておく。*1

# config/application.rb の最後に以下のコードを貼ったら、それっぽく動いた。

module EnvLogger
  def self.histories
    @histories ||= []
  end

  def [](key)
    EnvLogger.histories << key
    super
  end

  def fetch(key, default = nil)
    EnvLogger.histories << key
    super
  end
end
ENV.singleton_class.prepend EnvLogger

at_exit do
  EnvLogger.histories.uniq.sort.each { |key| puts key }
end

Special thanks

ruby-jp で聞いたら、 @p_ck_ さんが即レスしてくれた。はやい。

f:id:sinsoku:20190826204921p:plain
ruby-jp

*1:将来、気が向いたらgem化するかも?やる気ある人いたら誰か代わりに頼む

コミットメッセージを書くときに気をつけていること

コミットメッセージに関するブログを読んだので、自分がコミットメッセージを書くときに気にしてることを書いておく。

chiastolite.hatenablog.com

sue445.hatenablog.com

基本的な話

まずは Gitのコミットメッセージの書き方 を読んでおく。

コミットメッセージは "将来の同僚のため" に書く

3ヶ月後にチームに入った人が理解できる内容 になっているのが理想。

チームメンバーが分かる内容で満足すると、暗黙的な業務知識に依存したコミットメッセージになっていることがあるので注意。

XXXを追加/変更/削除しました はダメ

油断するとこういうメッセージを書いてしまいがち。

git-diff の内容を日本語で書いただけだと意味がないので、コミットメッセージで付加価値がつくように気をつける。

コミットした後に見直す

コミットした後、git show @ を実行して、以下の点を確認する。

  1. diffの全てをコミットメッセージで説明できているか?
    • 関係ない変更が混ざっていないか?
  2. コミットメッセージを読んで「なぜ?」と自問する
    • 更に Why を深掘りして書けないか?

参考にしたURLを残す

自分が知らなかったことは、たぶん他メンバーも知らない可能性が高い。

コードを書いていて参考になったWebページがあったら、コミットメッセージに残しておく。

GitHubのURLを固定する

  1. https://github.com/rails/rails/blob/master/README.md
  2. https://github.com/rails/rails/blob/v6.0.0.rc2/README.md

1だとmasterブランチが更新されると内容が変わってしまう可能性があるので、2のようにタグの入ったURLにする。

ちなみに、GitHubでファイルを表示しているときに y を押すとURLが master から blob に変わります。*1

f:id:sinsoku:20190817013444p:plain
GitHub shortcut

URLを張りつつ、文面も引用してメッセージに含める

@sue445 さんのブログと半分同じなのですが、文面を引用して残すところが少し違うかな。

  • コミットメッセージ単体だと意味が分からなくなる事が多い
  • 自分がログを見ていたときに別ページを毎回開くのが面倒だった
  • リンク先が将来も残っているとは限らない
    • 突然リポジトリが消えてissueを読めなくなった経験があるので...*2

コミットメッセージは長くても良い

コミットメッセージが短くて不足しているより、多少冗長でも変更理由が書いてある方が嬉しい。

*1:下のヘルプ画像は ? で出ます

*2:https://tech.grooves.com/entry/2017/01/18/111900

Re:VIEWで技術書を書いているときにファイルを変更したら自動でビルドする

ファイルの変更を検知して、Re:VIEWのビルドを自動化したら便利だったのでブログに書いておく。

設定方法

vvakame/docker-reviewのdockerイメージを使ってビルドするスクリプト{project_root}/bin/build として作成しておきます。

#!/bin/sh

SHELL_PATH=$(cd $(dirname $0)/;pwd)
ROOT_PATH="${SHELL_PATH}/.."
IMAGE="vvakame/review:3.2"

docker run --rm -v ${ROOT_PATH}:/work -w /work ${IMAGE} /bin/sh -c "rake clean preproc pdf"

次にGemfileに以下の行を追加して、bundle install を実行する。

 source 'https://rubygems.org'
 
 gem 'rake'
 gem 'review', '3.2.0'
+
+ group :development do
+   gem 'listen', '~> 3.0'
+ end

あとはファイルの変更を検知して自動ビルドする {project_root}/bin/auto-build を作る。

#!ruby

require 'listen'

root_path = File.expand_path('..', __dir__)
patterns = [/\.re$/, /\.yml$/]

listener = Listen.to(root_path, only: patterns) do |modified, _added, _removed|
  next if modified.size.zero?

  result = system("#{__dir__}/build")
  # Macの人は以下をコメントアウトするとビルド後に通知できる
  # status = result ? 'success' : 'failed'
  # system("osascript -e 'display notification \"#{status}\" with title \"Re:VIEW build\"'")
end
listener.start
sleep

bin/auto-build を実行した後に xxx.re のファイルを変更すると自動でビルドされて、pdfが更新されます。

自動ビルドの前に原稿を書こう

現実逃避をしてる場合ではない。

ActiveDecoratorにプルリクを投げるまでにやったこと

怪しい挙動を見つけたきっかけ

テストコードで以下のようなことをしてました。

Rails.application.eager_load!

testでもproductionと同じ環境になるように事前に読み込んでいたのですが、テストを個別に実行するときにも全ファイルを読み込んでしまうため消したいと思っていました。

試しに消してCIを回してみると、一部のテストがfailした。

どんな問題が起きたか?

再現するコード例は以下の通り。

form = Form::User.new
ActiveDecorator::Decorator.instance.decorate(form)
form.is_a?(UserDecorator)  #=> true

Form::UserDecorator を定義してないときに、なぜか UserDecorator が使われてしまう。

コードを読んで発生箇所を突き止める

ActiveDecorator のコード内で Object.const_get を使っている箇所が UserDecorator を返していた。

そして、モジュールの自動読み込みに関する処理は ActiveSupport に定義されている。

ここまで調べて、自動読み込み周りはどう直すべきかよく分からなかった。

OSSパッチ会で松田さんに聞く

ちょうど問題を見つけた2日後にパッチ会があったので、直接聞いてみることにした。

blog.agile.esm.co.jp

a_matsuda さんに聞いてみたところ、以下のことが分かった。

  • 最近、そのあたりのコードを変えた
    • v1.2.0、v1.3.0(最新)で挙動を確認してみて欲しい
  • 意図した挙動ではない(バグっぽい)

動作確認してみると、 v1.2.0 では問題が起きず v1.3.0 で問題が起きること が判明した!

再現するテストケースを書く

v1.2.0 では問題ない事が分かったら、すぐ amatsuda/active_decorator@73dcdd6 が原因だと分かった。

問題を再現させるテストケースを書き、意図通りに失敗させて、 73dcdd6 をリバートしてテストが通ることを確認した。

英語のコミットログを書く

自分が最初に書いた英語のコミットログがこれ。

Fix a wrong decoration for nested classes

There is a problem that unintended decorators are used for nested classes.
This commit reverts 73dcdd67ae146ee9e76b1da9ae9e9dbd55529193 to fix the problem.

英語が苦手なので、OSSパッチ会に参加していた @yahonda さんにレビューしてもらいました。フィードバックは以下の通り。

  • wrongunintended が何か分からない
    • actual と expected を書いた方が良い
  • 細かいけど a wrong decoration の a は要らない
  • unintended より unexpected の方が良い

レビュー頂いて気づいたけど、英語の問題の前にコミットログの内容が足りていなかった...。

直したコミットログ

どういう問題が起きていて、何を期待しているかが分かるようにコミットログを直してプルリクを作成した。

Fix wrong decoration for nested classes

ActiveDecorator decorates incorrectly for the nested class that do not define decorator.

comic = Foo::Comic.new
ActiveDecorator::Decorator.instance.decorate(comic)
comic.is_a?(ComicDecorator)  #=> true

If `Foo::ComicDecorator` is not defined, it should not use `ComicDecorator` instead of `Foo::ComicDecorator`.
The cause of the problem is 73dcdd6, so we will revert it.

github.com

まとめ

OSSパッチ会のおかげでプルリク作るところまで出来た。ありがたい。