トランクベース開発向けのバージョニング戦略「TrunkVer」を知った

頻繁にリリースするトランクベース開発でセマンティックバージョニングを用いると、「今回はメジャーバージョンを上げるべき？」といったバージョンを考える手間がいちいち発生してしまい、頻繁にリリースするのが面倒になってしまいます。TrunkVerはそうした問題を解決するために考案されたバージョニング戦略です。考え方がとても良さそうだったので共有したく記事にしました。

# 3行要約

- TrunkVerとは、バージョン議論をなくすバージョニング戦略
- 時間+コミットID+ビルドIDで構成され、機械的に決められる
- 不要な議論なし、自動化可能、出荷速度もアップ！


---

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/889/bb0ab7b1-a9d1-4953-a136-94cdb343ef17.png)
![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/889/85b3a87e-ff3a-4cad-bab8-fbc19afe3e9d.png)
![CleanShot 2025-04-03 at 10.49.29@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/889/6bf98f23-8a9d-43f3-b8e6-4a19bc6c90bf.png)
![CleanShot 2025-04-03 at 11.15.03@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/889/0415d43e-e783-4ef4-ba20-8e5c16de1843.png)
![CleanShot 2025-04-03 at 10.50.06@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/889/2ad93aa2-2d51-4a11-a165-5e780f7e8ca6.png)

# 公式ドキュメント

https://trunkver.org/

:::note info

以下は公式ドキュメントの和訳です

:::

# TrunkVer

バージョンについての議論をやめて、出荷を始めましょう。

## TL;DR

`TrunkVer`は、継続的デリバリー、トランクベース開発を行うアプリケーションやシステム向けのバージョニングスキームで、リリーススキームに従わないものに適しています。

これはSemVerと**構文互換性のある置き換え**で、バージョンを意味のあるメタデータに置き換え、アーティファクトが何か、いつビルドされたか、ビルドログがどこにあるかを一目で確認できます。

## 使用方法

### GitHub Actions

```yaml
- name: Generate trunkver
  id: trunkver
  uses: crftd-tech/trunkver@main

- name: Print trunkver
  env:
    TRUNKVER: ${{ steps.trunkver.outputs.trunkver }}
  run: |
    echo "$TRUNKVER"
```

### GitLab

#### https://gitlab.com/crftd-tech/trunkver-gitlab-ci からテンプレートを使用

```yaml
include:
  - remote: "https://gitlab.com/crftd-tech/trunkver-gitlab-ci/-/raw/main/trunkver.gitlab-ci.yml"
```

#### CLIを直接ダウンロードする方法

```yaml
build:
  script:
    - curl -sSL https://github.com/crftd-tech/trunkver/releases/latest/download/trunkver_linux_amd64 -o trunkver
    - chmod +x trunkver
    - export TRUNKVER=$(./trunkver generate)
```

### Docker

```sh
docker run --rm ghcr.io/crftd-tech/trunkver:latest generate --build-ref "$CI_JOB_ID" --source-ref "g$(git rev-parse --short HEAD)"
```

### その他のCI

```sh
curl -sSL https://github.com/crftd-tech/trunkver/releases/latest/download/trunkver_linux_amd64 -o trunkver
chmod +x trunkver
./trunkver generate
```

## 背景

私たちは、バージョニングすべきでないソフトウェア、またはバージョニングを自動化すべきソフトウェアに対してバージョニングを行うことで引き起こされる、回避可能な混乱、対立、そしてコストの源を特定しました。

歴史的に、ソフトウェアの変更を示すために、ベータ、ファイナル、リリース候補などの明確に定義されていない言葉や、特定のルールに従って（または悪い場合はルールなしで）増加する1つ以上の数字を含む任意の番号付けスキームなど、数え切れないほどのバージョニングスキームが使用されてきました。

時間の経過とともに、セマンティックバージョニングが提案され、多くの開発チームに採用されました。これは、バージョン番号の各部分が何を意味するかを明確に定義しています。例えば、最初の数字（メジャーバージョン）を増やすことで、「互換性のない」変更であることを示します。これは人間とマシンの両方が作業を改善するために使用でき、例えばマシンがこの場合に自動的に更新を適用することを拒否し、互換性のない変更に適応するよう人間に通知することができます。私たちはセマンティックバージョニングのファンです。

