Azure DevOps のブランチ戦略とリリース単位|検証した変更だけを本番に出す方法

検証ブランチから本番ブランチへのプルリクエストが本番へ運ぶのは、作成した時点の変更ではなく、完了した時点の検証ブランチの中身です。承認を待つ間に検証ブランチへ入った別の変更も、同じプルリクエストに乗って本番へ出ます。

しかも Azure Repos の既定では、先に付いた承認の票は、あとから変更が入っても消えません。一部の変更だけを本番へ出したいときも、出したい変更だけを別のブランチで持っていくのではなく、検証ブランチの側で一度戻し、本番に出すものと同じ組み合わせを検証環境で確かめてから出すのが安全です。

検証用と本番用の2本のブランチを持ち、検証ブランチは検証環境へ、本番ブランチは承認を経て本番環境へデプロイする。ここまでは Azure DevOps でお客様の環境を運用する方の多くが組んでいる構成だと思います。弊社もお客様専用の環境を Azure 上に構築し、この形で運用しています。この記事では、その構成で「何をまとめて本番へ出すか」、つまりリリースの単位をどう設計するかを、実際に運用して踏んだ事象をもとに解説します。本番へのデプロイに承認や自動確認を組み込む方法は、Azure DevOps で本番環境へのデプロイを管理するで解説しています。

株式会社ロクシアシステムズは、福岡と東京を拠点に、全国のお客様へ Microsoft 365・Azure の導入支援と、AIの業務活用(AIプライベート)の支援を提供しているIT企業です。

本番へのプルリクエストは、承認を待つ間に入った変更も運ぶ

この記事では、検証ブランチを dev、本番ブランチを main と呼びます。dev へマージされた変更は検証環境へ自動でデプロイされ、本番へ出すときは dev から main へのプルリクエストを作り、レビューと承認を経て完了します。

ここで押さえておきたいのは、プルリクエストの元(ソース)が特定の変更ではなく「ブランチ」だという点です。プルリクエストを作ったあとにソースのブランチへ変更が入ると、その変更は同じプルリクエストに追加されます。Microsoft Learn によると、プルリクエストの「Updates」タブには、ソースのブランチへ押し込まれた変更が順に並びます。dev が元のプルリクエストでは、別件のプルリクエストが dev へマージされるたびに、本番へのプルリクエストの中身が増えていきます。

弊社では、本番へのプルリクエストが承認を待っている間に、別件の変更が dev へ入り、その本番へのプルリクエストに含まれていたことがありました。承認する側から見ると、承認を頼まれたときの中身と、完了するときの中身が違っていたことになります。

先に付いた承認の票は、既定では消えない

中身が増えても、すでに付いた承認の票はそのまま残ります。ブランチポリシーの「Require a minimum number of reviewers」には、ソースのブランチに変更が入ったときの扱いを決める「When new changes are pushed」という設定があります。

Learn の説明は、ここで選んだ場合に「ソースのブランチが変わるたびに票を消す」という書き方で、選ばなければ票は残ります。Azure DevOps CLI でポリシーを作る場合も、票をリセットするかどうか(reset-on-source-push)を明示して指定します。主な選択肢は次のとおりです。

設定(When new changes are pushed)ソースのブランチに変更が入ったときの動き
Require at least one approval on the last iteration最後の変更に対して、少なくとも1つの承認を必要とする
Reset all approval votes (does not reset votes to reject or wait)承認の票をすべて消す。却下と「作成者を待つ」の票は残す
Reset all code reviewer votes承認・却下・待ちを含め、すべての票を消す

本番ブランチへのプルリクエストには、このどれかを有効にしておくことをお勧めします。弊社は、承認したあとに中身が変われば、もう一度見てから承認する形にしています。なお、プルリクエストが完了したあと、本番へのデプロイを承認待ちで止めている間は、中身は変わりません。パイプラインの実行は、起動したときのコミット(Build.SourceVersion)をデプロイするためです。中身が動くのは、プルリクエストを完了するまでの間です。

出したい変更だけを、別のブランチで本番へ持っていけばよいのでは?

