『入る学科間違えた高専生』の日記

プログラミングのコードを書いたりする予定です。あとは日記等。あといつまで高専生やねん

Firebase App Distribution で配信するための CircleCI Orb を自作した

この記事はフラー株式会社 Advent Calendar 2022 10日目の記事です。
9日目の記事は @su8 さんで「【突撃!自分の本棚】エンジニアの本棚卸し 2022」でした。

僕もおっきな本棚ほしい〜


はじめに

みなさんは CircleCI 使っていますか?
CircleCI には Orb という名前で普段みなさんが何かしらのソフトウェア開発しているときに使用するライブラリのようなものが提供されています。
この Orb は CircleCI が公式として提供していたり、サードパーティとして有志が開発をしてくれていたりします。

今回は、その Orb を自作してみたことについて記事にしていきます。

circleci.com

成果物

circleci.com

また GitHubソースコードも公開しています。
よくてもよくなくてもスターをください。

github.com

firebase-tools を使用して Firebase App Distribution に iOS アプリと Android アプリのバイナリをアップロードしてテスターに配信するという Orb です。
Node.js (npm) がインストールされていることが使用するための必要条件になっています。

macOS に Node.js 初めから入ってないだろ!ふざけるな!」という言葉が聞こえてくるかもしれません。でも安心してください。
CircleCI の提供する macOS の実行環境には標準で Node.js がインストールされています!!*1
Linux VM には Node.js がインストールされてるっぽいですが、 Android Machine や Android Docker にはインストールされていないっぽいのでそこは apt で頑張ってインストールしましょう。
もしくはワークスペースによるジョブ間のデータ共有を使って Node.js がインストールされている Docker イメージからアップロードするっていう手も使えるかと思います。

サンプルはこちら

version: 2.1

orbs:
  macos: circleci/macos@2
  ruby: circleci/ruby@2

jobs:
  build:
    macos:
      xcode: 14.1.0
    resource_class: macos.x86.medium.gen2
    steps:
      - checkout
      - macos/switch-ruby:
           version: "3.1.2"
      - ruby/install-deps
      - run:
           command: bundle exec fastlane gym
      - persist_to_workspace:
           root: .
           paths:
             - Products/Release.ipa

  dev-deploy:
    docker:
      - image: cimg/node:current
    resource_class: medium
    steps:
      - attach_workspace:
          at: .
      - run:
           command: # 何らかの処理

workflows:
  dev-deploy-workflow:
    jobs:
      - build:
          filters:
            branches:
              only: develop
      - dev-deploy:
          requires:
            - build    

iOS アプリは2023年4月以降 Xcode14.1 及び iOS16.1 SDK でビルドする必要がある*2のでここでの言及はあえて Xcode14.1 を前提としています。
また、今回の記事は Orb を自作するのが趣旨なので使い方はまた記事の下の方で記載します。

改善ポイント

firebase-tools を毎回インストールしていたりするので、改善ポイントはそこにあったりします。
npm が存在しないマシンについては今のところサポート外としているのでいい感じな方法があったら PR ください。お待ちしてます!!

Orb の実装

まずは Orb を作成する前に CircleCI CLI をインストールしたりすることがあります。(ドキュメントに書かれてなかったりすることがたまにあって一回詰んだ)

CLI のセットアップ

まずはインストール。私の環境は macOS なので Homebrew インストールしました。インストールしたら変わらんので各々の環境に合わせて。

circleci.com

$ circleci setup

CircleCI APIv2 を CLI が使ってくれるように Personal API Tokens を発行しましょう!
https://app.circleci.com/settings/user/tokens

ちょっと愚痴なのですが、 CircleCI は GitHub の SSO Organization に所属するアカウントだったらその Organization を Authorize していないと Personal API Tokens までその影響を受けて、再度 GitHub でその Organization の SSO を Authorize しないと影響が及んだままになります...(ちょっと何言ってるか伝わらないかもしれない)
GitHub の Personal Access Tokens のように Configure SSO によって特定の Personal API Tokens だけは SSO が Authorized された状態になっていてほしい。

Orb の初期セットアップ

さて Orb には開発キットが備わっていて上記でインストールした CLI によって Orb の開発テンプレートがダウンロードされ展開されます!!
ドキュメント*3には一切触れられていませんが、ここで私は詰まってしまったのですが、 CircleCI の Web アプリケーション側の Organization Setting の Security から以下の画像のように非公式の Orb の使用ができるように Yes にチェックを入れておかないと Orb が Registry に登録がされずに開発バージョンでテストができなくなってしまいます!!

以上の操作が完了したらようやくセットアップが進められます

circleci orb init <path> [flags] の形でコマンドを入力するといろんなことを聞かれるので聞かれた通りにセットアップ手順を踏んでいきます。

$ mkdir -p /path/to/circleci-orbs-firebase-app-distribution
$ circleci orb init /path/to/circleci-orbs-firebase-app-distribution

で今回は生成をしています。

? Would you like to perform an automated setup of this orb?  [Use arrows to move, type to filter]
> Yes, walk me through the process.
  No, just download the template.

