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

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

commit 32ed0575fac77640a241f535e079bc4527615ab0
Author: Nick <[email protected]>
AuthorDate: Tue Oct 27 10:25:04 2026 -0600

    SOLR-18119: Generate changes HTML only for releases (#5180)
    
    Changes.html is now generated by the release tooling instead of the gradle 
documentation build, so local builds no longer render it.
    
    (cherry picked from commit 8b7383e7acc1855a45de746fbc67ea14019c7ba3)
---
 build.gradle                                       |   1 -
 ...LR-18119-generate-changes-html-for-releases.yml |   8 ++
 dev-docs/how-to-contribute.adoc                    |   1 -
 dev-docs/solr-source-code.adoc                     |   2 -
 dev-tools/scripts/buildAndPushRelease.py           |   7 ++
 .../scripts}/changes-to-html/ChangesFancyStyle.css |   0
 .../changes-to-html/ChangesFixedWidthStyle.css     |   0
 .../changes-to-html/ChangesSimpleStyle.css         |   0
 .../scripts}/changes2html.py                       |  79 ++++++++++----
 dev-tools/scripts/releaseWizard.yaml               |   2 +-
 gradle/documentation/changes-to-html.gradle        | 117 ---------------------
 gradle/documentation/documentation.gradle          |   3 +-
 solr/distribution/build.gradle                     |   9 +-
 solr/distribution/gradle.lockfile                  |   2 +-
 solr/documentation/gradle.lockfile                 |   2 +-
 solr/solr-ref-guide/build.gradle                   |   2 +
 16 files changed, 82 insertions(+), 153 deletions(-)

diff --git a/build.gradle b/build.gradle
index 6bd4467abab..a402db4213a 100644
--- a/build.gradle
+++ b/build.gradle
@@ -209,7 +209,6 @@ apply from: 
file('gradle/lucene-dev/lucene-dev-repo-composite.gradle')
 
 apply from: file('gradle/documentation/pull-lucene-javadocs.gradle')
 apply from: file('gradle/documentation/documentation.gradle')
-apply from: file('gradle/documentation/changes-to-html.gradle')
 apply from: file('gradle/documentation/markdown.gradle')
 apply from: file('gradle/documentation/render-javadoc.gradle')
 
diff --git 
a/changelog/unreleased/SOLR-18119-generate-changes-html-for-releases.yml 
b/changelog/unreleased/SOLR-18119-generate-changes-html-for-releases.yml
new file mode 100644
index 00000000000..34bf2afc2a6
--- /dev/null
+++ b/changelog/unreleased/SOLR-18119-generate-changes-html-for-releases.yml
@@ -0,0 +1,8 @@
+title: >
+  Changes.html is generated by the release tooling instead of the 
documentation build, so local builds no longer render it.
+type: other
+authors:
+  - name: Nick Shanin
+links:
+  - name: SOLR-18119
+    url: https://issues.apache.org/jira/browse/SOLR-18119
diff --git a/dev-docs/how-to-contribute.adoc b/dev-docs/how-to-contribute.adoc
index 6e0ebfc9d53..8e6772dcb76 100644
--- a/dev-docs/how-to-contribute.adoc
+++ b/dev-docs/how-to-contribute.adoc
@@ -33,7 +33,6 @@ In order to make a new contribution to Solr you will use the 
fork you have creat
 1. Create a new Jira issue in the Solr project: 
https://issues.apache.org/jira/projects/SOLR/issues
 2. Create a new branch in your Solr fork to provide a PR for your contribution 
on the newly created issue. Make any necessary changes for the given 
bug/feature in that branch. You can use additional information in these 
dev-docs to build and test your code as well as ensure it passes code quality 
checks.
 3. Once you are satisfied with your changes, get your branch ready for a PR by 
