This is an automated email from the ASF dual-hosted git repository.

imbajin pushed a commit to branch feat/oink-community-content
in repository https://gitbox.apache.org/repos/asf/hugegraph-doc.git

commit a3eae85364905ba8ca7291ddd41ec3e7b85dbec8
Author: dark <[email protected]>
AuthorDate: Fri Sep 4 20:13:32 2026 +0800

    fix(community): verify artifact search contracts
    
    - publish a fixed bilingual query-to-route fixture\n- assert all 24 entries 
in generated offline indexes\n- accept minified and expanded Community artifact 
markup\n- refresh the checked-in roster through hardened network checks
---
 data/community/roster.json                     |  2 +-
 scripts/community_roster.py                    | 13 +++++--
 scripts/fixtures/community_search_queries.json | 26 ++++++++++++++
 scripts/test_community_roster.py               | 48 +++++++++++++++++---------
 4 files changed, 69 insertions(+), 20 deletions(-)

diff --git a/data/community/roster.json b/data/community/roster.json
index ee6197891..413853d88 100644
--- a/data/community/roster.json
+++ b/data/community/roster.json
@@ -1,7 +1,7 @@
 {
   "schema_version": 1,
   "project": "hugegraph",
-  "retrieved_at": "2026-09-04T11:03:16Z",
+  "retrieved_at": "2026-09-04T12:12:42Z",
   "source": {
     "committee": "https://whimsy.apache.org/public/committee-info.json";,
     "projects": "https://whimsy.apache.org/public/public_ldap_projects.json";,
diff --git a/scripts/community_roster.py b/scripts/community_roster.py
index bd12e9770..57ee72554 100644
--- a/scripts/community_roster.py
+++ b/scripts/community_roster.py
@@ -430,7 +430,14 @@ def validate_rendered_outputs(destination: pathlib.Path) 
-> None:
         if not path.is_file():
             raise RosterError(f"rendered output is missing {relative}")
         rendered = path.read_text(encoding="utf-8")
-        if any(marker not in rendered for marker in markers):
+        if relative.endswith(".html"):
+            has_markers = all(
+                re.search(rf'data-community-role=(?:"{role}"|{role})(?:\s|>)', 
rendered)
+                for role in ("pmc", "committers")
+            )
+        else:
+            has_markers = all(marker in rendered for marker in markers)
+        if not has_markers:
             raise RosterError(f"rendered output {relative} is missing 
Community markers")
         positions = [rendered.find(url.replace("&", "&amp;") if 
relative.endswith(".html") else url) for url in urls]
         if any(position < 0 for position in positions) or positions != 
sorted(positions):
@@ -487,9 +494,11 @@ def _commit_bundle(candidate: dict, candidate_avatars: 
dict[str, bytes]) -> None
         for name, avatar in sorted(candidate_avatars.items()):
             _validate_avatar_blob(name, avatar)
             destination = AVATAR_DIR / name
-            if destination.exists():
+            if destination.exists() or destination.is_symlink():
                 previous = destination.read_bytes()
                 try:
+                    if destination.is_symlink():
+                        raise RosterError(f"candidate destination is a 
symlink: {destination}")
                     _validate_avatar_blob(name, previous)
                     continue
                 except RosterError:
diff --git a/scripts/fixtures/community_search_queries.json 
b/scripts/fixtures/community_search_queries.json
new file mode 100644
index 000000000..a65cfdcb3
--- /dev/null
+++ b/scripts/fixtures/community_search_queries.json
@@ -0,0 +1,26 @@
+[
+  {"locale": "en", "query": "HugeGraph overview", "expected_ref": 
"/docs/introduction/"},
+  {"locale": "en", "query": "server quickstart", "expected_ref": 
"/docs/quickstart/hugegraph/hugegraph-server/"},
+  {"locale": "en", "query": "distributed storage", "expected_ref": 
"/docs/quickstart/hugegraph/hugegraph-hstore/"},
+  {"locale": "en", "query": "placement driver", "expected_ref": 
"/docs/quickstart/hugegraph/hugegraph-pd/"},
+  {"locale": "en", "query": "graph computing", "expected_ref": 
"/docs/quickstart/computing/hugegraph-computer/"},
+  {"locale": "en", "query": "bulk import", "expected_ref": 
"/docs/quickstart/toolchain/hugegraph-loader/"},
+  {"locale": "en", "query": "graph visualization", "expected_ref": 
"/docs/quickstart/toolchain/hugegraph-hubble/"},
+  {"locale": "en", "query": "Java client", "expected_ref": "/docs/clients/"},
+  {"locale": "en", "query": "HugeGraph REST API", "expected_ref": 
"/docs/clients/restful-api/"},
+  {"locale": "en", "query": "server config", "expected_ref": 
"/docs/config/config-guide/"},
+  {"locale": "en", "query": "StandardAuthenticator", "expected_ref": 
"/docs/config/config-authentication/"},
+  {"locale": "en", "query": "release artifacts", "expected_ref": 
"/docs/download/download/"},
+  {"locale": "cn", "query": "HugeGraph 介绍", "expected_ref": 
"/cn/docs/introduction/"},
+  {"locale": "cn", "query": "Server 快速开始", "expected_ref": 
"/cn/docs/quickstart/hugegraph/hugegraph-server/"},
+  {"locale": "cn", "query": "分布式存储", "expected_ref": 
"/cn/docs/quickstart/hugegraph/hugegraph-hstore/"},
+  {"locale": "cn", "query": "元数据管理", "expected_ref": 
"/cn/docs/quickstart/hugegraph/hugegraph-pd/"},
+  {"locale": "cn", "query": "图计算", "expected_ref": 
"/cn/docs/quickstart/computing/hugegraph-computer/"},
+  {"locale": "cn", "query": "批量导入", "expected_ref": 
"/cn/docs/quickstart/toolchain/hugegraph-loader/"},
+  {"locale": "cn", "query": "图可视化", "expected_ref": 
"/cn/docs/quickstart/toolchain/hugegraph-hubble/"},
+  {"locale": "cn", "query": "Java 客户端", "expected_ref": "/cn/docs/clients/"},
+  {"locale": "cn", "query": "HugeGraph REST API", "expected_ref": 
"/cn/docs/clients/restful-api/"},
+  {"locale": "cn", "query": "Server 配置", "expected_ref": 
"/cn/docs/config/config-guide/"},
+  {"locale": "cn", "query": "权限配置", "expected_ref": 
"/cn/docs/config/config-authentication/"},
+  {"locale": "cn", "query": "发布包", "expected_ref": 
"/cn/docs/download/download/"}
+]
diff --git a/scripts/test_community_roster.py b/scripts/test_community_roster.py
index 21fc6ac20..33a9e7315 100644
--- a/scripts/test_community_roster.py
+++ b/scripts/test_community_roster.py
@@ -459,30 +459,44 @@ class CommunityContentContractTests(unittest.TestCase):
             for marker in markers:
                 self.assertIn(marker, rendered)
 
+    def test_explicit_artifact_validator_accepts_prebuilt_site(self):
+        roster.validate_rendered_outputs(self.site)
+        result = subprocess.run(
+            [
+                sys.executable,
+                "scripts/community_roster.py",
+                "validate",
+                "--warn-after-days",
+                "90",
+                "--artifact",
+                str(self.site),
+            ],
+            cwd=ROOT,
+            text=True,
+            capture_output=True,
+        )
+        self.assertEqual(0, result.returncode, result.stderr)
+
     def test_fixed_metadata_is_present_in_actual_offline_indexes(self):
-        relative_refs = [
-            "docs/introduction/",
-            "docs/quickstart/hugegraph/hugegraph-server/",
-            "docs/quickstart/hugegraph/hugegraph-hstore/",
-            "docs/quickstart/hugegraph/hugegraph-pd/",
-            "docs/quickstart/computing/hugegraph-computer/",
-            "docs/quickstart/toolchain/hugegraph-loader/",
-            "docs/quickstart/toolchain/hugegraph-hubble/",
-            "docs/clients/",
-            "docs/clients/restful-api/",
-            "docs/config/config-guide/",
-            "docs/config/config-authentication/",
-            "docs/download/download/",
-        ]
-        for language, prefix in (("en", "/"), ("cn", "/cn/")):
+        fixture = json.loads(
+            (ROOT / 
"scripts/fixtures/community_search_queries.json").read_text(encoding="utf-8")
+        )
+        self.assertEqual(24, len(fixture))
+        self.assertEqual(24, len({(item["locale"], item["query"]) for item in 
fixture}))
+        for language in ("en", "cn"):
             indexes = 
list(self.site.glob(f"offline-search-index.{language}.*.json"))
             self.assertEqual(1, len(indexes))
             records = {record["ref"]: record for record in 
json.loads(indexes[0].read_text())}
-            for relative in relative_refs:
-                ref = prefix + relative
+            for item in (entry for entry in fixture if entry["locale"] == 
language):
+                ref = item["expected_ref"]
                 self.assertIn(ref, records)
                 self.assertTrue(records[ref]["keywords"], ref)
                 self.assertGreater(records[ref]["boost"], 1, ref)
+                normalized_query = item["query"].casefold()
+                searchable = " ".join(
+                    [records[ref]["title"], *records[ref]["keywords"]]
+                ).casefold()
+                self.assertIn(normalized_query, searchable, ref)
 
 
 if __name__ == "__main__":

Reply via email to