diff --git a/build.gradle b/build.gradle index b82fdbbdcad2..efa9a2ca69c2 100644 --- a/build.gradle +++ b/build.gradle @@ -132,6 +132,7 @@ ext { // Use gradle flag in root project for further referencing withUiModule = gradle.ext.withUiModule + withJsClient = gradle.ext.withJsClient } // Include smaller chunks configuring dedicated build areas. diff --git a/gradle/node.gradle b/gradle/node.gradle index 0cf7923bac09..5934db87fdd4 100644 --- a/gradle/node.gradle +++ b/gradle/node.gradle @@ -15,47 +15,50 @@ * limitations under the License. */ -configure([project(":solr:solr-ref-guide"), project(":solr:webapp")]) { - apply plugin: libs.plugins.nodegradle.node.get().pluginId +// Projects that need node/npm apply the node-gradle plugin themselves; this shared +// config reacts to whichever projects do so, rather than naming them explicitly. +allprojects { + pluginManager.withPlugin(libs.plugins.nodegradle.node.get().pluginId) { - def npmRegistry = "${-> propertyOrEnvOrDefault("solr.npm.registry", "SOLR_NPM_REGISTRY", '')}" - if (!npmRegistry.isEmpty()) { - tasks.npmSetup { - args.addAll(['--registry', npmRegistry]) - } - - afterEvaluate { - tasks.withType(NpmTask).each {npmTask -> - npmTask.environment.put('NPM_CONFIG_REGISTRY', npmRegistry) + def npmRegistry = "${-> propertyOrEnvOrDefault("solr.npm.registry", "SOLR_NPM_REGISTRY", '')}" + if (!npmRegistry.isEmpty()) { + tasks.npmSetup { + args.addAll(['--registry', npmRegistry]) } - tasks.withType(NpxTask).each {npxTask -> - npxTask.environment.put('NPM_CONFIG_REGISTRY', npmRegistry) + + afterEvaluate { + tasks.withType(NpmTask).each {npmTask -> + npmTask.environment.put('NPM_CONFIG_REGISTRY', npmRegistry) + } + tasks.withType(NpxTask).each {npxTask -> + npxTask.environment.put('NPM_CONFIG_REGISTRY', npmRegistry) + } } } - } - project.ext { - nodeProjectDir = layout.projectDirectory.dir(".gradle/node") - } + project.ext { + nodeProjectDir = layout.projectDirectory.dir(".gradle/node") + } - node { - download = true - version = libs.versions.nodejs.get() + node { + download = true + version = libs.versions.nodejs.get() - def nodeDistUrl = "${-> propertyOrEnvOrDefault("solr.node.distUrl", "SOLR_NODE_DIST_URL", '')}" - if (!nodeDistUrl.isEmpty()) { - distBaseUrl = nodeDistUrl - } + def nodeDistUrl = "${-> propertyOrEnvOrDefault("solr.node.distUrl", "SOLR_NODE_DIST_URL", '')}" + if (!nodeDistUrl.isEmpty()) { + distBaseUrl = nodeDistUrl + } - // The directory where Node.js is unpacked (when download is true) - workDir = file("${project.ext.nodeProjectDir.getAsFile().path}/nodejs") + // The directory where Node.js is unpacked (when download is true) + workDir = file("${project.ext.nodeProjectDir.getAsFile().path}/nodejs") - // The directory where npm is installed (when a specific version is defined) - npmWorkDir = file("${project.ext.nodeProjectDir.getAsFile().path}/npm") + // The directory where npm is installed (when a specific version is defined) + npmWorkDir = file("${project.ext.nodeProjectDir.getAsFile().path}/npm") - // The Node.js project directory location - // This is where the package.json file and node_modules directory are located - // By default it is at the root of the current project - nodeProjectDir = project.ext.nodeProjectDir + // The Node.js project directory location + // This is where the package.json file and node_modules directory are located + // By default it is at the root of the current project + nodeProjectDir = project.ext.nodeProjectDir + } } } diff --git a/settings.gradle b/settings.gradle index 782edec43251..39f8609f6365 100644 --- a/settings.gradle +++ b/settings.gradle @@ -74,5 +74,11 @@ if (gradle.ext.withUiModule) { include(":solr:ui") } +def disableJsClientValue = providers.gradleProperty('disableJsClient').orNull +gradle.ext.withJsClient = disableJsClientValue == null || disableJsClientValue != 'true' +if (gradle.ext.withJsClient) { + include(":solr:webapp:js-client") +} + // Configures development for joint Lucene/ Solr composite build. apply from: file('gradle/lucene-dev/lucene-dev-repo-composite.gradle') diff --git a/solr/solr-ref-guide/build.gradle b/solr/solr-ref-guide/build.gradle index 66f4437249b8..cf95613fecb1 100644 --- a/solr/solr-ref-guide/build.gradle +++ b/solr/solr-ref-guide/build.gradle @@ -19,6 +19,7 @@ import groovy.json.JsonOutput import org.apache.tools.ant.util.TeeOutputStream apply plugin: 'java' +apply plugin: libs.plugins.nodegradle.node.get().pluginId description = 'Solr Reference Guide' diff --git a/solr/webapp/build.gradle b/solr/webapp/build.gradle index 62d6a1305629..6ca3fcc61759 100644 --- a/solr/webapp/build.gradle +++ b/solr/webapp/build.gradle @@ -26,14 +26,10 @@ configurations { war {} serverLib solrCore - generatedJSClient generatedJSClientBundle -} - -ext { - jsClientWorkspace = layout.buildDirectory.dir("jsClientWorkspace").get() - jsClientBuildDir = layout.buildDirectory.dir("jsClientBuild").get() - jsClientBundleDir = layout.buildDirectory.dir("jsClientBundle").get() + // TODO: :solr:ui's dev/prod distributions are pulled in via raw cross-project task + // references (see generateUiDevFiles/generateUiProdFiles below) instead of a + // configuration like this one; consider exposing them as a real configuration too. } dependencies { @@ -42,57 +38,11 @@ dependencies { solrCore project(":solr:core") implementation(configurations.solrCore - configurations.serverLib) - generatedJSClient project(path: ":solr:api", configuration: "jsClient") - generatedJSClientBundle files(jsClientBundleDir) { - builtBy "finalizeJsBundleDir" - } -} - -task syncJSClientSourceCode(type: Sync) { - group = 'Solr JS Client' - from configurations.generatedJSClient - - into jsClientWorkspace - - // Keep the node modules, so that they don't need to be re-downloaded - preserve { - include "node_modules/**" + if (gradle.ext.withJsClient) { + generatedJSClientBundle project(path: ":solr:webapp:js-client", configuration: "jsClientBundle") } } -task jsClientDownloadDeps(type: NpmTask) { - group = 'Solr JS Client' - dependsOn tasks.syncJSClientSourceCode - - args = ["install"] - workingDir = file(jsClientWorkspace) - - inputs.dir("${jsClientWorkspace}/src") - inputs.file("${jsClientWorkspace}/package.json") - outputs.dir("${jsClientWorkspace}/node_modules") -} - -task jsClientBuild(type: NpmTask) { - group = 'Solr JS Client' - dependsOn tasks.jsClientDownloadDeps - - args = ["run", "build"] - workingDir = file(jsClientWorkspace) - - inputs.dir("${jsClientWorkspace}/src") - inputs.file("${jsClientWorkspace}/package.json") - inputs.dir("${jsClientWorkspace}/node_modules") - outputs.dir("${jsClientWorkspace}/dist") -} - -task downloadBrowserify(type: NpmTask) { - group = 'Build Dependency Download' - args = ["install", "browserify@${libs.versions.browserify.get()}"] - - inputs.property("browserify version", libs.versions.browserify.get()) - outputs.dir("${nodeProjectDir}/node_modules/browserify") -} - if (gradle.ext.withUiModule) { tasks.register("generateUiDevFiles") { description = "Generate new UI for development and add files to outputs for later referencing." @@ -113,39 +63,10 @@ if (gradle.ext.withUiModule) { } } -task generateJsClientBundle(type: NpxTask) { - group = 'Solr JS Client' - dependsOn tasks.downloadBrowserify - dependsOn tasks.jsClientBuild - - command = 'browserify' - args = ['dist/index.js', '-s', 'solrApi', '-o', "${jsClientBuildDir}/index.js"] - workingDir = file(jsClientWorkspace) - - inputs.dir(jsClientWorkspace) - inputs.property("browserify version", libs.versions.browserify.get()) - - outputs.file("${jsClientBuildDir}/index.js") -} - -task finalizeJsBundleDir(type: Sync) { - group = 'Solr JS Client' - - from configurations.generatedJSClient { - include "README.md" - include "docs/**" - } - - from tasks.generateJsClientBundle { - include "index.js" - } - - into jsClientBundleDir -} - war { from("web") + // note: nonetheless may be disabled, copying nothing from(configurations.generatedJSClientBundle, { into "libs/solr" }) diff --git a/solr/webapp/gradle.lockfile b/solr/webapp/gradle.lockfile index fec92697f635..fd5c99e1d63b 100644 --- a/solr/webapp/gradle.lockfile +++ b/solr/webapp/gradle.lockfile @@ -158,4 +158,4 @@ org.slf4j:jcl-over-slf4j:2.0.17=serverLib,solrCore org.slf4j:jul-to-slf4j:2.0.17=serverLib org.slf4j:slf4j-api:2.0.17=serverLib,solrCore org.xerial.snappy:snappy-java:1.1.10.8=solrCore -empty=compileClasspath,generatedJSClient,generatedJSClientBundle,jarValidation,missingdoclet,providedCompile,providedRuntime,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,war +empty=compileClasspath,generatedJSClientBundle,jarValidation,missingdoclet,providedCompile,providedRuntime,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,war diff --git a/solr/webapp/js-client/build.gradle.kts b/solr/webapp/js-client/build.gradle.kts new file mode 100644 index 000000000000..1ce43c25978b --- /dev/null +++ b/solr/webapp/js-client/build.gradle.kts @@ -0,0 +1,119 @@ +/* + * 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 com.github.gradle.node.npm.task.NpmTask +import com.github.gradle.node.npm.task.NpxTask + +// Builds the OpenAPI-generated JS client (from :solr:api) into a single bundled +// file, for :solr:webapp to include in the war. This is the only place in the +// build that needs npm/node for this purpose; it can be disabled entirely via +// -PdisableJsClient=true (see settings.gradle). + +plugins { + id("base") + alias(libs.plugins.nodegradle.node) +} + +description = "Generates a JavaScript client for Solr OpenApi" + +val jsClientWorkspace = layout.buildDirectory.dir("jsClientWorkspace").get().asFile +val jsClientBuildDir = layout.buildDirectory.dir("jsClientBuild").get().asFile +val jsClientBundleDir = layout.buildDirectory.dir("jsClientBundle").get().asFile + +val generatedJSClient = configurations.create("generatedJSClient") +val jsClientBundle = configurations.create("jsClientBundle") { + isCanBeConsumed = true + isCanBeResolved = false +} + +dependencies { + generatedJSClient(project(path = ":solr:api", configuration = "jsClient")) +} + +val syncJSClientSourceCode = tasks.register("syncJSClientSourceCode") { + from(generatedJSClient) + + into(jsClientWorkspace) + + // Keep the node modules, so that they don't need to be re-downloaded + preserve { + include("node_modules/**") + } +} + +val jsClientDownloadDeps = tasks.register("jsClientDownloadDeps") { + dependsOn(syncJSClientSourceCode) + + args.set(listOf("install")) + workingDir.set(jsClientWorkspace) + + inputs.dir("$jsClientWorkspace/src") + inputs.file("$jsClientWorkspace/package.json") + outputs.dir("$jsClientWorkspace/node_modules") +} + +val jsClientBuild = tasks.register("jsClientBuild") { + dependsOn(jsClientDownloadDeps) + + args.set(listOf("run", "build")) + workingDir.set(jsClientWorkspace) + + inputs.dir("$jsClientWorkspace/src") + inputs.file("$jsClientWorkspace/package.json") + inputs.dir("$jsClientWorkspace/node_modules") + outputs.dir("$jsClientWorkspace/dist") +} + +val downloadBrowserify = tasks.register("downloadBrowserify") { + args.set(listOf("install", "browserify@${libs.versions.browserify.get()}")) + + inputs.property("browserify version", libs.versions.browserify.get()) + outputs.dir(project.extra["nodeProjectDir"].toString() + "/node_modules/browserify") +} + +val generateJsClientBundle = tasks.register("generateJsClientBundle") { + dependsOn(downloadBrowserify) + dependsOn(jsClientBuild) + + command.set("browserify") + args.set(listOf("dist/index.js", "-s", "solrApi", "-o", "$jsClientBuildDir/index.js")) + workingDir.set(jsClientWorkspace) + + inputs.dir(jsClientWorkspace) + inputs.property("browserify version", libs.versions.browserify.get()) + + outputs.file("$jsClientBuildDir/index.js") +} + +val finalizeJsBundleDir = tasks.register("finalizeJsBundleDir") { + from(generatedJSClient) { + include("README.md") + include("docs/**") + } + + from(generateJsClientBundle) { + include("index.js") + } + + into(jsClientBundleDir) +} + +artifacts { + add("jsClientBundle", jsClientBundleDir) { + builtBy(finalizeJsBundleDir) + } +} diff --git a/solr/webapp/js-client/gradle.lockfile b/solr/webapp/js-client/gradle.lockfile new file mode 100644 index 000000000000..3a4ae5a9eaa5 --- /dev/null +++ b/solr/webapp/js-client/gradle.lockfile @@ -0,0 +1,5 @@ +# This is a Gradle generated file for dependency locking. +# 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:webapp:js-client:dependencies --write-locks +empty=generatedJSClient,jarValidation