しかし、私たちは、セマンティックバージョニングやカスタムバージョニングスキームを、それを必要としないソフトウェアに適用するチームや組織に遭遇し続けています。そして、これにより、特定のソフトウェアを「アルファ」「ベータ」「ロー」「本当に最終v4」などと呼ぶべきかどうかについての議論、変更を列挙するチケットの手動作成、さらには「リリースエンジニア」などの専門的なゲートキーパーの役割（最悪の場合、組織全体で1人だけ）など、驚くほど多くの不必要な作業が生まれています。これによりデプロイがより難しく、退屈で、コストがかかるものになるため、系統的にデプロイの数を減らし、それによって組織の配信パフォーマンスを低下させています。

これらの取り組みは、リリースプロセスを監査する必要性、現在実行されているソフトウェアのバージョンを見つける、または特定の認証されたプロセスに準拠するなど、様々な利害関係者の完全に有効な要件から生じていることを認識しています。皮肉なことに、その周りの手動プロセスはこれを高コストにするだけでなく、しばしば意図した目的を達成できません。コミットの欠落、コピー/ペーストエラーなどの監査証跡を見てきました。そのため、監査を正しく行う唯一の方法は、バージョン番号付けを含め、それも自動化することだと主張します。

信頼できる貢献者からなるチームがソフトウェアを作成し、一緒に制御された環境にソフトウェアをデプロイする組織では、このようなバージョニングの儀式はトランクベース開発や継続的統合/デプロイ/デリバリーなどのXPやDevOpsの実践を採用する上で大きな障害となり得ます。そして、それは少量のコードで置き換えることができ、そうすべきです。

## 原則

- TrunkVerはビルドプロセス中に自動的に作成され、通常は最初のステップとして、その後ビルド全体で再利用されます（例：レジストリ内の作成された画像にタグ付けしたり、ファイル名の一部として使用したりする）。
- 現在のコードには単一の真実源があります - 通常は `trunk`、`default`、または `main` という名前のgitブランチです。
- ソースから、ソフトウェアのデプロイ可能なアーティファクトを作成します - 例えばdockerイメージ、jarファイル、またはdebianパッケージです。12要素の原則で説明されているように、このアーティファクトはアーティファクトの一部ではない設定と組み合わせることで環境にデプロイされます。デプロイのバージョニングはツール固有であり、スコープ外です。
- リリース候補しかありません。トランクへのプッシュごとに、潜在的にリリース可能なアーティファクトを作成する自動プロセスがトリガーされ、通常はその後すぐに自動的にリリースされます。
- バージョン番号は開発者にとって有用です：技術的にはビルドジョブIDを使用することが完全に可能ですが、ソート可能にするためにタイムスタンプを前置し、ビルドサーバーを経由せずに識別するためにソースコードのリビジョンを付けます。
- リリースのすべての二次的なアーティファクト（チケットやChangelogなど）は、ビルド中にTrunkVerを参照して自動的に作成されます。

## 仕様