「今回は変更Aと変更Bだけを本番へ出し、変更Cは次回にしたい」という場面で、多くの方が最初に考えるのは、出したい変更だけを集めたブランチを作り、そこから main へプルリクエストを出す方法だと思います。Azure Repos には、完了したプルリクエストの変更を別のブランチへ写す「Cherry-pick」もあります。Learn によると、このボタンは対象のブランチから新しいブランチを作り、そこに変更を写したうえで、新しいプルリクエストを作るよう促します。

弊社はこの方法を使っていません。理由は、本番に出る組み合わせが、一度も検証環境で動いていないからです。検証環境で動いていたのは A・B・C がそろった dev で、A と B だけの組み合わせではありません。C を抜いたことで A や B の前提が崩れていても、本番に出るまで分かりません。Cherry-pick 自体は、修正を複数のブランチに当てるときなどに使われる機能です。弊社が使わないのは、検証環境で動かした組み合わせだけを本番に出す運用を選んでいるためです。

やり方本番に出る組み合わせ検証環境で動いたか弊社の判断
出したい変更だけを集めたブランチから main へA・B動いていない使わない(安全弁で止める)
Cherry-pick で main 向けのブランチへ写すA・B動いていない使わない(安全弁で止める)
dev で C を一度戻してから dev から main へA・B動いている(戻した後の dev が検証環境に出る)使う

本番へのプルリクエストを、検証ブランチからに限る

弊社は、main へのプルリクエストを dev からのものに限っています。これは Azure Repos の標準の設定ではありません。main のブランチポリシーにビルドの検証(Build validation)を「必須」として設定し、その中で、プルリクエストの元のブランチが dev でなければ失敗させる手順を入れています。

プルリクエストの元と先のブランチは、定義済みの変数 System.PullRequest.SourceBranch と System.PullRequest.TargetBranch で取れます。Learn によると、これらの変数はブランチポリシーによって起動したビルドでだけ値が入ります。

YAML — main へのプルリクエストを dev からに限る(ビルドの検証に入れる手順の書き方の例)
- task: PowerShell@2
  displayName: main へのプルリクエストは dev からのみ
  condition: and(eq(variables['Build.Reason'], 'PullRequest'), eq(variables['System.PullRequest.TargetBranch'], 'refs/heads/main'), ne(variables['System.PullRequest.SourceBranch'], 'refs/heads/dev'))
  inputs:
    targetType: inline
    script: throw 'main へのプルリクエストは dev からのみ受け付けます'

弊社では実際に、変更を分けて出そうとしてリリース用のブランチを切り、main へのプルリクエストを出したところ、この検証で失敗しました。止まったのは正しい動きです。部分的に出したいという理由で、この安全弁を外したり弱めたりはしません。

一部だけ出す手順:検証側で一度戻し、本番へ出してから戻しを取り消す

一部の変更だけを本番へ出す必要があるときは、出さない変更を dev の側で一度打ち消します。打ち消した後の dev は検証環境へ自動でデプロイされるので、本番に出すものと同じ組み合わせを、先に検証環境で確かめられます。

  1. 変更Cを dev へ入れたプルリクエスト(完了済み)で「Revert」を選び、C を打ち消すプルリクエストを dev へ出してマージする
  2. C を打ち消した dev が検証環境へデプロイされるので、A と B だけの組み合わせで動作を確かめる
  3. dev から main へのプルリクエストを出し、通常どおりレビューと承認を経て本番へデプロイする
  4. 本番への反映が終わったら、手順1で作った打ち消しのプルリクエスト(完了済み)でもう一度「Revert」を選び、C を dev へ戻す

Learn によると、完了したプルリクエストの「Revert」は、そのプルリクエストの変更を打ち消すコミットを持つ新しいブランチを作り、そこから新しいプルリクエストを作る形で動きます。打ち消しも、その取り消しも、通常の変更と同じくレビューを通るので、何をいつ戻したかが記録に残ります。この手順で気を付けている点は次のとおりです。

戻すときは、打ち消しのプルリクエストをもう一度「Revert」する

C を戻すときに、C を作った元の作業ブランチから同じプルリクエストを出し直しても、C は戻りません。C のコミットはすでに dev の履歴に含まれているため、差分が無いものとして扱われます。戻すには、打ち消しのコミットそのものを打ち消します。手順4で「打ち消しのプルリクエスト」の側を Revert するのはこのためです。

打ち消しが衝突するなら、リリースの単位が大きすぎる

