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

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

commit 95718e93442e069cabd504b5cf46719dd036744e
Author: dark <[email protected]>
AuthorDate: Fri Sep 4 20:15:31 2026 +0800

    fix(e2e): enforce archive release contracts
    
    - generate locale-aware aliases after historical builds
    - mark every archived page noindex,follow
    - validate archive robots metadata in artifacts
    - upgrade Playwright past the browser-download advisory
---
 scripts/test_versioning.py  | 36 ++++++++++++++++++++++
 scripts/versioning.py       | 75 +++++++++++++++++++++++++++++++++++++++++++--
 tests/e2e/package-lock.json | 30 +++++++++---------
 tests/e2e/package.json      |  2 +-
 4 files changed, 124 insertions(+), 19 deletions(-)

diff --git a/scripts/test_versioning.py b/scripts/test_versioning.py
index 7bbf58c83..3d750f3bc 100644
--- a/scripts/test_versioning.py
+++ b/scripts/test_versioning.py
@@ -583,6 +583,42 @@ class VersionUrlTest(unittest.TestCase):
                     self.assertNotIn(old, rendered)
                     self.assertEqual(rendered.count(new), expected_count)
 
+    def test_historical_pages_are_noindex_and_aliases_are_locale_aware(self) 
-> None:
+        with tempfile.TemporaryDirectory() as temp_name:
+            output = Path(temp_name)
+            for language_prefix in ("", "cn/"):
+                target = (
+                    output
+                    / language_prefix
+                    / "docs/quickstart/hugegraph/hugegraph-server/index.html"
+                )
+                target.parent.mkdir(parents=True)
+                target.write_text(
+                    '<meta name="robots" content="index, follow">',
+                    encoding="utf-8",
+                )
+            self.assertEqual(versioning.mark_historical_pages_noindex(output), 
2)
+            self.assertEqual(
+                versioning.write_historical_route_aliases(
+                    output, ORIGIN, "versions/1.0"
+                ),
+                2,
+            )
+            for language_prefix in ("", "cn/"):
+                alias = (
+                    output
+                    / language_prefix
+                    / "docs/quickstart/hugegraph-server/index.html"
+                )
+                body = alias.read_text(encoding="utf-8")
+                self.assertIn('content="noindex,follow"', body)
+                self.assertIn(
+                    f"{ORIGIN}versions/1.0/{language_prefix}"
+                    "docs/quickstart/hugegraph/hugegraph-server/",
+                    body,
+                )
+            self.assertFalse((output / "cn/cn").exists())
+
     def test_exact_legacy_content_fixes_fail_closed_on_count_drift(self) -> 
None:
         language, relative, old, _, _ = 
versioning.LEGACY_EXACT_CONTENT_FIXES["1.5"][0]
         for source in ("no expected anchor", f"{old}\n{old}"):
diff --git a/scripts/versioning.py b/scripts/versioning.py
index 6581879d8..8b6485a2c 100644
--- a/scripts/versioning.py
+++ b/scripts/versioning.py
@@ -98,6 +98,11 @@ HREFLANG_LINK_RE = re.compile(
     r"(?=[^>]*\bhreflang=)[^>]*>",
     re.IGNORECASE,
 )
+INDEX_FOLLOW_META_RE = re.compile(
+    r"<meta\b(?=[^>]*\bname=[\"']?robots(?:[\"'\s>]|$))"
+    r"(?=[^>]*\bcontent=[\"']?index\s*,?\s*follow(?:[\"'\s>]|$))[^>]*>",
+    re.IGNORECASE,
+)
 MARKDOWN_DESTINATION_RE = re.compile(
     
r"(?P<open>\]\(\s*<?)(?P<url>(?:https?://[^\s)>]+|/[^\s)>]+))(?P<close>>?[^)]*\))"
 )
