From a91c4f3e29f0512bec73daa538057498fe3e053d Mon Sep 17 00:00:00 2001 From: Ramakrishnan P K Date: Thu, 9 Jul 2026 21:19:11 +0530 Subject: [PATCH 1/3] #390 [TASK] Update broken links --- .github/workflows/test_and_docs.yml | 8 ++++++++ .github/workflows/test_links.yml | 2 +- docs/internals/extensions/extension_guide.md | 2 +- docs/internals/extensions/source_code_linker.md | 6 +++--- docs/reference/commands.md | 3 ++- 5 files changed, 15 insertions(+), 6 deletions(-) diff --git a/.github/workflows/test_and_docs.yml b/.github/workflows/test_and_docs.yml index aecf8a289..535102cbd 100644 --- a/.github/workflows/test_and_docs.yml +++ b/.github/workflows/test_and_docs.yml @@ -39,6 +39,14 @@ jobs: with: bazel-docs-verify-target: "--lockfile_mode=error //:docs_check" + docs-link-verify: + uses: eclipse-score/cicd-workflows/.github/workflows/docs-verify.yml@c1c90b1a82a1fab0fc202979dde6686b2162d5a8 # v0.0.0 + permissions: + pull-requests: write + contents: read + with: + bazel-docs-verify-target: "--lockfile_mode=error //:docs_link_check" + # This is the user configurable part of the workflow unit-tests: uses: ./.github/workflows/test.yml diff --git a/.github/workflows/test_links.yml b/.github/workflows/test_links.yml index cbc2d47a6..faa4a8ff2 100644 --- a/.github/workflows/test_links.yml +++ b/.github/workflows/test_links.yml @@ -32,7 +32,7 @@ jobs: # Run your link checker and generate log - name: Run LinkChecker run: | - bazel run --lockfile_mode=error //:link_check > linkcheck_output.txt + bazel run --lockfile_mode=error //:docs_link_check > linkcheck_output.txt continue-on-error: true # Run your Python script to parse the linkcheck log and generate issue body diff --git a/docs/internals/extensions/extension_guide.md b/docs/internals/extensions/extension_guide.md index 77da432f1..e2b550a77 100644 --- a/docs/internals/extensions/extension_guide.md +++ b/docs/internals/extensions/extension_guide.md @@ -44,7 +44,7 @@ Some events also expect a return value. For more information, please see the related documentation: - [Attaching function signature](https://www.sphinx-doc.org/en/master/extdev/appapi.html#sphinx.application.Sphinx.connect) - [Build API events](https://www.sphinx-doc.org/en/master/extdev/event_callbacks.html#core-events-overview) -- [sphinx-needs events](https://github.com/useblocks/sphinx-needs/blob/master/docs/contributing.rst#structure-of-the-extensions-logic) +- [sphinx-needs events](https://github.com/useblocks/sphinx-needs/blob/master/AGENTS.md#sphinx-extension-architecture) ## Adding a New Configuration Value diff --git a/docs/internals/extensions/source_code_linker.md b/docs/internals/extensions/source_code_linker.md index 4b3aed154..dd99d4a0a 100644 --- a/docs/internals/extensions/source_code_linker.md +++ b/docs/internals/extensions/source_code_linker.md @@ -375,7 +375,7 @@ score_source_code_linker/ To see working examples for CodeLinks & TestLinks, take a look at the Docs-As-Code documentation. -[Example CodeLink](https://eclipse-score.github.io/docs-as-code/main/requirements/requirements.html#tool_req__docs_common_attr_id_scheme) -[Example CodeLink](https://eclipse-score.github.io/docs-as-code/main/requirements/requirements.html#tool_req__docs_common_attr_status) +[Example CodeLink](https://eclipse-score.github.io/docs-as-code/main/internals/requirements/requirements.html#tool_req__docs_common_attr_id_scheme) +[Example CodeLink](https://eclipse-score.github.io/docs-as-code/main/internals/requirements/requirements.html#tool_req__docs_common_attr_status) -[Example TestLink](https://eclipse-score.github.io/docs-as-code/main/requirements/requirements.html#tool_req__docs_dd_link_source_code_link) +[Example TestLink](https://eclipse-score.github.io/docs-as-code/main/internals/requirements/requirements.html#tool_req__docs_dd_link_source_code_link) diff --git a/docs/reference/commands.md b/docs/reference/commands.md index 279476f02..5afe0d68a 100644 --- a/docs/reference/commands.md +++ b/docs/reference/commands.md @@ -8,8 +8,9 @@ | Target | What it does | | ---------------------------------------------- | ------------------------------------------------------------------------------------------------- | -| `bazel run //:docs` | Builds documentation (also writes `metrics.json`) | +| `bazel run //:docs` | Builds documentation (also writes `metrics.json`) | | `bazel run //:docs_check` | Verifies documentation correctness | +| `bazel run //:docs_link_check` | Lists broken links | | `bazel run //:docs_combo` | Builds combined documentation with all external dependencies included | | `bazel run //:traceability_gate -- --metrics-json bazel-bin/needs_json/_build/needs/metrics.json --min-req-code 70 --min-req-test 70 --min-req-fully-linked 60 --min-tests-linked 70` | Reads the pre-computed metrics.json and fails if coverage thresholds are not met | | `bazel run //:live_preview` | Creates a live_preview of the documentation viewable in a local server | From 68516dce1a9be8034baa296d31e482341728fa6c Mon Sep 17 00:00:00 2001 From: Ramakrishnan P K Date: Thu, 9 Jul 2026 21:19:11 +0530 Subject: [PATCH 2/3] #390 [FIX] Documentation and add alias --- BUILD | 6 ++++++ docs.bzl | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/BUILD b/BUILD index 80284fe40..01e2e04f1 100644 --- a/BUILD +++ b/BUILD @@ -38,3 +38,9 @@ alias( name = "actionlint", actual = "@score_devcontainer//tools:actionlint", ) + +# bazel run //:lint_check +alias( + name = "link_check", + actual = ":docs_link_check", +) diff --git a/docs.bzl b/docs.bzl index 7f5ad0ced..bdafc804e 100644 --- a/docs.bzl +++ b/docs.bzl @@ -252,7 +252,7 @@ def docs(source_dir = "docs", data = [], deps = [], scan_code = [], known_good = docs_env["ACTION"] = "linkcheck" py_binary( name = "docs_link_check", - tags = ["cli_help=Verify Links inside Documentation:\nbazel run //:link_check\n (Note: this could take a long time)"], + tags = ["cli_help=Verify Links inside Documentation:\nbazel run //:docs_link_check\n (Note: this could take a long time)"], srcs = [incremental_src], data = docs_data, deps = deps, From d33f586ee4e0b48e127fb2a33733693f8ce880c6 Mon Sep 17 00:00:00 2001 From: Ramakrishnan P K Date: Thu, 9 Jul 2026 21:19:11 +0530 Subject: [PATCH 3/3] #390 [TASK] Add broken link fix extension to ignore errors --- src/extensions/BUILD | 8 ++ src/extensions/broken_link_fix.py | 100 ++++++++++++++++++ src/extensions/score_sphinx_bundle/BUILD | 1 + .../score_sphinx_bundle/__init__.py | 1 + 4 files changed, 110 insertions(+) create mode 100644 src/extensions/broken_link_fix.py diff --git a/src/extensions/BUILD b/src/extensions/BUILD index 080f745f2..33c0de557 100644 --- a/src/extensions/BUILD +++ b/src/extensions/BUILD @@ -20,3 +20,11 @@ py_library( visibility = ["//visibility:public"], deps = ["@score_docs_as_code//src/helper_lib"], ) + +py_library( + name = "broken_link_fix", + srcs = ["@score_docs_as_code//src/extensions:broken_link_fix.py"], + imports = ["."], + visibility = ["//visibility:public"], + deps = ["@score_docs_as_code//src/helper_lib"], +) diff --git a/src/extensions/broken_link_fix.py b/src/extensions/broken_link_fix.py new file mode 100644 index 000000000..46ed7fad2 --- /dev/null +++ b/src/extensions/broken_link_fix.py @@ -0,0 +1,100 @@ +# ******************************************************************************* +# Copyright (c) 2025 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* +import re +from collections.abc import Callable +from enum import StrEnum +from typing import Any, cast + +from sphinx._cli.util.colour import darkgray +from sphinx.application import Sphinx +from sphinx.builders import linkcheck +from sphinx.builders.linkcheck import CheckExternalLinksBuilder, CheckResult +from sphinx.util import logging + +logger = logging.getLogger(__name__) + +linkcheckbase = cast( + Callable[[CheckExternalLinksBuilder, CheckResult], None], + linkcheck.CheckExternalLinksBuilder.process_result, +) + + +class _Status(StrEnum): + BROKEN = "broken" + + +def process_result(self: CheckExternalLinksBuilder, result: CheckResult) -> None: + """hook a custom process_result function. + + Args: + self: The CheckExternalLinksBuilder instance. + result: The CheckResult instance. + + Returns: + None + """ + match result.status: + case _Status.BROKEN: + """ Ignore client errors from sourceforge.io """ + if re.match( + r"^https://[^/]+\.sourceforge\.io(/|$)", result.uri + ) and "403 Client Error" in str(result.message): + logger.info(darkgray("-ignored- ") + result.uri) + self.write_entry( + "ignored", + result.docname, + self.env.doc2path(result.docname, False), + result.lineno, + f"{result.uri} to {result.message}", + ) + return + + """ Ignore Anchor errors from .md files """ + if re.match( + r"https?://.*?\.md#[A-Za-z0-9_.-]+$", result.uri + ) and "Anchor" in str(result.message): + logger.info(darkgray("-ignored- ") + result.uri) + self.write_entry( + "ignored", + result.docname, + self.env.doc2path(result.docname, False), + result.lineno, + f"{result.uri} to {result.message}", + ) + return + + linkcheckbase(self, result) + + +def setup(app: Sphinx) -> dict[str, object]: + """Register the custom linkcheck extension. + + The extension patches the default Sphinx linkcheck builder to ignore + known false-positive anchor errors reported for selected GitHub + Markdown URLs. + + Args: + app: The Sphinx application instance. + + Returns: + Extension metadata required by Sphinx. + """ + + # hook a custom process_result function + cast(Any, linkcheck.CheckExternalLinksBuilder).process_result = process_result + + return { + "version": "0.0.1", + "parallel_read_safe": True, + "parallel_write_safe": True, + } diff --git a/src/extensions/score_sphinx_bundle/BUILD b/src/extensions/score_sphinx_bundle/BUILD index c34d2907e..c30d0b6c9 100644 --- a/src/extensions/score_sphinx_bundle/BUILD +++ b/src/extensions/score_sphinx_bundle/BUILD @@ -25,6 +25,7 @@ py_library( visibility = ["//visibility:public"], deps = all_requirements + [ "@score_docs_as_code//src/extensions:score_plantuml", + "@score_docs_as_code//src/extensions:broken_link_fix", "@score_docs_as_code//src/extensions/score_draw_uml_funcs", "@score_docs_as_code//src/extensions/score_layout", "@score_docs_as_code//src/extensions/score_metamodel", diff --git a/src/extensions/score_sphinx_bundle/__init__.py b/src/extensions/score_sphinx_bundle/__init__.py index ded7ea095..6617ddc00 100644 --- a/src/extensions/score_sphinx_bundle/__init__.py +++ b/src/extensions/score_sphinx_bundle/__init__.py @@ -32,6 +32,7 @@ "needs_config_writer", "score_sync_toml", "score_metrics", + "broken_link_fix", ]