C の後に入った変更が C に依存していると、C を打ち消すときに衝突します。この場合は C だけを抜くことはできず、C に依存する変更もまとめて次回に回すことになります。衝突が起きるということは、dev にたまった変更が多すぎる、つまりリリースの単位が大きすぎるという合図です。次の節で扱います。

検証ブランチから本番ブランチへのマージに、squash を使わない

Azure Repos では、プルリクエストを完了するときのマージの種類を選べます。squash マージは、元のブランチの変更を1つの新しいコミットにまとめて取り込みます。Learn は、squash マージしたあとは元のブランチを削除することを勧めています。取り込んだ側には、元のブランチを取り込んだ記録が残らないためです。dev のように使い続けるブランチを squash で main へ取り込み続けると、dev と main の履歴が食い違っていきます。弊社は dev から main へは、マージのコミットを作る通常のマージにしています。

そもそも部分的に出さずに済むよう、リリースの単位を小さく保つ

前の節の手順は、部分的に出す必要が生じたときの対処です。打ち消しと取り消しの分だけレビューと検証の手間が増えるので、できれば使わずに済ませたいものです。弊社は、次の3つを決めておくことで、部分的に出す場面をほとんど無くしています。

検証ブランチに入れたものは、次の本番に出してよいものとして扱う

dev へのマージを「検証環境で試すため」ではなく「次に本番へ出してよい」という判断として扱います。お客様への告知が済んでいない、業務の繁忙期を避けたい、といった理由でまだ出せない変更は、dev へ入れずに作業用のブランチに置いておきます。

まだ出せない変更は、下書きのプルリクエストで止めておく

Azure Repos のプルリクエストは、下書き(Draft)として作れます。Learn によると、下書きのプルリクエストではビルドの検証は自動で動かず、投票もできず、必須のレビュー担当者も自動では追加されません。変更の内容は見られる状態にしつつ、承認が付いて dev へ入ってしまうことを防げます。出せる状態になったら「Publish」で通常のプルリクエストに切り替えます。

検証ブランチと本番ブランチの差をためない

dev に入ったまま本番へ出ていない変更が増えるほど、「この中のこれだけは出せない」という場面が生まれやすくなります。弊社は、検証環境で確かめた変更は、間を空けずに本番へ出すようにしています。1回に本番へ出す変更が少なければ、承認する人が中身を把握しやすくなり、問題が出たときにどの変更が原因かも絞り込みやすくなります。

自動化の落とし穴:パイプラインの一覧は、最新とは限らない

本番へのプルリクエストを完了すると、本番へのパイプラインが自動で起動し、承認のところで止まって待ちます。弊社は、この「起動したか」を REST API で確かめる手順を自動化の中に組み込んでいましたが、ここで判断を誤りました。

ビルドの一覧を取得する API で、本番へのパイプラインの最新の実行を確かめたところ、新しい実行が出てきませんでした。「起動していない」と判断して手動で起動したところ、実際には自動で起動しており、同じ内容のデプロイが2本走っていました。手動で起動した実行の番号が1つ飛んでいたことで気付きました。一覧に新しい実行が出てこない状態は、弊社の環境では最大で42分以上続きました。これは Microsoft Learn に書かれている仕様ではなく、弊社の環境で起きた事象です。

同じ誤りを何度か繰り返したため、自動で起動した実行が承認待ちのまま残り、承認待ちが十数件たまりました。中身はその後の実行で本番へ出ているため、承認しても意味が無く、承認の記録としては読みにくいものになりました。承認の記録をお客様への説明に使う以上、この状態は避けるべきでした。

いまは、起動したかどうかを次の方法で判定しています。

確かめ方使い方
承認待ちの一覧を見るApprovals の API(_apis/pipelines/approvals)で、状態が pending の承認を取得する。本番へのパイプラインの実行がここにあれば、自動で起動している
実行の ID を指定して取得する実行の ID が分かっている場合は、一覧ではなく ID を指定して取得する。弊社の環境では、一覧より早く反映された
手動では起動しない本番へのパイプラインは、自動で起動する前提で扱い、確かめられないときも手動で起動しない。重複して起動してしまった場合は、片方を取り消す

承認待ちの一覧は、承認者が「今、何の承認を頼まれているか」を見る画面でもあります。ここに不要な承認待ちを残さないことも、リリースの単位を整理しておくことの一部だと考えています。承認の期限が切れたときの扱いは、Azure DevOps で本番環境へのデプロイを管理するで解説しています。