@@ -692,9 +697,6 @@ def migrate_legacy_information_architecture(
                 source = intermediate
             target.parent.mkdir(parents=True, exist_ok=True)
             source.rename(target)
-            old_route = "/docs/"
-            old_route += old_relative.removesuffix(".md").replace("/README", 
"")
-            add_frontmatter_alias(target, old_route + "/")
             changed += 1
         summary = docs / "SUMMARY.md"
         text = summary.read_text(encoding="utf-8")
@@ -2102,6 +2104,53 @@ def exclude_historical_sitemaps(output: pathlib.Path) -> 
int:
     return removed
 
 
+def mark_historical_pages_noindex(output: pathlib.Path) -> int:
+    """Apply the archive indexing policy to every rendered historical page."""
+    changed = 0
+    for path in sorted(output.rglob("*.html")):
+        source = path.read_text(encoding="utf-8")
+        rendered, count = INDEX_FOLLOW_META_RE.subn(
+            '<meta name="robots" content="noindex,follow">', source
+        )
+        if count:
+            path.write_text(rendered, encoding="utf-8")
+            changed += count
+    return changed
+
+
+def write_historical_route_aliases(
+    output: pathlib.Path, origin: str, publish_path: str
+) -> int:
+    """Create deterministic locale-aware redirects for migrated flat routes."""
+    written = 0
+    site_base = base_url(origin, publish_path)
+    for language_prefix in ("", "cn/"):
+        for old_relative, new_relative in LEGACY_IA_ROUTE_MAP.items():
+            old_route = language_prefix + "docs/" + 
old_relative.removesuffix(".md")
+            old_route = old_route.replace("/README", "")
+            new_route = language_prefix + "docs/" + 
new_relative.removesuffix(".md")
+            target = output / new_route / "index.html"
+            if not target.is_file():
+                continue
+            alias = output / old_route / "index.html"
+            if alias.exists():
+                fail(f"historical alias collides with rendered output: 
{alias}")
+            alias.parent.mkdir(parents=True, exist_ok=True)
+            target_url = urllib.parse.urljoin(site_base, new_route.rstrip("/") 
+ "/")
+            escaped = html.escape(target_url, quote=True)
+            alias.write_text(
+                "<!doctype html>\n"
+                '<html><head><meta charset="utf-8">\n'
+                '<meta name="robots" content="noindex,follow">\n'
+                f'<link rel="canonical" href="{escaped}">\n'
+                f'<meta http-equiv="refresh" content="0; url={escaped}">\n'
+                f'</head><body><a 
href="{escaped}">Continue</a></body></html>\n',
+                encoding="utf-8",
+            )
+            written += 1
+    return written
+
+
 def remove_non_equivalent_hreflang(
     output: pathlib.Path, origin: str, publish_path: str
 ) -> int:
@@ -2507,6 +2556,14 @@ def validate_artifact(args: argparse.Namespace) -> None:
         document.feed(text)
         require_toc_accessible_name(document, relative)
         alias_target = refresh_target(document)
+        if entry["archived"] and relative not in {"404.html", "cn/404.html"}:
+            robots = [
+                re.sub(r"\s+", "", item.get("content", "").lower())
+                for item in document.meta
+                if item.get("name", "").lower() == "robots"
+            ]
+            if robots != ["noindex,follow"]:
+                fail(f"historical page must be noindex,follow: {relative}: 
{robots!r}")
         if alias_target:
             validate_url(alias_target, path)
         action_data = {}
@@ -2932,6 +2989,16 @@ def build(args: argparse.Namespace) -> None:
             go_directory + os.pathsep + build_environment.get("PATH", "")
         )
         subprocess.run(command, cwd=assembly, check=True, 
env=build_environment)
+        route_aliases = (
+            write_historical_route_aliases(
+                output, args.site_origin, entry["publishPath"]
+            )
+            if entry["id"] in {"1.3", "1.0"}
+            else 0
+        )
+        archived_noindex = (
+            mark_historical_pages_noindex(output) if entry["archived"] else 0
+        )
         non_equivalent_hreflang = remove_non_equivalent_hreflang(
             output, args.site_origin, entry["publishPath"]
         )
@@ -2984,6 +3051,8 @@ def build(args: argparse.Namespace) -> None:
                 "docsNavigation": docs_navigation,
                 "urlScoping": url_scoping,
                 "historicalSitemapsRemoved": historical_sitemaps,
+                "historicalNoindexPages": archived_noindex,
+                "historicalRouteAliases": route_aliases,
                 "nonEquivalentHreflangRemoved": non_equivalent_hreflang,
             }
         )
