Webアプリなど最新のリポジトリの状態しか参照しない場合、github flowの様にmainブランチに対してどんどんコミットしていくスタイルで良いと思います。
ですが、パッケージ開発の様にリリースしたバージョンを複数管理している場合、git flowをベースにリポジトリを管理していくことになります。
ただ、git glowをベースに管理していく場合、色々ルールが多く、ルールを知らないためにいつの間にか逸脱していたり、すべき作業が忘れられている場合があります。
このようなことを防ぐために行っている設定を記載します。
前提
以下の様にgit flowをベースに、developブランチで開発を行い、releaseブランチでリリース前作業、mainブランチで過去バージョンの保管を行っています。
このreleaseブランチではTypeScriptのyarn.lockやRustのCargo.lockなど孫参照のOSSなども含めて固定するためにコミットし、将来的にもmainブランチでビルドする際に依存するOSSなど含めて再現できるようにします。
developでこれらのlockファイルをコミットして運用している場合や依存するOSSなどがない場合、releaseブランチは必要ないです。
ブランチルール
「Settings > Rules > Rulesets」 には以下の3つの定義を行っています。
細かな規則については使用できる規則を参照してください。
develop
developブランチをターゲットとしてはブランチルールを指定しています。
- Restrict deletions
- 削除を制限
- デフォルトブランチの場合は削除できませんが、念の為
- Require linear history
- featureブランチなどをマージコミットすることを制限します
- developにマージコミットを行うとヒストリが複雑化するので、障害等が発生したときに履歴を訴求しやすくします
- Require a pull request before merging
- プルリクエストの作成を必須とします
- チームで行っている場合はRequired approvalsで必須のレビュアーの人数を指定しておきましょう
- Require conversation resolution before mergingで指摘については必ず解決するようにしましょう
- Allowed merge methodsでマージ方法を「Squash」に限定しましょう。「Rebase and merge」でも構いませんが、プルリクエスト内の誤字レベルのコミットまでdevelopに残すとメンテナンス性が悪化するので、プルリクエスト単位でdevelopには残しておきます
- Require status checks to pass
- 更新時に必要なステータスチェックを追加します
- Require branches to be up to date before mergingにチェックをいれておくと、マージ前に一度rebaseしてからステータスのチェックが行われるので、他のプルリクエストとの齟齬での障害発生などが防げます
- Add checksでチェックするGithub Actionsなどのチェックを指定できます
- Block force pushes
- 強制的な上書きを阻止します
main
mainブランチ、releaseブランチをターゲットとしてはブランチルールを指定しています。
- Restrict deletions
- 削除を制限
- Require a pull request before merging
- プルリクエストの作成を必須とします
- チームで行っている場合はRequired approvalsで必須のレビュアーの人数を指定しておきましょう
- Allowed merge methodsでマージ方法を「Merge」に限定しましょう
- Block force pushes
- 強制的な上書きを阻止します
exclude
ルールに従わないブランチの作成を防ぐため、以下のルールを作成しておきます。
ターゲットにExclude by patternで以下を登録します。
- main
- release
- develop
- feature/*
- JIRAなどと連携している場合、ブランチ名にキーを含めると思うのでfeature/<プロジェクト名>-*などでより細かい制限を付与すると良いです。
- bugfix/*
ルールとしては以下の通り。
- Restrict creations
- 作成を制限
- Restrict updates
- 更新を制限
- Restrict deletions
- 削除を制限
注意事項
Admin権限やバイパスリストに入っているメンバーはこのルールを貫通するので上記を理解しているメンバーにのみ権限を渡しましょう
定例操作
リリース時、定型的にする必要がある操作についてはGithub Actionsでgithub-scriptを使用して自動でプルリクエストを作成すると良いです。
以下には例を記載していきます。
ロックファイルの自動追加
developからreleaseにマージした際に自動でロックファイルのブランチを更新するプルリクエストを作成する方法としては以下です。
name: update-lockfiles
on:
pull_request:
types:
- closed
branches:
- release
jobs:
update-lockfiles:
if: github.event.pull_request.base.ref == 'release' && github.event.pull_request.head.ref == 'develop' && github.event.pull_request.merged == true
runs-on: ubuntu-latest
permissions:
pull-requests: write
id-token: write
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
ref: release
fetch-depth: 0
- name: Checkout new branch
run: |
git checkout -b feature/update-lockfiles
- name: Remove yarn.lock and Cargo.lock from .gitignore
run: |
sed -i '/yarn.lock/d' .gitignore
- name: Commit .gitignore changes
run: |
git add .gitignore
git commit -m 'chore: remove yarn.lock and Cargo.lock from .gitignore' || echo 'No changes to commit'
- name: Remove old lockfiles
run: |
rm -f yarn.lock
- name: Build project (make build)
run: make build
- name: Commit new lockfiles
run: |
git add yarn.lock
git commit -m 'chore: update lockfiles after build' || echo 'No changes to commit'
- name: Push changes
run: |
git push origin feature/update-lockfiles --force
- name: Create Pull Request
uses: actions/github-script@v6
with:
github-token: ${{ secrets.GIT_TOKEN }}
script: |
try {
const result = await github.rest.pulls.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: process.env.TITLE || 'release: lockファイル自動更新',
head: 'feature/update-lockfiles',
base: 'release',
body: process.env.BODY || ''
});
core.info('Pull request created.');
core.info(JSON.stringify(result, null, 2));
} catch (error) {
core.setFailed(`Failed to create PR: ${error.message}\n${JSON.stringify(error, null, 2)}`);
}
env:
TITLE: "release: lockファイル自動更新"
BODY: |
# 対処内容
yarn.lockを自動更新します。
mainブランチへのマージ
developからreleaseにマージした際にreleaseからmainブランチへの更新プルリクエストを作成する方法としては以下です。
name: merge-release-to-main
on:
pull_request:
types:
- closed
branches:
- release
jobs:
merge-release-to-main:
if: github.event.pull_request.base.ref == 'release' && github.event.pull_request.head.ref == 'develop' && github.event.pull_request.merged == true
runs-on: ubuntu-latest
permissions:
pull-requests: write
id-token: write
contents: read
steps:
- name: Checkout main branch
uses: actions/checkout@v4
with:
ref: release
fetch-depth: 0
- name: Create Pull Request to main
uses: actions/github-script@v6
with:
github-token: ${{ secrets.GIT_TOKEN }}
script: |
try {
const result = await github.rest.pulls.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: process.env.TITLE || 'Merge release to main',
head: 'release',
base: 'main',
body: process.env.BODY || ''
});
core.info('Pull request created.');
core.info(JSON.stringify(result, null, 2));
} catch (error) {
core.setFailed(`Failed to create PR: ${error.message}\n${JSON.stringify(error, null, 2)}`);
}
env:
TITLE: "release: 製品のリリース"
BODY: |
# 対処内容
製品のリリース作業を実施
内部バージョンの更新
Rustなどのパッケージが内部でバージョンを保持しているようなものについては以下のように自動で更新するプルリクエストを作成するとよいです。
name: bump-version
on:
pull_request:
types:
- closed
branches:
- release
jobs:
bump-version:
if: github.event.pull_request.base.ref == 'release' && github.event.pull_request.head.ref == 'develop' && github.event.pull_request.merged == true
runs-on: ubuntu-latest
permissions:
pull-requests: write
id-token: write
contents: write
steps:
- name: Checkout develop branch
uses: actions/checkout@v4
with:
ref: develop
fetch-depth: 0
- name: Bump Cargo.toml version (minor up)
id: bump_version
run: |
FILE=Cargo.toml
OLD_VERSION=$(grep '^version' $FILE | head -n1 | cut -d '"' -f2)
MAJOR=$(echo $OLD_VERSION | cut -d. -f1)
MINOR=$(echo $OLD_VERSION | cut -d. -f2)
PATCH=$(echo $OLD_VERSION | cut -d. -f3)
NEW_MINOR=$((MINOR + 1))
NEW_VERSION="$MAJOR.$NEW_MINOR.0"
sed -i "s/^version = \"$OLD_VERSION\"/version = \"$NEW_VERSION\"/" $FILE
- name: Commit and push version bump
run: |
git checkout -b feature/bump-version-${{ steps.bump_version.outputs.new_version }}
git add Cargo.toml
git commit -m "chore: Bump Cargo.toml version to v${{ steps.bump_version.outputs.new_version }}"
git push origin feature/bump-version-${{ steps.bump_version.outputs.new_version }}
- name: Create Pull Request
uses: actions/github-script@v6
with:
github-token: ${{ secrets.GIT_TOKEN }}
script: |
try {
const result = await github.rest.pulls.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: process.env.TITLE || 'release: Cargo.tomlバージョン自動更新',
head: `feature/bump-version-${process.env.NEW_VERSION}`,
base: 'develop',
body: process.env.BODY || ''
});
core.info('Pull request created.');
core.info(JSON.stringify(result, null, 2));
} catch (error) {
core.setFailed(`Failed to create PR: ${error.message}\n${JSON.stringify(error, null, 2)}`);
}
env:
TITLE: "release: Cargo.tomlバージョンを${{ steps.bump_version.outputs.new_version }}に自動更新"
BODY: |
# 対処内容
developブランチのCargo.tomlのバージョンをv${{ steps.bump_version.outputs.old_version }}→v${{ steps.bump_version.outputs.new_version }}に自動更新します。
NEW_VERSION: ${{ steps.bump_version.outputs.new_version }}
バージョンのタグ付
release時のmainへのタグ付けも一緒にやってあげると良いでしょう。
name: tag-version
on:
pull_request:
types:
- closed
branches:
- main
jobs:
tag-version:
if: github.event.pull_request.base.ref == 'main' && github.event.pull_request.head.ref == 'release' && github.event.pull_request.merged == true
runs-on: ubuntu-latest
steps:
- name: Checkout develop branch
uses: actions/checkout@v4
with:
ref: main
fetch-depth: 0
- name: Extract version from Cargo.toml
id: get_version
run: |
VERSION=$(grep '^version' backend/Cargo.toml | head -1 | sed 's/version = "\(.*\)"/\1/')
echo "version=$VERSION" >> $GITHUB_OUTPUT
- name: Create tag
id: create_tag
run: |
git tag v${{ steps.get_version.outputs.version }}
git push origin v${{ steps.get_version.outputs.version }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}