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

vladimirsitnikov pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/jmeter.git


The following commit(s) were added to refs/heads/master by this push:
     new df944af1e6 chore: deliver fonts from Apache website rather than 3rd 
party CDN
df944af1e6 is described below

commit df944af1e6dbee02b3c846844f73157619bead65
Author: Vladimir Sitnikov <[email protected]>
AuthorDate: Sat Nov 1 19:22:26 2025 +0300

    chore: deliver fonts from Apache website rather than 3rd party CDN
    
    Fixes https://github.com/apache/jmeter/issues/6552
---
 .gitignore                                         |  3 +
 .ratignore                                         |  4 ++
 .../main/kotlin/build-logic.autostyle.gradle.kts   |  8 ++-
 gradle/verification-metadata.xml                   | 36 +++++++++++
 settings.gradle.kts                                | 15 +++++
 .../jmeter/assertions/XMLSchemaAssertionTest.java  |  7 +-
 .../jmeter/assertions/XPathAssertionTest.java      |  3 +-
 .../apache/jmeter/assertions/XmlAssertionTest.java |  7 +-
 .../jmeter/extractor/TestXPathExtractor.java       | 22 +++----
 .../gui/action/template/TestTemplateManager.java   |  4 +-
 .../jmeter/assertions/AssertionResultExtensions.kt | 34 ++++++++++
 src/dist/build.gradle.kts                          | 75 ++++++++++++++++++++--
 xdocs/package-lock.json                            | 34 ++++++++++
 xdocs/package.json                                 | 10 +++
 xdocs/stylesheets/website-style.xsl                | 16 +++--
 xdocs/yarn.lock                                    | 13 ++++
 16 files changed, 257 insertions(+), 34 deletions(-)

diff --git a/.gitignore b/.gitignore
index 2400313e97..13f89d12a0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -86,3 +86,6 @@ jmeter-fb.*
 hs_err_pid*
 # other jvm related files
 .attach_*
+
+# npm/node dependencies and generated web assets
+/xdocs/node_modules/
diff --git a/.ratignore b/.ratignore
index 904b2802f2..8a99b77381 100644
--- a/.ratignore
+++ b/.ratignore
@@ -36,6 +36,10 @@ test/resources/**
 #**/*.log
 **/download_jmeter.cgi
 xdocs/extending/HTML_REPORT_README.TXT
+xdocs/node_modules/**
+xdocs/package.json
+xdocs/package-lock.json
+xdocs/yarn.lock
 # Rat should automatically detect below files as binary, however it fails
 **/*.sxi
 **/*.sxw
diff --git 
a/build-logic/verification/src/main/kotlin/build-logic.autostyle.gradle.kts 
b/build-logic/verification/src/main/kotlin/build-logic.autostyle.gradle.kts
index eb874732ff..ffcf7b8888 100644
--- a/build-logic/verification/src/main/kotlin/build-logic.autostyle.gradle.kts
+++ b/build-logic/verification/src/main/kotlin/build-logic.autostyle.gradle.kts
@@ -50,6 +50,8 @@ autostyle {
             include("**/*.xsd", "**/*.xsl", "**/*.xml")
             // Autostyle does not support gitignore yet 
https://github.com/autostyle/autostyle/issues/13
             exclude("out/**")
