【Rails6】ImageMagickからlibvipsに移行する

## はじめに
先日Rails6で画像処理ライブラリを変更するissueをやりました。
具体的には`ImageMagick`から`libvips`に移行しました。

そこで、これから`libvips`に移行したい人のために移行する方法を書いていきます。
それでは、やっていきましょう。

実際のプルリクエストです。参考にしてください

https://github.com/fjordllc/bootcamp/pull/7397

## libvipsに移行する手順
先に全体像を書いておきます。
また、Rails6バージョンを想定していることに注意です。

1. 設定ファイルの修正
1. 既存の`ImageMagick`に対応しているコードを修正する
1. (必要なら)Dockerfile, CI設定ファイルの修正もする

libvipsに移行するだけなら、簡単なのですがアプリによって2の修正箇所が違ったりするので大変です。
以下で詳しく書いていきます

### 1. 設定ファイルの修正
`config/application.rb`ファイルに1行加えるだけで、ライブラリ自体は変更できてしまいます。

```ruby: config/application.rb
# frozen_string_literal: true
...

module Myapp
  class Application < Rails::Application
  ...
    # 下記1行を追加する
    config.active_storage.variant_processor = :vips
  end
end

```

これだけです、簡単ですね。
ただ、ここから気をつけないとエラーを起こしてしまうので2は特に要チェックです。

### 2. 既存の`ImageMagick`に対応しているコードを修正する
ここから、既存のコードをlibvipsに対応させていきます。
主に見るのは`variant`メソッドについてです。

下記のように画像を`variant`で加工している箇所はないでしょうか？
```ruby: 修正対象のコード
avatar.variant(resize: '88x88>').processed.url
```

主にその箇所を修正していきます。
もっと具体的にいうと`resize: '88x88>'`という部分を修正します。

