背景
GitHubのtags機能でリリース管理をやろうと思っているのですが、リリース管理周りのベストプラクティスが見つけにくい...特に切り戻し、その後の再リリースなど含めて一気通貫してまとまっているものが少ない...
いろいろ探して、今のプロジェクトにあってそうなやり方、逆にほかにもこういうやり方があるけど見送ったやり方など諸々調べたので、備忘として残します。
めちゃ適当なテスト用のリポジトリも作成しました。ここでリリースノートなど含めていろいろ実験しています。
前提
Git Flowでのバージョン管理をしている前提です。Git Flowについて詳しくはこちらの記事が参考になります。
主にリリース管理面からみていきたいので、release, main, hotfixあたりが今回の議論の中心になっていきます。
リリースの流れ
主に今回重視したのは以下の点
- いま本番環境にどの資産が搭載されているかが明確になっている
- なるべくリリース/切り戻しにあたり資産に手を加えることがないようにする(revertなども最小限に)
- GitHubでできることはGitHubでやる(ローカルでなく後で履歴が追えるように)
これが満たせるような、かつできるだけシンプルなフローで考えてみました。
開発中~移行まで
基本的にはGit Flowに従って進めます。開発はdevelopブランチで行い、テストまである程度完了するとdevelopブランチからreleaseブランチの作成を行い、開発環境への搭載はreleaseブランチから行っていく形です。
移行
mainへのマージ
本番環境に搭載されているもの = mainブランチの内容
で進めたいため、まずはreleaseをmainにマージするところから。
mainの保護として直pushを禁止しているところも多いと思いますので、PR経由でmainにはマージしていくこととします。もしコンフリクトが発生すればここで解消しておきます。マージしたら、もともとあったreleaseブランチは削除しておきます。
releaseブランチから本番環境へ搭載し、それをmainブランチへあとで反映させるというフローもあるそうですが、どれが最新かわかりにくくなるため今回は見送りました
リリースノートとタグの作成
mainへのマージを行ったら、状態が分かるようタグ付けとリリースノートの作成を行います。
{repo名}/releasesに遷移して、「Draft a new release」をクリック

tagを選ぼうとすると、「Create new tag」ボタンがあるため、ここでタグを選択して新しいタグを作成します。
今回はメジャーなセマンティックバージョニングで進めようと思います。(vX.X.X)で管理していく。
「Generate release notes」ボタンを押すと、勝手にいろいろを書き込んでくれます。必要そうな情報はすべてあるため、このデフォルト状態で行こうかと思います。

一番下にこのチェックボックスがありますが、いったん無視で。「Set as the latest release」を選択しておきます。
(切り戻しがあった場合は、このあたり含めて使っていきます)
mainブランチの搭載
こんな形でリリースしたいタグを指定してデプロイが行われるようなワークフローを用意しました。
(ここでは単にそのバージョンのChangeLogなどがテキストで表示されるだけのワークフローにしています)
基本的には最新版をリリースすると思うので、デフォルトは最新版です
選択式でtagを選べるようにしたかったのですが、それはサポートされていないようです。ので手打ちでバージョンを指定する形式にしました。存在しないバージョンだとエラーでActionsが失敗するようにしています
切り戻し
もし切り戻しを行う際は、一つ前のバージョンのタグを指定して再デプロイを行うだけです。mainブランチの中身は変更せず(revertせず)、古いタグをデプロイする、の方針とします。
リリースノートの編集
環境とリリースノートの矛盾が発生しないよう、きちんとメンテしておきます。

最後にチェックボックスを「Set as a pre-release」にしてUpdateします。これにより、現在はリリースされていない内容であることが分かるようにしておきます。

すると、リリースノート上はこのような形でPre-releaseのラベルがつきます。
「Latest」のタグが付いているものが現在環境に搭載されているものだ、ということが分かりやすいように。
もしどのリリースにも「Latest」タグが付いていなければ、該当tagのリリースノートに対して「Set as the latest release」のチェックをつけておきましょう。これで「Latest」のラベルがつけられます。
改修前の状態をデプロイ

