モノレポにおけるgitブランチ運用との戦い
こんにちは。Another works CTOのソルです。
最近リポジトリーの数が増えてきて、graphqlやgrpcといったリポジトリー間でのスキーマ共有をすることが増えてきたので、リポジトリーがバラバラに分かれていることに辛みを感じで、
モノレポに変えたので、その話をできればと思います。
なぜモノレポにしたのか?
Another worksでは、いまリポジトリーの数としては10以上あって、そのほとんどが完全に分離しているサービスではなく、マイクロサービスとして依存していたり、フロントエンドをユーザーの種類によって分離しているなど、ほとんどのリポジトリーが弊社サービスの「複業クラウド」を構成するものでした。
弊社にはまだエンジニアが私含めて5人しかおらず、その中でリポジトリーが分かれすぎているため、リリースが塩漬けになるといった問題が発生していました。
また、graphqlやgrpcのほかにcdkも使っていて、こちらのコードもプロジェクトを横断で共通化していきたいため、モノレポに移行することにしました。
ブランチ戦略
Another worksでは、モノレポにする前はstagingブランチをdefaultブランチとしてそこから切って開発をしていました。stagingをmasterに対してマージするといったブランチの運用の仕方をしていました。
stagingブランチはpushされると自動的に検証環境に、masterブランチは本番環境にそれぞれデプロイされるようになっています。
モノレポになった時に、ブランチ運用でいろいろためしてみたので、それらを紹介していきたいと思います。
失敗その1 master ← staging ← feature
まず初めにブランチ運用のルールを特に変えずに運用スタートしました。
その結果問題となったのが、stagingが検証環境のため、コードレビューが通った段階でマージしていたのですがapiやフロント、アプリなど複数のリポジトリーがstagingにマージされるため、それらのどれかにバグがあって本番環境にあげられないといった場合に、masterにマージできないという問題がありました。
また、検証で早めにQAしたいけど、リリースは即日では困るというようなものも、その変更が他のリポジトリーのリリースに影響を及ぼしてしまうので、結果としてリリースが遅くなるといった問題が発生しました。
失敗その2 master ← release/xxx ← staging ← feature
失敗その1でstagingブランチが肥大化してしまう問題を踏まえ、
stagingブランチをmasterにマージするのではなく、その中からリリースしたいものだけをcherry-pickしてreleaseブランチを作成し、それをマージするというような運用にしました。
この運用でstagingにでかいものが混ざった際にも、リリースしたいもののみを分離してリリースができるので、リリースが溜まって行ってしまうという問題は解消されました。
ですが、結論この運用も失敗でこの運用だと毎日なにかしらをリリースする弊社にとって、このcherry-pickの作業が非常に工数でした。
コミット履歴が綺麗であればまだいいですが、
検証環境に上げたあといくつかバグがあったり、どのコミットがリリースされていないといったことが明示的にわかりづらく、長い期間stagingに残ってしまうようなものだと、コミット履歴が見づらくなり、リリースの心理的なストレスも高くなってしまったため、こちらもやめました。
現在の運用方法
上記二つの失敗からこれらを意識して再度運用フローを作り直しました。
- cherry-pickなどはせず、PRをマージするだけでリリースがされるように
- リリースサイクルが他のリポジトリーに依存しないように
実際の運用フローはこのようになっています。
# Pull Request命名規則
## タイトル
`[プロジェクト名] XXX`
(例)
`[fc_api] ユーザー退会APIの実装`
# Branch命名規則
## `feature/[platform名]/*`
機能開発系のブランチ
stagingブランチから切って作業を行う
## `release/[platform名]/staging`
検証環境用ブランチ
プラットフォームごとに分かれている
## `master`
本番環境用ブランチ
## `hotfix/*`
本番環境に対する緊急リリース
masterから切ってmasterに対してマージを行う
# リリース手順
1. featureブランチを"release/[platform名]/staging"に対してマージを行う
この際に必ずsquash mergeを行う
2. release/[platform名]/stagingをmasterに対してマージする
# 基本的な心がけ
- コミットメッセージは適当に書かない
- stagingでエラーが発生したら、一度リバートして、修正を行う(コミット履歴を汚さない)
- stagingに混ぜる際は本番と同じ気持ちで(のちに開発環境を作成する)
- PRに情報をしっかりと書く
- マージしたブランチは消す
stagingブランチを廃止し、リポジトリーごとにstagingブランチを作成するようにしました。
例えば以下の構成だった場合に、
- api
- src
- xxx
- xxx
- front
- src
- xxx
- xxx
release/api/staging
とrelease/front/staging
を作ります。
apiに変更があればrelease/api/stagingから切って変更をおこなうといったルールで運用を行います。
stagingブランチを分けることで、apiとfrontをそれぞれ独立してリリースすることができ、またreleaseブランチをマージするだけなので、工数も掛からずリリースをすることができます。
それでもまだ課題は残っている
その中で残っている課題として、
masterブランチが更新された時にreleaseブランチを一つ一つマージしていかなければならない
という問題が残っています。
Github Actionsで運用をサポート
masterブランチに向けたreleaseブランチのpull requestを自動で作成する
name: auto_create_pull_request
on:
push:
branches:
- release/**
jobs:
pull_request:
name: Create Pull Request to main
runs-on: ubuntu-latest
steps:
- name: Exist Pull Request
id: exist-pull-request
uses: actions/github-script@v4
with:
script: |
const branch = context.ref.replace('refs/heads/','')
return await github.pulls.list({
owner: context.repo.owner,
repo: context.repo.repo,
head: branch,
base: 'master',
state: 'open',
})
- name: Create Pull Request
uses: actions/github-script@v4
with:
script: |
const branch = context.ref.replace('refs/heads/','')
github.pulls.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: `${branch} 本番環境リリース`,
head: branch,
base: 'master'
})
PRに対してどこのディレクトリーを変更しているのか一眼でわかるようにラベルを自動でつける
.github/labelar.yml
api
- api/**/*
front
- front/**/*
.github/workflows/add_label.yml
name: AddLabel
on: [pull_request]
jobs:
label:
runs-on: ubuntu-latest
steps:
- uses: actions/labeler@v2
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"
最後に
モノレポのいいなと思う個人的なポイントは、
会社が大きくなって、関わらないプロダクトが増えていく中で、
一つのリポジトリーを開発していることで、他のプロダクトのコードを参考にしたりする
機会が生まれやすくなるのかなと個人的には思いました。
Another worksでは一緒に働ける仲間を探しています
Discussion