くりにっき

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

Rails 5.2.2.1にしたらErrno::ENOENT: No such file or directoryのエラーになった

Railsのsecurity fixが出ました

weblog.rubyonrails.org

個人アプリ2つアップデートしたんですが、どっちもRails 5.2.2だし余裕だろうと思ったら片方だけ下記のようなエラーになってちょっとハマったのでメモ。

+ ./bin/rails db:create

(snip)

Errno::ENOENT: No such file or directory @ rb_sysopen - /home/circleci/app/tmp/development_secret.txt
/home/circleci/app/vendor/bundle/ruby/2.6.0/gems/railties-5.2.2.1/lib/rails/application.rb:597:in `binwrite'
/home/circleci/app/vendor/bundle/ruby/2.6.0/gems/railties-5.2.2.1/lib/rails/application.rb:597:in `generate_development_secret'
/home/circleci/app/vendor/bundle/ruby/2.6.0/gems/railties-5.2.2.1/lib/rails/application.rb:430:in `secret_key_base'
/home/circleci/app/vendor/bundle/ruby/2.6.0/gems/devise-4.6.1/lib/devise/secret_key_finder.rb:24:in `key_exists?'
/home/circleci/app/vendor/bundle/ruby/2.6.0/gems/devise-4.6.1/lib/devise/secret_key_finder.rb:16:in `find'
/home/circleci/app/vendor/bundle/ruby/2.6.0/gems/devise-4.6.1/lib/devise/rails.rb:37:in `block in <class:Engine>'
/home/circleci/app/vendor/bundle/ruby/2.6.0/gems/railties-5.2.2.1/lib/rails/initializable.rb:32:in `instance_exec'
/home/circleci/app/vendor/bundle/ruby/2.6.0/gems/railties-5.2.2.1/lib/rails/initializable.rb:32:in `run'
/home/circleci/app/vendor/bundle/ruby/2.6.0/gems/railties-5.2.2.1/lib/rails/initializable.rb:61:in `block in run_initializers'
/home/circleci/app/vendor/bundle/ruby/2.6.0/gems/railties-5.2.2.1/lib/rails/initializable.rb:60:in `run_initializers'
/home/circleci/app/vendor/bundle/ruby/2.6.0/gems/railties-5.2.2.1/lib/rails/application.rb:361:in `initialize!'
/home/circleci/app/config/environment.rb:5:in `<top (required)>'

原因

tmp/ がコミットされていなくて tmp/development_secret.txt の作成に失敗してた

今回CIがコケたアプリはRails 4.1系からupdateしてたので tmp/.keep の追従忘れてたんだろうなあと納得

対応内容

tmp/ を作ってコミットしただけ

mkdir -p tmp
touch tmp/.keep
git add -f tmp/.keep
git commit -am "Add tmp/"

Dependabotの設定ファイルを置くようにした

なにげなくDependabotを見てたらリポジトリ.dependabot/config.yml を置けることを気づいたので置いてみた

https://dependabot.com/docs/config-file/

ちなみに .dependabot/config.yml をコミットしてると設定画面でこんな表示になります

f:id:sue445:20190310145727p:plain

設定ファイルを置くメリット

  • 新しくリポジトリを作ってDependabotを導入する時にボタンをポチらずに済む
    • リポジトリから設定ファイルをコピーしてくればいつもの設定が導入できる
  • 設定をgitで履歴管理

いつも使ってる設定晒し

Rubyだとこれでだいたいいいはずですが他言語だと微妙に変えた方がいいかもしれないです。

# c.f. https://dependabot.com/docs/config-file/
version: 1

update_configs:
  - package_manager: "ruby:bundler"

    directory: "/"

    update_schedule: "daily"

    default_assignees:
      - sue445

    allowed_updates:
      - match:
          # Disable. Only top-level dependencies (and security patches for subdependencies)
          update_type: "all"

    automerged_updates:
      - match:
          dependency_type: "development"
          update_type: "all"
      - match:
          dependency_type: "production"
          update_type: "semver:patch"

    # Enable. Only lockfile updates (ignore updates that require Gemfile changes)
    version_requirement_updates: "off"

解説

default_assignees

PRが作られた時に誰をassignさせるか。

