GitHub Actionsからもローカルからもkamal deployしたい時に秘密の文字列をどうやって注入するか

秘密の文字列の保存には1Passwordを利用している前提です。1Passwordの中に入っている各種値を使ってデプロイするだけなら.kamal/secretsに次のような形で書くと良いです。

SECRETS=$(kamal secrets fetch --adapter 1password --account my-account --from MyVault/MyItem KAMAL_REGISTRY_PASSWORD RAILS_MASTER_KEY)
KAMAL_REGISTRY_PASSWORD=$(kamal secrets extract KAMAL_REGISTRY_PASSWORD $SECRETS)
RAILS_MASTER_KEY=$(kamal secrets extract RAILS_MASTER_KEY $SECRETS)

ローカルからデプロイするにはこれでいいんだけどGitHub Actionsから1passwordを使えるようにするのは大変。fnox みたいなツールを使うといい感じに統一できそうだけどちょっと敷居が高い。

kamal deployコマンドにsecretsファイルの場所を指定するオプションがあれば、ローカルとGitHub Actionsで切り替えられて便利なんだけど現状ない。

というのを考えると今のところは環境変数に寄せておくのがいいのかな、となっています。まず./kamal/secretsは次のようにします。

KAMAL_REGISTRY_PASSWORD=$KAMAL_REGISTRY_PASSWORD
RAILS_MASTER_KEY=$RAILS_MASTER_KEY

GitHub上でSecrets keyにこれらの値を入れておけばGitHub ActionsからのデプロイはOK。

ローカルは次のように.envなどに書いておく

KAMAL_REGISTRY_PASSWORD="op://MyVault/MyItem/KAMAL_REGISTRY_PASSWORD"
RAILS_MASTER_KEY="op://MyVault/MyItem/RAILS_MASTER_KEY"

そして op run --env-file=.env -- bundle exec kamal deploy とすると、1Password中の値を環境変数として注入してコマンド実行できます。やったね。

考えたけど試していない方法

.kamal/secrets$()の中に書かれたものはシェルスクリプトとして動くはずなので、次のように1行で環境変数と1passwordとを切り替えるスクリプトを書くという案もあります。.envをつくらないで済むのはメリット。多分動くけど何やってるのかパッとわからないので保留にしています。

RAILS_MASTER_KEY=$(if [ -n "$RAILS_MASTER_KEY" ]; then echo "$RAILS_MASTER_KEY"; else SECRETS=$(kamal secrets fetch --adapter 1password --from MyVault/MyItem RAILS_MASTER_KEY) && kamal secrets extract RAILS_MASTER_KEY $SECRETS; fi)

RailsのDBをMySQLからsqliteに移行する

異なるDBのデータ移行はめっちゃ面倒な印象があったけど、やってみたらそこまででもなかったぞという話をメモしておきます。

前提

MySQL特有のクエリを書いていない

データ移行手順

rroblak/seed_dump: Rails task to dump your data to db/seeds.rb を利用する。このgemを使うとrake db:seed:dumpでdb/seeds.rb にDBの内容をダンプできる。ダンプした内容を移行先に持ち込んで、おもむろにbin/rails db:seedするとデータの移行ができる。便利。

ただしrake db:seed:dumpだとREADMEに書いてあるようにモデルのcreate!メソッドの実行になるし、idが指定されない形になってしまう。

Product.create!([
  { category_id: 1, description: "Long Sleeve Shirt", name: "Long Sleeve Shirt" },
  { category_id: 3, description: "Plain White Tee Shirt", name: "Plain T-Shirt" }
])
User.create!([
  { password: "123456", username: "test_1" },
  { password: "234567", username: "test_2" }
])

これだと移行後に外部キーがマッチしない可能性がある。あと各モデルのコールバックが不用意に発火する可能性がある。

INSERT_ALL=true INCLUDE_ALL=true RAILS_ENV=production bundle exec rake db:seed:dumpのようにしてダンプするとこの問題を解決できる。

