前回のDay 22では、リリースブランチ(
release/v1.0.0)を活用して、次期新機能開発をブロックせずにステージング検証とバグ修正だけを綺麗に収束させる手法を学びました。本日のDay 23は、第5部 ケーススタディの第3弾として、開発サイクルの集大成である【本番リリースとタグ打ち(安定バージョンの凍結)】を徹底解説します!
Subversion (SVN) や Visual SourceSafe (VSS) で長年開発されてきたベテランエンジニアの方なら、「リリース時に tags フォルダへ丸ごとコピーしたけれど、後から誰かが tags 配下のファイルを直接編集・コミットしてしまい、リリース時と実際のバイナリが一致しなくなった」「リビジョン番号が大きすぎてどのコミットが製品版かわからない」といった苦いトラブルを経験したことがあるはずです。
Gitのタグ機能は、特定コミットに対する「暗号学的に改ざん不可能な不変のピン留め」です。特に「注釈付きタグ(Annotated Tag)」を使えば、リリース担当者の署名・日時・リリースノートがリポジトリ内に永久に記録され、いつでも寸分の狂いもなく過去のリリース状態を100%再現できます。
本日は、検証完了後の安全なmainマージ手順、セマンティックバージョニング(v1.0.0)に基づくタグ打ち、C#のアセンブリバージョン(
Directory.Build.props)とGitHub Releasesの自動公開連携、そしてリリース直後に絶対に忘れてはならない「developへのマージバック(先祖返り完全防止)」まで、現場で即座に役立つ実践ノウハウを完全網羅してお届けします!
① 導入・本日の達成目標
結合テストやステージング環境でのQA検証が無事に完了すると、いよいよ製品をエンドユーザーに届ける「本番リリース」を迎えます。
この最終段階で最も求められるのは、「リリースしたコードが絶対に改ざんされず、1年後でも10年後でも寸分違わず同一のビルドを再現できる確実性」です。
Gitのタグ(Tag)とブランチ統合の完全サイクルをマスターすることで、人為的ミスやデグレ(退行バグ)を極限まで排除したプロフェッショナルなリリース体制を構築できます。
- SVNの「タグフォルダコピー(重い・変更可能)」とGitの「コミットへの不変タグ打刻」の本質的違いを腹落ちさせる
- リリースブランチからmainブランチへの安全なマージ作法(
git merge --no-ff)を習得する - 「軽量タグ(Lightweight)」と「注釈付きタグ(Annotated)」の違いを理解し、本番リリースには必ず注釈付きタグ(
-a)を使う原則を徹底する - C# / .NET プロジェクトにおけるバージョン一括管理(
Directory.Build.props)とGitHub Releases自動公開の連携フローを理解する - mainリリース完了後にバグ修正をdevelopへマージバックし、先祖返り(バグ復活)を100%防止するサイクルを完結させる
② 【図解】SVNのタグコピー vs Gitのタグ打刻:なぜGitのタグは「完全不変」なのか?
まずは、SVNなどの集中型バージョン管理とGitにおける「タグ(Tag)」の決定的な設計思想の違いを確認しましょう。
SVNにおけるタグは、技術的には「/trunk または /branches のディレクトリを /tags/v1.0.0/ へ丸ごとコピー(svn copy)したもの」に過ぎません。
そのため、運用ルールで「tags配下は変更禁止」と決めていても、うっかり誰かが /tags/v1.0.0/ 内のソースコードを編集してコミットできてしまう重大な脆弱性がありました。
その結果、「本番環境で動いているプログラムと、tagsフォルダのコードが微妙に違う!」という致命的なトラブルが現場で後を絶たなかったのです。
対照的に、Gitのタグは「特定コミットオブジェクトのSHA-1/SHA-256ハッシュ値を指す、不変の参照ポインタ」です。
Gitのコミット履歴は暗号学的なハッシュチェーンで保護されているため、コミット内容や親コミットを1ビットでも書き換えるとハッシュ値が変わります。したがって、タグが指すコードの内容を後から改ざんすることは不可能です。
さらに、実体は数十バイトの参照情報にすぎないため、作成は一瞬(0秒)であり、リポジトリの容量を圧迫することも一切ありません。
📊 SVNタグとGitタグの徹底対比表
| 比較項目 | SVNのタグ(svn copy) | Gitのタグ(git tag) |
|---|---|---|
| 仕組み・実体 | 別フォルダへの丸ごと複製 | 特定コミットへの不変ポインタ(0バイト級) |
| 改ざん耐性・不変性 | 後から編集・コミット可能(危険) | 暗号学的ハッシュで完全保護(改ざん不能) |
| 作成速度・負荷 | サーバー通信とコピー処理が必要 | ミリ秒単位で瞬時に完了 |
| メタデータ記録 | SVNコミットログのみ | 作成者、作成日時、GPG署名、詳細メッセージ |
③ 【図解】mainマージと注釈付きタグ打刻(Annotated Tag)の実践手順
ステージング環境でのQAテストが合格したら、リリースブランチを本番リリース用ブランチ(main)へと統合し、バージョンタグを打刻します。
1. なぜ「軽量タグ」ではなく「注釈付きタグ」を使うのか?
Gitのタグには2種類が存在します。
- 軽量タグ(Lightweight Tag):
git tag v1.0.0のようにオプションなしで作成。単なるコミットへの別名(エイリアス)であり、作成者や日時のメタデータは一切保持されません。個人開発の一時的なブックマーク用です。 - 注釈付きタグ(Annotated Tag):
git tag -a v1.0.0 -m "メッセージ"で作成。Git内部で独立した「タグオブジェクト」として保存され、作成者の名前・メールアドレス・作成日時・リリースノートメッセージ・(任意で)GPG電子署名が強固に記録されます。
git tag -a を使用すること商用ソフトウェアやWebサービスの本番リリースでは、「誰が・いつ・どのような理由でリリースしたのか」というトレーサビリティ(追跡性)が監査上も極めて重要です。軽量タグを本番リリースに使うことは厳禁と心得ましょう。
2. CLIでの本番マージ&タグ打ちコマンド完全手順
リリースブランチ release/v1.0.0 から main へマージし、タグを打ってプッシュする標準的なコマンドフローです。
git switch main
git pull origin main
# 2. リリースブランチをマージ(必ず --no-ff でマージコミットを記録)
git merge --no-ff release/v1.0.0 -m "Merge branch 'release/v1.0.0' into main for release"
# 3. マージコミットに対して注釈付きタグ(Annotated Tag)を打刻
git tag -a v1.0.0 -m "Release v1.0.0: 正式本番リリース(C# 顧客管理システム新機能・性能改善)"
# 4. mainブランチとタグをリモートリポジトリへプッシュ
git push origin main
git push origin v1.0.0 # または git push --tags
※ git push origin main だけではローカルで作成したタグはリモートに送信されません。必ず git push origin v1.0.0 (または git push origin --tags)を実行してタグを明示的にプッシュする必要があります。
④ 【図解】C#現場でのバージョン管理(Directory.Build.props)とGitHub Releases連携
C#(.NET 8 / .NET 9)の大規模プロジェクトでは、複数のプロジェクト(Web API、Domain、Infrastructure、Batch等)が存在します。
各プロジェクトの .csproj ファイルにそれぞれバージョン番号を手動で記載していると、更新漏れやバージョンの不一致事故が発生します。
1. Directory.Build.props によるソリューション全体の一括バージョン管理
ソリューション(.sln)と同じルートディレクトリに Directory.Build.props ファイルを配置すると、配下のすべての .csproj に自動で共通プロパティが適用されます。
<Project>
<PropertyGroup>
<Version>1.0.0</Version>
<AssemblyVersion>1.0.0.0</AssemblyVersion>
<FileVersion>1.0.0.0</FileVersion>
<InformationalVersion>1.0.0+build.release</InformationalVersion>
<Company>Our Enterprise Tech Inc.</Company>
<Product>Customer Management System</Product>
</PropertyGroup>
</Project>
リリース時には、この1箇所の <Version> を 1.0.0 に書き換えてコミットするだけで、全DLLやEXEのアセンブリ情報、SwaggerのAPIバージョン、NuGetパッケージ番号が一括で同期されます!
2. タグプッシュをトリガーとしたGitHub Actions自動リリース連携
git push origin v1.0.0 を行うと、GitHub Actionsが tags: ['v*'] のイベントを検知し、自動的に本番ビルドを実行してGitHub ReleasesにZIP配布物とリリースノートを公開するワークフローが組めます。
name: Production Release
on:
push:
tags:
- 'v*'
jobs:
build-and-release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'
- name: Publish App
run: dotnet publish -c Release -o ./publish
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
generate_release_notes: true
files: ./publish/*
⑤ 【図解】developへのマージバックとリリースサイクルの完結
「mainにマージしてタグを打ったから、これで本日のリリース業務は完了!」とPCを閉じてしまっては絶対にダメです!
リリースサイクルにおける最大の落とし穴がここにあります。
1. なぜ「developへのマージバック」を忘れると悲劇が起きるのか?
Day 22で学んだ通り、リリースブランチ(release/v1.0.0)ではステージング検証中に発見された不具合の緊急修正コミット(fix: ...)や、バージョン番号の更新コミットが行われています。
もしこの修正内容を develop ブランチ(次期バージョン v1.1.0 の開発本流)へ合流させずに放置した場合、何が起きるでしょうか?
次回の v1.1.0 リリース時に、「v1.0.0 で苦労して直したはずのバグが、v1.1.0 で綺麗さっぱり復活する(先祖返り・リグレッション)」という大惨事に見舞われます!
2. サイクルの完結手順(マージバックとブランチ削除)
git switch develop
git pull origin develop
# 2. リリースブランチのバグ修正をdevelopへマージバック
git merge --no-ff release/v1.0.0 -m "Merge branch 'release/v1.0.0' back into develop"
git push origin develop
# 3. mainとdevelop双方への合流が完了したため、役目を終えたリリースブランチを安全に削除
git branch -d release/v1.0.0
git push origin --delete release/v1.0.0
git branch -d (小文字のd)を使えば、対象ブランチが完全にマージされていない場合にエラーで教えてくれるため、誤って未マージのコミットを消してしまう心配もありません。
⑥ 現場で使える!実践CLIコマンド&トラブルシューティング集
📌 タグの一覧確認と詳細情報の表示
git show v1.0.0 # タグの作成者、日時、メッセージ、コミット差分を詳細表示
git show <tagname> を実行すると、注釈付きタグに付与された電子署名や作成者情報が即座に確認できます。
📌 誤って打ったタグの削除と差し替え手順
git push origin --delete v1.0.0 # リモートリポジトリのタグを削除
※ 一度リモートに公開したタグの削除や差し替えは、他メンバーのローカルキャッシュとの不整合を招くため、チーム内への事前告知を徹底してください。
📌 過去のリリースポイント(タグ)から緊急調査・パッチブランチを作成
本番で重大バグが発生した際も、タグ名を指定するだけで「リリース当時のコードベース」へ一瞬でタイムワープし、緊急ホットフィックスを開始できます。
📌 本日のまとめと次回予告
本番リリースとタグ打刻は、開発チームがユーザーと交わす「品質保証の契約」です。今回の重要ポイントをおさらいしましょう。
- SVNのタグはただのフォルダコピー(変更可能): Gitのタグは特定コミットを暗号学的に固定する不変のピン留め。
- 本番リリースは必ず注釈付きタグ(
git tag -a): 作成者、日時、詳細メッセージをメタデータとして永久保持する。 - C#バージョンは
Directory.Build.propsで一元化: 複数プロジェクトのバージョンズレを防ぎ、タグプッシュからGitHub Releasesを自動生成。 - developへのマージバックを絶対忘れない: リリースブランチの修正を次期開発本流へ合流させ、先祖返りを100%遮断する。
- 双方向合流の確認後にブランチ削除: 役目を終えたブランチをクリーンアップし、プロジェクトを常に清潔に保つ。
本番環境で深刻なバグが発生!developや進行中のリリースブランチを一切汚さず、mainのv1.0.0タグから最速でホットフィックスブランチを切り、修正・検証・v1.0.1パッチリリース・全ブランチ統合を完了する「火消しの極意」を徹底伝授します!
コメント
コメントを投稿