running `./gradlew tidy updateLicenses check -x test`. This will format your 
source code, update licenses of any dependency version changes and run all 
pre-commit tests. Commit the changes.
-* Note: the `check` command requires `python3` to be present on your `PATH` to 
validate documentation.
 4. Open a PR of your branch against the `main` branch of the apache/solr 
repository. When you open a PR on your fork, this should be the default option.
 * The title of your PR should include the Solr Jira issue that you opened, 
i.e. `SOLR-12345: New feature`.
 * The PR description will automatically populate with a pre-set template that 
you will need to fill out.
diff --git a/dev-docs/solr-source-code.adoc b/dev-docs/solr-source-code.adoc
index fd22d4e0c7d..3301dbc7503 100644
--- a/dev-docs/solr-source-code.adoc
+++ b/dev-docs/solr-source-code.adoc
@@ -34,8 +34,6 @@ To build the documentation, type `./gradlew -p solr 
documentation`.
 
 `./gradlew check` will assemble Solr and run all validation tasks unit tests.
 
-NOTE: the `check` command requires `python3` to be present on your `PATH` to 
validate documentation.
-
 To build the final Solr artifacts run `./gradlew assemble`.
 
 Lastly, there is developer oriented documentation in `./dev-docs/README.adoc` 
that you may find useful in working with Solr.
diff --git a/dev-tools/scripts/buildAndPushRelease.py 
b/dev-tools/scripts/buildAndPushRelease.py
index 44416eed723..dc91a3d0e87 100755
--- a/dev-tools/scripts/buildAndPushRelease.py
+++ b/dev-tools/scripts/buildAndPushRelease.py
@@ -26,8 +26,10 @@ from subprocess import TimeoutExpired
 import textwrap
 import urllib.request, urllib.error, urllib.parse
 import xml.etree.ElementTree as ET
+from pathlib import Path
 
 import scriptutil
+from changes2html import write_changes_html
 
 LOG = '/tmp/release.log'
 dev_mode = False