INSERT_ALL=trueとするとRails6から利用可能なinsert_allを使う形式になりコールバックをスキップできる(あと速い)。INCLUDE_ALL=trueとするとidやcreated_at, updated_atも含んだ形でダンプできる。

細かい振る舞いは公式のドキュメント読んでください。

devcontainers環境で1passwordのssh agentを利用してコミット署名する

Dev containerでの1Password SSH Agentを使ったコミット署名時のエラーを対処する を参考にして設定したのだけど、一部リンク先のエントリとは異なる点があったのと、別の要因にハマったのでそれのメモです。リンク先はwindowsで僕はmacという違いなのかVScodeのバージョンが変わって振る舞いが変わったのかはよくわかりません。

ホスト側のgitconfigを必要な分だけコピーするときの設定

リンク先の「解決策2: gitconfigのコピーを無効化する」を参考にしてdotfilesを使ってみたのだけど、~/.gitconfigに追記するやり方だとなぜか[credential]の項目が書き込まれなくて期待通りの挙動にはなりません。そして~/.config/git/configに書き込んだ場合には同じファイル(~/.config/git/config)の末尾に[credential]が書き込まれます。

謎な挙動だけど動いたのでとりあえずこれでいいか…となっています。

1passwordのsocketを利用する

1password公式のドキュメント Sign Git commits with SSH | 1Password Developer だとSSH_AUTH_SOCKはoptional扱いだったので設定していなかったのだけど、devcontainers環境で1passwordを利用したい場合は設定必須のようで、SSH_AUTH_SOCKを1passwordのものに設定するとうまく動きました。

SSH_AUTH_SOCKの設定前にdevcontainers環境をVSCodeで起動していて、その上でリビルドするとホスト側のSSH_AUTH_SOCK環境変数を読まないので設定が反映されない、という事があり30分くらいハマりました。VSCodeを起動しなおしたら解決しました。

(追記)-slim系のDockerイメージを利用している場合はopenssh-clientを明示的にインストールしておく必要がある

apt-get update && apt-get install openssh-client

最近のsecret_key_baseの変遷についてのざっくりまとめ

Active StorageでアップロードしたファイルのURLを即返すときに気をつけること

(注記)これはだいぶ雑なまとめです。間違いあったら教えて下さい!

  • ActiveStorageでDiskServiceを利用している場合、URLのドメイン部分はリクエストからいい感じに生成される
  • ActiveStorage::SetCurrentというモジュールをコントローラにincludeするとbefore_actionでリクエストの情報をCurrentAttributeにアサインする
  • 開発環境だとActiveStorage::SetCurrentをincludeすれば問題ないんだけどテスト環境で問題になることがある
  • ActiveStorage::Current.host not set causing disk service to throw URI::InvalidURIError · Issue #40855 · rails/rails
  • テスト環境(例: コントローラスペック)でアップロードしたファイルのURLを即返すようなケース
    • テスト時にuser.save→userの画像がアップロードされる→サムネイルを作るタイミングでActiveStorage::DiskControllerのshowアクションが実行される→Executorが実行される→CurrentAttributesの値のクリアが実行される→ActiveStorage::SetCurrentで設定したはずの値がクリアされてしまう→ファイルのurlを参照しようとしてエラーになる、という流れになるはず
    • これはテストが1スレッドで実行されているのが原因なので、複数スレッドな環境では起きないはず
  • 解決策としては、rails_helper.rbなどに↓を書く
Rails.application.executor.to_complete do
  ActiveStorage::Current.url_options = { host: 'http://example.com' }
end

rubymineでFind Actionしたいのにできないのを解決する方法

  • rubymineのキーバインドにcmd+shift+aがfind action(アクションに絞り込んだ検索)として用意されている
  • が、mac上で実際にそれを打つとなぜか次のようになる
  • これは↓にある「ターミナルのmanページインデックスで検索」のキーバインドと競合しているから
  • ↑のようにチェックを消すと良い