この記事の要点

本番へのプルリクエスト

  • 本番に出るのは、プルリクエストを完了した時点の検証ブランチの中身。承認を待つ間に入った変更も含まれる
  • 先に付いた承認の票は、既定では消えない。「When new changes are pushed」で票をリセットするか、最後の変更への承認を必須にする

一部だけ出すとき

  • 出したい変更だけを別のブランチで持っていくと、検証環境で動いていない組み合わせが本番に出る。本番へのプルリクエストは検証ブランチからに限る
  • 検証側で出さない変更を Revert し、検証環境で確かめてから本番へ出し、最後に打ち消しのプルリクエストを Revert して戻す

リリースの単位

  • 検証ブランチに入れたものは、次の本番に出してよいものとして扱う。まだ出せない変更は下書きのプルリクエストで止める
  • パイプラインが起動したかは、一覧ではなく承認待ちの一覧か ID の指定で確かめる

弊社の支援

弊社は、お客様専用のAI基盤やアプリを Azure 上に構築する形で、AIの業務活用を支援しています。構築した環境は、検証環境と本番環境の2面を同じ構成で持ち、検証ブランチの変更は検証環境へ自動で、本番ブランチの変更は承認を経て本番環境へデプロイする形で運用しています。

すでに Azure DevOps で運用している環境についても、ブランチポリシーの見直し、本番へのプルリクエストを検証ブランチからに限る仕組みの追加、リリースの単位の決め方まで、運用の状況に合わせてご提案しています。インフラをコードで管理するときの注意点はAzureの構成をBicepで管理するときの落とし穴もあわせてご覧ください。

よくあるご質問

こちらをクリックして「よくある質問」を表示

承認したプルリクエストに後から変更が入ると、承認は取り消されますか?

既定では取り消されません。ブランチポリシーの「When new changes are pushed」で、承認の票をリセットするか、最後の変更への承認を必須にする設定を有効にしておくと、中身が変わったときに改めて承認が必要になります。

出したい変更だけを Cherry-pick で本番ブランチへ持っていってはいけませんか?

弊社はお勧めしていません。本番に出る組み合わせが、一度も検証環境で動いていないためです。検証ブランチの側で出さない変更を一度打ち消し、同じ組み合わせを検証環境で確かめてから本番へ出す方法をお勧めします。

検証ブランチで打ち消した変更は、どうやって元に戻しますか?

打ち消しに使ったプルリクエスト(完了済み)で、もう一度「Revert」を選びます。打ち消しを打ち消すプルリクエストができるので、それをマージすると元の変更が戻ります。

元の作業ブランチからプルリクエストを出し直せば、打ち消した変更は戻りますか?

戻りません。その変更のコミットはすでに検証ブランチの履歴に含まれているため、差分が無いものとして扱われます。打ち消しのコミットそのものを打ち消す必要があります。

本番へのプルリクエストを検証ブランチからに限る設定は、Azure Repos の標準機能ですか?

標準の設定としてはありません。弊社は、本番ブランチのブランチポリシーにビルドの検証を必須として設定し、その中でプルリクエストの元のブランチを確かめて、検証ブランチでなければ失敗させています。

本番へのパイプラインが起動したかどうかは、どう確かめればよいですか?

弊社は、承認待ちの一覧(Approvals の API)か、実行の ID を指定した取得で確かめています。弊社の環境では、ビルドの一覧に新しい実行が出てくるまで42分以上かかったことがあり、一覧に無いことを「起動していない」根拠にはしていません。

お客様専用の Azure 環境を、検証環境で確かめた変更だけが本番に出る形で運用したい、というご相談を承っています。現在のブランチの構成と運用の体制をお聞かせいただければ、ブランチポリシーの設定から、リリースの単位の決め方まで、進め方をご提案します。お問い合わせよりお気軽にご相談ください。

関連サービス・関連コラム

まずは無料トライアルで、効果をご確認ください

「AIプライベート」は短期・低コストで試せます。帳票入力(AI-OCR)・電話対応(AI-Voice)・計画づくり(AI-Enhance)の自動化から、クラウド(Azure・Microsoft 365)・DXのご相談まで、お気軽にどうぞ。