@@ -120,6 +122,11 @@ def prepare(root, version, pause_before_sign, mf_username, 
gpg_key_id, gpg_passw
   if pause_before_sign:
     input("Tests complete! Please press ENTER to proceed to assembleRelease: ")
 
+  changes_html_dir = Path(root) / 'solr' / 'documentation' / 'build' / 'site' 
/ 'changes'
+  print('  Generate Changes.html for the release')
+  changes_html_file = write_changes_html(Path(root) / 'CHANGELOG.md', 
changes_html_dir)
+  print('  Generated %s' % changes_html_file)
+
   print('  prepare-release')
   cmd = './gradlew --no-daemon assembleRelease' \
         ' -Dversion.release=%s' % version
diff --git a/gradle/documentation/changes-to-html/ChangesFancyStyle.css 
b/dev-tools/scripts/changes-to-html/ChangesFancyStyle.css
similarity index 100%
rename from gradle/documentation/changes-to-html/ChangesFancyStyle.css
rename to dev-tools/scripts/changes-to-html/ChangesFancyStyle.css
diff --git a/gradle/documentation/changes-to-html/ChangesFixedWidthStyle.css 
b/dev-tools/scripts/changes-to-html/ChangesFixedWidthStyle.css
similarity index 100%
rename from gradle/documentation/changes-to-html/ChangesFixedWidthStyle.css
rename to dev-tools/scripts/changes-to-html/ChangesFixedWidthStyle.css
diff --git a/gradle/documentation/changes-to-html/ChangesSimpleStyle.css 
b/dev-tools/scripts/changes-to-html/ChangesSimpleStyle.css
similarity index 100%
rename from gradle/documentation/changes-to-html/ChangesSimpleStyle.css
rename to dev-tools/scripts/changes-to-html/ChangesSimpleStyle.css
diff --git a/gradle/documentation/changes-to-html/changes2html.py 
b/dev-tools/scripts/changes2html.py
similarity index 94%
rename from gradle/documentation/changes-to-html/changes2html.py
rename to dev-tools/scripts/changes2html.py
index 287c3205145..003a00d1dfc 100755
--- a/gradle/documentation/changes-to-html/changes2html.py
+++ b/dev-tools/scripts/changes2html.py
@@ -18,16 +18,26 @@
 #
 
 """
-Transforms Solr's CHANGELOG.md into Changes.html
+Transforms Solr's CHANGELOG.md into Changes.html.
 
-Input is from CHANGELOG.md, output is to STDOUT
+By default, output is written to STDOUT. Release tooling can pass --output-dir
+to write Changes.html and its stylesheets to a directory for packaging.
 """
 
-import sys
+import argparse
 import re
+import shutil
+import sys
 from pathlib import Path
 
 
+STYLESHEETS = (
+    'ChangesFancyStyle.css',
+    'ChangesSimpleStyle.css',
+    'ChangesFixedWidthStyle.css',
+)
+
+
 class ChangelogParser:
     """Parse CHANGELOG.md generated by logchange"""
 
@@ -776,36 +786,59 @@ class HTMLGenerator:
         return ''.join(html_parts)
 
 
-def main():
-    """Main entry point"""
-    if len(sys.argv) < 2:
-        # Try to read from CHANGELOG.md in current directory
-        changelog_file = Path('CHANGELOG.md')
-        if not changelog_file.exists():
-            print("Usage: changes2html.py <changelog-file>", file=sys.stderr)
-            sys.exit(1)
-    else:
-        changelog_file = Path(sys.argv[1])
-
+def render_changes_html(changelog_file):
+    """Render a changelog file and return its HTML."""
+    changelog_file = Path(changelog_file)
     if not changelog_file.exists():
-        print(f"Error: {changelog_file} not found", file=sys.stderr)
-        sys.exit(1)
+        raise FileNotFoundError(changelog_file)
 
-    # Read changelog
     with open(changelog_file, 'r', encoding='utf-8') as f:
         content = f.read()
 
-    # Parse
     parser = ChangelogParser()
     parser.parse(content)
 
-    # Generate HTML
     generator = HTMLGenerator()
-    html = generator.generate(parser.releases, parser.title, parser.preamble)
+    return generator.generate(parser.releases, parser.title, parser.preamble)
+
 
-    # Output
-    sys.stdout.reconfigure(encoding='utf-8') #UTF-8 encoding for Windows 
compatibility
-    print(html)
+def write_changes_html(changelog_file, output_dir):
+    """Write Changes.html and its stylesheets into output_dir."""
+    output_dir = Path(output_dir)
+    output_dir.mkdir(parents=True, exist_ok=True)
+
+    html = render_changes_html(changelog_file)
+    output_file = output_dir / 'Changes.html'
+    with output_file.open('w', encoding='utf-8') as f:
+        print(html, file=f)
+
+    stylesheet_dir = Path(__file__).parent / 'changes-to-html'
+    for stylesheet in STYLESHEETS:
+        shutil.copyfile(stylesheet_dir / stylesheet, output_dir / stylesheet)
+
+    return output_file
+
+
+def main():
+    """Main entry point"""
+    parser = argparse.ArgumentParser(description=__doc__)
+    parser.add_argument('changelog_file', nargs='?', default='CHANGELOG.md')
+    parser.add_argument(
+        '--output-dir',
+        type=Path,
+        help='write Changes.html and stylesheets to this directory instead of 
STDOUT',
+    )
+    args = parser.parse_args()
+
+    try:
+        if args.output_dir:
+            write_changes_html(args.changelog_file, args.output_dir)
+        else:
+            html = render_changes_html(args.changelog_file)
+            sys.stdout.reconfigure(encoding='utf-8') #UTF-8 encoding for 
Windows compatibility
+            print(html)
+    except FileNotFoundError as error:
+        parser.error(f"{error.args[0]} not found")
 
 
 if __name__ == '__main__':
diff --git a/dev-tools/scripts/releaseWizard.yaml 
b/dev-tools/scripts/releaseWizard.yaml
index 27437a82882..504f8478eb9 100644
--- a/dev-tools/scripts/releaseWizard.yaml
+++ b/dev-tools/scripts/releaseWizard.yaml
@@ -778,7 +778,7 @@ groups:
         cmd: git diff --stat changelog CHANGELOG.md
         stdout: true
       - !Command
-        cmd: "{{ gradle_cmd }} :solr:documentation:changesToHtml"
+        cmd: python3 -u dev-tools/scripts/changes2html.py CHANGELOG.md 
--output-dir solr/documentation/build/site/changes
         logfile: changes-to-html.log
         tee: true
       - !Command
diff --git a/gradle/documentation/changes-to-html.gradle 
b/gradle/documentation/changes-to-html.gradle
deleted file mode 100644
index 46635ca0dc5..00000000000
--- a/gradle/documentation/changes-to-html.gradle
+++ /dev/null
@@ -1,117 +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
-
-def resources = scriptResources(buildscript)
-
-configure(project(':solr:documentation')) {
-  task changesToHtml(type: ChangesToHtmlTask) {
-    siteDir = resources
-    script = file("${resources}/changes2html.py")
-  }
-
-  // Make the rendered HTML of changes available as a separate
-  // artifact for the distribution.
-  configurations {
-    changesHtml
-  }
-
-  artifacts {
-    changesHtml changesToHtml.targetDir, {
-      builtBy changesToHtml
-    }
-  }
-}
-
-// compile CHANGELOG.md into an html file
-abstract class ChangesToHtmlTask extends DefaultTask {
-
-  @Inject
-  abstract ExecOperations getExecOperations()
-
-  @Internal
-  Project productProject = project.parent
-
-  @Internal
-  String productName = productProject.name
-
-  @InputFile
-  File changesFile = project.rootProject.file('CHANGELOG.md')
-
-  @InputDirectory
-  File siteDir
-
-  @OutputDirectory
-  final DirectoryProperty targetDir = project.objects.directoryProperty()
-      .fileProvider(project.providers.provider 
{project.file("${project.docroot}/changes")})
-
-  @InputFile
-  def script
-
-  def toHtml() {
-    def output = new ByteArrayOutputStream()
-
-    // Check if Python is available
-    if (!pythonExists()) {
-      logger.warn("WARNING: Python is not installed, skipping creating 
Changes.html")
-      return
-    }
-
-    def result = execOperations.exec {
-      executable "python3"
-      standardOutput 
project.file("${targetDir.get().getAsFile()}/Changes.html").newOutputStream()
-      errorOutput = output
-      ignoreExitValue = true
-
-      args += [
-          script.toString(),
-          changesFile.toString()
-      ]
-    }
-
-    if (result.getExitValue() != 0) {
-      throw new GradleException("Changes generation failed:\n${output}")
-    }
-  }
-
-  @TaskAction
-  def convert() {
-    project.mkdir targetDir
-    if (changesFile.exists()) {
-      toHtml()
-      project.copy {
-        from siteDir
-        into targetDir
-        include "*.css"
-      }
-    } else {
-      throw new GradleException("Changes file ${changesFile} not found.")
-    }
-  }
-
-  def pythonExists() {
-    try {
-      def process = "python3 --version".execute()
-      process.waitFor()
-      return process.exitValue() == 0
-    } catch (Exception e) {
-      return false
-    }
-  }
-}
diff --git a/gradle/documentation/documentation.gradle 
b/gradle/documentation/documentation.gradle
index c680fe25d54..29a585b9025 100644
--- a/gradle/documentation/documentation.gradle
+++ b/gradle/documentation/documentation.gradle
@@ -67,8 +67,7 @@ configure(project(':solr:documentation')) {
     dependsOn project.parent.subprojects.collect {prj ->
       prj.tasks.matching {it.name == 'renderSiteJavadoc'}
     }
-    dependsOn 'changesToHtml', 'copyDocumentationAssets',
-        'markdownToHtml', 'createDocumentationIndex'
+    dependsOn 'copyDocumentationAssets', 'markdownToHtml', 
'createDocumentationIndex'
   }
 
   // Javadoc references are validated at compile time by DocLint
diff --git a/solr/distribution/build.gradle b/solr/distribution/build.gradle
index b3e2e6e0bee..8de83fe54c4 100644
--- a/solr/distribution/build.gradle
+++ b/solr/distribution/build.gradle
@@ -48,15 +48,16 @@ if (project.ext.useGpgForSigning) {
 // Prepare the "source" distribution artifact.
 apply from: 
buildscript.sourceFile.toPath().resolveSibling("source-release.gradle")
 
-// Set up the HTML-rendered "changes" distribution artifact by linking to 
documentation's output.
+// The release tooling creates this Python-rendered output after `clean check`.
+def documentationProject = project(':solr:documentation')
+def releaseChangesHtmlDir = new File(documentationProject.buildDir, 
'site/changes')
+
 configurations {
-  changesHtml
   openApiSpecFile
   docker
 }
 
 dependencies {
-  changesHtml project(path: ":solr:documentation", configuration: 
"changesHtml")
   openApiSpecFile project(path: ":solr:api", configuration: "openapiSpec")
   docker project(path: ':solr:docker', configuration: 'packagingOfficial')
 }
@@ -128,7 +129,7 @@ task assembleRelease(type: Sync) {
   description "Assemble all Solr artifacts for a release."
   dependsOn ":mavenToLocalFolder"
 
-  from(configurations.changesHtml, {
+  from(releaseChangesHtmlDir, {
     into "changes"
   })
 
diff --git a/solr/distribution/gradle.lockfile 
b/solr/distribution/gradle.lockfile
index 61a77fa5e03..08fd62ba52a 100644
--- a/solr/distribution/gradle.lockfile
+++ b/solr/distribution/gradle.lockfile
@@ -2,4 +2,4 @@
 # Manual edits can break the build and are not advised.
 # This file is expected to be part of source control.
 # To regenerate this file, run: ./gradlew :solr:distribution:dependencies 
--write-locks
-empty=changesHtml,docker,jarValidation,openApiSpecFile
+empty=docker,jarValidation,openApiSpecFile
diff --git a/solr/documentation/gradle.lockfile 
b/solr/documentation/gradle.lockfile
index 141af17f307..0282140511f 100644
--- a/solr/documentation/gradle.lockfile
+++ b/solr/documentation/gradle.lockfile
@@ -9,4 +9,4 @@ org.apache.lucene:lucene-expressions:10.4.0=javadocs
 org.apache.lucene:lucene-queries:10.4.0=javadocs
 org.apache.lucene:lucene-queryparser:10.4.0=javadocs
 org.apache.lucene:lucene-spatial-extras:10.4.0=javadocs
-empty=changesHtml,jarValidation,minimalSite,site
+empty=jarValidation,minimalSite,site
diff --git a/solr/solr-ref-guide/build.gradle b/solr/solr-ref-guide/build.gradle
index c84ce7c4b10..3e69411a5d5 100644
--- a/solr/solr-ref-guide/build.gradle
+++ b/solr/solr-ref-guide/build.gradle
@@ -487,6 +487,8 @@ task checkSiteLinks(type: NpxTask) {
       "--debug",
       // Ignore the java docs url in the header, since it is an absolute path
       "--url-ignore", 
"${project(':solr:documentation').docroot.toPath()}/index\\.html",
+      // Changes.html is generated by release tooling; relative links to it 
are expected to be missing locally.
+      "--url-ignore", "changes/+Changes\\.html",
       "--json",
       "-v",
   ]

Reply via email to