@@ -169,10 +169,31 @@ REST="${CURRENT#*.}"
169169MINOR=" ${REST%% .* } "
170170PATCH=" ${CURRENT##* .} "
171171
172+ # CHANGELOG の [Unreleased] に宣言された「挙動の破壊」を読む。
173+ #
174+ # **シンボル差分は挙動の破壊を見ない。** 今まで値を返していた関数が throw するように
175+ # なっても、型は 1 文字も変わらないので removed=0 added=0 になる。しかし利用者のコードは
176+ # 実行時に壊れるので、SemVer 上これは破壊的変更(SemVer 8 項は「後方互換でない変更」であって
177+ # 「シグネチャの変更」ではない)。
178+ #
179+ # 実例: structured-data で YAML のタグ・alias を「黙って捨てる」から「throw する」に変えた回。
180+ # シンボル差分は minor と算出したが、`!!str 42` を渡していた利用者は全員壊れる。
181+ #
182+ # そこで CHANGELOG に宣言の口を 1 つだけ開ける。**上げることしかできない。**
183+ # 下げられないので、§0.4.3 が潰した「儀式的な major」は復活しない
184+ # (依存のピンを上げただけの回は、この印を書かない限り patch のまま)。
185+ UNRELEASED_BREAKING=false
186+ if [ -f " $REPO /CHANGELOG.md" ]; then
187+ if awk ' /^## \[?[Uu]nreleased\]?/{f=1;next} /^## \[?[0-9]/{f=0} f' " $REPO /CHANGELOG.md" \
188+ | grep -qiE ' ^\s*(###\s*Removed|.*\bBREAKING\b)' ; then
189+ UNRELEASED_BREAKING=true
190+ fi
191+ fi
192+
172193# 0.x は minor が破壊的軸(SemVer 4 項: 0.y.z の互換性は保証されない)。
173194# 1.0.0 未満で major を繰り上げると「安定版を出した」という別の意味になってしまうため、
174195# 破壊的変更は minor、非破壊は patch に写す。oss-doctor の dep-generation-gap と同じ規則。
175- if [ " $REMOVED " -gt 0 ] || [ -n " $PUBLIC_DEP_BREAK " ]; then
196+ if [ " $REMOVED " -gt 0 ] || [ -n " $PUBLIC_DEP_BREAK " ] || [ " $UNRELEASED_BREAKING " = true ] ; then
176197 BREAKING=true
177198else
178199 BREAKING=false
@@ -182,6 +203,8 @@ if [ -n "$PUBLIC_DEP_BREAK" ]; then
182203 REASON=" public dependency の世代変化:$PUBLIC_DEP_BREAK "
183204elif [ " $REMOVED " -gt 0 ]; then
184205 REASON=" public シンボルの削除・変更 ${REMOVED} 件"
206+ elif [ " $UNRELEASED_BREAKING " = true ]; then
207+ REASON=" CHANGELOG が挙動の破壊を宣言(シグネチャ差分には映らない)"
185208elif [ " $ADDED " -gt 0 ]; then
186209 REASON=" public シンボルの追加 ${ADDED} 件"
187210else
0 commit comments