Rettyアプリチームの今泉 @imaizume です。
昨今の開発において、バージョン管理ツール、特にGitとGitHubを多くの方が使っていると思います。 日常的に高頻度で行う作業ですので、かける手間や時間は極力抑えたいもの。 とりわけブラウザ、開発環境、ターミナル間の移動やロード時間を抑え、1つの画面内で開発からPull Request作成までを完了させたいものです。
そこで今回は、普段のモバイル開発で私が使っているGit及びGitHub CLI (gh
コマンド)の活用事例について紹介します。
GitHub CLIについて
GitHub CLI (gh
コマンド)は、GitHubへの操作をターミナルから行うことができる公式コマンドラインツールです。
gh
を使う主なメリット
- ブラウザを開かずにPull Requestを作成可能なのでウィンドウ切り替えが減るまたは不要
- ブラウザのロード時間がない
- PR文の作成などに慣れ親しんだエディタ(viなど)が使用可能
macであればHomebrewでインストールできます。
brew install gh
そして gh
でローカルからGitHubでの様々な操作をすることができるので、ブラウザへの移動・待ち時間が減りマウス操作が不要でかなり快適です!
普段の開発業務でのGitHub CLIの活用
普段アプリ開発を中心に行っている私が、ghコマンドをどう使っているかを紹介します。
1. Pull Requestの作成
まずは基本のPull Request作成。
gh pr
で、Pull Requestに関する参照・作成といった操作をターミナル上からすることができます。
https://cli.github.com/manual/gh_pr
作成時は、マージ元となるブランチを checkout
したうえで gh pr create
を実行します。
主に以下のようなオプションを与えることで、各種メタデータを付与しての作成も可能です。
-a
Authorを指定-B
Target Branchを指定(指定しない場合はデフォルトブランチになる)-r
Reviewerを指定
例えば私のチームでは
- Authorは自分
- Target Branch = メインブランチは
develop
- チームメンバー全員
で固定となることがほとんどなので、毎回以下のようなコマンドを実行します。
gh pr create -a imaizume -r reviewer1,reviewer2 -B develop
(reviewer1,reviewer2 にはレビュワーのGitHubユーザー名をカンマ区切りで指定)
チームの人数やブランチ戦略によりますが、これらのパラメータが固定される場合はターミナルのコマンド履歴から同じコマンドを実行するだけです。
なおPull Request作成に必要なタイトル、本文、Draft指定などのメタデータは、すべてプロンプトで聞かれます。 無論Pull Requestテンプレートも使うことができ、ブラウザで作成するのと比べて作業効率も良いと感じています。
そして私は、この作業をより簡潔に実行できるようzshの関数にラップして利用しています。
# Create Pull Request cpr () { author=imaizume reviewers=reviewer1,reviewer2 # 引数がない場合はターゲットブランチにdevelopを指定 gh pr create -a $author -r $reviewers -B ${1:-develop} && gh pr view -w }
Target Branchが変わることがあるため、そこだけを引数で受けられるようなインターフェースとしています。
ただし、残念ながらメディア(写真・動画)のアップロードはできません。
そのため続けて gh pr view -w
でブラウザで開くようにもしています。
gh
にファイルアップロードのコマンドがあればよいのですが 今のところそれはできないようです。
2. 開発ベータ版アプリの配信
Rettyアプリチームでは2年前にCICDの構成を見直し、iOSはXcode Cloud、AndroidはCircle CIを使用するようになりました。
そしてGitHub Actionsを使い、特定のコメントをすると開発ベータ版を配信するようなワークフローを整備したことで、Pull Requestコメント駆動でのビルド・配信が可能になりました。
例えばiOS/AndroidリポジトリのPull Requestには以下のようなコメントをすると、そのブランチでの開発ベータ版アプリの配信ができます。
この「Pull Requestに特定のコメントをする」という作業も gh pr comment
で行うことができますので、 gh
コマンドでベータ版の配信までできるというわけです。
gh pr comment -b "/testflight"
そして次のようにシェル(私の場合はzsh)の関数として定義しておくことで、さらに高速に起動が可能です。
# Create Distribution for iOS function cdi() { github=https://github.com/RettyInc/iOS_App/pull # 引数あり(数値): 指定したIDのPull Requestにコメント if [[ $1 == "" ]] then id=$(gh pr view --json number | jq ".number" -c) gh pr comment "$github/$id" -b "/testflight" # 引数あり("s"): percolで絞り込んだPull Requestにコメント elif [[ $1 == "s" ]] then id=$(gh pr list | percol | awk '{print $1}') gh pr comment "$github/$id" -b "/testflight" # 引数なし: 現在checkout中のブランチのPull Requestにコメント else gh pr comment "$github/$1" -b "/testflight" fi } # 現在checkoutしているブランチでベータ版を配信(push済みの前提) $ cdi # ID #1234 のPull Requestのブランチでベータ版を配信 $ cdi 1234
これまでは開発完了後ベータ版を配信するのにブラウザへ移動しての操作が必要で、一日数回~十数回のこの作業は地味にストレスでした。
しかし今は git push
を終えてから続けて cpr
や cdi
と打つだけでPull Requestの作成・配信がができるのでなかなか快適です。
3. リリース版アプリの配信
2と似ていますが、リリース版の配信には別のGitHub ActionsのWorkflowを設定しています。
このWorkflowは、開発上のメインブランチである develop
ブランチからアーカイブ用のブランチである master
にむけ Pull Requestを作成し、コード上のバージョンをインクリメントするなどの作業を行っています。
そのため開始時点ではPull Requestが存在せず2のようにコメントで開始することができません。
そこで、直接GitHub Actionから起動するため gh workflow run
コマンドを使うことで同様の作業が実現できます。
https://cli.github.com/manual/gh_workflow
ただしworkflowに引数を渡したい場合にコマンドが冗長になりがちなので、こちらもzshの関数にラップして実行すると楽です。
# Create Release Version for iOS releaseiOS () { repo=RettyInc/iOS_App # リリース用のworkflow定義ファイル yml=create-release-pr.yml if [[ $1 == "" ]] # バージョンインクリメントする then gh workflow run $yml -R $repo # バージョンインクリメントしない else gh workflow run $yml -R $repo -F should_skip_update_version=true fi }
リリース用のワークフローでは、不具合発覚時のビルドのやり直しを考慮してアプリバージョンを上げない(内部バージョンのみインクリメントする)ための should_skip_update_version
オプションが設定されているので、これも考慮する形にしています。
4. アプリ向けサーバーのリリース有無の確認と実行
Rettyアプリチームでは、Web用と別にアプリ向けのサーバーを開発・運用していますが、こちらでは昨年、リリースの方式を手動からGitHub Releaseを使った方式へと移行しました。
具体的なリリース手順としては
- メインブランチ上の未リリース差分を確認
- GitHub Actionsのworkflowを使ってDraft Releaseを作成
- 1名以上のレビューを経てPublish
- Circle CIのデプロイワークフローが起動してデプロイ
という流れです。
下図のブランチの状態で言うと B...D
i.e. release-20240201...origin/develop
を、前述のステップ1,2で確認、リリースを作成したいということになります。
ただWebと異なり、アプリチームはサーバーの開発量が少なくクライアント依存となることが多いため、提供価値の薄い差分は極力蓄積してからデプロイするという方針を取っています。
このため「今日はリリースした方がいいのかな?」と毎回未リリース差分を確認するのですが、GitHubのUIから行うには複数の導線を経由する必要があり少々面倒です。
そこで最新のリリースを取得し直接URLを開くことで、この作業をショートカットすることができます。
# Check Release function cr () { # リポジトリのpathを設定 reponame=RettyInc/AppServer repopath=~/go/src/github.com/$reponame # タグ情報をfetch git -C $repopath fetch # 名前の降順でソートして最新のタグを取得 tag=$(git -C $repopath tag -l --sort -authordate | grep "production" -m 1) echo "Latest Release: $tag" # 引数に"w"を指定した場合はWebで開く if [[ $1 == "w" ]] then open "https://github.com/$reponame/compare/$tag...develop" # それ以外はローカルで表示 else git -C $repopath diff develop $tag fi }
タグ名は規則でprefixと日付が入っているので、grepとsortで最新のタグ=直近のリリースを取得し、現在の最新の差分と比較することができます。
これにより未リリースの差分をチェック後、3と同様にActionsのWorkflowを起動してDraft Releaseを作成する関数も定義できます。
# 最新のdevelopブランチでGitHub Releaseを作成するworkflow function releaseAppServer () { reponame=RettyInc/zeus repopath=~/go/src/github.com/$reponame git -C $repopath fetch COMMIT_HASH=$(git -C $repopath rev-parse origin/develop) yml=create-draft-release.yml gh workflow run $yml -R $reponame -f TARGET_HASH=$COMMIT_HASH }
まとめ
以上が普段の私のGit/GitHub CLIの活用方法でした。
個人的には、Web開発やサーバー用スクリプトで活用は多く聞くものの、私が担当しているようなアプリの開発では案外使っている方が少なさそうだったので今回扱ってみた次第です。
ちなみにAndroidStudioなどのJetBrains製IDEやVSCodeなどはターミナルインテグレーションがあるので1つのウィンドウで git switch
から開発、Pull Request作成までができてしまうので本当に便利ですね。
かつてはiOSにもAppCodeという夢のエディタが存在しましたが、いつの日かXcodeにもターミナルインテグレーションは搭載されるのでしょうか…
まだ使ったことがない方は、参考にして使ってみていただければ幸いです。