はじめに
自社で開発しているSlack連携のタスク管理サービスを、誰でも開発に参加できる「社内OSS」 にしていくことになりました。
今はまだ1人で開発しているため、運用はかなり属人的です。
- GitHub Actionsのワークフローが1本もない(CIなし)
- 本番デプロイは、開発者のPCからシェルスクリプトを手動で実行
-
CLAUDE.mdにはdevelopブランチの運用が書いてあるのに、実際にはmainしかない - Issueは0件
「誰でもIssueを立てられて、誰でもPRを出せて、オーナーが確認してマージし、自動でデプロイされる」状態を目指して、ブランチ・ラベル・リリース・デプロイの運用を作りました。
この記事では、社内OSSに向けて、このタスク管理サービスの運用をどう決めたかを紹介します。
運用を作るときに参考にしたOSSのやり方も、あわせて紹介します。
◼︎ この記事でわかること
- 1人開発のプロダクトを社内OSSにするために決めた運用(ブランチ・ラベル・リリース・デプロイ)
- インナーソース(社内OSS)とは何か、なぜルールが厳しくなるのか
- 参考にしたもの:GitHub Flow / Git Flow / Trunk-Based Developmentの違い、Flutter・Kubernetes・Rust・Go・VS Codeなどのラベル運用、SemVer(セマンティックバージョニング)
対象読者:社内ツールを複数人で開発できる体制にしたい人、ブランチ戦略やリリース運用を一度ちゃんと決めたい人
1. 自社のタスク管理サービスを社内OSSにするために決めた運用
まず、実際に決めた運用を紹介します。
それぞれの運用を決めるときに参考にしたOSSのやり方は、3章以降で紹介します。
2026年10月時点で、導入済みのものと計画中のものが混ざっています。各見出しに【導入済み】【計画中】を付けています。
前提として、このタスク管理サービスは次のような状況でした。
- Slackと連携するタスク管理サービス(Slackのやり取りからタスクを作り、Webの画面でも管理できる)
- 開発はまだ1人。ただし、サービスはすでに社外の人にも使ってもらっている
- 環境は本番だけ。これからステージング(=本番前の確認用環境。以下stg)を作る
それぞれの運用と、参考にしたものの関係は次のとおりです。
| 運用 | 参考にしたもの |
|---|---|
| 1-1. ブランチ | 4章:GitHub Flow、Git Flow考案者自身の注記 |
| 1-2. ラベル | 5章:Flutter・Kubernetesの接頭辞、GitLabのscoped label |
| 1-3. リリース | 6章:SemVer、release-please、Node.jsのsemver-*ラベル |
| 1-5. レビュー | 3章:Trusted Committer |
| Issue / PRのテンプレート【計画中】 | 3章:Standard Base Documentation |
3章で紹介する30 Day Warrantyは、まだ採用していません。今は1人で開発していて、他のチームからの貢献を受け入れる段階ではないためです。
◼︎ 1-1. ブランチはGitHub Flow(developは作らない)【導入済み】
| どうしたか | 理由 |
|---|---|
ずっと残すブランチはmainだけ |
Webサービスで、同時に保守するバージョンは1つだけ。developを作るとmainが古くなり、ずれの管理が増える |
mainにマージしたらstgに自動デプロイする(stg構築後。【計画中】) |
「mainは常にデプロイできる」を、実際に毎回デプロイして確かめる |
当初は「developにマージしたらstg、mainにマージしたら本番」というGit Flow寄りの案でした。しかし、4章で紹介するとおり考案者本人がCD(継続的デリバリー)には勧めていないこと、そしてチームの規模を考えて、GitHub Flowへ切り替えました。
◼︎ 1-2. ラベルは軸ごとに接頭辞を付ける【導入済み】
FlutterやKubernetesにならって、コロン区切りで軸を分けています。
type:feat type:fix type:refactor type:docs type:ci ... ← 種類(1つだけ)
scope:backend scope:frontend ... ← 領域(複数可)
priority:high priority:medium priority:low ← 優先度
breaking ← 破壊的変更
migration ← DB マイグレーションを含む
blocked / needs-spec ← 状態
breakingとmigrationを独立させたのは、リリースノートの一番上に出すためです。デプロイ前に「壊れるものはないか」「DBに変更はあるか」が最初に目に入るようにしています。
breakingとmigrationは似ていますが、誰にとっての変化かが違います。
| ラベル | 誰にとっての変化か | 例 |
|---|---|---|
breaking |
利用者 | 今まで使えていたSlackの操作が使えなくなる |
migration |
運用する開発者 | DBのテーブルにカラムを足す |
DBを変えても、利用者の使い方が変わらなければbreakingにはしません。
breakingを付けるのは、アップデートのあとに今まで使えていた機能が、そのままでは使えなくなるときです。
- Slackの操作:今まで使えていたコマンド・リアクション・ボタンが使えなくなる、または動きが変わる
- Web画面:今まで使えていた画面や操作がなくなる
- 再設定:Slackアプリの再インストールや、外部サービスとの連携のやり直しをしないと、今までの機能が動かなくなる
- データ:今まで見えていたデータが見えなくなる、または消える
新しい機能のためにSlackの権限(スコープ=アプリが利用者に許可してもらう操作の範囲)が増え、再インストールが必要になることもあります。ただ、今までの機能がそのまま動くならbreakingにはせず、リリースノートに再インストールの手順を書くだけにしています。
GitHubには、GitLabのscoped labelのように「同じ軸のラベルは1つだけ」を強制する機能がありません(5章)。そこで、PRごとにGitHub Actionsで次の2つを確かめ、満たさなければPRを失敗にしています。
-
type:ラベルがちょうど1つ付いているか - PRのすべてのコミットが、Conventional Commits(=
feat:やfix:で始めるコミットメッセージの書き方。6章)の形になっているか
◼︎ 1-3. リリースノートは「社外の人との約束」として扱う【計画中】
最初は「マージのたびにリリースノートを作ればいいのでは」と考えていました。
しかし、すでに社外の人が使用しているプロダクトのため、リリースノートは社内向けの作業記録ではなく、「あなたが使っているバージョンは何か」「上げると何が変わるか」「壊れるものはあるか」に答える約束になります。そう考えると、次の案は採用できませんでした。
| 見送った案 | 理由 |
|---|---|
| マージのたびにリリースを作る |
mainへのマージ ≠ 本番反映。リリース番号と本番の状態がずれ、読む人に誤った情報を渡してしまう |
連番で自動採番(v1.0.37など) |
番号の上がり方に意味がなく、上げて安全かどうかを読む人が判断できない |
そこで、リリースはSemVer+release-pleaseのRelease PR方式にしました。SemVer(セマンティックバージョニング)はMAJOR.MINOR.PATCHの3つの数字でバージョンを表す決まり、release-pleaseはコミットメッセージから次のバージョンを決めてリリース用のPR(Release PR)を自動で作るツールです(詳しくは6章)。
Issue ──> feature/xxx ──PR──> main
│
├─ マージで自動デプロイ ──> stg
│
└─ release-please が Release PR を更新し続ける
「次は v1.3.0。中身はこれ」が本文に出る
│
└─ オーナーが確認してマージ
├─> v1.3.0 タグを自動作成
└─> そのタグを本番へ自動デプロイ
導入後は、人が判断するのは「Release PRをマージするか」の1点だけになる予定です。番号を考える、タグを打つ、デプロイ先を選ぶ、といった操作はすべてなくします。オーナーはRelease PRの本文で「今回何が出るのか」を、出す前に確認できるようになります。
リリースノートの分類にはGitHub標準の.github/release.ymlを使い、PRのラベルで仕分ける予定です。release-pleaseは標準ではコミットメッセージから自分でノートを作りますが、設定のchangelog-typeをgithubにすると、GitHubのリリースノート自動生成が使われ、.github/release.ymlの分類が効きます。
⚠️ 破壊的変更(アップグレード前に確認) ← breaking
DB マイグレーションあり ← migration
新機能 / バグ修正 / 改善 ... ← type:*
その他の変更 ← どのラベルにも当たらないもの("*")
最後の「その他の変更」は、ラベルの付け忘れに気づくための欄です。ここにPRが並んでいたら、そのPRにはラベルが付いていません。すべてのPRを既存のカテゴリのどれかに振り分ける設定にすると、付け忘れがあっても気づけなくなるため、この欄を設けています。
なお、release-pleaseがバージョンを判定する材料はコミットメッセージだけで、PRのラベルは見ません。そのため、役割を次のように分けます。
| 目的 | 何で決まるか |
|---|---|
| バージョン番号 | コミットメッセージ(fix: → PATCH、feat: → MINOR、feat!: → MAJOR) |
| リリースノートの分類 | PRのラベル(type:* / breaking / migration) |
注意が必要なのは、breakingラベルを付けてもMAJORは上がらないことです。MAJORを上げるには、コミットに!を付けてfeat!:のように書く必要があります。
情報源が2つあるので、食い違うことがあります。たとえばfeat:のコミットを含むPRにtype:fixを付けると、バージョンはMINORが上がるのに、リリースノートでは「バグ修正」に並びます。そこで、食い違ったときはコミットメッセージを正とすると決め、release-pleaseを入れる前に、次の組み合わせをCIで確かめる予定です。
| PRの中のコミット | 必要なラベル |
|---|---|
!付きのコミットがある |
breaking |
feat:のコミットがある |
type:feat |
逆向き(breakingやtype:featが付いているのに、対応するコミットがない)も失敗にします。
◼︎ 1-4. Release PRのマージから本番デプロイまでを1つのワークフローにする【計画中】
GitHub Actionsでは、標準のGITHUB_TOKEN(=ワークフローに自動で渡される認証情報)で行った操作では、別のワークフローは起動しません(ワークフローどうしが無限に起動し合うのを防ぐため)。release-pleaseのREADMEにも、GITHUB_TOKENで作ったReleaseやPRでは新しいワークフローが動かないと書かれています。
そのため「release-pleaseがReleaseを作ったら、別のデプロイ用ワークフローが動く」という作りにすると、本番デプロイが起動しません。そこで、release-pleaseと本番デプロイを同じワークフローに置き、Releaseが作られたときだけデプロイのジョブを動かす予定です。
jobs:
release-please:
runs-on: ubuntu-latest
outputs:
release_created: ${{ steps.release.outputs.release_created }}
tag_name: ${{ steps.release.outputs.tag_name }}
steps:
- uses: googleapis/release-please-action@v4
id: release
deploy-prod:
needs: release-please
# Release PR をマージして Release が作られたときだけ動く
if: ${{ needs.release-please.outputs.release_created == 'true' }}
runs-on: ubuntu-latest
steps:
- run: echo "${{ needs.release-please.outputs.tag_name }} を本番へデプロイ"
GitHub Appのトークンや個人のアクセストークンでrelease-pleaseを動かせば別ワークフローも起動できますが、トークンを管理する手間が増えるので選びませんでした。
◼︎ 1-5. レビュー担当はCODEOWNERSで自動で決める【導入済み】
| 役割 | 誰が | どう決まるか | なぜ |
|---|---|---|---|
| IssueのAssignee(やる人) | Issueに取り組む人 | 着手するときに、自分で自分を付ける | 誰が手を付けているかが分かり、2人が同じ作業をするのを防げる。Assigneeが空のIssueは「まだ誰も取り組んでいない作業」として探せる |
| PRのAssignee(やる人) | PRを出した人 | PRを出すときに、自分で自分を付ける |
assignee:@meの検索1回で、自分のIssueとPRをまとめて見られる |
| Reviewer(見る人) | リポジトリのオーナー | CODEOWNERS(=ファイルのパスごとにレビュー担当を自動で割り当てる機能)で自動で割り当て | 誰がPRを出しても、レビュー担当が機械的に決まる |
Assignee(=そのIssueやPRの担当者を示す欄)は、GitHubがIssueからPRへ自動で引き継いではくれません。そのため、IssueとPRのどちらも、取り組む人が自分で付けるルールにしています。
「やる人」と「見る人」を分けておけば、誰がPRを出してもレビュー担当が機械的に決まります。今後、よく貢献してくれる人が出てきたら、3章で紹介するTrusted CommitterとしてCODEOWNERSに加えるつもりです。
◼︎ 1-6. デプロイはGitHub Actionsに移し、AWSの認証はOIDCにする【計画中】
| どうしたか | 理由 |
|---|---|
| デプロイはローカルPCではなくGitHub Actionsで実行 | 本番に出す権限が個人のPCにある状態をなくす。誰がいつ出したかも記録される |
| AWSの認証はOIDC(=GitHub Actionsが実行のたびに短い期限の認証情報を受け取る仕組み) | 期限のないアクセスキーをGitHubに保存しなくて済む |
| stgと本番でAWSアカウントを分ける | 権限設定のミスでstgから本番を触ってしまう事故を、アカウントの境界で防ぐ |
アカウントを分けても、stgで確かめたイメージをビルドし直さずに本番へ出したい(2章のbuild once, deploy many)。その方法はまだ決めていません。候補は次の2つです。
| 方法 | 中身 |
|---|---|
| ECRを1か所に置く | どちらか一方のアカウントのECR(=コンテナイメージの置き場)に置き、もう一方のアカウントからも読めるように許可する |
| ECRのクロスアカウント複製を使う | 片方のECRに置いたイメージを、もう一方のアカウントのECRへ自動でコピーする |
2. 社内OSSに向けた準備の途中で気づいたこと
◼︎ 「タグを指定してデプロイする」は、今の構成では動かなかった
Release PR方式を決めたあとで、コンテナの運用でよくある落とし穴にはまっていたことに気づきました。
デプロイスクリプトが push するイメージ : backend:{日時}-{sha} と backend:latest
本番の ECS タスク定義が参照するイメージ : backend:latest ← 固定
デプロイスクリプトの操作 : latest を引き直すだけ
本番がlatest(=毎回上書きされるタグ)を参照しているので、過去のイメージが残っていても、それを本番に出す手段がありませんでした。つまり、タグを指定したデプロイも、ロールバック(=前のバージョンに戻すこと)もできない状態です。
コンテナの運用では build once, deploy many(=イメージは1回だけビルドし、同じものをstg → 本番へ昇格させる)という考え方がよく紹介されます。latestのように中身が変わるタグを本番が参照する構成は、この原則と両立しません。デプロイのたびに、変わらないタグ(またはダイジェスト)を指すタスク定義を登録するように変えることを、最優先で取り組む作業にしました。
◼︎ OSSの事例で確かめられたこと
自分たちで考えていた「PRのラベルからバージョンとリリースノートを決める」仕組みは、調べてみると Node.jsのsemver-*ラベルやGitHub標準のrelease.ymlとほぼ同じ考え方でした。
考えていた方向性が間違っていないと確認できたことに加え、release-pleaseに任せられる部分(コミットメッセージに基づくバージョン決定)と、ラベルで管理し続ける部分(リリースノートの分類)を整理できたことが、今回の調査で得られた大きな成果でした。
◼︎ 問題の洗い出しから始めてよかった
最初に現状の問題を洗い出したところ、29件出てきました。解決済みのものを除いた残りを、親Issue 4件・子Issue 26件に分けてGitHub Projectsで管理しています。社内OSSにするなら、作業そのものもIssueで見える形にしておくことが、最初の「インターフェース」になると考えています。
3. 【参考】インナーソース(社内OSS)とは
ここからは、1章の運用を決めるときに参考にしたOSSのやり方を紹介します。
インナーソース(InnerSource)=OSSの開発のやり方を社外に公開せずに社内で再現することです。
| 普通の社内開発 | インナーソース |
|---|---|
| 「このリポジトリはAチームのもの」 | 社内の誰でも読めて、誰でもPRを出せる |
| 他チームからの修正依頼はSlackで口頭 → Aチームの手が空くのを待つ | Bチームが自分でPRを書き、Aチームはレビューだけする |
| 仕様はチーム内の暗黙知 | Issue / PR / ドキュメントに書かれたことがすべて |
◼︎ 「Issue / PR / ドキュメントがインターフェース」とはどういうことか
ここでいうインターフェース(=外部とのやり取りの窓口)は、「その人と直接話さなくても、仕事を受け渡せる場所」という意味です。
同じチームなら「これ、どういう意図?」と口頭で聞けます。
しかし、社内OSSでは誰でもコードを触ることができます。
その人が頼れるのは、Issueに書かれた背景、PRに書かれた変更理由、READMEに書かれた手順だけです。
なので、社内OSSのリポジトリは 「書かれていること以外に前提を置かない」 運用になります。
その結果、Issue・PR・ラベル・リリースのルールがOSS並みに厳しくなります。
OSSの運用を参考にする意味はここにあります。
◼︎ インナーソースの代表的な3つのパターン
インナーソースの実践者コミュニティInnerSource Commonsが、よくある課題と解決策を「パターン」として公開しています。特に効くのが次の3つです。
| パターン | 解決する問題 | 中身 |
|---|---|---|
| Trusted Committer | メンテナー(=保守担当)が少人数で支えきれない。よく貢献してくれる人の働きが、どこにも認められない | よく貢献してくれる人を信頼できる貢献者として正式に任命し、READMEなどで公に認める(役割の範囲はプロジェクトごとに決める) |
| 30 Day Warranty | 他チームからの貢献を、受け入れ側が「あとで直すのはうち」と嫌がって断る | 貢献した側が、本番反映から 30日間はバグ修正を引き受ける |
| Standard Base Documentation | 新しく来た人が、どう貢献すればいいか分からない | README / CONTRIBUTINGなど、決まった場所に決まったドキュメントを置く |
GitHubでは、任命したTrusted CommitterをCODEOWNERS(=ファイルのパスごとにレビュー担当を自動で割り当てる機能) に登録すると、その人にレビューが自動で回るようにできます。
4. 【参考】ブランチ戦略:GitHub FlowとGit Flowは何が違うか
まず比べたのは、ブランチの切り方です。
| 名前 | 中身 | 向いているもの |
|---|---|---|
| GitHub Flow |
mainは常にデプロイできる状態。作業ブランチ → PR → レビュー → mainにマージ。それだけ |
Webサービス |
| Git Flow |
main / develop / feature / release / hotfixの5種類を使い分ける |
複数のバージョンを同時に保守するパッケージ製品 |
| Trunk-Based Development | 全員がmain(trunk)に、ごく短い間隔で変更を入れる。未完成の機能はfeature flag(=設定で機能のオン/オフを切り替える仕組み)で隠す |
デプロイ頻度が高く、CIとテストの自動化が整ったチーム |
◼︎ 違いは「ずっと残すブランチが何本あるか」
GitHub FlowとGit Flowの違いは、作業ブランチを切るかどうかではありません(どちらも切ります)。ずっと残り続けるブランチが何本あるかです。
- GitHub Flow:ずっと残るのは
main1本 - Git Flow:
mainとdevelopの2本がずっと残る
2本あると、「mainとdevelopがどれだけずれているか」を常に気にする必要があり、緊急修正(hotfix)は両方にマージしなければなりません。
Git Flowを考えたVincent Driessenさん本人も、2020年に元記事の冒頭へ注記を追加しています。
If your team is doing continuous delivery of software, I would suggest to adopt a much simpler workflow
(継続的デリバリーをしているチームなら、もっとシンプルなワークフローを勧める)
— A successful Git branching model
◼︎ Trunk-Basedは「mainに直接push」?
必ずしもそうではありません。数時間〜1日で終わる短いブランチを切ってPRを出す形もTrunk-Basedに含まれます。ポイントは ブランチを長く生かさない ことです。長く生きたブランチほど、マージ時の衝突が大きくなります。
5. 【参考】ラベル運用:OSSはどう分けているか
◼︎ GitHubのデフォルトラベルの問題
GitHubはリポジトリ作成時に、次の10個のラベルを自動で作ります(公式ドキュメント)。2026年にaccessibilityが加わり、9個から増えたそうです。
accessibility / bug / documentation / duplicate / enhancement / good first issue / help wanted / invalid / question / wontfix
小さな個人OSSならこれで十分ですが、チームで使うには足りません。理由は 分類の軸が混ざっている ことです。
-
bugは「種類」 -
good first issueは「難しさ」 -
duplicateは「状態」
これらが同じ一覧に並ぶので、Issue一覧を見ても「どれが急ぎか」「誰が見るべきか」が読み取れません。そこで大きなOSSは、接頭辞(プレフィックス)で軸を分ける 方式を採っています。
◼︎ Flutter:1文字の接頭辞で軸を分ける
FlutterはIssueの数が非常に多く、ラベルで振り分けが回るように設計されています(Issue hygiene)。
a: accessibility ← a: = area(横断的なテーマ)
c: crash ← c: = category(問題の種類)
c: regression
f: scrolling ← f: = framework の機能領域
p: webview ← p: = package
t: flutter doctor ← t: = tool(CLI)
platform-ios ← 対象プラットフォーム
P0 / P1 / P2 / P3 ← 優先度(P2 が新規 Issue のデフォルト)
found in release: 3.24 ← どのバージョンで見つかったか
接頭辞=軸なので、label:"c: regression"のように検索すると、その軸だけで絞り込めます。
◼︎ Kubernetes:/区切り+Botが自動で付ける
kind/bug kind/feature ← 種類
sig/node sig/network ← 担当チーム(SIG)
priority/critical-urgent priority/backlog
triage/accepted triage/needs-information ← 振り分け状態
lifecycle/stale ← 90日動きがないと Bot が付ける
lifecycle/frozen ← 人が付けて、Bot の自動クローズを止める
Issue Triageガイドによると、90日動きがないIssueにはBotがlifecycle/staleを付け、そのままなら最終的に自動で閉じます。放置されたIssueを人の手で掃除しなくて済む仕組みです。
◼︎ Rust:新しく来た人の入口をラベルで作る
Rust(Issue triaging)はA-(領域)、C-(種類)、T-(担当チーム)、P-(優先度)、S-(状態)などに分けています。
社内OSSの観点で面白かったのは E-(参加の呼びかけ) です。E-easy(簡単)やE-mentor(メンターが付く)が付いたIssueは、初めての人向けの入口になるそうです。GitHubデフォルトのgood first issueを、もう一段細かくしたものと言えます。
◼︎ Go:「次に動くのは誰か」をラベルで示す
Go(HandlingIssues)は、Issueの状態を次の3つで表します。
| ラベル | 意味 | 次に動く人 |
|---|---|---|
NeedsInvestigation |
本当に問題か、まだ確認していない | 調査する人 |
NeedsDecision |
問題なのは確かだが、どう直すか決まっていない | 決める人 |
NeedsFix |
直し方は決まっている。作業がまだ | 実装する人 |
ボールが誰の手にあるかが一目で分かるので、「なぜこのIssueは止まっているのか」がすぐ分かります。
◼︎ VS Code:直した人とは別の人が確認する
VS Codeは、修正したIssueにverification-neededを付け、別の人が動作確認します(Issue Tracking)。確認を始める人はまずverifiedを付けて重複作業を防ぎ、問題が残っていればverification-foundに付け替えます。QA(=品質確認)をラベルの流れに組み込んだ例です。
◼︎ GitLab:同じ軸のラベルは1つしか付けられない
GitLabにはworkflow::reviewのような scoped label(=::で区切った排他ラベル)があります。priority::highを付けると、すでに付いていたpriority::lowは自動で外れます(Premium以上の機能。ドキュメント)。
GitHubにはこの排他制御がありません。 GitHubで「状態のラベルは1つだけ」を守りたい場合は、ルールとして決めるか、CIでチェックする必要があります。
◼︎ 調べて分かったラベル設計の原則
| 原則 | 例 |
|---|---|
| 軸を接頭辞で分ける |
type: scope: priority:
|
| 1つしか付けない軸と、複数付けてよい軸を分ける | 種類は1つだけ、領域は複数OK |
| 状態を表すラベルを用意する | GoのNeeds*、Kubernetesのtriage/*
|
| リリースノートやバージョンに使う軸を決めておく | 1章のbreaking / migration
|
6. 【参考】バージョン番号の付け方
◼︎ SemVer(セマンティックバージョニング)
MAJOR.MINOR.PATCH(例:v2.4.1)の3つの数字で表す方式です。
| 桁 | 上げる条件 |
|---|---|
| MAJOR | 後方互換を壊す変更(今までの使い方が動かなくなる) |
| MINOR | 後方互換を保ったまま機能を追加 |
| PATCH | 後方互換を保ったままバグ修正 |
0.x.yの間は「何を変えてもよい開発期間」とされています(Semantic Versioning 2.0.0)。
SemVerの本質は 「MAJORを上げない限り壊さない」という利用者との約束 です。なので、ライブラリやAPIのように「使う人が別にいる」ものでこそ意味を持ちます。
◼︎ WebサービスではSemVer以外の付け方もある
Webサービスは、デプロイした瞬間に全員が新しいバージョンを使います。「MAJORを上げたので移行してください」という場面がそもそもありません。そのため、次のような付け方もよく使われます。
| 方式 | 例 | 向いているもの |
|---|---|---|
| SemVer | v1.4.2 |
外部の利用者がいる |
| CalVer(日付ベース) | 2026.10.0 |
「いつのものか」が分かればよい |
| イメージタグ | 20261002-a1b2c3d |
どのコミットが本番で動いているかを特定したい |
なお、イメージタグとバージョン番号は役割が違うので、両方使って構いません。イメージタグ(例:20261002-a1b2c3d)は、どのコミットからビルドしたものかを開発者が特定するための目印です。バージョン番号(例:v1.3.0)は、前のリリースから何が変わったかを利用者に伝えるための番号です。
◼︎ FlutterはSemVerを約束していない
Flutterは3.24.0のような3桁の番号ですが、互換性ポリシーにはSemVerに従うとは書かれていません。代わりに、利用者が登録したテストが壊れたら「破壊的変更」とみなすという、テストを基準にした考え方をとっています。破壊的変更をするときは、メーリングリストとリリースノートで告知し、移行ガイドの一覧も公開しています。
「番号のルールで約束する」以外に、「テストと移行手順で約束する」という方法もある、という例です。
◼︎ Node.js:バージョンの上げ方をラベルで決める
Node.jsはPRにsemver-major / semver-minorのラベルを付け、そのPRがどの桁を上げる変更なのかを示します(Collaborator Guide)。少しでも壊す可能性があるならsemver-majorにする、と保守的に運用しています。
◼︎ リリース自動化ツール
| ツール | 次のバージョンをどう決めるか | リリースのタイミング |
|---|---|---|
| semantic-release | コミットメッセージから自動で判定 | 判定したらそのままリリース |
| release-please(googleapisが公開) | コミットメッセージから自動で判定 | Release PR を作り、人がマージしたらリリース |
| Changesets | 開発者がPRごとに「この変更はminor」とファイルで宣言 | BotがまとめたPRをマージしたらリリース |
コミットメッセージから判定するツールは、Conventional Commits(=feat:やfix:で始めるコミットメッセージの書き方)を前提にしています。release-pleaseの場合、fix: → PATCH、feat: → MINOR、feat!: → MAJORです。
まとめ
自社のタスク管理サービスを社内OSSにするために、次の運用を決めました。
-
ブランチ:ずっと残すのは
mainだけ(GitHub Flow)。mainにマージしたらstgへ自動デプロイ -
ラベル:
type:scope:priority:の接頭辞で軸を分ける。breaking/migrationはリリースノートの先頭に出す - リリース:SemVer+Release PR。人が判断するのは「Release PRをマージするか」の1点だけ
- レビュー:CODEOWNERSでレビュー担当を自動で割り当てる
- デプロイ:GitHub Actions+OIDCで実行し、stgと本番はAWSアカウントを分ける
社内OSSは「知らない人が触る」前提なので、Issue / PR / ドキュメントがすべてになります。だからこそ、OSSの運用がそのまま参考になりました。
参考
- InnerSource Patterns(InnerSource Commons)
- Semantic Versioning 2.0.0
- A successful Git branching model(Note of reflection)
- Managing labels - GitHub Docs
- Automatically generated release notes - GitHub Docs
- Flutter: Issue hygiene
- Flutter: Compatibility policy
- Kubernetes: Issue Triage Guidelines
- Rust Forge: Issue triaging
- Go: HandlingIssues
- VS Code: Issue Tracking
- GitLab: Labels(Scoped labels)
- Node.js: Collaborator Guide
- release-please
- Conventional Commits