GitLab実践ハンズオン: 既存システムの改修を安全に回す
前提バージョン: GitLab 19.2以上 / 必要プラン: Ultimate(Epic・Roadmap・Vulnerability Report・Merge request承認ルールなどに必要)。GitLab Duo Agent Platformの機能(Duo Chat・Code Review Flow・Agentic Chatなど)はFree/Premium/Ultimateいずれのプランでも利用可能で、Duo Core/Pro/Enterpriseのようなアドオン契約は不要です(プランに応じて割り当てられるGitLab Creditsを消費して動作します。トライアルでも一定量のクレジットが付与されます)。
既存システムの開発・保守をGitLabへ移管するにあたり、本当に確認すべきは「ソースコードが移せるか」ではなく、「日々の改修業務を安全かつ継続的に回せる基盤かどうか」です。本ハンズオンは、その論点を実際の操作を通じて検証する構成になっています。
既存システムへの改修要件をIssueとして起票し、設計・開発・レビュー・テスト・リリースまでを、GitLab標準機能のみで一気通貫に接続します。Issue参照のないマージを防ぐ制御や、テスト結果をJUnitレポートとしてMRに可視化する仕組みは、改修業務を属人化させずに、かつ品質を担保しながら回し続けるための土台となります。人によるレビューを強制するMerge Request承認ルールについても、その場所と使い方を紹介します。加えて、変更に紛れるシークレットの自動検知とトリアージ、リリースノートの自動生成により、セキュリティとリリース管理までを一貫して体験いただけます。
GitLab Duo Agent Platform(DAP)は特別な「オプション機能」として別枠で扱うのではなく、この一連の改修業務の中に組み込む形で体験します。設計時にはDuo Chatで既存コードの調査に、2件目の改修ではMRのレビュアーにGitLab Duoを指名して人によるレビューの前段(一次チェック)に、そして改修が一巡した後の振り返りではDuo Agentic Chatに進捗を問いかける——AIエージェントが日々の改修業務の各段階に自然に関わる開発体験を、通しのストーリーの中で確認いただけます。
単なるバージョン管理ツールの置き換えではなく、AI活用も見据えた開発・保守・品質管理の基盤としてGitLabが機能しうるか——その判断材料を、最小構成で得られるハンズオンです。
移管元について: 本ハンズオンで使うサンプルの既存システムは、Bitbucket Cloud と GitHub の両方に、まったく同じ内容でPublicリポジトリとして用意しています。お使いの環境や、実際の移管シナリオに近い方をお好きに選んで進めてください(以降、両方のURLを並記します)。
0. 事前準備: インポート元となる既存システムを確認する
本ハンズオンでは、「クラウド上のGitホスティングサービスで稼働している既存システムを、GitLabへ移管する」というシナリオで進めます。移管元となる既存システムは、以下の2つのPublicリポジトリとして、内容はまったく同一のものをあらかじめ用意しています。どちらを使うかは自由に選んでください。
| 移管元 | URL |
|---|---|
| Bitbucket Cloud | https://bitbucket.org/gl_tsukasa/hello-legacy-app.git |
| GitHub | https://github.com/gl-tsukasa/hello-legacy-app.git |
このリポジトリを、セクション3-2で New project > Import project を使ってGitLabへそのままインポートします。以降の本文では原則としてBitbucketを例に手順を記載しますが、GitHubを選んだ場合の差分がある箇所ではその都度補足します(基本的には、URLを読み替えるだけで同じ手順が使えます)。
本書の記載について: 以降の手順(5章のCI設定、7章の実装内容など)は、このリポジトリに
README.md/package.json/src/discount.js(割引計算ロジック) /test/discount.test.js(そのテスト)が含まれている前提で記載しています。実際にインポートした結果、ファイル名や関数名が異なっていた場合は、該当箇所を実際の内容に読み替えて進めてください(Bitbucket版・GitHub版のどちらでインポートしても、この前提は変わりません)。
0-1. リポジトリを確認する(任意)
-
Bitbucketを選ぶ場合: ブラウザで
https://bitbucket.org/gl_tsukasa/hello-legacy-appにアクセスします。Publicリポジトリのため、Bitbucketアカウントがなくてもソースコードを閲覧できることを確認します。Sourceタブから、README.md・package.json・src/discount.js・test/discount.test.jsなどのファイル構成をひと通り眺めておきます。 -
GitHubを選ぶ場合: ブラウザで
https://github.com/gl-tsukasa/hello-legacy-appにアクセスします。同様にPublicリポジトリのため、GitHubアカウントがなくても閲覧できます。トップページのファイル一覧から、同じREADME.md・package.json・src・testディレクトリが存在することを確認します。
0-2. ローカルにクローンして中身を確認する(任意)
手元の環境でより詳しく中身を確認したい場合は、以下でクローンできます(どちらもPublicリポジトリのため認証は不要です)。
# Bitbucketの場合
git clone https://bitbucket.org/gl_tsukasa/hello-legacy-app.git
cd hello-legacy-app
# GitHubの場合(どちらか一方で構いません)
git clone https://github.com/gl-tsukasa/hello-legacy-app.git
cd hello-legacy-app
Node.js(v20以上)がインストールされている環境であれば、テストもそのまま実行できます。
node --test
すべてのテストが成功(pass)することを確認してください。
コマンドについて:
node --testにパス引数を渡すとエラーになる場合があります。引数なしで実行してください(理由は5-4で詳しく説明します)。
このクローンは動作確認のためのものです。GitLabへの取り込みは、このローカルコピーをpushするのではなく、セクション3-2で
Import project機能を使ってBitbucketまたはGitHubから直接インポートします。ローカルへのクローンが不要な方は、0章全体を読み飛ばして3章から始めても構いません。
1. ゴールとプラン早見表
このハンズオンを最後まで実施すると、以下のワークフローが体験・確認できます。
- 新しく グループ を作り、その下にプロジェクトを作る(既存システムの移管を想定)
- 既存システムの保守・改修計画は Epic で管理し、Roadmap でタイムライン表示を確認する
- コードを変更する前に、必ず 1件の改修要件をIssueとして起票する(Scoped Label で改修種別を分類し、イシューボード のアサインリストで着手状況を追跡する)
- 実装前に、GitLab Duo Chat をアドホックに使って既存コードの設計・仕様を確認する
- イシューに紐づく ブランチ で作業する
- 変更は必ず マージリクエスト(MR) 経由で提出する
- MRには必ず対象イシューへの参照が含まれていないと、mainへのマージがブロックされる
- MRの変更は Secret Detection で自動スキャンされ、検知結果を Vulnerability Report でトリアージする
- MRのテスト結果は JUnitレポート としてMR上に可視化される
- MRのマージに**必須承認者(Merge request承認ルール)**を課す仕組みも用意されている(自分でユーザーを追加できる環境向けの機能のため、本ハンズオンでは場所と使い方の説明のみ)
-
mainブランチへの直接プッシュは一切できない -
mainへのマージ後、タグ付けは手動で行い、GitLab Release作成(Changelogの自動生成を含む)はCIジョブで自動化する - 2件目の改修では、MRのレビュアーにGitLab Duoも指名し、人によるレビューの前段としてAIによる一次レビュー(Code Review Flow) を体験する
- 2件の改修が一巡したところで、Duo Agentic Chatに進捗・レビュー状況・品質状況を問いかけ、アドホックなレポーティングを体験する
必要な権限: グループの Owner、プロジェクトの Maintainer または Owner ロール
全体の流れを図にすると、以下のようになります。
以下は、実際の手順の順番に並べたプラン早見表です。
| 機能 | 対応する手順 |
|---|---|
| 事前準備:インポート元(Bitbucket / GitHub)の確認 | 0 |
| グループ作成 | 3-1 |
| Import project(Bitbucket または GitHubからの既存システム移管) | 3-2 |
Scoped Label(key::value形式、改修種別の分類用) |
4-1 |
| イシューボード(Assigneeリスト) | 4-2 |
| イシュー/MRテンプレート | 5-1, 5-2 |
| Secret Detection(パイプライン診断) | 5-3 |
| ユニットテスト・JUnitレポート | 5-4, 7(手順9) |
| GitLab Releases(タグpushでCIジョブが自動作成) | 5-5, 7 |
| Changelog API(リリースノートの自動生成) | 5-5, 8-1 |
| Duo Code Reviewカスタム指示ファイル | 5-6, 7(手順19以降) |
| main保護・MRタイトルテンプレート・タイトル検証(まとめて設定) | 6-1 |
| Merge request承認ルール(必須承認者・説明のみ・任意) | 6-2, 7(手順11) |
| Protected tags(ロールベース) | 6-3 |
| Epic / Roadmap(既存システムの保守計画) | 7(手順1〜2) |
| GitLab Duoの設定確認(個人設定・プロジェクト設定) | 2-1, 3-3 |
| GitLab Duo Chat(設計・コード理解のアドホック活用) | 0-1, 7(手順4) |
| Secret Detectionの結果をVulnerability Reportで管理 | 7 |
| GitLab Duo Code Review(2件目の改修でAIレビューを体験) | 7(手順18〜22) |
| GitLab Duo Agentic Chat(進捗のふりかえり・レポーティング) | 7(手順23) |
このチュートリアルで使う機能はUltimateプランに含まれています。GitLab Duo Agent Platform関連の機能(Duo Chat・Code Review Flow・Agentic Chatなど)は、Free/Premium/Ultimateいずれのプランでも、別アドオンの契約なしにGitLab Creditsを消費して利用できます。
2. 最初に確認すること
- GitLabのアカウントを持っていること(
https://gitlab.com、または会社・学校のGitLabインスタンス) - 新しくグループを作成できること(セクション3-1で作成します)。作成後、そのグループにUltimateプランを適用できること(トライアルで構いません)
- グループを作成できるロール(通常は誰でも作成可能)、または既存グループでの Owner ロールを持っていること
- GitLab Duoが利用できること(手順4でDuo Chatを使います。利用できない場合は、当該手順は読み飛ばしても後続の流れに影響しません)。事前に2-1のチェックステップで、個人設定(Preferences)側の準備ができているか確認しておいてください
- インポート元として Bitbucket / GitHub のどちらを使うかを決めておくこと(0章に用意した2つのサンプルリポジトリはどちらもPublicなので、事前のアカウント作成やアクセス権の準備は不要です)
Ultimateの契約がまだない場合は、無料トライアル(30日間)でも本チュートリアルの内容は一通り試せます。
実際の移行プロジェクトでは、お手元にある既存のリポジトリ(GitLab/GitHub/Bitbucketや、社内Gitサーバー)を、
New project > Import projectからそのまま取り込みます。本ハンズオンでは、0章で示すBitbucket CloudおよびGitHub上の実在のサンプルリポジトリ(内容は同一)を、セクション3-2で実際にインポートする形で体験します。
2-1. (個人設定)GitLab Duoの設定を確認する
DAP(Duo Agent Platform)を使う機能(4章のDuo Chat、7章のCode Review・Agentic Chatなど)を試す予定がある場合は、グループやプロジェクトを作る前に、自分個人のPreferences設定を確認しておくと後戻りが少なくなります。この設定はグループ・プロジェクトに関係なく、アカウント単位で1つだけ存在します。
-
GitLab画面右上の自分のアバターアイコンを選択し、
Preferences(環境設定)を開きます。 -
デフォルトのGitLab Duoのネームスペースの項目を確認します。ここに表示されているのが、自分のDuo/DAP利用がどのネームスペース(グループ)の契約・クレジットに紐づくかです。 -
選択肢の中から、実際にDuo/DAPの契約・クレジットが割り当てられているネームスペースが選ばれていることを確認します。
- Ultimate Trialの場合: トライアル開始時点のネームスペースに、最初から一定量のクレジットが付与されています。通常はそのネームスペースを選びます。
- GitLab社主催のハンズオン・イベントに参加している場合: 講師・主催側から指定されたネームスペースを選びます。
- 誤ったネームスペースが選ばれていると、契約・クレジットの無いネームスペース扱いになり、DAP関連の機能全般が動作しない、またはクレジット不足のエラーになることがあります。3-1でグループを新しく作る場合は、作成後にこの設定を作ったグループに向けて選び直す必要がある場合があるので、3-1の直後にもう一度この設定を見直してください。
-
同じPreferences画面にある
GitLab DuoのOrbit(ベータ版)セクションを確認します。7章の手順23(Agentic Chat)でPlanner Agentの一部の回答に使われる機能のため、以下が有効になっていることを確認しておきます。項目 内容 GitLab DuoでOrbitを使用する Orbit機能全体のマスタースイッチ。OFFにすると以下すべてが無効になる エージェント型チャット Agentic ChatでOrbitのツールを利用できるようにする Orbitエージェント スタンドアロンのOrbitエージェントを利用できるようにする その他の基本エージェント 非チャットの基本エージェント(ソフトウェア開発・セキュリティ分析など)でOrbitのツールを利用できるようにする カスタムエージェント ユーザー作成のカスタムエージェントでOrbitツールを利用できるようにする
3. ★(実質的なスタート) グループとプロジェクトを作る
Epic・Roadmap・グループレベルのラベルをこのハンズオンで使うため、新しくグループを1つ作るところから始めます。
3-1. グループを作る
- Create group または Create Sub group を選択します。
-
Group name に
hello-gitlab-groupのような分かりやすい名前を入力します(URLも自動で決まります)。 -
Visibility Level はお好みで(
Private推奨)。 - 画面下部の Create group ボタンを選択します。
- 作成したグループにUltimateプランが適用されていることを確認します(トライアルの場合は
Settings > Billingから開始できます)。
3-2. Bitbucket または GitHub から既存システムをインポートする
0章で確認したhello-legacy-appを、GitLabのImport project機能でそのまま取り込みます。これが「既存システムのGitLabへの移管」に相当する操作です。Bitbucket・GitHubのどちらを選んでも、以降の手順・体験内容はまったく同じです(インポート方法の入り口が少し異なるだけです)。
-
画面右上の New project をクリックします。
-
Import project を選択します。
-
ここで、選んだ移管元に応じて進め方が少し分かれます。
① Bitbucketを選んだ場合(汎用的な方法: Repository by URL)
-
インポート元の一覧から Repository by URL を選択します(BitbucketのPublicリポジトリはGitの標準プロトコルでcloneできるため、専用のBitbucketコネクタを使わずこの方法で取り込めます)。
-
Git repository URL に以下を入力します。
https://bitbucket.org/gl_tsukasa/hello-legacy-app.git -
Publicリポジトリのため、認証情報の入力は不要です(空欄のまま進めます)。
② GitHubを選んだ場合
GitHubの場合は、以下の2つの方法のどちらでも同じ結果になります。
-
方法A: Repository by URL(認証不要・最も簡単)
-
インポート元の一覧から Repository by URL を選択します。
-
Git repository URL に以下を入力します。
https://github.com/gl-tsukasa/hello-legacy-app.git -
Publicリポジトリのため、認証情報の入力は不要です(空欄のまま進めます)。
-
-
方法B: GitHub専用インポーター(GitHubアカウントでの認証が必要)
- インポート元の一覧から GitHub を選択します。
- 画面の指示に従ってGitHubアカウントで認証(OAuthまたはPersonal Access Tokenの入力)します。
- 表示されたリポジトリ一覧から
gl-tsukasa/hello-legacy-appを選択してインポートします。 - コミット履歴に加えて、関連情報もあわせてインポートできる場合があります。
本ハンズオンでは方法Bを使う必要はありません。手軽に進めたい場合は方法Aで十分です。
-
-
Project name を確認します(既定では
hello-legacy-appになります。変更しなくて構いません)。 -
Project URL の左側にあるプルダウンから、3-1で作ったグループを選びます。
-
Visibility Level はお好みで(
Private推奨)。 -
画面下部の Create project ボタンを選択します。
-
インポート処理が始まります。画面上部にインポートの進捗が表示されるので、Finished(完了)になるまで待ちます。
-
完了後、プロジェクトのトップページを開き、
README.md・package.json・src/discount.js・test/discount.test.jsが表示されていることを確認します。移管元(BitbucketまたはGitHub)上のコミット履歴もそのまま引き継がれていることを、Repository > Commitsから確認できます。
これで、「既存システムをGitLabへ移管する」という体験が完了しました。ここから先の手順(ラベル追加、CI設定など)は、選んだ移管元にかかわらず、このプロジェクトに対して同じ内容で行っていきます。
ブランチ保護(6-1)を設定するのはこの後の6章です。保護前の今は
mainへ直接pushできる状態ですが、これは意図的なものです(既存システムの移管や初期セットアップの間は直接pushが必要になるため)。保護を有効化した後は、直接pushができなくなり、必ずMR経由になります。
社内Gitサーバーなど、外部から到達できないリポジトリの場合:
Repository by URLはGitLabのサーバーからインポート元URLへHTTPS/SSHで到達できることが前提です。到達できないネットワーク構成の場合は、いったんローカルにcloneした上で、空のGitLabプロジェクトへgit remote add+git pushする方法に切り替えてください。
参考(公式マニュアル): Migrate from Bitbucket Server / Migrate from Bitbucket Cloud / Migrate from GitHub
3-3. プロジェクトのGitLab Duo設定を確認する
2-1で確認した個人設定(Preferences)とは別に、プロジェクト単位のGitLab Duo設定もここで確認しておきます。プロジェクトが存在しないと確認できないため、3-2でのインポート完了後に行います。
- 3-2で作成したプロジェクトを開きます。
- 左サイドバーから
Settings > 一般設定を選択し、GitLab Duoセクションを展開します。 - 一番上の
GitLab Duoトグルの状態を確認します。- ONであれば、そのまま次に進みます。
- OFFで🔒(ロック)アイコンが付いている場合、この設定はグループまたはインスタンス側で強制されており、プロジェクト単体では変更できません。グループのOwnerまたは管理者に有効化を依頼してください(手順4のDuo Chatが使えない場合、当該手順は読み飛ばしても後続の流れには影響しません)。
- 7章の手順19以降(Code Review)のFlow系機能を試す予定がある場合は、
フロー実行を許可と基本フロー実行を許可の2つのトグルがONになっていることも確認します。 -
セッションに対するツールの承認(IDE・CLIでのツール承認)は、既定のON状態のままで構いません。
4. ラベルとイシューボードを作る
4-1. 改修種別のScoped Labelを作る
key::value 形式のラベル(Scoped Label)を使うと、同じスコープ内のラベルは同時に付けられなくなります(例: type::bug を付けると type::feature は自動的に外れる)。ここでは進行状況の追跡には使わず、改修の種別(不具合修正/仕様変更/新規機能追加など)を分類するためだけに使います。厳密な運用ルールは設けず、作成時に当てはまるものを1つ、とりあえず付けておく程度でかまいません。
- 画面上部の
Search or go toで、3-1で作ったグループを開きます。 - グループの
Manage > Labelsを開き、New labelを選択します。 -
Titleにtype::bugと入力し、色を選んでCreate labelを選択します。 - 同じ手順で
type::feature、type::changeも作成します。
ポイント:
- ここでラベルをプロジェクトではなくグループに作っているのには理由があります。
- セクション7で、プロジェクトのイシューにラベルを付けようとすると、ここで作った
type::*がプロジェクト側で何も設定しなくても選択肢に表示されます。グループのラベルは配下のすべてのプロジェクトに自動的に「降ってくる」ためです。- 実際にその様子はセクション7の手順2で確認します。
4-2. イシューボードにアサインリストを作る
進行状況の追跡には、Assigneeリスト(特定のユーザーにアサインされたイシューを表示するリスト)を使います。カードをこのリストにドラッグするだけでイシューが自分にアサインされます。
- プロジェクトの
Plan > Issue boardsを開きます。 -
New listを選択します。 - リストタイプで
Assigneeを選び、ドロップダウンから自分自身を選択してAdd to boardを選択します。 - 新しいリストが
OpenとClosedの間に追加されます(両端のOpen・Closedは常に固定されます)。
デフォルトの Open リストはそのまま残します。イシュー作成時はOpenリストに入り、着手を宣言するタイミングで自分のアサインリストへドラッグする、という流れになります。
5. 最初に必要なファイルを追加する
プロジェクトを保護する前に、必要なファイルを先に追加しておきます(保護後だとファイル追加自体がMR経由になり手間が増えるため)。
ファイルの追加方法: プロジェクトのトップページで、ファイル一覧の近くにある 「+」アイコン → New file を選択し、パスとファイル内容を入力して、下部の Commit changes でmainに直接コミットします。
5-1. イシューテンプレート
.gitlab/issue_templates/Default.md を作成します。
## 改修内容
<!-- 何を改修したいか、なぜ必要かを記述してください -->
## 完了条件
- [ ]
- [ ]
## 関連情報
<!-- 参考リンクやスクリーンショットなど -->
これで、以降 New issue 作成時に自動でこのテンプレートが適用されます。ラベルの/labelクイックアクションは入れていません。4-1で作ったtype::bug / type::feature / type::changeは種別分類用なので、当てはまるものがあればイシュー作成後に手動で1つ付けておく程度で十分です(必須ではありません)。
5-2. MRの説明文テンプレート
.gitlab/merge_request_templates/Default.md を作成します。
## 関連イシュー
Closes #
## 変更内容
## 動作確認方法
Closes #123 のように書くと、MRがマージされた際に対象イシューが自動でクローズされる副次効果もあります。
5-3. .gitlab-ci.yml(Secret Detection用)
コミットに紛れ込んだAPIキーやトークンなどを検知する Pipeline secret detection を追加します。
stages:
- test
- release
variables:
AST_ENABLE_MR_PIPELINES: "true"
include:
- template: Jobs/Secret-Detection.gitlab-ci.yml
AST_ENABLE_MR_PIPELINESは、後でセクション7で作るMRのパイプラインとしてこのジョブを確実に実行させるための変数です。これだけで、以降のパイプラインに secret_detection というジョブが追加され、push・MR作成のたびに自動でスキャンされます。
includeは、GitLabが公式に用意・保守しているテンプレート(ジョブ定義一式)を、自分の.gitlab-ci.ymlに取り込む機能です。ジョブの中身(イメージやスクリプト)を自分で書かずに再利用できるのが利点で、実際に実行される際は、この1行が展開されてsecret_detectionジョブの完全な定義(イメージ指定・スクリプトなど)に置き換わった状態でパイプラインが動きます。
Ultimateプランでは、検知結果はパイプラインの成果物(
gl-secret-detection-report.json)としてダウンロードできるだけでなく、MRのReportsタブやSecure > Vulnerability reportにも表示され、一元管理できます(セクション7で実際に確認します)。初めて有効化した場合、今回のスキャンでは既存のコミット履歴は対象外(現在のツリー状態のみ)です。過去のコミットも含めて検査したい場合は、
Build > Pipelines > New pipelineから、変数SECRET_DETECTION_HISTORIC_SCANをtrueにして1回だけ実行してください。
このファイルをコミットすると、mainブランチで最初のパイプラインが走ります。Build > Jobs を開いてsecret_detectionジョブのログを確認すると、no leaks foundと表示されているはずです(セクション7で、実際に漏えいを検知させる体験をします)。
5-4. .gitlab-ci.yml(テスト実行・JUnitレポート用)
5-3のファイルに、以下のtestジョブを追記してください。テスト結果をJUnit形式で出力し、MR上に自動で可視化させます。
unit-test:
stage: test
image: node:22
rules:
- if: '$CI_COMMIT_TAG'
when: never
- when: on_success
script:
- node --test
--test-reporter=spec --test-reporter-destination=stdout
--test-reporter=junit --test-reporter-destination=report.xml
artifacts:
when: always
reports:
junit: report.xml
-
--test-reporter=spec --test-reporter-destination=stdoutで、通常通り人が読める形式のログをジョブ画面に出力します。 - 同時に
--test-reporter=junit --test-reporter-destination=report.xmlで、JUnit形式のXMLレポートをreport.xmlに出力します。 -
artifacts.reports.junitにそのパスを指定すると、GitLabがこのXMLを解析し、MRのTests(テストサマリー)ウィジェットに結果を表示してくれます。 -
artifacts.when: alwaysを指定しているのは、テストが失敗してジョブが失敗した場合でも、レポートファイルをアップロードしてMR上で失敗内容を確認できるようにするためです。 -
あえて
node --testにパス引数を渡していません。test/のようなディレクトリ名を明示的な引数として渡すと、Node.jsはそれをglob(グロブ)パターンとして解釈しようとして一致せず、単一ファイルへのパスとして誤解釈してしまいCannot find moduleエラーになることがあります。引数なしであれば、Node.js標準の探索パターン(**/test/**/*.jsなど)により、test/discount.test.jsが自動的に見つかります。 -
rulesを明示的に指定しています。rules/only/exceptを何も書かない場合、GitLab CIは暗黙的にonly: [branches, tags]を適用するため、タグをpushしたときにもこのジョブが実行されてしまいます。このコミットは6-1のタイトル検証・このジョブ自体のJUnitチェック(および設定していれば6-2の承認ルール)を経て既にmainにマージ済みのはずなので、タグpush時に再度テストを実行する必要はありません。むしろ、releaseステージはtestステージの後に実行されるため、タグpush時のテストがもし何らかの理由で失敗すると、create-releaseジョブが実行されず、Releaseの作成がブロックされてしまいます。そのため、$CI_COMMIT_TAGが設定されている(=タグpushである)場合はwhen: neverでこのジョブ自体をスキップし、通常のブランチpush・MRのときだけ実行されるようにしています。
JUnitレポートの対象ファイルは
.xml拡張子である必要があります。それ以外の形式を指定するとGitLab側でエラーになるので注意してください。
5-5. .gitlab-ci.yml(Release自動作成用)
タグがpushされたら、Changelog APIで生成したリリースノートを添えて、GitLab Releaseを自動作成するジョブを追加します。5-3・5-4のファイルに以下を追記してください。
create-release:
stage: release
image: registry.gitlab.com/gitlab-org/cli:latest
rules:
- if: '$CI_COMMIT_TAG'
before_script:
- apk add --no-cache curl jq git
- git fetch --tags
script:
- |
PREV_TAG=$(git tag --sort=-v:refname | sed -n '2p')
if [ -n "$PREV_TAG" ]; then
FROM_REF="$PREV_TAG"
else
FROM_REF=$(git rev-list --max-parents=0 HEAD | tail -n 1)
fi
echo "PREV_TAG=${PREV_TAG:-(none)}"
echo "FROM_REF=${FROM_REF}"
echo "--- commits in range ${FROM_REF}..HEAD ---"
git log --oneline "${FROM_REF}..HEAD"
RESPONSE=$(curl --silent --header "JOB-TOKEN: ${CI_JOB_TOKEN}" \
--get \
--data-urlencode "version=${CI_COMMIT_TAG}" \
--data-urlencode "from=${FROM_REF}" \
"${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/repository/changelog")
echo "--- raw API response ---"
echo "$RESPONSE"
NOTES=$(echo "$RESPONSE" | jq -r '.notes')
if [ -z "$NOTES" ] || [ "$NOTES" = "null" ]; then
echo "Changelog APIから内容を取得できませんでした(対象コミットにtrailerが無いか、API呼び出しに失敗しています)。" > release_notes.md
else
echo "$NOTES" > release_notes.md
fi
release:
tag_name: '$CI_COMMIT_TAG'
name: 'Release $CI_COMMIT_TAG'
description: './release_notes.md'
このジョブは、GitLabのChangelog APIをGETで呼び出して(リポジトリを変更しない読み取り専用の呼び出しです)、直前のタグからの変更点をカテゴリ別にまとめたテキストを取得し、そのままReleaseの説明文として使います。対象になるのは、8-1で説明する形式のコミットだけです。
なぜ
fromを毎回明示的に指定するのか:
- Changelog APIは、
fromを省略すると「直前のバージョンタグ」を自動的に探します。- しかし、最初のリリース(直前タグが存在しない場合)に
fromを省略するとAPIがエラーを返します。- このエラーレスポンスには
notesフィールドが無いため、jq -r '.notes'が文字列"null"を出力し、それがそのままリリースの説明文になってしまいます。- これを避けるため、直前のタグが無い場合はリポジトリの最初のコミット(
git rev-list --max-parents=0 HEAD)をfromとして明示的に渡しています。
5-3〜5-5で組み立てたパイプライン全体は、以下のような構成になります。
unit-test(5-4)は、rulesで明示的に$CI_COMMIT_TAGが設定されている場合(=タグpush)をwhen: neverにしているため、タグpush時にはスキップされます。releaseステージのcreate-releaseジョブは、rulesでタグpush時にしか動かないよう制限されているため、通常のpush時には実行されません。
一方secret_detection(5-3、GitLab公式テンプレート由来)がタグpush時にも実行されるかどうかは、テンプレート側のrules次第であり、本書では断定できません。実際に手順16でタグをpushした際、Build > Pipelinesでこのジョブが動いているかどうかを確認してみてください。もし不要に動いていて気になる場合は、5-3のジョブをテンプレートから上書き(override)してrulesを追加することもできます。
5-6. Duo Code Reviewのカスタム指示ファイル
7章の手順19以降で、MRのレビュアーにGitLab Duoを指名してレビューコメントを自動投稿してもらう体験(Code Review Flow)をします。Duoのレビューは、標準のレビュー観点(セキュリティ・保守性など)に加えて、プロジェクト固有の観点を.gitlab/duo/mr-review-instructions.yamlというカスタム指示ファイルとして追加できます。
このファイルはmainに置いておくだけで効果を持つ設定ファイルであり、7章のように「1件の改修」としてIssue→ブランチ→MRを経由する必要はありません。mainを保護する6-1より前のこのタイミングでまとめて追加しておくと、後から(保護後に)追加する場合のようにIssue→ブランチ→MR→マージという手順を踏まずに済みます。7章の2件目の改修を試す予定がない場合は、このファイルは追加せずに読み飛ばして構いません。
5-1と同じ 「+」アイコン → New file の方法で、.gitlab/duo/mr-review-instructions.yaml を作成し、以下の内容にします。
instructions:
- name: レビュー言語ポリシー
fileFilters:
- "**/*"
instructions: |
1. レビューコメント、要約、提案するコード変更の説明はすべて日本語で書くこと。
2. コードの識別子・既存のコメント・ドキュメントが英語であっても、レビュー本文は日本語で書くこと。
3. 技術用語(関数名・クラス名・フレームワーク名など)は英語のまま使ってよいが、その意味や影響は必ず日本語で説明すること。
4. システムヘッダーなど、英語のまま残す必要がある固定文言・自動生成文言がある場合はそのままにしてよいが、フィードバック本文は日本語で書くこと。
5. コードやエラーメッセージを引用する場合、コード自体は原文のまま保持し、問題点や推奨事項の説明は日本語で書くこと。
6. 各コメントは、詳細な説明の前に「何が問題で、なぜ重要か」を1文で要約することから始めること。
7. 各コメントの重要度は「バグ」「提案」「質問」「要確認」のいずれかで明示すること。
8. 指摘を行う場合は、必ず具体的な修正コード例を示すこと。一般的な方向性のみを述べるコメント(例:「エラーハンドリングを検討してください」)で終わらせてはならず、実際に適用可能なコード(diffまたはコードブロック)を必ず添えること。「質問」区分のコメント(仕様確認など、修正を前提としない問い合わせ)のみ、この修正コード例の提示を免除する。
9. コメントは簡潔にすること(問題が複雑な場合を除き、1コメントにつき5文以内を目安とする。ただし修正コード例のコードブロック自体は文数制限に含めない)。
- name: 境界値レビュー標準
fileFilters:
- "**/*"
instructions: |
1. 数値の閾値判定(以上/より大きい/以下/未満など)を含むコードでは、比較演算子(>= / > / <= / <)が
対応するIssueの受け入れ条件と一致しているかを必ず確認すること。一致していない場合は指摘し、
正しい演算子に修正したコード例を必ず示すこと。
2. 新しい条件分岐やロジックを追加した変更では、対応するテストケースが追加されているかを確認すること。
不足している場合は、追加すべきテストケースのコード例(test関数の具体的な記述)を必ず示すこと。
ポイント: mr-review-instructions.yamlは、プロジェクト全体に適用される汎用的な観点を書く場所です。「シルバー会員は5000円以上」のような個別の仕様・具体的な数値は、このファイルには書かず、7章の2件目の改修(手順18)で作るIssueの受け入れ条件(や該当コードのコメント)に書きます。ルール自体は「閾値判定の演算子はIssueの受け入れ条件と一致させる」という汎用的な原則にとどめ、Duoにはそれを個々のMRの文脈(Issue内容)と突き合わせて判断してもらう、という役割分担です。
カスタム指示は、Duoの標準レビュー基準を置き換えるものではなく、追加するものです。また、あくまで「AIレビュアーへのガイダンス」であり、すべてのケースで確実に適用される保証はないため、セキュリティ統制やコンプライアンス要件をこのファイルだけに依存させないようにしてください。
6. 画面設定
6-1. mainへの直接編集を防ぎ、Issueとの紐づけを必須にする
やりたいことはシンプルです。①mainへの直接pushを禁止し、②MRには必ず対象イシューへの参照を入れさせる——この2つを、以下の3ステップでまとめて設定してしまいましょう。
-
mainブランチを保護する
Settings > Repository > Branch rulesを開き、mainを選択して以下のように設定し、Save changes。項目 設定値 Allowed to merge Developers + Maintainers Allowed to push and merge No one -
MRタイトルにイシュー番号が自動で入るようにする
Settings > Merge requestsを開き、Merge request title templateに以下を入力してSave changes。Resolve #%{issue_id} "%{issue_title}" -
イシュー番号のないMRはマージできないようにする
同じ
Settings > Merge requests画面で、タイトルは指定されたパターンに一致する必要がありますをONにし、以下を入力してSave changes。Title pattern: ^(Draft: )?.*#\d+.* Title example: Resolve #123 "ゴールド会員向け追加割引の実装"
これで、①mainへは誰も直接pushできなくなり、②イシューの詳細画面の「Create merge request」から作ればタイトルは自動でイシュー番号入りになり、③万が一タイトルを手動で書き換えてイシュー番号を消してしまっても、マージボタンが物理的に押せなくなります。実際の動作はセクション7の手順6・12で確認します。
補足(細かいけど大事なポイント)
- 手順1で「Allowed to push and merge」を明示的に「No one」にしないと、直接pushは止まりません。デフォルトのままにしないよう注意してください。
- 手順2の
%{issue_id}は、イシュー詳細画面の「Create merge request」ボタンから作成した場合だけ自動で埋まります(手動でブランチを切ると空欄になります)。実際の作成手順はセクション7の手順5で行います。- 手順2だけでは「デフォルト値」に過ぎず強制力はありません。手順3のタイトル検証と必ずセットで使ってください。
- 手順3の正規表現はRE2構文です。PCRE(多くのオンライン正規表現テスターで使われる構文)とは一部異なり、後読み(lookahead)や後方参照は使えません。
- 手順3のタイトル検証はMRのタイトルのみを対象とし、説明文(description)は見ません。「Closes #123」を説明文だけに書く運用にしていると食い違うので、手順2のタイトルテンプレートと必ずセットで運用してください。
6-2. Merge request承認ルールについて(説明のみ・任意)
タイトル検証は「イシューへの参照」を強制する仕組みでしたが、GitLabにはもう一つ、「人によるレビュー」を強制する仕組みがあります。改修業務を属人化させないための核心機能なので、場所と使い方を紹介します。
-
どこにあるか:
Settings > Merge requestsのMerge request approvalsセクション →Approval rules→Add approval rule -
何ができるか:
Approvals required(必要な承認数、1以上を指定)とAdd approvers(承認者として指定するユーザーまたはグループ)を設定すると、指定した承認者から必要数の承認(Approve)が得られるまでマージボタンがブロックされるようになります
ここでは場所と使い方の説明だけにとどめ、本ハンズオンでは実際には設定しません。承認者(Add approvers)には自分以外の誰かが必要で、その人が実際にApproveボタンを押してくれることで初めて「人によるレビューを強制する」という効果が生まれるからです。試してみたい方は、プロジェクトかグループに自分以外のユーザーを追加できる環境で、上記の手順で実際に設定してみてください。設定した場合の動作は、この先の手順11で確認できます。
参考: MRの作成者自身による承認は既定で禁止されています(
Merge request approvalsのApproval settingsにあるPrevent approval by author)。一人だけの検証用に一時的にこれを解除する方法もありますが、実運用ではこの設定は有効(チェックあり)のままにし、Add approversに自分以外のメンバーを指定するのが本来の使い方です。
6-3. タグを保護しておく(推奨)
- 対象プロジェクトを開く
- 左サイドバーから
Settings > Repositoryを選択 -
Protected tagsセクションを展開 -
Add newを選択 -
Tagを選択し、入力欄に*(すべてのタグにマッチするワイルドカード)を入力 -
Create wildcardを選択 -
Allowed to createでMaintainersを選択 -
Protectを選択
これで、タグの作成をMaintainer以上に限定でき、意図しないタグが誰でも作成できてしまう事態を防げます。
6-1・5-4で設定した2つのチェックは、以下のようにすべて揃って初めてマージが可能になります(承認ルールを自分で設定した場合は、そのチェックも加わります)。
なお、Secret Detection(5-3)の検知結果はこのマージチェックには含まれません。検知はあくまで警告であり、マージするかどうかの最終判断は人が行う設計になっています(セクション7の手順12で改めて確認します)。
7. 実際に体験する: Issue → 設計 → 開発 → レビュー → テスト → リリース
ここまでの設定ができたら、実際に一連の流れを体験してみましょう。「既存の割引ロジックに、ゴールド会員向けの追加割引を実装する」という改修要件を例にします。
手順1: Epicを作る(既存システムの保守計画)
- 画面上部の
Search or go toで、3-1で作ったグループを選びます。 - 左サイドバーの
Plan > Work itemsを選択します。 -
New itemを選択します。 -
TypeドロップダウンからEpicを選択します。 -
Titleに既存システム 定期改修計画のような、保守活動全体を表す名前を入力します。 -
Descriptionに「既存システムへの改修要件を継続的に受け付け、各改修をこのEpic配下のIssueとして管理する」のような一文を添えます。 -
Start dateを開き、Fixed(固定)を選択して今日の日付を入力します。 -
Due dateにも同様に、Fixedを選択して今日から30日後の日付を入力します(保守サイクルの一区切りとして)。 -
Create epicを選択します。 - 作成後、左サイドバーの
Plan > Roadmapを開きます。 - デフォルトの表示設定(
Weeksプリセット)のまま、作成したEpicのバーが「今日」を示す赤い縦線を跨いで表示されていることを確認します。 - この画面はガントチャート形式で、期間を横棒(バー)として表示するため、保守計画全体をWBS(作業分解構成図)のように俯瞰できることを確認してください。
手順2: イシューを作成する(Epicから)
- 手順1で開いたRoadmap画面から、作成したEpicのバーまたはタイトルを選択して、Epicの詳細画面を開きます。
-
Child itemsセクションのAddを選択します。 -
Add a new issueを選びます。 -
Titleにゴールド会員向けに追加5%割引を適用すると入力します。 -
Projectのドロップダウンから3-2で作ったプロジェクトを選びます。 -
Create issueを選択します。作成されたイシューは自動的にこのEpicの子アイテムになります。 -
Child itemsの一覧に追加されたイシューを選択して詳細画面を開きます。5-1のイシューテンプレートが自動適用されていることを確認できます。 - 右サイドバーの
LabelsからEditを選択し、ラベル選択画面を開きます。プロジェクト側では何も設定していないのに、4-1でグループに作ったtype::bug・type::feature・type::changeがそのまま選択肢に表示されていることを確認します(グループラベルがプロジェクトに「降ってきている」体験です)。 -
type::featureを選んで付けておきます(必須ではありません)。
手順3: 着手を宣言する(ボードでカードを移動する)
- プロジェクトの
Plan > Issue boardsを開きます。 - 手順2で作成したイシューは、ボード上ではまだ
Openリストに表示されています(Epicの子アイテムであることはボードのリスト分けには影響しません)。 - このカードを
Openのリストから4-2で作った自分のAssigneeリストへドラッグします。これでイシューが自分にアサインされ、着手したことがチームに分かるようになります。
手順4: 設計方針を検討する(GitLab Duo Chatをアドホックに使う)
実装に入る前に、既存コードの挙動を確認します。ここでGitLab Duoを使います。
-
プロジェクト画面右上の Duo Chat アイコンを選択し、右側にチャットパネルを開きます(IDE利用時は同様のパネルがVS Code等からも開けます)。
-
src/discount.jsを開いた状態、またはパスを指定して次のように尋ねます。src/discount.js の calculateDiscount 関数の現在の挙動を説明して -
Duoが「10000円以上の注文に対して10%の割引を返す関数である」といった説明を返すことを確認します。
-
続けて、実装方針を相談します。
会員ランクがgoldの場合に、追加で5%の割引を加算したい。 calculateDiscount関数をどう変更すればよいか案を出して。 -
Duoの提案(引数を追加する、条件分岐を足すなど)を参考に、次の手順5以降で実際にコードを変更します。提案をそのまま採用するかどうかは必ず人間が判断してください——ここでは「調査・設計の壁打ち相手」としてアドホックに使う体験がポイントです。
Duo Chatは、既存コードの理解が必要な改修(=移管したばかりの既存システムでは特に多い)で威力を発揮します。常設のワークフローに組み込む必要はなく、必要な場面でその都度呼び出せば十分です。
手順5: ブランチを作成する
- イシューの詳細画面で
Create merge requestを選択します。 - ポップアップが表示され、作成するフィーチャーブランチの名前(例:
2-gold-discount)を確認・編集できます。 - ポップアップ内の
Create merge requestボタンを選択します。この操作をした瞬間に、初めてフィーチャーブランチが作られます(それまではブランチはまだ存在しません)。
手順6: MRを作成する
- ブランチ作成後、MRタイトルが6-1のテンプレートに従って自動で埋まった状態の、まだ中身が空のMR作成画面に遷移します。説明文には5-2のテンプレートが入っています。
-
プロジェクトにMR説明文テンプレート(5-2)が設定されている場合、
Closes #の後ろにイシュー番号は自動で入りません(テンプレートが無ければブランチ名から自動挿入されますが、テンプレートがあるとその挙動が上書きされます)。 - 説明文の
Closes #の行を見つけ、#の後ろに手動で対象イシューの番号(例:Closes #2)を書き加えます。 - 画面下部の
Create merge requestを選択してMRを作成します。
手順7: ファイルを編集する(実装)
-
画面左上のブランチ選択は、MRを作成した時点で対象のフィーチャーブランチ(例:
2-gold-discount)に自動で切り替わっています。 -
画面右上の
Code→Web IDEで開くを選びます。 -
src/discount.jsを、手順4の設計検討をふまえて次のように変更します。function calculateDiscount(amount, memberRank = 'normal') { let discount = 0; if (amount >= 10000) { discount += amount * 0.1; } if (memberRank === 'gold') { discount += amount * 0.05; } return discount; } module.exports = { calculateDiscount }; -
test/discount.test.jsにも、ゴールド会員のケースを追加します。test('ゴールド会員は追加で5%割引が加算される', () => { assert.strictEqual(calculateDiscount(10000, 'gold'), 1500); }); test('通常会員は追加割引が加算されない', () => { assert.strictEqual(calculateDiscount(10000, 'normal'), 1000); }); -
さらに、Secret Detectionの動作を実際に確認するため、
src/discount.jsの末尾に以下の1行も追記します(ハイフンの前後のスペースを削除して、個人アクセストークンの見た目に合わせます)。// 動作確認用トークン(削除予定): glpat - 12345678901234567890
手順8: コミットする
-
左側のアクティビティバーで
Source Controlアイコンを選択します(またはCtrl+Shift+G / Control+Shift+G)。 -
コミットメッセージの入力欄に、次のように1行目にタイトル、1行空けてから
Changelog: addedを入力します(8-1で詳しく説明します)。ゴールド会員向けに追加5%割引を適用する Changelog: added -
ブランチは手順5で作られたフィーチャーブランチが選択された状態のままなので、そのまま
Commitを選択します(新しいブランチを作る必要はありません)。すでにMRが存在するため、このコミットは自動的に手順6のMRに追加されます。
手順9: テスト結果を確認する(JUnitレポート)
- コミット後、パイプラインが自動で走ります。
Build > Pipelinesからパイプラインの完了を待ちます。 - MRの詳細画面を開き、
Tests(またはパイプライン内のTest summary)ウィジェットを確認します。 - 5-4で追加した
unit-testジョブの結果として、追加した2件のテストを含む合計テストケースがすべて成功(Passed)していることが表示されていることを確認します。 - 試しに
test/discount.test.jsの期待値を意図的に間違えた値に変えて再度コミットし、テストが失敗した場合にMR上でどのように表示されるか(失敗したテスト名・件数)も確認してみてください。確認後は正しい値に戻してください。
手順10: 検知結果を確認する(Secret Detection)
- MRの詳細画面で
Reportsタブを開きます。 -
Security scanを選択し、「Secret detection detected 1 new potential vulnerability」のように表示されていることを確認します(View all pipeline findingsで詳細一覧も見られます)。 -
Build > Jobsからsecret_detectionジョブのログを開き、leaks found: 1と表示されていることを確認します。 -
Job artifactsから成果物をダウンロードすると、検知されたシークレットの種類(GitLab personal access token)、重大度(Critical)、ファイル内の行番号などの詳細も確認できます。
手順11: Draft状態を解除する(承認ルールを設定した場合はレビュアーの承認も得る)
- イシューから作成したMRは初期状態で
Draftになっているため、画面上部のMark as ready(準備済みとしてマーク)を選択してDraft状態を解除します。 -
6-2の承認ルールを自分で設定した場合(自分でユーザーを追加できる環境で設定した場合): マージチェック欄に6-2で設定した承認ルールにより「Approval is required」のような表示がされ、承認者アカウントでMRを開いて
Approveボタンを選択するまでマージボタンが押せないことを確認します。承認後、マージチェック欄の承認ステータスが緑色(達成)に変わります。 - 承認ルールを設定していない場合(本ハンズオンの既定の進め方): マージチェック欄にこの項目自体が表示されません。そのまま手順12に進んでください。
手順12: マージチェックを確認する
- マージチェック欄にタイトル検証の結果が表示され、問題なければ緑色になっていることを確認します。
- タイトル検証(イシュー参照)・JUnitテスト(品質) の2つが揃えばマージ可能になります(6-2の承認ルールを自分で設定した場合は、手順11の承認ステータスも含めた3つがすべて揃う必要があります)。
- Secret Detectionの検知結果はマージ自体をブロックしません(検知はあくまで警告で、マージ判断は人が行う設計です)。
手順13: マージする
-
Mergeボタンを選択します。 - MR説明文の
Closes #パターンにより、対象イシューも自動でクローズされることを確認します。 - 手順7の偽のシークレットが
mainにマージされたことで、この「finding」は「vulnerability」に変わります。
手順14: 脆弱性を確認する
- 左サイドバーの
Secure > Vulnerability reportを開きます。 - 手順10で検知された脆弱性(Secret type:
GitLab personal access token)を選択します。 -
Descriptionから詳細(シークレットの種類、対処方法、検知日時・場所)を確認します。
手順15: ステータスを変更する(トリアージ)
- 脆弱性の一覧で、ステータスの選択肢(
トリアージが必要・確認済み・却下済み・解決済み)から却下済みを選びます。 -
却下理由を選択のドロップダウンからテストで使用を選びます。 -
コメントを追加(必須)に「デモ用の偽のトークンのため」のような理由を記入します。 - Vulnerability Reportのトップ画面から、この脆弱性が表示されなくなることを確認します。トリアージはあくまで「今は対応不要」という記録であり、コード自体は変わりません。
- きれいにしておきたい場合は、新しいイシューを1つ作り(例:
discount.jsの偽トークンを削除)、手順2〜13と同じ手順(Create merge request → 編集 → コミット → マージ)で該当行を削除してください。本物の漏えいが起きた場合は、トリアージや削除だけでなく、該当のトークンを直ちに無効化(revoke)する必要があります。 Gitの履歴には削除前のコミットが残るため、削除だけでは漏えいのリスクが完全には消えません。
手順16: タグを作成する
- 左サイドバーの
Code(またはRepository) →Tagsを開きます。 -
New tagを選択します。 -
Tag nameにv0.1.0のようなバージョン番号を入力し、Create fromはmainのままにします。 -
Release notesは空欄のままにしてください(5-5のCIジョブが自動生成するため、ここで書いた内容とは別にReleaseが作られて重複してしまいます)。 -
Create tagを選択します。
手順17: Releaseを確認する
- タグのpushをトリガーにパイプラインが走ります。
-
Build > Jobsでcreate-releaseジョブが成功していることを確認します。 - 左サイドバーの
Deploy > Releasesを開きます。 - 手順8でtrailerを付けたコミットの内容が、リリースノートとして自動的に反映されていることを確認します。
これで、既存システムへの1件目の改修要件について、設計(Duo活用)→開発→レビュー(タイトル検証)→テスト(JUnit)→リリースまでの一連の流れを一通り体験できました。改修業務はここで終わりではなく、Epicの下には次々と改修要件が積まれていきます。続けて2件目の改修を扱い、レビューの一次チェックにもGitLab Duoを関わらせてみましょう。
手順18: 2件目の改修要件を起票する(シルバー会員向け割引)
-
画面上部の
Search or go toで、3-2でインポートしたプロジェクトを開きます(既に開いている場合はこの手順は不要です)。 -
左サイドバーの
Plan > Issuesを選択します。 -
New issueを選択します。 -
Titleにシルバー会員向けに追加3%割引を適用すると入力します。 -
Descriptionの欄に、5-1のイシューテンプレートが自動で挿入されているので、以下のように書き込みます。## 改修内容 src/discount.js の calculateDiscount 関数に、シルバー会員向けの追加割引を実装する。 ## 完了条件 - [ ] memberRank が 'silver' の場合、5000円以上の注文に対して追加で3%の割引を加算する - [ ] test/discount.test.js に、シルバー会員向けのテストケースを追加する ## 関連情報 -
右サイドバーの
Labelsからtype::featureを選んでおきます(4-1で作ったグループラベルが選択肢に表示されます。必須ではありません)。 -
画面下部の
Create issueを選択します。
受け入れ条件として書いた「5000円以上」という具体的な数値は、あえてこの完了条件の中にだけ明記しています。プロジェクト固有のレビュー観点を書いた.gitlab/duo/mr-review-instructions.yaml(5-6でmainに追加済み)には、こうした個別の数値ではなく「閾値判定の演算子はIssueの受け入れ条件と一致させる」という汎用的な原則だけを書き、Duoにはそれを個々のMRの文脈(Issue内容)と突き合わせて判断してもらう、という役割分担です。
手順19: ブランチとMRを作成し、Duoをレビュアーに指名する
- イシューの詳細画面で
Create merge requestを選択し、ブランチとMRを作成します(手順5〜6と同じ操作です)。 - MRの右サイドバーの
ReviewersからGitLabDuoを選択しておきます。実装前にここで指名しておくと、以降のコミットのたびにDuoのレビューが自動で走るようになります。
前提条件: MRのレビュアーにGitLab Duoを指名し、コード変更を分析してレビューコメントを自動投稿してもらう機能(Code Review Flow)は、GitLab Duo Agent Platformの一部として、Free・Premium・Ultimateいずれのプランでも利用できます(Duo Core/Pro/Enterpriseのようなアドオン契約は不要です)。プランに応じて割り当てられるGitLab Creditsを消費して動作する点に注意してください。3-3で確認した
フロー実行を許可と基本フロー実行を許可のトグルが有効になっていることに加え、Code Review FlowはCI/CDジョブとして実行されるため、gitlab--duoタグ付きのRunner、またはプロジェクトでGitLab-hosted runnersが有効になっている必要があります。
手順20: 実装する(あえて境界値をそのままにしてみる)
-
src/discount.jsを次のように変更します。受け入れ条件をコード上でも参照できるよう、コメントとして残しておきます。// 受け入れ条件(Issue参照): 5000円以上の注文が対象 if (memberRank === 'silver' && amount > 5000) { discount += amount * 0.03; }受け入れ条件は「5000円以上」ですが、コードは
amount > 5000(5000円より大きい)になっており、あえて境界値をずらしています。人によるレビュー前に、この手の細かい食い違いをAIレビューがどこまで拾えるかを確認するのがこの改修の狙いです。 -
さらに、あえて
test/discount.test.jsにはシルバー会員のテストケースを追加せず、コミットします。
手順21: Duoのレビュー結果を確認する
-
Automate > Sessionsからレビューの進行状況を確認します。 - しばらくすると、MRのタイムラインにDuoからのレビューコメントが投稿されます。5-6で追加した境界値レビュー標準のルールに沿って、Duoが「変更内容(
> 5000)」と「Issueの受け入れ条件(5000円以上)・コード中のコメント」を突き合わせ、以下のような指摘が得られることを確認します。- コメント冒頭に一文で要約があり、
「バグ」のようなラベルが付いた上で、境界値条件(>になっているが、受け入れ条件は「以上」)についての指摘 - シルバー会員向けの新しいテストケースが追加されていない、という指摘(こちらも具体的な修正コード例が添えられているはずです)
- コメント本文がすべて日本語で書かれていること(5-6の
Review language policyの指示が効いていることの確認)
- コメント冒頭に一文で要約があり、
手順22: 指摘を修正し、人によるレビュー・マージまで進める
- 指摘を受けて、実際に修正します(
>を>=に直し、テストケースを追加してコミット)。再度Duoにレビューしてもらい、指摘が解消されていることを確認します。 -
Draft状態の解除・マージまでは、手順11〜13と同じ手順で行います。6-2の承認ルールを自分で設定している場合は、Duoが指名されたMRであっても同様に適用され、AIによるレビューが人によるレビューを代替することはありません。あくまで、人がレビューする前に機械的に拾える指摘を先に済ませておくための一次チェックという位置づけです。 - マージ・タグ付け・リリースも、手順12〜17と同じ手順で進めてください。
これで、2件の改修要件を通じて、Duoによる一次レビュー(AI)を体験できました。承認ルール(人によるレビューの強制)を実際に組み合わせたい場合は、6-2を参考に、自分でユーザーを追加できる環境で試してみてください。
手順23: 進捗をふりかえる(Duo Agentic Chat)
2件の改修が一巡したところで、Duo Agentic Chatに進捗を聞いてみましょう。専用のダッシュボードを都度作り込まなくても、Agentic Chatに聞くだけでその場でレポートを生成できます。プロジェクトまたはグループの画面右上にあるDuo Chatアイコンから、Agentic Chatを開きます。Agentic Chatは、質問の内容に応じて内部で適切な専門エージェント(Planner Agent・Data Analyst Agent・Security Analyst Agentなど)に振り分けて回答します。以下のような問いかけを試してみます。
-
進捗確認(Planner Agent):
このプロジェクトで今週クローズしたIssueと、現在Openなままの改修要件を一覧にして -
レビュー状況の要約(Planner Agent):
現在レビュー待ちのMR一覧と、それぞれの承認ステータスを要約して -
生産性の可視化(Planner AgentでもData Analyst Agentでも対応可能):
直近1か月にマージされたMRについて、MR作成からマージまでの平均リードタイムを教えて -
品質状況の把握(Security Analyst Agent):
Vulnerability Reportで現在トリアージが必要な状態になっている脆弱性を、重大度別に集計して
前提条件: 「Planner Agentが
Orbitを使ってMRデータを照会する」という下記の挙動は、2-1で確認した**GitLab DuoのOrbit(ベータ版)**という個人設定に依存しています。まだ確認していない場合は、先に2-1のチェックステップを行ってください。設定がOFFになっていると、Planner AgentがMR関連の質問に答える際に「Orbitが利用できないため回答できません」といった応答になることがあります。なお、この設定は3-3で確認したプロジェクト側のGitLab Duoトグルの🔒(ロック)表示とは別の仕組みです。🔒表示はグループ/インスタンス側の管理設定によるものですが、こちらは個人がどの契約を使うかを選ぶ設定であり、両者を混同しないよう注意してください。
実際にやってみると(エージェントごとの挙動の違い):
- Planner Agentは、Issueの一覧・状態確認については
state='closed'・updated_after=...のようなフィルタでGitLabのデータを直接取得し、テーブル形式で回答してくれます。さらに「Open中の2件は担当者・ラベルが未設定です」のような気づき(PM視点のコメント)まで添えてくれるのが特徴です。- 一方、MRのリードタイムやレビュー状況をPlanner Agentに聞くと、「MR一覧を直接取得するツールはないため、Orbit(GitLab Duoのベータ機能)を使ってMRデータを照会します」という前置きとともに回答が返ってきます。この経路には反映遅延がある旨の注意書きが毎回付き、「正確な監査用途であればMR一覧画面での確認をおすすめします」と案内されます。
- Data Analyst Agentに同じリードタイムの質問をすると、Orbit経由ではなくGLQL(GitLab Query Language)を直接使って、より簡潔に(反映遅延の注意書きなしで)回答してくれます。回答には実際に使ったGLQLクエリの説明も添えられ、
⋮メニューからクエリ自体をコピーしたり、埋め込み表示として残したりできます。数値の分析・集計が目的の質問は、Planner AgentよりData Analyst Agentの方が、より正確で再利用しやすい回答になる、という使い分けの目安が見えてきます。- Security Analyst Agentは、Vulnerability Reportを直接参照し、重大度別の集計に加えて、手順15でトリアージ済み(却下済み)にしたシークレット検知の結果についても「人間による判断で既に却下済みのため、再トリアージの対象外」と正しく認識した回答を返してくれました。
- おまけの気づき: レビュー状況を尋ねた際、Planner Agentが「両MRとも承認ルールが設定されておらず、人による正式なレビュー・承認のプロセスが構成されていません。第三者レビューを必須にしたい場合は、Merge request承認ルールの設定を検討する価値があります」と指摘してきました。これはまさに6-2で紹介した「承認ルールは自分でユーザーを追加できる環境でこそ意味を持つ」という位置づけを言い当てたコメントです。ハンズオンとして承認ルールを設定していないことは分かっていても、AIが同じ観点を自発的に拾ってくることが確認できます。
Agentic Chatは、イシュー・MR・パイプライン・セキュリティ検出結果など、プロジェクトのリアルタイムなデータを参照しながら回答するため、都度違う切り口で聞き直せる点が、固定フォーマットのダッシュボードとの大きな違いです。ただし上記の通り、同じ質問でもどのエージェント(あるいは内部的にどの経路)が答えているかによって、鮮度や正確性の前提が変わるため、重要な判断に使う場合は、返ってきた回答に「反映遅延」等の注意書きがないか、必ず確認してください。
標準の管理画面との使い分け: Agentic Chatによるアドホックなレポーティングに加えて、GitLab標準の以下の画面も、定点観測用途では引き続き有用です。「毎週の定例会で必ず見る決まったグラフ」はダッシュボード側に定着させ、「今この瞬間に知りたい、都度変わる質問」はAgentic Chatに聞く、という役割分担がおすすめです。
画面 主な用途 Analyze > Value stream analyticsIssue起票からマージまでの各ステージの所要時間の傾向を、時系列で追う Analyze > Insights(設定が必要)ラベル別・担当者別のIssue/MR件数など、カスタム集計をダッシュボード化する Plan > RoadmapEpic単位の計画・進捗をガントチャートで俯瞰する(手順1で使用済み) Secure > Vulnerability reportセキュリティ検出結果の一覧・トリアージ状況(手順14で使用済み)
8. バージョン番号の決め方(参考)
タグを作るたびに毎回悩まないよう、目安を決めておきます。
- patch(例: v1.2.0 → v1.2.1): バグ修正のみ
- minor(例: v1.2.1 → v1.3.0): 後方互換性のある新機能追加
- major(例: v1.3.0 → v2.0.0): 後方互換性を壊す変更
8-1. コミットメッセージにtrailerを付ける
リリースノートを自動生成するには、コミットメッセージの末尾に専用のGit trailerを1行加えます。
ゴールド会員向けに追加5%割引を適用する
Changelog: added
なぜ
Changelog: addedという文字列なのか:
- これはGitLab独自の記法ではなく、Gitの世界で昔から使われている「trailer」という標準的な仕組みに乗っかったものです。
- コミットメッセージの末尾に
Key: Valueの行を1つ追加する書き方で、Signed-off-by: 名前 <email>やCo-authored-by: 名前 <email>と同じ仲間です(Gitにはgit interpret-trailersというtrailerを扱う専用コマンドもあります)。- コミットの1行目(subject)の書き方を変える必要がないのが利点です。
- 5-5のジョブが呼び出すChangelog APIは、この
Changelogというキー名を(デフォルトの設定として)探しに来ます。- 値の
added/fixed/changed/removed/securityという語彙も、GitLabの独自発明ではなく、OSSでよく使われるKeep a Changelogという慣習の分類(Added/Changed/Removed/Fixed/Securityなど)をほぼそのまま使っています。
使えるカテゴリは主に added / fixed / changed / removed / security です。このtrailerが付いたコミットだけが、5-5のcreate-releaseジョブが呼び出すChangelog APIの対象になります(trailerがないコミットは無視されます)。
出力フォーマットをカスタマイズしたい場合は、.gitlab/changelog_config.ymlというデフォルトの設定ファイルを作成し、日付フォーマットやテンプレートを定義できます。
8-2. trailerを「いつ」付けるか(運用の目安)
すべてのコミットに反射的にChangelog: addedを付けてしまうと、些細な変更までリリースノートに載ってしまい、かえって読みにくくなります。かといって、コミットする段階では「これはChangelogに載せるべき規模か」を判断しづらいことも多いです(例: READMEの更新のつもりが、実は利用者への影響がある変更だった、など)。
基本方針: trailerを付けるかどうかは、コミット時点ではなく、MRをマージする直前、変更全体を見渡せるタイミングで決めるのがおすすめです。
-
マージ時の
Modify commit messagesを基本に使うMRのマージ画面には
Modify commit messagesというリンクがあり、実際にマージされるコミットメッセージ(Squash設定が有効なら、1つにまとめられた後のメッセージ)をその場で編集できます。「このMR、思ったより変更が大きいからChangelogに残しておこう」という判断を、マージする瞬間に確定できるため、コミット時点で先読みする必要がありません。表示条件: このリンクを使うには、プロジェクトのMaintainer以上のロールが必要です(本ハンズオンではグループ・プロジェクトのOwner/Maintainerとして進めているので、通常は問題になりません)。
注意:
Squash commitsが有効なMRの場合、既定ではSquash後の1コミットに、個々のコミットのChangelog:trailerは自動的には引き継がれません(プロジェクト側でSquashコミットメッセージのテンプレートに%{first_multiline_commit}という変数を設定している場合のみ引き継がれます)。Squash運用のプロジェクトでは、マージ時のModify commit messagesでtrailerを手動で書き足すのが、実質的な最終確認ポイントになります。 -
マージ前で、まだ自分のフィーチャーブランチにいる場合(Web IDEだけで完結)
直前のコミットに付け忘れていたことに気づいたら、Web IDEの
Source Controlパネルで以下の操作をします。- コミットメッセージ入力欄の右側にある
...(その他のアクション)を選択します。 -
Amend commit and Force push(コミットを修正してForce push)を選択します。 - メッセージ入力欄に、trailerを追記した内容を入力して確定します。
これで、直前のコミットのメッセージが書き換えられ、そのままforce pushまで行われます。フィーチャーブランチは6-1で保護していないため、force pushしても問題ありません。ターミナルもGitコマンドも不要です。
この機能はWeb IDEに比較的最近追加されたものです。お使いのGitLabのバージョンによっては、
...メニューに表示されない場合があります。その場合は、ローカル環境でgit commit --amend→git push --force-with-leaseを実行してください。もう少し前のコミットを編集したい場合: Web IDEは直前1つのコミットのamendまでしか対応していません。ローカル環境で
git rebase -iを使ってください。 - コミットメッセージ入力欄の右側にある
-
マージ後に気づいた場合
mainは6-1で保護済みのため、マージ済みのコミットを書き換えることはできません。この場合は、次のリリース作成時に5-5のジョブが自動生成するrelease_notes.mdの内容を、Deploy > Releases画面から手動で編集して該当項目を追記してください。CIジョブが生成するものはあくまで叩き台、という位置づけです。
9. 補足: よくあるハマりどころ
-
Duo Chatが表示されない: 3-3の
GitLab Duoトグルの状態を確認してください。🔒(ロック)アイコンが付いてOFFになっている場合は、グループまたはインスタンス側の設定で強制されているため、プロジェクト側では変更できません。管理者またはグループのOwnerに契約状況・グループ設定の確認を依頼してください。利用できない場合も、手順4を飛ばして手順5以降にそのまま進めば、他の検証には影響しません。 -
7章の手順19以降のFlow(Code Review)がうまく動かない: 3-3で確認した
フロー実行を許可または基本フロー実行を許可がOFFになっている可能性があります。両方がONになっていることを確認してください。 -
7章の手順23(Agentic Chat)で、Planner AgentがMR関連の質問にうまく答えられない: 2-1で確認した
GitLab DuoのOrbit(ベータ版)セクションのGitLab DuoでOrbitを使用するおよびエージェント型チャットがOFFになっている可能性があります。次の項目もあわせて確認してください。 -
4章のDuo Chat・7章のCode Review・Agentic Chatなど、DAP関連の機能全般がうまく動かない/クレジット不足のようなエラーになる: 2-1で確認した
デフォルトのGitLab Duoのネームスペースが、実際にDuo/DAPの契約・クレジットが割り当てられているネームスペースになっているか、あらためて確認してください。これはOrbitに限らずDAP機能全般に影響する設定です。誤ったネームスペースが選ばれていると、契約・クレジットの無いネームスペース扱いになり、他のトグルがONでも機能しないことがあります。 -
承認ルールを設定したのに誰でもマージできてしまう:
Approvals requiredが0のままになっていないか確認してください。0は「任意」の意味になり、必須にはなりません。 -
6-2の承認ルールを自分で設定したが、自分のアカウントでは承認できない: GitLabには既定で「Prevent approval by author」(MRの作成者自身による承認を禁止する)という設定が有効になっており、通常はこれが理由で自己承認ができません。自分以外にプロジェクトへ追加できるユーザーがいない環境で、それでも動作を確認したい場合は、
Settings > Merge requests > Merge request approvals > Approval settings内のPrevent approval by authorのチェックを一時的に外すことで自己承認できるようになります。実運用ではオンのままにし、自分以外のメンバーを承認者に指定することを推奨します。 -
JUnitレポートがMRに表示されない:
artifacts.reports.junitに指定したパスと、実際に出力されたXMLファイルのパスが一致しているか確認してください。またXML以外の拡張子を指定するとエラーになります。 - RE2構文の制約: 6-1の正規表現はRE2構文のため、後読み(lookahead)や後方参照が使えません。PCRE前提のオンライン正規表現テスターで動作確認する際は注意してください。
-
7章の手順21のDuoレビューで、期待した指摘(境界値レビュー標準によるテスト未追加チェックなど)が出てこない:
.gitlab/duo/mr-review-instructions.yamlが5-6の手順通りmainに追加されていない可能性があります。Code Review Flowはこのファイルがmainに存在して初めて反映されるため、レビュー結果にReview language policyの指示(日本語化)しか効いていないように見える場合は、まずRepository > Filesでmainブランチにこのファイルが実際に存在し、内容も正しいかを確認してください。 -
GitHubの「GitHub専用インポーター(方法B)」で認証がうまくいかない: OAuth連携やPersonal Access Tokenの権限不足が原因になっていることがあります。うまくいかない場合は、方法A(
Repository by URL)であればPublicリポジトリなので認証不要で進められます。本ハンズオンの後続手順への影響はありません。