+            exclude(".gradle/**")
+            exclude("node_modules")
             if (project == rootProject) {
                 if (isActualPluginApplication) {
                     exclude(rootDir.resolve(".ratignore").readLines())
@@ -65,7 +67,11 @@ autostyle {
         license()
     }
     format("markdown") {
-        filter.include("**/*.md")
+        filter {
+            include("**/*.md")
+            exclude(".gradle/**")
+            exclude("node_modules")
+        }
         endWithNewline()
     }
 }
diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml
index b8880e8cd5..d46bb6a739 100644
--- a/gradle/verification-metadata.xml
+++ b/gradle/verification-metadata.xml
@@ -373,6 +373,19 @@
             <pgp value="8756C4F765C9AC3CB6B85D62379CE192D401AB61"/>
          </artifact>
       </component>
+      <component group="com.github.node-gradle" name="gradle-node-plugin" 
version="7.1.0">
+         <artifact name="gradle-node-plugin-7.1.0.jar">
+            <sha256 
value="9aa26c3b075f42eb801fc94cb32fc59c6968a78119efda9ce02356834a2d3198" 
origin="Generated by Gradle" reason="Artifact is not signed"/>
+         </artifact>
+         <artifact name="gradle-node-plugin-7.1.0.module">
+            <sha256 
value="6cf277b82986e413210de0e3e4fe9c109c7bdaa5169d48cfed526d06a1c3d0b0" 
origin="Generated by Gradle" reason="Artifact is not signed"/>
+         </artifact>
+      </component>
+      <component group="com.github.node-gradle.node" 
name="com.github.node-gradle.node.gradle.plugin" version="7.1.0">
+         <artifact name="com.github.node-gradle.node.gradle.plugin-7.1.0.pom">
+            <sha256 
value="dc0e8e782ba0dac5c4d0d8678081c11380a5c793b3c46f2d0f8d242d1733b62b" 
origin="Generated by Gradle" reason="Artifact is not signed"/>
+         </artifact>
+      </component>
       <component group="com.github.spotbugs" 
name="com.github.spotbugs.gradle.plugin" version="6.0.4">
          <artifact name="com.github.spotbugs.gradle.plugin-6.0.4.pom">
             <sha256 
value="f029abeb54aa13d5631ece27f58f2e20039cb28b9ce3b1b314947af5b06d8a65" 
origin="Generated by Gradle" reason="Artifact is not signed"/>
@@ -822,6 +835,29 @@
             <sha256 
value="d8531a746c988f7f68ed5f188cdea945006aea993ec5df9e524e0d27d61491da" 
origin="Generated by Gradle"/>
          </artifact>
       </component>
+      <component group="org.nodejs" name="node" version="25.1.0">
+         <artifact name="node-25.1.0-darwin-arm64.tar.gz">
+            <sha256 
value="811dcf54580d9eef0abfed9f65457545d2aac8e24853b6e638194fcba0257168" 
origin="https://nodejs.org/dist/v25.1.0/SHASUMS256.txt"; reason="Artifact is not 
signed"/>
+         </artifact>
+         <artifact name="node-25.1.0-darwin-x64.tar.gz">
+            <sha256 
value="5c2d45214e70327e683df301eeb11d8fc9265ab22ad41ff6335e4a7befc07169" 
origin="https://nodejs.org/dist/v25.1.0/SHASUMS256.txt"; reason="Artifact is not 
signed"/>
+         </artifact>
+         <artifact name="node-25.1.0-linux-arm64.tar.gz">
+            <sha256 
value="9cc83fc5dfdc36692aabec37ab1c6d070d11c44f02764b5e50777491552e8449" 
origin="https://nodejs.org/dist/v25.1.0/SHASUMS256.txt"; reason="Artifact is not 
signed"/>
+         </artifact>
+         <artifact name="node-25.1.0-linux-s390x.tar.gz">
+            <sha256 
value="bdd93b9ce06a6e6bcdc0af9d838b9861cbcb4271481f5c5bd4e965601bc5b155" 
origin="https://nodejs.org/dist/v25.1.0/SHASUMS256.txt"; reason="Artifact is not 
signed"/>
+         </artifact>
+         <artifact name="node-25.1.0-linux-x64.tar.gz">
+            <sha256 
value="0b8de924cd43546a2098c091b73002442cd61340622b2c1489df408755de21fc" 
origin="https://nodejs.org/dist/v25.1.0/SHASUMS256.txt"; reason="Artifact is not 
signed"/>
+         </artifact>
+         <artifact name="node-25.1.0-win-arm64.zip">
+            <sha256 
value="8063f6caa9f865cd1ddc30b7bccb6ddb457b7cac0d61e30e74ff85b97b32673b" 
origin="https://nodejs.org/dist/v25.1.0/SHASUMS256.txt"; reason="Artifact is not 
signed"/>
+         </artifact>
+         <artifact name="node-25.1.0-win-x64.zip">
+            <sha256 
value="abab8d2fff33dad361e51b51dfadd1f8aaef8677f88d21e3cccaf7b6e6f46339" 
origin="https://nodejs.org/dist/v25.1.0/SHASUMS256.txt"; reason="Artifact is not 
signed"/>
+         </artifact>
+      </component>
       <component group="org.nosphere.apache" name="creadur-rat-gradle" 
version="0.8.1">
          <artifact name="creadur-rat-gradle-0.8.1.jar">
             <sha256 
value="c1a58cbbe3d0a5e207cd90e79a9e22c9eb01d9e77f7313f4f5aa7d9e478a0550" 
origin="Generated by Gradle" reason="Artifact is not signed"/>
diff --git a/settings.gradle.kts b/settings.gradle.kts
index d129bee88d..97082d1e4d 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -18,6 +18,7 @@
 pluginManagement {
     plugins {
         id("com.github.vlsi.stage-vote-release") version "2.0.0"
+        id("com.github.node-gradle.node") version "7.1.0"
     }
 }
 
@@ -32,6 +33,20 @@ dependencyResolutionManagement {
     repositories {
         // TODO: support enableMavenLocal
         mavenCentral()
+        // Declare the Node.js download repository
+        ivy {
+            name = "Node.js"
+            setUrl("https://nodejs.org/dist/";)
+            patternLayout {
+                
artifact("v[revision]/[artifact](-v[revision]-[classifier]).[ext]")
+            }
+            metadataSources {
+                artifact()
+            }
+            content {
+                includeModule("org.nodejs", "node")
+            }
+        }
     }
 }
 
diff --git 
a/src/components/src/test/java/org/apache/jmeter/assertions/XMLSchemaAssertionTest.java
 
b/src/components/src/test/java/org/apache/jmeter/assertions/XMLSchemaAssertionTest.java
index a3e6500d4f..4e290e833b 100644
--- 
a/src/components/src/test/java/org/apache/jmeter/assertions/XMLSchemaAssertionTest.java
+++ 
b/src/components/src/test/java/org/apache/jmeter/assertions/XMLSchemaAssertionTest.java
@@ -17,6 +17,7 @@
 
 package org.apache.jmeter.assertions;
 
+import static 
org.apache.jmeter.assertions.AssertionResultExtensionsKt.assertEnFailureMessageContains;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -99,7 +100,7 @@ public class XMLSchemaAssertionTest extends JMeterTestCase {
         AssertionResult res = assertion.getResult(jmctx.getPreviousResult());
         testLog.debug("isError() " + res.isError() + " isFailure() " + 
res.isFailure());
         testLog.debug("failure " + res.getFailureMessage());
-        assertTrue(res.getFailureMessage().indexOf("Failed to read schema 
document") > 0);
+        assertEnFailureMessageContains(res, "Failed to read schema document");
         assertTrue(res.isError());// TODO - should this be a failure?
         assertFalse(res.isFailure());
     }
@@ -147,7 +148,7 @@ public class XMLSchemaAssertionTest extends JMeterTestCase {
         AssertionResult res = assertion.getResult(jmctx.getPreviousResult());
         testLog.debug("isError() " + res.isError() + " isFailure() " + 
res.isFailure());
         testLog.debug("failure " + res.getFailureMessage());
-        assertTrue(res.getFailureMessage().indexOf("Premature end of file") > 
0);
+        assertEnFailureMessageContains(res, "Premature end of file");
         assertTrue(res.isError());
         assertFalse(res.isFailure());
     }
@@ -162,7 +163,7 @@ public class XMLSchemaAssertionTest extends JMeterTestCase {
         AssertionResult res = assertion.getResult(jmctx.getPreviousResult());
         testLog.debug("isError() " + res.isError() + " isFailure() " + 
res.isFailure());
         testLog.debug("failure " + res.getFailureMessage());
-        assertTrue(res.getFailureMessage().indexOf("Content is not allowed in 
trailing section") > 0);
+        assertEnFailureMessageContains(res, "Content is not allowed in 
trailing section");
         assertTrue(res.isError());
         assertFalse(res.isFailure());
     }
diff --git 
a/src/components/src/test/java/org/apache/jmeter/assertions/XPathAssertionTest.java
 
b/src/components/src/test/java/org/apache/jmeter/assertions/XPathAssertionTest.java
index a33e4ba8f4..f33e78fc16 100644
--- 
a/src/components/src/test/java/org/apache/jmeter/assertions/XPathAssertionTest.java
+++ 
b/src/components/src/test/java/org/apache/jmeter/assertions/XPathAssertionTest.java
@@ -17,6 +17,7 @@
 
 package org.apache.jmeter.assertions;
 
+import static 
org.apache.jmeter.assertions.AssertionResultExtensionsKt.assertEnFailureMessageContains;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -271,7 +272,7 @@ public class XPathAssertionTest extends JMeterTestCase {
         AssertionResult res = assertion.getResult(result);
         testLog.debug("isError() {} isFailure() {}", res.isError(), 
res.isFailure());
         testLog.debug("failure message: {}", res.getFailureMessage());
-        assertTrue(res.getFailureMessage().indexOf("Premature end of file") > 
0);
+        assertEnFailureMessageContains(res, "Premature end of file");
         assertTrue(res.isError(), "Should be an error");
         assertFalse(res.isFailure(), "Should not be a failure");
     }
diff --git 
a/src/components/src/test/java/org/apache/jmeter/assertions/XmlAssertionTest.java
 
b/src/components/src/test/java/org/apache/jmeter/assertions/XmlAssertionTest.java
index bc35a1c505..9b83912643 100644
--- 
a/src/components/src/test/java/org/apache/jmeter/assertions/XmlAssertionTest.java
+++ 
b/src/components/src/test/java/org/apache/jmeter/assertions/XmlAssertionTest.java
@@ -17,6 +17,8 @@
 
 package org.apache.jmeter.assertions;
 
+import static 
org.apache.jmeter.assertions.AssertionResultExtensionsKt.assertEnFailureMessageContains;
+import static 
org.apache.jmeter.assertions.AssertionResultExtensionsKt.assertFailureMessageContains;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -62,8 +64,7 @@ public class XmlAssertionTest extends JMeterTestCase {
         result = assertion.getResult(sampleResult);
         assertTrue(result.isFailure());
         assertTrue(result.isError());
-        assertEquals("DOCTYPE is disallowed when the feature 
\"http://apache.org/xml/features/disallow-doctype-decl\"; set to true.",
-                    result.getFailureMessage());
+        assertFailureMessageContains(result, 
"http://apache.org/xml/features/disallow-doctype-decl";);
     }
 
     @Test
@@ -91,6 +92,6 @@ public class XmlAssertionTest extends JMeterTestCase {
         assertTrue(result.isFailure());
         assertTrue(result.isError());
         assertNotNull(result.getFailureMessage());
-        assertTrue(result.getFailureMessage().contains("Content is not allowed 
in prolog"));
+        assertEnFailureMessageContains(result, "Content is not allowed in 
prolog");
     }
 }
diff --git 
a/src/components/src/test/java/org/apache/jmeter/extractor/TestXPathExtractor.java
 
b/src/components/src/test/java/org/apache/jmeter/extractor/TestXPathExtractor.java
index 15ebb4ed25..f612232682 100644
--- 
a/src/components/src/test/java/org/apache/jmeter/extractor/TestXPathExtractor.java
+++ 
b/src/components/src/test/java/org/apache/jmeter/extractor/TestXPathExtractor.java
@@ -17,6 +17,7 @@
 
 package org.apache.jmeter.extractor;
 
+import static 
org.apache.jmeter.assertions.AssertionResultExtensionsKt.assertEnFailureMessageContains;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -276,13 +277,7 @@ public class TestXPathExtractor {
                 firstResult.getFailureMessage(),
                 "<",
                 "'<' is an invalid character in xpath, so it is expected to be 
present in the error message");
-        if 
(Locale.getDefault().getLanguage().startsWith(Locale.ENGLISH.getLanguage())) {
-            assertContains(
-                    firstResult.getFailureMessage(),
-                    "A location path was expected, but the following token was 
encountered",
-                    ""
-            );
-        }
+        assertEnFailureMessageContains(firstResult, "A location path was 
expected, but the following token was encountered");
         assertEquals("Default", vars.get(VAL_NAME));
         assertEquals("0", vars.get(VAL_NAME_NR));
     }
@@ -294,9 +289,9 @@ public class TestXPathExtractor {
         extractor.process();
         assertEquals(1, result.getAssertionResults().length);
         assertEquals(extractor.getName(), 
result.getAssertionResults()[0].getName());
-        assertContains(result.getAssertionResults()[0].getFailureMessage(),
-                "Content is not allowed in prolog",
-                "");
+        assertEnFailureMessageContains(
+            result.getAssertionResults()[0],
+            "Content is not allowed in prolog");
         assertEquals("Default", vars.get(VAL_NAME));
         assertEquals("0", vars.get(VAL_NAME_NR));
     }
@@ -309,10 +304,9 @@ public class TestXPathExtractor {
 
         assertEquals(1, result.getAssertionResults().length);
         assertEquals(extractor.getName(), 
result.getAssertionResults()[0].getName());
-        assertContains(result.getAssertionResults()[0].getFailureMessage(),
-                "XML document structures must start and end within the same 
entity",
-                "");
-
+        assertEnFailureMessageContains(
+                result.getAssertionResults()[0],
+                "XML document structures must start and end within the same 
entity");
         assertEquals("Default", vars.get(VAL_NAME));
         assertEquals("0", vars.get(VAL_NAME_NR));
     }
diff --git 
a/src/components/src/test/java/org/apache/jmeter/gui/action/template/TestTemplateManager.java
 
b/src/components/src/test/java/org/apache/jmeter/gui/action/template/TestTemplateManager.java
index 92de5766f9..cefbe7a2bc 100644
--- 
a/src/components/src/test/java/org/apache/jmeter/gui/action/template/TestTemplateManager.java
+++ 
b/src/components/src/test/java/org/apache/jmeter/gui/action/template/TestTemplateManager.java
@@ -112,7 +112,9 @@ public class TestTemplateManager extends JMeterTestCase {
             TemplateManager.getInstance().parseTemplateFile(templateFile);
         } catch (SAXParseException ex) {
             String message = ex.getMessage();
-            assertTrue(message.contains("Element type \"key\" must be 
declared."), "Exception did not contains expected message, got:" + message);
+            assertTrue(
+                    message.contains("\"key\""),
+                    "Exception did not contains expected message (e.g. Element 
type \"key\" must be declared.), got:" + message);
         }
     }
 
diff --git 
a/src/components/src/test/kotlin/org/apache/jmeter/assertions/AssertionResultExtensions.kt
 
b/src/components/src/test/kotlin/org/apache/jmeter/assertions/AssertionResultExtensions.kt
new file mode 100644
index 0000000000..6769ff6509
--- /dev/null
+++ 
b/src/components/src/test/kotlin/org/apache/jmeter/assertions/AssertionResultExtensions.kt
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+package org.apache.jmeter.assertions
+
+import org.junit.jupiter.api.Assertions.assertTrue
+import java.util.Locale
+
+fun AssertionResult.assertEnFailureMessageContains(substring: String) {
+    if (Locale.getDefault().language.startsWith(Locale.ENGLISH.language)) {
+        assertFailureMessageContains(substring)
+    }
+}
+
+fun AssertionResult.assertFailureMessageContains(substring: String) {
+    val failureMessage = getFailureMessage()
+    assertTrue(failureMessage.contains(substring)) {
+        "expected failure message to contain <<$substring>> but got 
<<$failureMessage>>"
+    }
+}
diff --git a/src/dist/build.gradle.kts b/src/dist/build.gradle.kts
index 20e5bc8f61..44bfe693e4 100644
--- a/src/dist/build.gradle.kts
+++ b/src/dist/build.gradle.kts
@@ -15,6 +15,8 @@
  * limitations under the License.
  */
 
+import com.github.autostyle.gradle.AutostyleTask
+import com.github.gradle.node.yarn.task.YarnTask
 import com.github.vlsi.gradle.crlf.CrLfSpec
 import com.github.vlsi.gradle.crlf.LineEndings
 import com.github.vlsi.gradle.git.FindGitAttributes
@@ -28,6 +30,7 @@ plugins {
     id("com.github.vlsi.crlf")
     id("com.github.vlsi.stage-vote-release")
     id("build-logic.jvm-library")
+    id("com.github.node-gradle.node")
 }
 
 var jars = arrayOf(
@@ -98,6 +101,16 @@ dependencies {
     buildDocs("org.jdom:jdom")
 }
 
+node {
+    // Do not declare the repository
+    // See 
https://github.com/node-gradle/gradle-node-plugin/blob/master/docs/faq.md#is-this-plugin-compatible-with-centralized-repositories-declaration
+    distBaseUrl = null
+    download = true
+    version = "25.1.0"
+    yarnVersion = "1.22.22"
+    // nodeProjectDir = rootProject.layout.projectDirectory.dir("xdocs")
+}
+
 tasks.clean {
     // copyLibs uses Sync task, so it can't predict all the possible output 
files (e.g. from previous executions)
     // So we register patterns to remove explicitly
@@ -311,7 +324,7 @@ val gitProps by 
rootProject.tasks.existing(FindGitAttributes::class)
 
 fun createAnakiaTask(
     taskName: String,
-    baseDir: String,
+    baseDir: Directory,
     extension: String = ".html",
     style: String,
     velocityProperties: String,
@@ -336,7 +349,7 @@ fun createAnakiaTask(
                 p.load(it)
             }
             p["resource.loader"] = "file"
-            p["file.resource.loader.path"] = baseDir
+            p["file.resource.loader.path"] = baseDir.asFile.absolutePath
             p["file.resource.loader.class"] = 
"org.apache.velocity.runtime.resource.loader.FileResourceLoader"
             val specials = Regex("""([,\\])""")
             val lines = p.entries
@@ -392,7 +405,7 @@ fun createAnakiaTask(
     }
 }
 
-val xdocs = "$rootDir/xdocs"
+val xdocs = rootProject.layout.projectDirectory.dir("xdocs")
 
 fun CopySpec.docCssAndImages() {
     from(xdocs) {
@@ -420,12 +433,24 @@ fun CopySpec.printableDocumentation() {
     }
 }
 
+tasks.yarnSetup {
+    mustRunAfter(tasks.withType<AutostyleTask>())
+}
+
+val yarn_install = tasks.named<YarnTask>("yarn_install") {
+    workingDir = xdocs
+    mustRunAfter(":rat")
+    
inputs.file(xdocs.file("package.json")).withPropertyName("package_json").withPathSensitivity(PathSensitivity.NONE)
+    outputs.file(xdocs.file("yarn.lock")).withPropertyName("yarn.lock")
+    outputs.dir(xdocs.dir("node_modules")).withPropertyName("node_modules")
+}
+
 val buildPrintableDoc = createAnakiaTask(
     "buildPrintableDoc", baseDir = xdocs,
     style = "stylesheets/site_printable.vsl",
     velocityProperties = "$xdocs/velocity.properties",
     projectFile = "stylesheets/printable_project.xml",
-    excludes = arrayOf("**/stylesheets/**", "extending.xml", 
"extending/*.xml"),
+    excludes = arrayOf("**/stylesheets/**", "extending.xml", 
"extending/*.xml", "node_modules", "package.json", "package-lock.json", 
"yarn.lock"),
     includes = arrayOf("**/*.xml")
 )
 
@@ -466,7 +491,14 @@ fun xslt(
 
 val processSiteXslt by tasks.registering {
     val outputDir = layout.buildDirectory.dir("siteXslt").get().asFile
-    
inputs.files(xdocs).withPathSensitivity(PathSensitivity.RELATIVE).withPropertyName("xdocs")
+    inputs.files(
+        fileTree(xdocs) {
+            exclude("node_modules")
+            exclude("package.json")
+            exclude("package-lock.json")
+            exclude("yarn.lock")
+        }
+    ).withPathSensitivity(PathSensitivity.RELATIVE).withPropertyName("xdocs")
     inputs.property("year", lastEditYear)
     outputs.dir(outputDir)
     outputs.cacheIf { true }
@@ -490,10 +522,39 @@ fun CopySpec.siteLayout() {
     from(processSiteXslt)
     docCssAndImages()
     manuals()
+    into("fonts") {
+        from(
+            fileTree(xdocs.dir("node_modules/@fontsource/merriweather/files")) 
{
+                builtBy(yarn_install)
+            }
+        ) {
+            include("*400*normal*.woff2")
+        }
+        from(xdocs.dir("node_modules/@fortawesome/fontawesome-free/webfonts")) 
{
+            include("fa-brands*.woff2")
+        }
+    }
+    into("css") {
+        from(xdocs.dir("node_modules/@fontsource/merriweather")) {
+            include("400.css")
+            rename { "merriweather.css" }
+            filter {
+                it.replace("./files", "../fonts")
+            }
+        }
+        from(xdocs.dir("node_modules/@fortawesome/fontawesome-free/css")) {
+            include("fontawesome.min.css")
+            include("brands.min.css")
+            filter {
+                it.replace("../webfonts", "../fonts")
+            }
+        }
+    }
 }
 
 // See https://github.com/gradle/gradle/issues/10960
 val previewSiteDir = layout.buildDirectory.dir("site")
+
 val previewSite by tasks.registering(Sync::class) {
     group = JavaBasePlugin.DOCUMENTATION_GROUP
     description = "Creates preview of a site to build/docs/site"
@@ -566,6 +627,10 @@ fun CrLfSpec.sourceLayout() = copySpec {
         from(rootDir) {
             gitignore(gitProps)
             excludeLicenseFromSourceRelease()
+            exclude("xdocs/node_modules")
+            exclude("xdocs/package.json")
+            exclude("xdocs/package-lock.json")
+            exclude("xdocs/yarn.lock")
         }
     }
 }
diff --git a/xdocs/package-lock.json b/xdocs/package-lock.json
new file mode 100644
index 0000000000..4c1ad3b2bd
--- /dev/null
+++ b/xdocs/package-lock.json
@@ -0,0 +1,34 @@
+{
+  "name": "jmeter-docs",
+  "version": "1.0.0",
+  "lockfileVersion": 3,
+  "requires": true,
+  "packages": {
+    "": {
+      "name": "jmeter-docs",
+      "version": "1.0.0",
+      "dependencies": {
+        "@fontsource/merriweather": "^5.1.0",
+        "@fortawesome/fontawesome-free": "^6.5.0"
+      }
+    },
+    "node_modules/@fontsource/merriweather": {
+      "version": "5.2.11",
+      "resolved": 
"https://registry.npmjs.org/@fontsource/merriweather/-/merriweather-5.2.11.tgz";,
+      "integrity": 
"sha512-ZiIMeUh5iT8d73o6xlSF8GKgjV5pgiFrufYc5jZTVAfExtWKqM2vQHnsqXSFMv4ELhAcjt6Vf+5T3oVGXhAizQ==",
+      "license": "OFL-1.1",
+      "funding": {
+        "url": "https://github.com/sponsors/ayuhito";
+      }
+    },
+    "node_modules/@fortawesome/fontawesome-free": {
+      "version": "6.7.2",
+      "resolved": 
"https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.7.2.tgz";,
+      "integrity": 
"sha512-JUOtgFW6k9u4Y+xeIaEiLr3+cjoUPiAuLXoyKOJSia6Duzb7pq+A76P9ZdPDoAoxHdHzq6gE9/jKBGXlZT8FbA==",
+      "license": "(CC-BY-4.0 AND OFL-1.1 AND MIT)",
+      "engines": {
+        "node": ">=6"
+      }
+    }
+  }
+}
diff --git a/xdocs/package.json b/xdocs/package.json
new file mode 100644
index 0000000000..122ec167cc
--- /dev/null
+++ b/xdocs/package.json
@@ -0,0 +1,10 @@
+{
+  "name": "jmeter-docs",
+  "version": "1.0.0",
+  "description": "Web fonts for Apache JMeter documentation",
+  "private": true,
+  "dependencies": {
+    "@fontsource/merriweather": "^5.1.0",
+    "@fortawesome/fontawesome-free": "^6.5.0"
+  }
+}
diff --git a/xdocs/stylesheets/website-style.xsl 
b/xdocs/stylesheets/website-style.xsl
index 9b8dbddae9..b94178d312 100644
--- a/xdocs/stylesheets/website-style.xsl
+++ b/xdocs/stylesheets/website-style.xsl
@@ -67,11 +67,15 @@
         <!-- VIEWPORT -->
         <meta name="viewport" content="width=device-width, initial-scale=1" />
         <link
-          href='https://fonts.googleapis.com/css?family=Merriweather:400normal'
+          href="{concat($cssdir, '/merriweather.css')}"
           rel='stylesheet' type='text/css'
         ></link>
         <link
-          
href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.1/css/font-awesome.min.css";
+          href="{concat($cssdir, '/fontawesome.min.css')}"
+          rel="stylesheet" type='text/css'
+        ></link>
+        <link
+          href="{concat($cssdir, '/brands.min.css')}"
           rel="stylesheet" type='text/css'
         ></link>
         <link rel="stylesheet" type="text/css"
@@ -179,8 +183,8 @@
   <xsl:template name="social-media-links">
     <div class="social-media">
       <ul class="social-media-links">
-        <li class="twitter"><a href="https://twitter.com/ApacheJMeter"; 
title="Follow us on Twitter"><i class="fa fa-twitter" 
aria-hidden="true"></i>Twitter</a></li>
-        <li class="github"><a href="https://github.com/apache/jmeter"; 
title="Fork us on github"><i class="fa fa-github" 
aria-hidden="true"></i>github</a></li>
+        <li class="twitter"><a href="https://twitter.com/ApacheJMeter"; 
title="Follow us on Twitter"><i class="fa-brands fa-twitter" 
aria-hidden="true"></i>Twitter</a></li>
+        <li class="github"><a href="https://github.com/apache/jmeter"; 
title="Fork us on github"><i class="fa-brands fa-github" 
aria-hidden="true"></i>github</a></li>
       </ul>
     </div>
   </xsl:template>
@@ -189,8 +193,8 @@
     <div class="share-links">
       Share this page:
       <ul>
-        <li class="fb"><a 
data-social-url="https://facebook.com/sharer/sharer.php?u="; title="Share on 
facebook"><i class="fa fa-facebook" aria-hidden="true"></i>share</a></li>
-        <li class="twitter"><a 
data-social-url="https://twitter.com/intent/tweet?url="; title="Tweet on 
twitter"><i class="fa fa-twitter" aria-hidden="true"></i>tweet</a></li>
+        <li class="fb"><a 
data-social-url="https://facebook.com/sharer/sharer.php?u="; title="Share on 
facebook"><i class="fa-brands fa-facebook" aria-hidden="true"></i>share</a></li>
+        <li class="twitter"><a 
data-social-url="https://twitter.com/intent/tweet?url="; title="Tweet on 
twitter"><i class="fa-brands fa-twitter" aria-hidden="true"></i>tweet</a></li>
       </ul>
     </div>
   </xsl:template>
diff --git a/xdocs/yarn.lock b/xdocs/yarn.lock
new file mode 100644
index 0000000000..b29c80ade6
--- /dev/null
+++ b/xdocs/yarn.lock
@@ -0,0 +1,13 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@fontsource/merriweather@^5.1.0":
+  version "5.2.11"
+  resolved 
"https://registry.yarnpkg.com/@fontsource/merriweather/-/merriweather-5.2.11.tgz#e0bc5aef743145e3d662e8a542c3da799d26bf7e";
+  integrity 
sha512-ZiIMeUh5iT8d73o6xlSF8GKgjV5pgiFrufYc5jZTVAfExtWKqM2vQHnsqXSFMv4ELhAcjt6Vf+5T3oVGXhAizQ==
+
+"@fortawesome/fontawesome-free@^6.5.0":
+  version "6.7.2"
+  resolved 
"https://registry.yarnpkg.com/@fortawesome/fontawesome-free/-/fontawesome-free-6.7.2.tgz#8249de9b7e22fcb3ceb5e66090c30a1d5492b81a";
+  integrity 
sha512-JUOtgFW6k9u4Y+xeIaEiLr3+cjoUPiAuLXoyKOJSia6Duzb7pq+A76P9ZdPDoAoxHdHzq6gE9/jKBGXlZT8FbA==

Reply via email to