PRを見た後にchangelogを見に行ったりしてると実際にマージするのをちょいちょい忘れるので自分をassignしておくと便利

allowed_updates

設定画面の「Only top-level dependencies (and security patches for subdependencies)」に相当。

f:id:sue445:20190310150258p:plain

デフォルトだとこれに入っているんですが、そうするとGemfileに直接書かれてるgemしかbundle updateの対象にならなくて、gemの依存の依存のgemがupdateされないのでいつもこのチェックを外すようにしてます。

設定ファイルだと update_type: "all" を書けば同じ効果になります

automerged_updates

設定画面のAutomatic PR mergingに相当

f:id:sue445:20190310150517p:plain

個々の好みが分かれるところですが、僕は普段development dependencyのアップデートは全部自動マージして、runtime dependencyはパッチバージョンのアップデートのみ自動マージ(マイナーバージョンアップは手動)にしてます

version_requirement_updates

設定画面の「Only lockfile updates (ignore updates that require Gemfile changes)」に相当

f:id:sue445:20190310150832p:plain

デフォルトだとチェックが外れているんですが、そうすると Gemfile でバージョン固定していた場合にGemfileを書き換えてbundle updateするので、これにチェック入れとくことで Gemfile.lock のみ更新かけるようにしてます。

Gemfile でバージョン固定しているのはだいたいなんらかの理由があるはずなので書き換えられるのは困る)

設定ファイルだと version_requirement_updates: "off" に相当。

まとめ

ローカルで bundle update した時と同じ挙動にしたければ allowed_updatesversion_requirement_updates を書いとけばいいかと。

gitlabci-bundle-update-mrを作った

gitlab.com

rubygems.org

経緯

試験勉強を始めるとついつい部屋の掃除をしちゃいますよね?

(訳:RubyKaigiの資料の現実逃避で全く関係ないgemを作った)

どんなgem ?

名前からピンときた人もいるかもしれませんが、CircleCIで bundle update のPRを作る circleci-bundle-update-pr のGitLab版です。

f:id:sue445:20190307230836p:plain

使い方

0. 事前準備

READMEを参考に GITLAB_API_ENDPOINTGITLAB_API_PRIVATE_TOKENOCTOKIT_ACCESS_TOKEN をEnvironment variablesに設定します

f:id:sue445:20190307231509p:plain

1-A. .gitlab-ci.ymlに直接書く

.gitlab-ci.yml に下記のようなものを書きます

stages:
  - build

continuous_bundle_update:
  stage: build

  image: ruby

  cache:
    key: "$CI_BUILD_NAME"
    paths:
      - vendor/bundle/

  before_script:
    # Set timezone to Asia/Tokyo
    - cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime

  script:
    - bundle install --path vendor/bundle
    - bundle clean
    - gem install --no-doc gitlabci-bundle-update-mr
    - gitlabci-bundle-update-mr --user="GitLab CI" --email="gitlabci@example.com" --labels="bundle update"

  only:
    - schedules

1-B

GitLab v11.4でinclude *1がCoreに入ったので、せっかくの機会なのでincludeに対応しました。

その場合は下記のような書き方です。

include:
  - remote: "https://gitlab.com/sue445/gitlabci-bundle-update-mr/blob/master/gitlabci-templates/continuous_bundle_update.yml"

continuous_bundle_update:
  stage: build

  variables:
    # override variables (followings are defailts)
    CACHE_VERSION: "v1"
    GIT_EMAIL:     "gitlabci@example.com"
    GIT_USER:      "GitLab CI"
    LABELS:        "bundle update"
    OPTIONS:       ""

  before_script:
    # Set timezone to Asia/Tokyo
    - cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime

さっきと違って変数の上書きを除けばちょっと短くなるのがポイントです。

2. スケジューラに登録する

GitLab CIでscheduler(cronのように定期実行する機能)を使うにはymlではなくリポジトリの設定から登録する必要があります

f:id:sue445:20190307231334p:plain

今回のgemを作るにあたって

アップデートされたgemのcompareリンクを生成する部分は前述のcircleci-bundle-update-pr同様 compare_linker を使っているので、circleci-bundle-update-prと同じ内容のMRが作れます。

gitlabci-bundle-update-mrで作ったMR