ワークフローは同じで、タグだけ戻したいバージョンのタグを入力してから切り戻したバージョンの搭載を行います。
修正して再移行
修正を行って再度搭載まで行います。修正の方針として、
- 一つ前のタグの状態からブランチを作成し、再度1から開発する
- 今のmainブランチ(障害は含むが最新の状態)からブランチを作成し、修正部分だけ実装する
の二つが考えられます。今回は改修規模が多いことを想定し、後者の方法で進めようと思います。
前者だと、本来はリリースしたかった内容の作り直しから始めるので、簡単な修正だったら良いですが大規模な改修には向きません。これを行いたい場合のメリットもあまり考えられなかったので、基本は後者でよいと思います。
ブランチの作成~修正対応
mainブランチからhotfixブランチを作成します。障害を含む状態なはずですので、その修正を行ってテストなどを行った後、再度PR経由でmainブランチへマージまで行います。
リリースノートの作成~搭載
初回の移行時と同じように、リリースノートを作成します。
今後はこのバージョンが最新になるはずなので、「Set as the latest release」のチェックもお忘れなく。
リリースノートまで作成完了したら、同じようにActionsで搭載を行います。
こちらもきちんと最新版が搭載されていることを確認しておきましょう。

以上で修正を含めたリリース作業が完了!リリースノートもいい感じにまとまりました。
まとめと感想
Git Flow自体は調べればいろいろありますが、じゃあ切り戻すときは?そのあとどう障害修正する?あたりまでを考え出すと、結構難しいし規模などによってとるべき手も変わってくるなという感じです。
今回は比較的大規模なプロジェクト向けのフローで考えましたが、実際アジャイル的な開発だったり毎日リリースがあるような環境だと、とるべきフローは全然変わってくるでしょうね...
補足:mainをrevertして資産を戻す
今回は古いtagを使うことにより切り戻しの際の搭載周りの管理を行いました。
が、一つだけ問題があり、「mainブランチの最新と搭載されている資産が一致しない」という状態が起こりえます。
どのタイミングで起こるかというと、「切り戻した後~修正のブランチをmainにマージするまで」のタイミングです。
なるべくその状況もわかるようにreleasesの機能を使いましたが、やっぱり「main=搭載資産」の前提は崩したくないので、一応この原則が保たれるフローも少し考えてみます。
切り戻しフロー
- mainブランチをrevertする
下記コマンドでrevertを行い、mainブランチへのマージをなかったことにするコミットで上書きします。
git revert -m 1 {取り消したいタグ名}
{取り消したいタグ名}を指定する必要があります。例えばv1.1.1 -> V1.2.0でのリリースした内容を切り戻したいときは、v1.2.0を指定するのが正しいです
-
mainブランチの内容をpush
もしmainへの直pushが禁止されている場合は、mainブランチから切り戻し用のブランチを作成して対応しましょう。 -
再登載
いまのmainブランチの内容で搭載しましょう。今回はタグベースでの搭載でなく、常にmainブランチの最新状態からの搭載を前提(main=搭載資産の前提)としているため、タグの作成などは不要です。
上記フローで切り戻しは可能です。
ただし、この方法で切り戻すと、その後の修正~から再登載の際に、「今のmainブランチ(=1つ前のバージョン)」から修正を行うしかなくなってしまいます。
もとのreleaseブランチなどが残っていても、mainブランチには「releaseを取り込む」「取り込み操作を取り消す」コミットが行われてしまっているため、releaseからの再マージをしようとすると差分が一つもない(releaseブランチにあったコミットはすべてmainブランチにもある)状態となります
これをどうにかする方法がいまいちなく、どう頑張ってもrebaseなどを行う、でもそうなるとコンフリクトが発生した時の解消が...などがややこしくなりで、今回は見送りました。
もしこのあたりのベストプラクティスがあれば教えてほしいです...