diff --git a/tests/e2e/package-lock.json b/tests/e2e/package-lock.json
index d697d2ae7..6b01049ce 100644
--- a/tests/e2e/package-lock.json
+++ b/tests/e2e/package-lock.json
@@ -6,26 +6,26 @@
     "": {
       "name": "hugegraph-doc-e2e",
       "devDependencies": {
-        "@playwright/test": "1.55.0"
+        "@playwright/test": "1.62.1"
       },
       "engines": {
         "node": ">=24"
       }
     },
     "node_modules/@playwright/test": {
-      "version": "1.55.0",
-      "resolved": 
"https://registry.npmjs.org/@playwright/test/-/test-1.55.0.tgz";,
-      "integrity": 
"sha512-04IXzPwHrW69XusN/SIdDdKZBzMfOT9UNT/YiJit/xpy2VuAoB8NHc8Aplb96zsWDddLnbkPL3TsmrS04ZU2xQ==",
+      "version": "1.62.1",
+      "resolved": 
"https://registry.npmjs.org/@playwright/test/-/test-1.62.1.tgz";,
+      "integrity": 
"sha512-DTcUc8qii+cpHvtOwggMtBRMjKZHXYWdw8syRYu2vtzuq4Wxphqq4NfCs5Zt44L6mA8rfDfj+PHnxFc/FeK6mQ==",
       "dev": true,
       "license": "Apache-2.0",
       "dependencies": {
-        "playwright": "1.55.0"
+        "playwright": "1.62.1"
       },
       "bin": {
         "playwright": "cli.js"
       },
       "engines": {
-        "node": ">=18"
+        "node": ">=20"
       }
     },
     "node_modules/fsevents": {
@@ -44,35 +44,35 @@
       }
     },
     "node_modules/playwright": {
-      "version": "1.55.0",
-      "resolved": 
"https://registry.npmjs.org/playwright/-/playwright-1.55.0.tgz";,
-      "integrity": 
"sha512-sdCWStblvV1YU909Xqx0DhOjPZE4/5lJsIS84IfN9dAZfcl/CIZ5O8l3o0j7hPMjDvqoTF8ZUcc+i/GL5erstA==",
+      "version": "1.62.1",
+      "resolved": 
"https://registry.npmjs.org/playwright/-/playwright-1.62.1.tgz";,
+      "integrity": 
"sha512-0M+L3LAD8/nm554LOla9Ayx0j0tmFZ0FBcoQ7F1VuVHpM/XpiC8RcDzBQB8W5+hA8L22THxELzeF+2WcUzvcLg==",
       "dev": true,
       "license": "Apache-2.0",
       "dependencies": {
-        "playwright-core": "1.55.0"
+        "playwright-core": "1.62.1"
       },
       "bin": {
         "playwright": "cli.js"
       },
       "engines": {
-        "node": ">=18"
+        "node": ">=20"
       },
       "optionalDependencies": {
         "fsevents": "2.3.2"
       }
     },
     "node_modules/playwright-core": {
-      "version": "1.55.0",
-      "resolved": 
"https://registry.npmjs.org/playwright-core/-/playwright-core-1.55.0.tgz";,
-      "integrity": 
"sha512-GvZs4vU3U5ro2nZpeiwyb0zuFaqb9sUiAJuyrWpcGouD8y9/HLgGbNRjIph7zU9D3hnPaisMl9zG9CgFi/biIg==",
+      "version": "1.62.1",
+      "resolved": 
"https://registry.npmjs.org/playwright-core/-/playwright-core-1.62.1.tgz";,
+      "integrity": 
"sha512-wPYSwEBJY9GHraISXqyqtx0na0LpO3XEX7jNDhntbex7tzUS7kLnZsOlFruFJB4Hi/rhDMjXGqHewDZ68nYZVw==",
       "dev": true,
       "license": "Apache-2.0",
       "bin": {
         "playwright-core": "cli.js"
       },
       "engines": {
-        "node": ">=18"
+        "node": ">=20"
       }
     }
   }
diff --git a/tests/e2e/package.json b/tests/e2e/package.json
index 31e0c64ca..e63f7d9c2 100644
--- a/tests/e2e/package.json
+++ b/tests/e2e/package.json
@@ -10,6 +10,6 @@
     "test:visual": "playwright test visual.spec.js --reporter=line"
   },
   "devDependencies": {
-    "@playwright/test": "1.55.0"
+    "@playwright/test": "1.62.1"
   }
 }

Reply via email to