f:id:sue445:20190307230836p:plain

circleci-bundle-update-prで作ったPR

f:id:sue445:20190308134328p:plain

ただしgitlabci-bundle-update-mrから使おうとすると一部使いづらい部分があったので、汎用的に使えるように一部ロジックをpublicメソッドに切り出すPRを投げました。

github.com

*1:CircleCIのorbのように外部のymlを取り込めるようにする機能

pixiv TECH SALONで「スペックを上げてクラウドで殴るCI」という発表をしました #pixivTECHSALON

昨日 pixiv TECH SALON という完全招待制のイベントで「スペックを上げてクラウドで殴るCI」で発表したので、その時のスライドを公開します。

他の人たちのスライドも別途まとめて公開される予定ですが、いったん自分の分だけ先に出します。

おまけ:発表中の様子

togetter.com

git-updateがクソ便利

git-syncインスパイヤされて作りました

qiita.com

ソースコード

gist.github.com

モチベーション

例えばトピックブランチで作業してて、リポジトリのmasterが更新されたから最新のmasterを取り込んでrebaseするってことよくやると思うのですが、その時にいちいち

git checkout master
git pull --ff
git checkout topic_branch
git rebase master

みたいなことをやるのが大変なのでサブコマンドにしました。 *1

3ヶ月くらい使ってるけど割と開発が捗ってます。

~/.gitconfig のaliasにも up = update で登録してるので、1時間に1回くらいは g up 叩いてるんじゃないかなw

https://github.com/sue445/dotfiles/blob/65468baf32e391a78216018942bc4a158024e7af/_gitconfig#L57

おまけ:dotfilesでオレオレgitサブコマンドを運用する方法

自分のdotfilesに bin/ を置いて、そこにPATHを通して使っています

設定はこんな感じ

https://github.com/sue445/dotfiles/blob/65468baf32e391a78216018942bc4a158024e7af/_bashrc#L113-L114

2019/3/4追記

ブコメで色々突っ込まれてたので追記

id:manaten

git pull --rebase origin master でよさそうな気がするのだけど、違いはどこだろ

id:ramen31

git pull --rebase origin masterでいいのでは?

普段 pull --rebase は使ってないから完全に頭から抜けてたw

挙動的にはこれでいいんだけど、ローカルのmasterやorigin/masterが更新されなくてどこからブランチ伸びてるか分かりづらかったので使うのを以前やめた記憶。

それを気にしなければ git pull --rebase origin master で全然いいと思います。

id:tofu-kun

俺は“rebase origin/master”派だな

id:PEEE

自分もorigin/master派

自分は怠惰なので rebase origin/master の前に fetch すら叩きたくない派でした。

fetch && rebase くらいなら ~/.gitconfigaliasワンライナーで書けるのでできないことはないけど、master以外がデフォルトブランチに設定されてた場合(よくあるのはdevelopブランチ)に途端に複雑度が増すのがアレ。

一応自分の git-update に関してはmaster以外がデフォルトブランチになってた場合に自動判別してます。

*1:fetchしてrebase origin/masterでもいいんだけど、masterからトピックブランチ作ることが多いので僕はこっちの方が好きです

Travis CIのビルド終了後にPixelaで草を生やす

mike-neck.hatenadiary.com

ちょっとしたことですが、 pixela-java-client の CI をまわした回数を Pixela で数えてみることにしました。

に影響されてやってみました。たぶんTravis CI以外でもできるはず

tl;dr;

webhook作成後に .travis.ymlに下記を書く

after_script:
  - if [ "$PIXELA_WEBHOOK_URL" != "" ]; then curl -X POST $PIXELA_WEBHOOK_URL -H 'Content-Length:0'; fi

前置き

Travis CIではテストの前後などに任意の処理を差し込めるのですが、何が使えるかは下記に全部載ってます

docs.travis-ci.com

このうちビルド終了後の処理は

  • after_success
  • after_failure
  • after_script