[Railsガイド](https://railsguides.jp/active_storage_overview.html#%E7%94%BB%E5%83%8F%E3%82%92%E5%A4%89%E5%BD%A2%E3%81%99%E3%82%8B)に書いてある下記の表が、参考になります。
> |パラメータ|	例	|説明|
> |---|---|---|
> |resize_to_limit|resize_to_limit: [100, 100]|元の縦横比を維持したまま、指定の寸法に収まるように画像を縮小します。指定の寸法より大きい場合のみ、画像のサイズを変更します。|
> |resize_to_fit|resize_to_fit: [100, 100]|元の縦横比を維持したまま、指定の寸法に収まるように画像をリサイズします。指定の寸法より大きい場合は縮小し、小さい場合は拡大します。|
> |resize_to_fill|resize_to_fill: [100, 100]|元のアスペクト比を維持したまま、指定の寸法に収まるように画像をリサイズします。必要な場合は、大きい方の寸法で画像を切り取ります。|
> |resize_and_pad|resize_and_pad: [100, 100]|元の縦横比を維持したまま、指定の寸法に収まるように画像をリサイズします。必要に応じて、元画像にアルファチャンネルがある場合は透明色で、ない場合は黒で残りの領域を塗ります。|
> |crop|crop: [20, 50, 300, 300]|画像から領域を抽出します。最初の2つの引数は、抽出する領域の左端と上端、最後の2つの引数は、抽出する領域の幅と高さです。|
> |rotate|rotate: 90|画像を指定の角度だけ回転します。|

例えば、`resize_to_limit`を使って先ほどの修正対象のコードを修正したら下記のようになります。
```ruby: 修正後のイメージ
avatar.variant(resize_to_limit: [88, 88]).processed.url
```

厳密には違うかもですが、あくまでイメージですので許してください。

#### 注意: ImageMagick依存のgemもチェックしよう
上記の修正を終えた僕は「なんだ〜〜簡単じゃん！！」と油断していました。

しかし、本番環境にリリースされた時バグを発生させてしまったのです。
(しかも、結構クリティカルなバグ...)

なぜバグが起こったのでしょうか？
それは`MiniMagick`というgemを使って画像処理をしていた箇所でエラーを起こしていました。

どうやら、`MiniMagick`を使うには`ImageMagick`が必要だったそう...
なので、下記のようなコードを見つけたら`libvips`に対応した書き方に修正しましょう。
```ruby: 修正対象のコード2
MiniMagick::Image.read()
```

結局チームリーダーが対処してくださいました↓
ありがとうございました。

https://github.com/fjordllc/bootcamp/pull/7568

### (必要なら)Dockerfile, CI設定ファイルの修正もする
開発しているアプリにもよりますが、Dockerfile, CI設定のファイルでも`libvips`をインストールしないとエラーになります。
具体的には、下記のようなエラーになりました。
```bash: エラーログ
rails aborted!
LoadError: Could not open library 'vips.so.42': vips.so.42: cannot open shared object file: No such file or directory.
Could not open library 'libvips.so.42': libvips.so.42: cannot open shared object file: No such file or directory.
Searched in <system library path>, /usr/lib, /usr/local/lib, /opt/local/lib
/home/circleci/project/vendor/bundle/ruby/3.1.0/gems/ffi-1.16.3/lib/ffi/dynamic_library.rb:65:in `load_library'
/home/circleci/project/vendor/bundle/ruby/3.1.0/gems/ffi-1.16.3/lib/ffi/library.rb:95:in `block in ffi_lib'
/home/circleci/project/vendor/bundle/ruby/3.1.0/gems/ffi-1.16.3/lib/ffi/library.rb:94:in `map'
/home/circleci/project/vendor/bundle/ruby/3.1.0/gems/ffi-1.16.3/lib/ffi/library.rb:94:in `ffi_lib'
/home/circleci/project/vendor/bundle/ruby/3.1.0/gems/ruby-vips-2.2.0/lib/vips.rb:573:in `<module:Vips>'
...
```

`libvips`を開けないよ！みたいなエラーなので、インストールしていきましょう。
ただ、設定ファイルは各自の開発しているアプリにより違いそうなので、カスタマイズしてください。

#### Dockerfileの修正
`imagemagick`の部分を`libvips`をインストールするように置き換えています。

```Dockerfile: .devcontainer/Dockerfile
# Install OS packages
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
    # 修正前
    && apt-get -y install --no-install-recommends imagemagick pngcrush optipng gsfonts

    # 修正後
    && apt-get -y install --no-install-recommends libvips42 libvips-dev pngcrush optipng gsfonts
```

```Dockerfile: Dockerfile
# ImageMagick(修正前)
RUN apk add --no-cache imagemagick imagemagick-heic imagemagick-jpeg imagemagick-pdf imagemagick-svg imagemagick-webp bash pngcrush optipng=0.7.8-r0 ghostscript-fonts

# libvips(修正後)
RUN apk add --no-cache vips-dev orc-dev bash pngcrush optipng=0.7.8-r0 ghostscript-fonts
```

#### CI設定ファイル
こちらは単純に`apt-get`でパッケージをインストールするだけになります。
```yaml: .circleci/config.yml
...
  # (すでに同様のcommandがあれば不要)パッケージの更新
  - run:
    name: Update package list
    command: sudo apt-get update
  # libvipsをインストールする
  - run:
    name: Install libvips
    command: sudo apt-get install -y libvips
```

ひとまずこれでOKだと思います。

#### HEIC形式に注意する
上記の方法で、またもや本番エラーを起こしてしまいました。
HEIC形式に対応していなかったのです。

なので、先ほどのDockerfileを修正しましょう。
```Dockerfile: Dockerfile
# vips-heifを追加する
RUN apk add --no-cache vips-dev vips-heif orc-dev bash pngcrush optipng=0.7.8-r0 ghostscript-fonts
```

こちらのリーダーに対処していただきました。何度もすいません...

https://github.com/fjordllc/bootcamp/pull/7576

また、こちらのコメントも参考になりました↓
(僕のインストール方法がそもそもでよくなかった？)

https://github.com/janko/image_processing/issues/97#issuecomment-1019520835

## 終わりに
以上で完了です！意外とスッといけたと思ったら本番でエラーばかりでした...
申し訳ございません。フォローありがとうございました

この記事を読んで少しでも事故が減ればいいなと思っています。

いいねなどもしていただけると嬉しいです
それでは！