と聞かれるのでとりあえず、 Yes と答えておけば最初は良いかと思います。私も今回は Yes を選択しました。
玄人になるともうテンプレートをダウンロードするだけで完了とすることができるんですかね。

? Are you using GitHub or Bitbucket?  [Use arrows to move, type to filter]
> GitHub
  Bitbucket

次は、 GitHub か BitBucket を使うかという選択肢ですが GitHub を使う想定だったのでそのまま選択しました。

? Enter your github username or organization

これは各々の GitHub のユーザ名もしくは Organization 名で進めます。私は nnsnodnb です。

? Enter the namespace to use for this orb (nnsnodnb)

Orb に使用するネームスペースを入力します。

orbs:
  ruby: circleci/ruby@2

上記のように Orb は使用することができるのですが、上記の circleci に当たる部分をここで決定します。
ちなみに CircleCI ユーザに対して1つしか作れないっぽいです。 Organization については試していませんが同じでしょう。

? Orb name (circleci-orbs-firebase-app-distribution)

Orb の名前を決定します。上のネームスペースの後ろの部分を決定します。
デフォルトでは circleci orb init <path> で入力した部分の一番後ろのパス名が与えられるようです。
用途は CircleCI Orb なので私はここでは firebase-app-distribution と入力しました。

この時点で

orbs:
   orb-name: nnsnodnb/firebase-app-distribution@x.y.z

のようにして使える名前が生成されます。

? What categories will this orb belong to?  [Use arrows to move, space to select, <right> to all, <left> to none, type to filter]
> [ ]  Artifacts/Registry
  [ ]  Build
  [ ]  Cloud Platform
  [ ]  Code Analysis
  [ ]  Collaboration
  [ ]  Containers
  [ ]  Deployment
  [ ]  Infra Automation
  [ ]  Kubernetes
  [ ]  Language/Framework
  [ ]  Monitoring
  [ ]  Notifications
  [ ]  Reporting
  [ ]  Security
  [ ]  Testing

カテゴリはなにかと聞かれるのでそれぞれの目的にあったカテゴリを選択しましょう。私は今回は Deployment を選択しました。
自分的には Delivery なので Artifacts かなとか思いましたが配信なのでまぁいいかという

? Automatically set up a publishing context for your orb?  [Use arrows to move, type to filter]
> Yes, set up a publishing context with my API key.
  No, I'll do this later.

個人の用途なので特にいいかという気持ちで Yes としておきました。

CircleCI にはコンテキストとして環境変数をプロジェクトに跨って共有できる機能があります。
問題としては2点あって、 Organization としてコンテキストを使用する場合はそのコンテキストを使用するユーザ(トリガーするユーザ)が CircleCI 上にユーザを保持している必要があります。これについては企業等で使う場合は、メンバーに対して「 CircleCI に登録しておいてね〜」とかアナウンスをしておけば防げるかと思いますがしないメンバーもいるのでゴニョゴニョ...
またもう1つの問題点としてコンテキストは誰でも編集できてしまうということ...権限管理してほしい〜。

circleci.com

? Would you like to set up your git project? (Y/n)

しておきましょうw

? Enter your primary git branch. (main)

main ブランチで良きです!

? Enter the remote git repository

SSH 用の GitHub リポジトリ URL を登録しておくのがベターですかね。
GitHub Personal Access Token 派の方はそちらでも。

? I have pushed to my git repository using the above commands (y/N)

プッシュしてもいいですけど、テストが100%落ちるので N でいいかと思います。

N を選択すると Error: Could not follow project とか言われまずが CircleCI 上に以下の様な感じで Unpublish な Orb ができていれば成功と思って大丈夫です。

CircleCI の Web アプリケーション側で普段の設定の通り Project タブからリポジトリのセットアップを行えば設定が可能です。それにしてもなんでエラーにするんだw

先程の選択肢で push をするように選択した場合でも初めは必ず失敗します。

Dynamic Config を有効にしてくれという警告です。
またこれを有効にしたあとでも必ず失敗します。リンターのエラーです。めげずに頑張りましょう。

はい、ここまでで Orb のセットアップが完了しました。
記事のボリュームはここが一番長くなったんじゃないかなぁ〜(ここを書いている時点)

Orb の実装

https://github.com/nnsnodnb/circleci-orbs-firebase-app-distribution/tree/dca3fbe9b97e9059d7167debe3725eead1eb8fb7

前節の通りにするとテンプレートが生成されています。(執筆時点)
.circleci を見るとなにやらテストやデプロイなどが記述されているっぽいので特に今後は config のテスト以外に CLI を使わなくても良さそうです。

$ circleci orb validate ./src/@orb.yml

にてテストができます。が、 @orb.yml のテストしかしてないっぽいので CircleCI にプッシュしてテストを実行したほうが良さそうです。

CircleCI は寛大なので、 Free and Open Source なプロジェクトに対しては Organization について 400,000 クレジット/月が付与されます。これは年間 2,880ドルの価値があるようです。(条件としてリポジトリを公開にする必要があります。)
400,000 クレジットは個人で適当に使う分には全然使い切れない量なので本当にありがたいです。 ありがとうございます!ありがとうございます!(さっきまでの愚痴は忘れてください🙇‍♂️🙇‍♂️🙇‍♂️)