ですが、after_successafter_failureは名前の通りビルド成功時と失敗時のみ実行されるやつなので今回はビルド結果に関わらず実行したいのでafter_script` を使います

手順

グラフとwebhookを作成

拙作の pixela gem を使うとこんな感じ。

require "pixela"

client = Pixela::Client.new(username: ENV["USERNAME"], token: ENV["TOKEN"])

client.graph("pixela-gem-ci").create(name: "CI activity of pixela-gem", unit: "build", type: "int", color: "shibafu", timezone: "Asia/Tokyo")

client.create_webhook(graph_id: "pixela-gem-ci", type: "increment")

create_webhook の戻り値に webhookHash が入ってるので忘れずにコピペしておきます

.travis.yml修正

after_script:
  - if [ "$PIXELA_WEBHOOK_URL" != "" ]; then curl -X POST $PIXELA_WEBHOOK_URL -H 'Content-Length:0'; fi

.travis.ymlにwebhookのURLをベタ書きするとforkされた先のリポジトリでも草が生えるのが嫌だったので、環境変数が登録されてる時のみPOSTするようにしています。

TravisCIに環境変数を登録する

Settingsに https://pixe.la/v1/users/【USERNAME】/webhooks/【webhookHash】 の形式でwebhookのurlを登録する

f:id:sue445:20190218234444p:plain

READMEとかに草を貼る

こんな感じ

[![CI activity of pixela-gem](https://pixe.la/v1/users/sue445/graphs/pixela-gem-ci)](https://pixe.la/v1/users/sue445/graphs/pixela-gem-ci.html)

グラフにpurge_cache_urlsを登録する

GitHubに画像のURLを貼ると自動的にCDN経由のURLになってしまいwebhookで草を生やしてもGitHub上に反映されないため、CDNのURL(https://camo.githubusercontent.com/〜)を purge_cache_urls に登録してやる必要があります。

client.graph("pixela-gem-ci").update(purge_cache_urls: ["https://camo.githubusercontent.com/XXXXXXXXXXXXXXXXXX"])

purge_cache_urls の仕組については下記を参照

github.com

対応時のPR

github.com

実際にできたやつ

f:id:sue445:20190218233502p:plain

pixe.la

僕がRubyKaigiのCFPを書く時に意識したり実践したこと

というわけで RubyKaigi 2019 のCFPが採択されました!!!

実際に採択されたCFPがこちらになります。

https://www.pixiv.net/fanbox/creator/2348843/post/258661

ただ、完全ネタバレになるので今は限定公開にしてます。(登壇後に全体公開します)

実際にCFPを通すにあたってむっちゃ意識してたことがあるので忘れないようにメモ

前提

  • RubyKaigi 2019の開催地は福岡
  • 僕の出身も福岡
  • 僕の所属してるピクシブも福岡オフィスがある *1
  • だったら僕が登壇するしかないじゃん???みたいな感じで、RubyKaigi 2018終了直後から2019に登壇することを意識してました。
  • とはいえ過去2回出してどっちもCFP不採択だったので、同じことをやっても同じ結果になることは分かっていたので新しい試みをすることにしました

1. CFPを2本以上書いた

採択されたのを含めて3本くらいは書いてます。

ただ、今回出した以外の残り2つは書いてる途中で違和感があるというか、なんかCFPとして弱いなあと思って8〜9割くらい書いたところでボツってるので実際に応募したのは今回の1本だけですが。

余談ですが普段5〜10分のLT慣れしてるせいで細かいネタはいくらでも出てくるんですが、40分ネタとなると大変ということを痛感しました。。。

この辺のギャップは昔見た id:daiksy さんの下記ツイートが参考になりました。(とはいえ複数のLTを1つにまとめるのも結構難しかったw)

2. 実際に採択されたCFPを参考にした

だいたいこの辺を参考にしました

今回のCFPを一通り書いた後に上記CFPを読んで、完全に絶望して全部書き直しました。

特に id:onk さんのCFPをだいぶ意識したので今度銀座Railsで会った時にお礼言わないとw

3. 他の人にレビューしてもらった

今までこれといってレビューしてもらっていなかった(と思う)ので、今回は社内でレビューしてもらいました。

特に @igaiga555 さんは RubyWorld Conference 2018 のCFPも採択されていたので心強さしかなかったです。

レビュー後に2割くらい加筆しました。

余談:現在の心境

さっさと3月分のスライドを完成させてRubyKaigiのスライドに着手しないと、、、

*1: RubyKaigi 2018の時点でピクシブに内定決まってた