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

dsmiley pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/solr.git


The following commit(s) were added to refs/heads/main by this push:
     new ef732be0998 SOLR-18523: Remove the checkJavadocLinks crawl from the 
documentation build (#5062)
ef732be0998 is described below

commit ef732be0998865aab635db323ca98a6d15d5bb2c
Author: Nick <[email protected]>
AuthorDate: Fri Oct 16 18:11:15 2026 -0600

    SOLR-18523: Remove the checkJavadocLinks crawl from the documentation build 
(#5062)
    
    Mostly redundant with javadoc "DocLint", which we do.
---
 build.gradle                                |   1 -
 changelog/unreleased/SOLR-18523.yml         |   7 +
 dev-tools/scripts/checkJavadocLinks.py      | 277 ----------------------------
 gradle/documentation/documentation.gradle   |  17 ++
 gradle/validation/check-broken-links.gradle |  81 --------
 5 files changed, 24 insertions(+), 359 deletions(-)

diff --git a/build.gradle b/build.gradle
index 35961bc3e10..5b0a550e0be 100644
--- a/build.gradle
+++ b/build.gradle
@@ -178,7 +178,6 @@ apply from: 
file('gradle/validation/dependency-analyze.gradle.kts')
 apply from: file('gradle/validation/ecj-lint.gradle')
 apply from: file('gradle/validation/gradlew-scripts-tweaked.gradle')
 apply from: file('gradle/validation/validate-log-calls.gradle')
-apply from: file('gradle/validation/check-broken-links.gradle')
 
 apply from: file('gradle/validation/solr.config-file-sanity.gradle')
 apply from: file('gradle/validation/dependencies.gradle')
diff --git a/changelog/unreleased/SOLR-18523.yml 
b/changelog/unreleased/SOLR-18523.yml
new file mode 100644
index 00000000000..16a1d472154
--- /dev/null
+++ b/changelog/unreleased/SOLR-18523.yml
@@ -0,0 +1,7 @@
+title: "Remove the checkJavadocLinks crawl from the documentation build"
+type: other
+authors:
+  - name: Nick Shanin
+links:
+  - name: SOLR-18523
+    url: https://issues.apache.org/jira/browse/SOLR-18523
diff --git a/dev-tools/scripts/checkJavadocLinks.py 
b/dev-tools/scripts/checkJavadocLinks.py
deleted file mode 100644
index 56dffc8ac41..00000000000
--- a/dev-tools/scripts/checkJavadocLinks.py
+++ /dev/null
@@ -1,277 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements.  See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.
-# The ASF licenses this file to You under the Apache License, Version 2.0
-# (the "License"); you may not use this file except in compliance with
-# the License.  You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import traceback
-import os
-import sys
-import re
-from html.parser import HTMLParser
-import urllib.parse as urlparse
-
-reHyperlink = re.compile(r'<a(\s+.*?)>', re.I)
-reAtt = re.compile(r"""(?:\s+([a-z]+)\s*=\s*("[^"]*"|'[^']?'|[^'"\s]+))+""", 
re.I)
-
-# Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | 
[#x10000-#x10FFFF] /* any Unicode character, excluding the surrogate blocks, 
FFFE, and FFFF. */
-reValidChar = 
re.compile("^[^\u0000-\u0008\u000B-\u000C\u000E-\u001F\uFFFE\uFFFF]*$")
-
-# silly emacs: '
-
-class FindHyperlinks(HTMLParser):
-
-  def __init__(self, baseURL):
-    HTMLParser.__init__(self)
-    self.stack = []
-    self.anchors = set()
-    self.links = []
-    self.baseURL = baseURL
-    self.printed = False
-
-  def handle_starttag(self, tag, attrs):
-    # NOTE: I don't think 'a' should be in here. But try debugging 
-    # NumericRangeQuery.html. (Could be javadocs bug, it's a generic type...)
-    if tag not in ('link', 'meta', 'frame', 'br', 'wbr', 'hr', 'p', 'li', 
'img', 'col', 'a', 'dt', 'dd', 'input'):
-      self.stack.append(tag)
-    if tag == 'a':
-      id = None
-      name = None
-      href = None
-      for attName, attValue in attrs:
-        if attName == 'name':
-          name = attValue
-        elif attName == 'href':
-          href = attValue
-        elif attName == 'id':
-          id = attValue
-
-      if name is not None:
-        assert href is None
-        if name in self.anchors:
-          if name in ('serializedForm',
-                      'serialized_methods',
-                      'readObject(java.io.ObjectInputStream)',
-                      'writeObject(java.io.ObjectOutputStream)') \
-                      and self.baseURL.endswith('/serialized-form.html'):
-            # Seems like a bug in Javadoc generation... you can't have
-            # same anchor name more than once...
-            pass
-          else:
-            self.printFile()
-            raise RuntimeError('anchor "%s" appears more than once' % name)
-        else:
-          self.anchors.add(name)
-      elif href is not None:
-        assert name is None
-        href = href.strip()
-        self.links.append(urlparse.urljoin(self.baseURL, href))
-      elif id is None:
-        raise RuntimeError('couldn\'t find an href nor name in link in %s: 
only got these attrs: %s' % (self.baseURL, attrs))
-
-  def handle_endtag(self, tag):
-    if tag in ('link', 'meta', 'frame', 'br', 'hr', 'p', 'li', 'img', 'col', 
'a', 'dt', 'dd', 'input'):
-      return
-    
-    if len(self.stack) == 0:
-      raise RuntimeError('%s %s:%s: saw </%s> no opening <%s>' % 
(self.baseURL, self.getpos()[0], self.getpos()[1], tag, self.stack[-1]))
-
-    if self.stack[-1] == tag:
-      self.stack.pop()
-    else:
-      raise RuntimeError('%s %s:%s: saw </%s> but expected </%s>' % 
(self.baseURL, self.getpos()[0], self.getpos()[1], tag, self.stack[-1]))
-
-  def printFile(self):
-    if not self.printed:
-      print()
-      print('  ' + self.baseURL)
-      self.printed = True
-                   
-def parse(baseURL, html):
-  global failures
-  # look for broken unicode
-  if not reValidChar.match(html):
-    print(' WARNING: invalid characters detected in: %s' % baseURL)
-    failures = True
-    return [], []
-
-  parser = FindHyperlinks(baseURL)
-  try:
-    parser.feed(html)
-    parser.close()
-  except:
-    # TODO: Python's html.parser is now always lenient, which is no good for 
us: we want correct HTML in our javadocs
-    parser.printFile()
-    print('  WARNING: failed to parse %s:' % baseURL)
-    traceback.print_exc(file=sys.stdout)
-    failures = True
-    return [], []
-  
-  #print '    %d links, %d anchors' % \
-  #      (len(parser.links), len(parser.anchors))
-  return parser.links, parser.anchors
-
-failures = False
-
-def checkAll(dirName):
-  """
-  Checks *.html (recursively) under this directory.
-  """
-
-  global failures
-
-  # Find/parse all HTML files first
-  print()
-  print('Crawl/parse...')
-  allFiles = {}
-
-  if os.path.isfile(dirName):
-    root, fileName = os.path.split(dirName)
-    iter = ((root, [], [fileName]),)
-  else:
-    iter = os.walk(dirName)
-
-  for root, dirs, files in iter:
-    for f in files:
-      main, ext = os.path.splitext(f)
-      ext = ext.lower()
-
-      # maybe?:
-      # and main not in ('serialized-form'):
-      if ext in ('.htm', '.html') and \
-         not f.startswith('.#') and \
-         main not in ('deprecated-list',):
-        # Somehow even w/ java 7 generaged javadocs,
-        # deprecated-list.html can fail to escape generics types
-        fullPath = os.path.join(root, f).replace(os.path.sep,'/')
-        fullPath = 'file:%s' % urlparse.quote(fullPath)
-        # parse and unparse the URL to "normalize" it
-        fullPath = urlparse.urlunparse(urlparse.urlparse(fullPath))
-        #print '  %s' % fullPath
-        allFiles[fullPath] = parse(fullPath, open('%s/%s' % (root, f), 
encoding='UTF-8').read())
-
-  # ... then verify:
-  print()
-  print('Verify...')
-  for fullPath, (links, anchors) in allFiles.items():
-    #print fullPath
-    printed = False
-    for link in links:
-
-      origLink = link
-
-      # TODO: use urlparse?
-      idx = link.find('#')
-      if idx != -1:
-        anchor = link[idx+1:]
-        link = link[:idx]
-      else:
-        anchor = None
-
-      # remove any whitespace from the middle of the link
-      link = ''.join(link.split())
-
-      idx = link.find('?')
-      if idx != -1:
-        link = link[:idx]
-        
-      # TODO: normalize path sep for windows...
-      if link.startswith('http://') or link.startswith('https://'):
-        # don't check external links
-
-        if link.find('lucene.apache.org/java/docs/mailinglists.html') != -1:
-          # OK
-          pass
-        elif link == 'http://lucene.apache.org/core/':
-          # OK
-          pass
-        elif re.match("^https?://lucene.apache.org/core/[^/]+/index.html", 
link):
-          # OK
-          pass
-        elif link == 'http://lucene.apache.org/solr/':
-          # OK
-          pass
-        elif link == 'http://lucene.apache.org/solr/resources.html':
-          # OK
-          pass
-        elif link.find('lucene.apache.org/java/docs/discussion.html') != -1:
-          # OK
-          pass
-        elif link.find('lucene.apache.org/core/discussion.html') != -1:
-          # OK
-          pass
-        elif 
link.find('lucene.apache.org/solr/mirrors-solr-latest-redir.html') != -1:
-          # OK
-          pass
-        elif link.find('lucene.apache.org/solr/guide/') != -1:
-          # OK
-          pass
-        elif link.find('lucene.apache.org/solr/downloads.html') != -1:
-          # OK
-          pass
-        elif (link.find('svn.apache.org') != -1
-              or link.find('lucene.apache.org') != -1)\
-             and os.path.basename(fullPath) != 'Changes.html':
-          if not printed:
-            printed = True
-            print()
-            print(fullPath)
-          print('  BAD EXTERNAL LINK: %s' % link)
-      elif link.startswith('mailto:'):
-        if link.find('@lucene.apache.org') == -1 and link.find('@apache.org') 
!= -1:
-          if not printed:
-            printed = True
-            print()
-            print(fullPath)
-          print('  BROKEN MAILTO (?): %s' % link)
-      elif link.startswith('javascript:'):
-        # ok...?
-        pass
-      elif 'org/apache/solr/client/solrj/beans/Field.html' in link:
-        # see LUCENE-4011: this is a javadocs bug for constants 
-        # on annotations it seems?
-        pass
-      elif link.startswith('file:'):
-        if link not in allFiles:
-          filepath = urlparse.unquote(urlparse.urlparse(link).path)
-          if not (os.path.exists(filepath) or os.path.exists(filepath[1:])):
-            if not printed:
-              printed = True
-              print()
-              print(fullPath)
-            print('  BROKEN LINK: %s' % link)
-      elif anchor is not None and anchor not in allFiles[link][1]:
-        if not printed:
-          printed = True
-          print()
-          print(fullPath)
-        print('  BROKEN ANCHOR: %s' % origLink)
-      else:
-        if not printed:
-          printed = True
-          print()
-          print(fullPath)
-        print('  BROKEN URL SCHEME: %s' % origLink)
-    failures = failures or printed
-
-  return failures   
-
-if __name__ == '__main__':
-  if checkAll(sys.argv[1]):
-    print()
-    print('Broken javadocs links were found! Common root causes:')
-    # please feel free to add to this list
-    print('* A typo of some sort for manually created links.')
-    print('* Public methods referencing non-public classes in their 
signature.')
-    sys.exit(1)
-  sys.exit(0)
-  
diff --git a/gradle/documentation/documentation.gradle 
b/gradle/documentation/documentation.gradle
index 700116ea13d..c680fe25d54 100644
--- a/gradle/documentation/documentation.gradle
+++ b/gradle/documentation/documentation.gradle
@@ -71,6 +71,23 @@ configure(project(':solr:documentation')) {
         'markdownToHtml', 'createDocumentationIndex'
   }
 
+  // Javadoc references are validated at compile time by DocLint
+  // (gradle/java/javac.gradle). Building the full documentation is costly, so
+  // this project's check builds it only on CI, where it also exercises
+  // references that only the javadoc tool sees, such as those in overview
+  // files, and the missing-doclet checks. The gate covers this project's
+  // check path only; a root check still builds the site through the ref
+  // guide's checkSiteLinks task, when the ref guide is included
+  // (refguide.include, on by default when a .git directory exists).
+  //
+  // Overview references are checked when the render tasks run. overview.html
+  // is not a declared input of the render task, so in a reused workspace an
+  // overview-only change can leave the task up-to-date and unchecked. A
+  // fresh CI checkout runs it: the build cache is off on CI.
+  if (isCIBuild) {
+    check.dependsOn 'documentation'
+  }
+
   task copyDocumentationAssets(type: Copy) {
     includeEmptyDirs = false
     from(project.assets)
diff --git a/gradle/validation/check-broken-links.gradle 
b/gradle/validation/check-broken-links.gradle
deleted file mode 100644
index 66b426abcf7..00000000000
--- a/gradle/validation/check-broken-links.gradle
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import org.gradle.process.ExecOperations
-import javax.inject.Inject
-
-configure(rootProject) {
-  task checkBrokenLinks {
-    group 'Verification'
-    description 'Check broken links in the entire documentation'
-
-    dependsOn ':solr:documentation:checkBrokenLinks'
-  }
-}
-
-configure(project(':solr:documentation')) {
-  task checkBrokenLinks(type: CheckBrokenLinksTask, 'dependsOn': 
'documentation')
-  check.dependsOn checkBrokenLinks
-}
-
-@CacheableTask
-abstract class CheckBrokenLinksTask extends DefaultTask {
-
-  @Inject
-  abstract ExecOperations getExecOperations()
-
-  // wraps input directory location in DirectoryProperty so as to lazily 
evaluate 'docroot' property
-  // (see gradle/documentation/documentation.gradle)
-  @InputDirectory
-  @PathSensitive(PathSensitivity.RELATIVE)
-  final DirectoryProperty docsDir = project.objects.directoryProperty()
-      .fileProvider(project.providers.provider {project.docroot})
-
-  @InputFile
-  @PathSensitive(PathSensitivity.RELATIVE)
-  File script = 
project.rootProject.file("dev-tools/scripts/checkJavadocLinks.py")
-
-  @OutputFile
-  Provider<RegularFile> outputFile = project.layout.buildDirectory.file(
-      'check-broken-links-output.txt')
-
-  @TaskAction
-  def check() {
-    def result
-    outputFile.get().asFile.withOutputStream {output ->
-      result = execOperations.exec {
-        executable project.externalTool("python3")
-        ignoreExitValue = true
-        standardOutput = output
-        errorOutput = output
-        args = [
-            "-B",
-            script.absolutePath,
-            docsDir.get().getAsFile()
-        ]
-      }
-    }
-
-    if (result.getExitValue() != 0) {
-      println "Broken links check failed. First lines of the output:"
-      outputFile.get().asFile.readLines().take(20).each {line ->
-        println line
-      }
-      throw new GradleException("Broken links check failed. Command output at: 
${outputFile.get()}")
-    }
-  }
-}

Reply via email to