🐕

IcomoonのエラーをCircleCIでキャッチする

2025/01/15に公開

はじめに

現在参画しているプロジェクトにて、アイコンの管理をicomoonで行っています。
その際に問題が起こりました。新しいiconを追加した際に不本意にiconが削除されてしまい、iconが表示されないという事象が起こってしまいました。そこで対策として、PRにpushした際にCircle CIで不本意にiconが削除されていないかを自動でチェックできるように実装を行ったので、記事にまとめていきたいと思います✏️

前提

使用技術

  • Next.js,TypeScript
  • パッケージ
    • yarn
  • ライブラリ
  • 開発ツール
    • Github Actions
    • Circle CI

Icomoonとは

IcomoonとはWebフォントとしてカスタムアイコンを作成・管理するためのツールです。
主な特徴として以下があります。
https://icomoon.io/

  • アイコンのカスタマイズ
    SVGをフォントに変換
    既存のアイコンの編集
    複数のアイコンを1つのフォントファイルにまとめる

  • 使用方法
    Web上のアプリ(IcoMoon App)でアイコンを選択
    必要なアイコンだけを含むフォントファイルを生成
    プロジェクトに導入して使用

  • メリット
    アイコン管理が容易
    ファイルサイズの最適化
    カスタマイズの自由度が高い
    スケーラブルなベクター形式

  • 出力ファイル
    フォントファイル(.eot, .svg, .ttf, .woff)
    CSSファイル
    selection.json(アイコン設定の保存用)

今回のプロジェクトでは、Next.jsを採用しているのでicomoon-reactをinstallし、使用しています。
また出力ファイルはselection.jsonに設定しています。

https://www.npmjs.com/package/icomoon-react

運用手順

運用手順は以下です。

  1. 運用しているicomoonサイトで既存のselection.jsonを読み取る。
  2. selection.jsonは必ず最新の検証環境のブランチにあるものを読みとる
  3. 既存のアイコンに加え、追加したいアイコンSVG画像を追加し選択後、右下の「Generate Font」を押す。
  4. 「Download」でオリジナルのフォントセットをダウンロード。
  5. ダウンロードしたicomoonフォルダの中のselection.jsonで既存のものを更新。
  6. IcomoonReactコンポーネントで使いたいアイコンをセット。
  7. 色やサイズをpropsで指定。また、細かいスタイルの指定はclassNameを追加して設定する。

今回の事象

運用手順の2で最新のブランチから読み取らなく、古いブランチの状態から読みとった際に、アイコンが不本意に削除されてしまいました。
buildのログを確認するとicomoonのimportが失敗していることがわかるのですが、分かりずらく見落してしまい、リリース後にアイコンが消えている事象に気づきました。

今回の事象の例

  1. 最新の検証ブランチ -> 🌸のアイコンが含まれている。
  2. 古いブランチの状態から派生したブランチ -> 🌸のアイコンが含まれていない。🗼のアイコンを追加し、リリース。
  3. リリース後の検証ブランチ -> 🌸のアイコンが含まれていない。🗼のアイコンは含まれている。

=> 結果として、不本意に🌸のアイコンが削除されている。

課題

最新のブランチから取り組めば解決する問題ではありますが、今回、手順を記載しても実際に間違えてしまうこともあるので、間違える前提で対策を取ることも必要だと思います。
また、目視でselection.jsonを確認したりする方法もありますが、それも結局見落とす可能性があります。
なので、そもそもヒューマンエラーが起こりやすいので、自動的に検知する必要がありそうです。

解決案

チームとして解決案が3つほど挙げられました。

[解決案1]
jestのsnapshotを導入しUIチェックする
[解決案2]
icomoonではなく別のツールでアイコンを管理する
[解決案3]
Circle CIでicomoonのアイコンが呼び出されているが、アイコンが存在しない場合に、エラーが出るように対応。

その中で[解決案3]が確実性やメンテナンスも楽との理由で採用されました。

その他の解決案が採用されなかった理由

  • [解決案1]->工数がかかる。既存のコードすべてにsnapshotの処理を書く必要がある。また、追加のテストにも記載していく必要がある。
  • [解決案2]->デザイナーさんが独自で作成したアイコンもあるため、容易に変えれない。

取り組んだこと

