diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 543d8b4..067f3ec 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,5 +1,8 @@ # 依存の version updates(マニフェスト = version catalog / ビルドスクリプト / ワークフロー由来)。 # Kotlin の更新 PR は catalog 経由で配布版 -<自版> の導出まで自動で追随し、CI が検証する。 +# README / docs / .idea 中の現行バージョン表記はマニフェスト更新では変わらないため、 +# CI(ルートの checkVersionMentions)が乖離を検出したら、./gradlew syncVersionMentions の +# 実行結果を更新 PR へコミットして追随させる。 # 解決済み依存グラフ(推移依存含む)に対する alerts は build.yml の dependency-submission ジョブが補完する # # npm(kotlin-js-store/yarn.lock)は version updates の対象に含めない。lockfile は KGP が diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 265aae1..9fddc63 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,7 +12,8 @@ name: build # 本ファイル自身は列挙し、この CI 定義の変更では起動させる。 # .gitattributes は LF 正規化経由でフィクスチャの挙動に影響しうるため入力に含める # (.editorconfig はビルドが読まないため列挙しない)。 -# 列挙パス内でもビルドに影響しない種別(.md)は個別に除外する。 +# 列挙パス内でもビルドに影響しない種別(.md)は個別に除外する。ただし現行バージョン表記の検証は +# README / docs を入力に持つため、PR では changes ジョブの専用フィルタで version-mentions ジョブを起動する。 # .txt は TestKit フィクスチャの入力種別のため、除外の対象にしてはならない。 # # PR では起動条件に paths を置かない。paths でスキップされたワークフローのチェックは pending のまま残り、 @@ -60,6 +61,9 @@ jobs: pull-requests: read outputs: build: ${{ github.event_name != 'pull_request' || steps.filter.outputs.build }} + # push はビルド入力の変更でしか起動せず、そちらは build ジョブの check が版表記検証を含むため、 + # このフィルタは PR のみ評価する(push では常に false = ジョブをスキップ) + versionMentions: ${{ github.event_name == 'pull_request' && steps.filter.outputs.versionMentions }} steps: - uses: dorny/paths-filter@4711b7a31b4aa89103d8c6ffab2e3b8e7b6381c7 # master(v4.0.2 + some-with-excludes) id: filter @@ -81,6 +85,10 @@ jobs: - ".gitattributes" - ".github/workflows/build.yml" - "!**/*.md" + versionMentions: + - "README.md" + - "docs/**" + - ".idea/kotlinc.xml" build: needs: changes @@ -211,6 +219,24 @@ jobs: key: konan-${{ runner.os }}-${{ hashFiles('gradle/libs.versions.toml') }} - run: ./gradlew.bat -p integration-test :mpp-producer:mingwX64Test + # 現行バージョン表記と単一情報源の一致検証(ルートの checkVersionMentions。乖離時は + # ./gradlew syncVersionMentions の結果をコミットして追随する)。ビルド入力を変更する PR + # (Dependabot の版更新を含む)は build ジョブの check が同じ検証を含むため、このジョブは + # ビルド入力の allowlist から除外されている .md など、表記側だけを変更する PR を拾う + version-mentions: + needs: changes + if: needs.changes.outputs.versionMentions == 'true' + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0 + with: + distribution: temurin + java-version: 17 + - uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6.2.0 + - run: ./gradlew :checkVersionMentions + # 必須チェックはこの 1 本へ集約する(docs/チェックリスト.md §8 R-6)。 # matrix を含む個別のジョブ名を必須チェックとして列挙すると、shard 数の変更で保護設定側が # 存在しない名前を要求し、マージ不能または無防備のどちらかへ静かに倒れるため。 @@ -225,6 +251,7 @@ jobs: - integration-maven - build-macos - native-windows + - version-mentions runs-on: ubuntu-latest timeout-minutes: 5 steps: diff --git a/build.gradle.kts b/build.gradle.kts index 13a9c22..7363f02 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -32,6 +32,96 @@ allprojects { version = enumizerFullVersion } +// README / docs / .idea 中の「現行バージョン」表記を単一情報源(Kotlin / Maven = version catalog・ +// 自版 = enumizerVersion・Gradle = wrapper properties)から導出した値へ揃える。 +// Dependabot 等の版更新はマニフェストしか書き換えず表記が置き去りになるため、 +// checkVersionMentions(check へ紐付け。CI では version-mentions ジョブ)が乖離を検出し、 +// syncVersionMentions が書き換える。 +// 置換は「現行版を指す表記」に限る。サポート下限("Kotlin 2.4" / "Gradle 9+" / "Maven 3.9+")・ +// 特定版に固定した実測エビデンス(設計00 の "Kotlin 2.4.0 実測")・別版での版形式の例示は +// 書き換えてはならないため、対象ファイルの許可リストと文脈付きパターンの両方で絞る +fun minorOf(version: String) = version.split(".").take(2).joinToString(".") + +val kotlinCurrent = libs.versions.kotlin.get() +val mavenCurrent = libs.versions.maven.get() +val enumizerReleaseVersion = + providers.gradleProperty("enumizerVersion").get().removeSuffix("-SNAPSHOT") +val gradleWrapperVersion = + providers + .fileContents(layout.projectDirectory.file("gradle/wrapper/gradle-wrapper.properties")) + .asText + .map { checkNotNull(Regex("""/gradle-(\d+(?:\.\d+)+)-""").find(it)).groupValues[1] } + .get() + +// 3 成分(パッチ付き)と 2 成分(マイナーまで)の表記は、それぞれの粒度を保ったまま現行値へ置換する +val versionMentionRules: List> = + listOf( + // 配布版のフル形式 -<自版>(README のセットアップ例・版形式の現行例) + Regex("""(? + val current = file.readText() + val synced = rules.fold(current) { text, (regex, value) -> regex.replace(text, value) } + if (synced != current) file.writeText(synced) + } + } +} + +val checkVersionMentions = + tasks.register("checkVersionMentions") { + val targets = versionMentionTargets + val rules = versionMentionRules + doLast { + val stale = targets.filter { (_, file) -> + val current = file.readText() + rules.fold(current) { text, (regex, value) -> regex.replace(text, value) } != + current + } + if (stale.isNotEmpty()) { + throw GradleException( + "現行バージョンとズレた表記があります: ${stale.joinToString { it.first }}。" + + "./gradlew syncVersionMentions で追随してください" + ) + } + } + } + +tasks.named("check") { dependsOn(checkVersionMentions) } + // gradle.properties の kotlin.code.style=official に合わせ、ktfmt も Kotlin 公式スタイル // (ブロック・継続ともインデント 4、末尾カンマ付与)で揃える allprojects {