前職から CircleCI にはいつもお世話になっているので CircleCI のデータセンターに足を向けて眠ことができません。

さて、きれいな手のひら返しを披露したところであとは、思うまま実装をするだけになりました。

src フォルダを見ると以下のようになっていました。

ホーン。

思うままに実装をしたら Pull Request を生成してテストの実行を待ちます。
また、開発用 Orb を試す機能も備わっているので先程の Orb セットアップでコンテキストを生成した場合は GITHUB_TOKEN を同様に追加しておくと良いです。
テストが最後まで完了すると該当の Pull Request に以下のようなコメントが届きます。

コメントの URL に移動すると Orb のページが開きハッシュ値ベースで Orb が使用できることが確認できます。

orbs:
  firebase-app-distribution: nnsnodnb/firebase-app-distribution@dev:d33aa961828628e3dce7aa3183246c1b0040b25c

のようにして使用することができます。またここでの開発用 Orb は30日間の有効期限が設定されています。
手動で確認することももちろん可能ですが、せっかく .circleci/test-deploy.yml があるのでこちらに記述してテストすることがおすすめです。
今回のように Deployment 系だと実際にデプロイするしかテストする方法がないような気がしたので私は手元にあった個人でリリースしている iOS アプリのバイナリを GPG コマンドで暗号化しリポジトリに配置し、 CircleCI のジョブ内で復号をし Firebase App Distribution に配信をするというテストデプロイを実装しました。

実装が完了したらマージ!!
マージしたら先程と同様にマージコミットのハッシュ値での開発用 Orb のコメントが届きます。

Orb の公開

最後に本番に公開です!
これもめっちゃ簡単です。

.circleci/config.yml を見ると下記のようにすべての tag のフォーマットでトリガーをフィルタリングされているようです。

filters: &filters
  tags:
    only: /.*/

私は以下のように README.md にかかれてるように semantically versioned に従うように v○.○.○ の形式でタグを生成しました。あとはプッシュするだけ!

$ git tag v0.0.1
$ git push origin v0.0.1

CircleCI のジョブが最後まで成功すると Orb が公開され、 Pull Request に先程とはちょっとだけ文言が変更されたコメントが届きます。

これで無事公開作業が終わりました🎉🎉🎉

nnsnodnb/firebase-app-distribution Orb の使い方

自己満の紹介章です。

commandsdeploy を提供しています。

orbs:
  firebase-app-distribution: nnsnodnb/firebase-app-distribution@0.0.8 # 現時点最新

jobs:
  dev-deploy:
    docker:
      - image: cimg/node:current
    resource_class: medium
    steps:
      - attach_workspace: # ワークスペース内でジョブ間のデータ共有をしている前提
           at: .
      # Firebase App Distribution のサービスアカウントファイルの書き込み
      - run:
          name: Setup app-distribution-key.json
          command: echo "${FIREBASE_SERVICE_ACCOUNT_JSON}" > /tmp/app-distribution-key.json
      - firebase-app-distribution/deploy:
          app: ${FIREBASE_APP_ID}
          binary_path: Products/Release.ipa
          release_note: リリースノートです!
          service_credentials_file: /tmp/app-distribution-key.json
          testers: nnsnodnb

のような感じです。必須なパラメータは appbinary_path のみです。
サービスアカウントを使用するもしくは Firebase CLI トークンを使用することで Firebase App Distribution でのテスターへの配信が可能です。
また、サービスアカウントを使用する場合は、パラメータに渡さずに自ら GOOGLE_APPLICATION_CREDENTIALS環境変数としてサービスアカウントファイルを設定しておくことでも可能です。

- run:
    command: |
      echo "${GOOGLE_SERVICE_ACCOUNT_JSON}" > /tmp/app-distribution-key.json
      echo "export GOOGLE_APPLICATION_CREDENTIALS=\"/tmp/app-distribution-key.json\"" >> "${BASH_ENV}"

最後に

今回の Orb は公開モードで実装をし公開をしました。しかしながら企業などでは公開にしたくない場合もあるでしょう。
そのときは --private をつけることで非公開の Orb を実装することができます。

$ circleci orb init /path/to/your-orb --private

公開・非公開の設定は Orb 生成時にのみ選択ができ、後で変更ができません。
また、 Bitrise のように野良配布というような Orb の運用をすることができません。
なので必ず前述したような Orb の公開という作業が必要となり、 CircleCI の中の人からは見えてしまったりするかもしれません(名言がないのでわからん)

今回は、初めて CircleCI の Orb を自作してみて詰まったところや注意ポイントなどを含めつつ実装をしてみたことについて書いてみました!
毎年 Advent Calendar で iOS 周辺の間接ネタしか書いていない気がするな?まぁいっか!!

明日は @ManabuSekiさんで「X-Accel-RedirectとgoでS3ファイルに認証をつけて配信する方法」です!
お楽しみにノシ