このドキュメントにおけるキーワード "MUST"、"MUST NOT"、"REQUIRED"、"SHALL"、"SHALL NOT"、"SHOULD"、"SHOULD NOT"、"RECOMMENDED"、"MAY"、および "OPTIONAL" は、[RFC 2119](https://tools.ietf.org/html/rfc2119)に記述されているように解釈されます。

1. TrunkVerは、アーティファクトと統合する第三者との互換性を考慮せずにリリースされるあらゆるアーティファクトのバージョニングに適しています。

2. TrunkVerはアーティファクトに使用される一方で、SemVerや他のバージョニングスキームはそのアーティファクトの特定のインターフェース（例：REST API）に使用される場合があります。

3. TrunkVerはSemVerと構文的に互換性がありますが、バージョン番号のセマンティック解釈は尊重しません。

4. TrunkVerは3つのコンポーネントで構成されます：**タイムスタンプ**、**ソース参照**、および**ビルド参照**。

5. **タイムスタンプ**は1秒単位の精度で、常にUTC形式、つまり `YYYYMMDDHHMMSS`（例：`20241230142105`）でフォーマットされます。これはSemVerの**メジャー**部分を置き換えます。

6. **ソース参照**は、アーティファクトのビルドに使用された正確なソースコードを識別します。識別子はASCII英数字 `[0-9A-Za-z]` のみで構成する必要があります。識別子は空であってはなりません。ソース参照がgitコミットチェックサムの場合、それは切り詰められていることがあり（例：`git rev-parse --short HEAD`を使用）、`g`を前置することがあります（`git describe`を使用する際の慣例として）。

7. **ビルド参照**は、アーティファクトのビルドに使用された正確なビルドジョブを識別します。識別子はASCII英数字とハイフン `[0-9A-Za-z-]` のみで構成する必要があります。

8. TrunkVerは次のようにフォーマットされます：**タイムスタンプ**、続いて `.0.0-`、続いて**ソース参照**、続いて `-`、続いて**ビルド参照**。これはSemVerバージョンを置き換えるために使用できます。

9. あるいは、例えばSemVerでバージョン付けされたアーティファクトのプレリリースなどの目的で、TrunkVerはSemVerバージョンのプレリリース部分に次のように組み立てることができます：**タイムスタンプ**、続いて `-`、続いて**ソース参照**、続いて `-`、続いて**ビルド参照**。

### EBNF定義

```ts
TRUNKVER = ( MAJOR_TRUNKVER | PRERELEASE_TRUNKVER );
MAJOR_TRUNKVER = TIMESTAMP, '.0.0-', SOURCE_REF, '-', BUILD_REF;
PRERELEASE_TRUNKVER = TIMESTAMP, '-', SOURCE_REF, '-', BUILD_REF;

TIMESTAMP = NON_ZERO_DIGIT, 11*DIGIT;
BUILD_REF = { ALPHANUMERIC | '-' };
SOURCE_REF = { ALPHANUMERIC };

DIGIT = "0" | NON_ZERO_DIGIT;
NON_ZERO_DIGIT = "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9";
HEXADECIMAL = "a" | "b" | "c" | "d" | "e" | "f" | "A" | "B" | "C" | "D" | "E" | "F" | "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9";
ALPHANUMERIC = DIGIT | "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z" | "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J" | "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z";
```

## よくある質問

- **なぜSemVerのMAJOR部分だけをタイムスタンプとして使うのですか？**  
  ツール（例：コンプライアンス、監査証跡）がSemVerセマンティクスに基づいてバージョンを分類することがあるため、TrunkVerはバージョン間の互換性のない変更を常に防御的に暗示しています。必要に応じて（または実用的に）、TrunkVerを使ってPRERELEASE部分のみを生成することもできます。例：`20250102141131-g39d0b59-12583705231-1`（<code>1.0.5-<strong>20250102141131-g39d0b59-12583705231-1</strong></code>のように）。次の質問でこの適切な場所について説明します。

- **互換性情報を伝える必要があるアーティファクトにTrunkVerを使用できますか？**  
  いいえ、その場合は実際に[SemVer](https://semver.org)を使用してください。ビルドなどのための一意のPRERELEASE部分を作成するためにtrunkverを使用することができます。例えば、TrunkVerの安定バージョンを、特定のメジャーバージョンの安定したAPI/CLIで提供しています。「最新」バージョンは常にmainからビルドされ、SemVerの意味でプレリリースとしてtrunkverを使用してバージョニングされています。例：`1.0.0-20250102141131-g39d0b59-12583705231-1`。

- **ソース情報にSemVerのPRERELEASE部分を使用しBUILD部分を使わないのはなぜですか？**  
  OCIタグは `+` をサポートしていないためです（[distribution/distribution#1201](https://github.com/distribution/distribution/issues/1201)と[opencontainers/distribution-spec#154](https://github.com/opencontainers/distribution-spec/issues/154)を参照）。アーティファクト間で一貫したバージョンを持つ方が良いと考えています。意味的には、TrunkVerのソート関連部分はMAJORバージョンのみで、競合（同じ秒に2つのアーティファクトを作成すること）は避けるべきです。

- **タイムスタンプにタイムゾーンを含めないのはなぜですか？**  
  「Z」があるとSemVerと構文的に互換性がなくなるからです。TrunkVerのタイムスタンプは常に**UTC**です。

## 先行技術

- [Sam's Versioning: Samver](https://samver.org/)

## リンク

- [https://github.com/crftd-tech/trunkver](https://github.com/crftd-tech/trunkver)
- [https://crftd.tech/](https://crftd.tech/)
- [https://semver.org](https://semver.org)