From fd198251bd66874999af155fc34a0dc0487bff10 Mon Sep 17 00:00:00 2001 From: Ben Lee Date: Thu, 30 Jul 2026 23:01:04 -0400 Subject: [PATCH] Reject Dokka aggregate root path collisions --- README.md | 4 ++-- dokka/private/multi_module.bzl | 18 ++++++++++++++++++ tests/rules/BUILD.bazel | 13 +++++++++++++ tests/rules/dokka_analysis_test.bzl | 14 ++++++++++++++ 4 files changed, 47 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e560cd3..f41a835 100644 --- a/README.md +++ b/README.md @@ -159,8 +159,8 @@ Both module attributes are optional. `module_name` defaults to the target name. `module_path` defaults to the target's full Bazel package path, preserving nested project structure; a target in the root package falls back to its target name. Use either attribute to override its derived value. Module paths must be -portable relative paths and unique within an aggregate. Only HTML `dokka` -targets can be aggregated. +portable relative paths, unique within an aggregate, and must not collide with +Dokka's root assets or index files. Only HTML `dokka` targets can be aggregated. The aggregate rule uses the same precedence and consumes the selected configuration's `offline_mode`, `plugins`, and `plugins_configuration` settings. diff --git a/dokka/private/multi_module.bzl b/dokka/private/multi_module.bzl index 98da0cf..dd13fd6 100644 --- a/dokka/private/multi_module.bzl +++ b/dokka/private/multi_module.bzl @@ -3,6 +3,15 @@ load(":providers.bzl", "DokkaConfigInfo", "DokkaInfo") _DOKKA_TOOLCHAIN_TYPE = Label("//dokka:toolchain_type") +_DOKKA_RESERVED_ROOT_ENTRIES = [ + "images", + "index.html", + "navigation.html", + "package-list", + "scripts", + "styles", + "ui-kit", +] _WINDOWS_INVALID_PATH_CHARACTERS = ["<", ">", ":", "\"", "|", "?", "*"] def _validate_module_path(module_path, label): @@ -27,6 +36,15 @@ def _validate_module_path(module_path, label): ).format(label, module_path), ) + root_entry = segments[0].lower() + if root_entry in _DOKKA_RESERVED_ROOT_ENTRIES: + fail( + ( + "Dokka target {} has invalid module_path '{}': first segment '{}' is reserved " + + "by Dokka's aggregate output." + ).format(label, module_path, segments[0]), + ) + def _dokka_multi_module_impl(ctx): modules = [] module_paths = {} diff --git a/tests/rules/BUILD.bazel b/tests/rules/BUILD.bazel index 1207e79..01f5ec7 100644 --- a/tests/rules/BUILD.bazel +++ b/tests/rules/BUILD.bazel @@ -108,6 +108,13 @@ dokka( tags = ["manual"], ) +dokka( + name = "reserved_module_path_fixture", + srcs = ["Fixture.kt"], + module_path = "scripts/api", + tags = ["manual"], +) + dokka_multi_module( name = "multi_module_fixture", config = ":shared_config", @@ -135,6 +142,12 @@ dokka_multi_module( tags = ["manual"], ) +dokka_multi_module( + name = "reserved_path_aggregate_fixture", + modules = [":reserved_module_path_fixture"], + tags = ["manual"], +) + dokka_multi_module( name = "non_html_aggregate_fixture", modules = [":analysis_fixture"], diff --git a/tests/rules/dokka_analysis_test.bzl b/tests/rules/dokka_analysis_test.bzl index 05e4513..33501b4 100644 --- a/tests/rules/dokka_analysis_test.bzl +++ b/tests/rules/dokka_analysis_test.bzl @@ -384,6 +384,11 @@ def _invalid_module_path_test_impl(ctx): asserts.expect_failure(env, "has invalid module_path '../invalid'") return analysistest.end(env) +def _reserved_module_path_test_impl(ctx): + env = analysistest.begin(ctx) + asserts.expect_failure(env, "first segment 'scripts' is reserved") + return analysistest.end(env) + def _non_html_module_test_impl(ctx): env = analysistest.begin(ctx) asserts.expect_failure(env, "only accepts HTML `dokka` targets") @@ -405,6 +410,10 @@ _invalid_module_path_test = analysistest.make( _invalid_module_path_test_impl, expect_failure = True, ) +_reserved_module_path_test = analysistest.make( + _reserved_module_path_test_impl, + expect_failure = True, +) _non_html_module_test = analysistest.make( _non_html_module_test_impl, expect_failure = True, @@ -444,6 +453,10 @@ def dokka_analysis_test_suite(name): name = name + "_invalid_module_path", target_under_test = ":invalid_path_aggregate_fixture", ) + _reserved_module_path_test( + name = name + "_reserved_module_path", + target_under_test = ":reserved_path_aggregate_fixture", + ) _non_html_module_test( name = name + "_non_html_module", target_under_test = ":non_html_aggregate_fixture", @@ -458,6 +471,7 @@ def dokka_analysis_test_suite(name): ":" + name + "_invalid_module_path", ":" + name + "_multi_module", ":" + name + "_non_html_module", + ":" + name + "_reserved_module_path", ":" + name + "_reusable_config", ], )