実際に取り組んだ、[解決案3]のCircle CIでicomoonのアイコンが呼び出されているが、アイコンが存在しない場合に、エラーが出るように対応についてまとめていきます。

状況を整理

状況を整理します。

  • 何が起こるとエラー(アイコンがない)と認識するのか。
  • Circle CIのconfig.ymlでエラー内容をキャッチする。

現在、不本意にアイコンが削除された状態でbuildを行うと、icon * does not exist.というエラーログが確認できます。なので、それをCircle CIで検知しようと思います。

実践

まずconfig.ymlでrunコマンドを追加。nameは何を行っているコマンドなのかをわかりやすくします。commandの始めには複数行のコマンドを記述することを示す|を追加します。(YAMLの記法)

- run:
    name: Run build and check for missing icons
    command: |

次にcommandの内容を記載していきます。
ビルドの出力を後で再利用するためにyarn buildの実行結果をBUILD_OUTPUTに保存します。変数に入れることでビルドを1回だけ実行します。次に通常の出力とエラー出力の両方を変数に保存する。

ここで2>&1について説明します。
まず、UNIXシステムでは、出力に2種類のストリーム(データの流れ)があります。

  • 標準出力(stdout) - 1番
    通常のプログラム出力
    正常な実行結果

  • 標準エラー出力(stderr) - 2番
    エラーメッセージ
    警告メッセージ

なので、標準出力とエラー出力の両方を取得し、確実にアイコンエラーを確実に検出するために
$(yarn build 2>&1)と記載しています。(yarn buildのみにするとエラーを見逃してしまいました。)

- run:
    name: Run build and check for missing icons
    command: |
     BUILD_OUTPUT=$(yarn build 2>&1)

シェルスクリプトの構文を追記していきます。
まず条件分岐を記載。文末はfiで終わります。
echo "$BUILD_OUTPUT"で 変数の内容を出力し、| パイプで左のコマンドの出力を右のコマンドに渡します。grep -qで テキストパターンを検索し、"icon .* does not exist"があればそれを抽出して表示。エラーコードで終了するように、exit 1を記載。

if echo "$BUILD_OUTPUT" | grep -q "icon .* does not exist"; then
  echo "🚨 Missing icons detected:"
  echo "$BUILD_OUTPUT" | grep "icon .* does not exist"
  exit 1
fi

完全版

- run:
    name: Build and check for missing icons
    command: |
       echo "Check for Missing Icons in Build Log"
       BUILD_OUTPUT=$(yarn build 2>&1)
       echo "$BUILD_OUTPUT"
       if echo "$BUILD_OUTPUT" | grep -q "icon .* does not exist"; then
        echo "Error: Missing icons detected in build log"
        echo "$BUILD_OUTPUT" | grep "icon .* does not exist"
        exit 1
       fi
    no_output_timeout: 20m # 無限ループに陥るのを防ぐために追加

まとめ

今回はicomoonでの意図しないUIの変更に対してのアプローチでした。
あまりcircle.ymlを変更したことはあまりないのですが、一つ一つ調べながら解決する事ができました。
このようにアプリケーションではできるだけヒューマンエラーを出さない開発環境作りを心がけていこうと思います。

補足🍪

上記を進める際に段階的に取り組んだことや学んだこともあったので、3つほど記載します。

CircleCI内でbuild内容を確認

CircleCI内でbuildができているかよくわからなかったので、一度buildだけのcommandを実装しました。

run:
name: Check build output
command: |
echo "Starting build..."
yarn build
no_output_timeout: 20m

sentryを無効化

同プロジェクトでエラー監視ツールでsentryを使用しているのですが、今回のCircleCI上でbuildを実行する際にsentryの権限エラーが出ました。認証方法を設定する方法もありますが、今回のCI環境では必ずしもSentryは必要ないので以下のようにconfig.ymlに記載に無効化しました。

environment:
      SENTRY_PROJECT: ""
      SENTRY_AUTH_TOKEN: ""

UNIXシステムの基本的なストリームは3つあります。

上記では出力のみに関するストリームなので。1と2のみ記載しています。

  • 標準入力(stdin) - 0番
    キーボードからの入力など
    プログラムへデータを送り込む経路

  • 標準出力(stdout) - 1番
    通常の出力
    プログラムの実行結果が流れる経路

  • 標準エラー出力(stderr) - 2番
    エラーメッセージの出力
    警告やエラーが流れる経路

Discussion