diff --git a/README.md b/README.md index cd783f98..5527dc68 100644 --- a/README.md +++ b/README.md @@ -13,12 +13,18 @@ on how to do that, including how to develop and test locally and the versioning ## Release Notes +### TBD +*Released*: TBD +(Earliest compatible LabKey version: 26.6.0) +- Update to Gradle 9.5.1 +- Remove GWT plugin and extension +- Fix caching bug related to output file declaration for `ClientLibsCompress` task + ### 8.2.0 *Released*: 11 May 2026 (Earliest compatible LabKey version: 26.5.0) - Add `BuildUtils.hasArtifactoryProperties` method for brevity - Add cacheability annotations to tasks so they work with stricter plugin validation -- Update to Gradle 9.5.0 ### 8.1.0 *Released*: 22 April 2026 diff --git a/build.gradle b/build.gradle index f6e7c34a..2acec88c 100644 --- a/build.gradle +++ b/build.gradle @@ -43,7 +43,7 @@ dependencies { } group = 'org.labkey.build' -project.version = "8.3.0-SNAPSHOT" +project.version = "8.3.0-removeGwt-SNAPSHOT" gradlePlugin { plugins { @@ -75,10 +75,6 @@ gradlePlugin { id = 'org.labkey.build.fileModule' implementationClass = 'org.labkey.gradle.plugin.FileModule' } - gwt { - id = 'org.labkey.build.gwt' - implementationClass = 'org.labkey.gradle.plugin.Gwt' - } javaModule { id = 'org.labkey.build.javaModule' implementationClass = 'org.labkey.gradle.plugin.JavaModule' diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index d997cfc6..b1b8ef56 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index c61a118f..df6a6ad7 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,9 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.1-bin.zip networkTimeout=10000 +retries=0 +retryBackOffMs=500 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 739907df..b9bb139f 100755 --- a/gradlew +++ b/gradlew @@ -57,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/2d6327017519d23b96af35865dc997fcb544fb40/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/3d91ce3b8caaf77ad09f381f43615b715b53f72c/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. diff --git a/gradlew.bat b/gradlew.bat index c4bdd3ab..24c62d56 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -23,8 +23,8 @@ @rem @rem ########################################################################## -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal +@rem Set local scope for the variables, and ensure extensions are enabled +setlocal EnableExtensions set DIRNAME=%~dp0 if "%DIRNAME%"=="" set DIRNAME=. @@ -51,7 +51,7 @@ echo. 1>&2 echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo location of your Java installation. 1>&2 -goto fail +"%COMSPEC%" /c exit 1 :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% @@ -65,7 +65,7 @@ echo. 1>&2 echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo location of your Java installation. 1>&2 -goto fail +"%COMSPEC%" /c exit 1 :execute @rem Setup the command line @@ -73,21 +73,10 @@ goto fail @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* +@rem endlocal doesn't take effect until after the line is parsed and variables are expanded +@rem which allows us to clear the local environment before executing the java command +endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +:exitWithErrorLevel +@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts +"%COMSPEC%" /c exit %ERRORLEVEL% diff --git a/src/main/groovy/org/labkey/gradle/plugin/Api.groovy b/src/main/groovy/org/labkey/gradle/plugin/Api.groovy index 7fa137aa..bcd0460c 100644 --- a/src/main/groovy/org/labkey/gradle/plugin/Api.groovy +++ b/src/main/groovy/org/labkey/gradle/plugin/Api.groovy @@ -71,7 +71,7 @@ class Api implements Plugin { api { java { - srcDirs = [project.file(SOURCE_DIR).exists() ? SOURCE_DIR : ALT_SOURCE_DIR, 'internal/gwtsrc'] + srcDirs = [project.file(SOURCE_DIR).exists() ? SOURCE_DIR : ALT_SOURCE_DIR] } resources { srcDirs = ["schemas"] diff --git a/src/main/groovy/org/labkey/gradle/plugin/FileModule.groovy b/src/main/groovy/org/labkey/gradle/plugin/FileModule.groovy index bc1739d2..a73d5587 100644 --- a/src/main/groovy/org/labkey/gradle/plugin/FileModule.groovy +++ b/src/main/groovy/org/labkey/gradle/plugin/FileModule.groovy @@ -156,7 +156,6 @@ class FileModule implements Plugin jar.from project.labkey.explodedModuleDir jar.exclude '**/*.uptodate' jar.exclude "META-INF/${project.name}/**" - jar.exclude 'gwt-unitCache/**' jar.archiveBaseName.set(project.name) jar.archiveExtension.set('module') jar.destinationDirectory.set(project.layout.buildDirectory) diff --git a/src/main/groovy/org/labkey/gradle/plugin/Gwt.groovy b/src/main/groovy/org/labkey/gradle/plugin/Gwt.groovy deleted file mode 100644 index ca94ea54..00000000 --- a/src/main/groovy/org/labkey/gradle/plugin/Gwt.groovy +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright (c) 2016-2018 LabKey Corporation - * - * Licensed 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.labkey.gradle.plugin - - -import org.gradle.api.Plugin -import org.gradle.api.Project -import org.gradle.api.file.FileTree -import org.gradle.api.specs.AndSpec -import org.gradle.api.tasks.Copy -import org.gradle.api.tasks.JavaExec -import org.gradle.api.tasks.TaskProvider -import org.labkey.gradle.plugin.extension.GwtExtension -import org.labkey.gradle.plugin.extension.LabKeyExtension -import org.labkey.gradle.task.GzipAction -import org.labkey.gradle.util.BuildUtils -import org.labkey.gradle.util.GroupNames - -/** - * Used to compile GWT source files into Javascript - */ -class Gwt implements Plugin -{ - public static final String SOURCE_DIR = "gwtsrc" - - private Project project - private static final String GWT_EXTENSION = ".gwt.xml" - - static boolean isApplicable(Project project) - { - return project.file(SOURCE_DIR).exists() - } - - @Override - void apply(Project project) - { - this.project = project - project.apply plugin: 'java-base' - project.extensions.create("gwt", GwtExtension) - if (LabKeyExtension.isDevMode(project)) - { - project.gwt.style = "PRETTY" - project.gwt.draftCompile = true - project.gwt.allBrowserCompile = false - } - - addSourceSets() - addDependencies() - addTasks() - } - - private void addDependencies() - { - project.dependencies { - gwtImplementation "org.gwtproject:gwt-user:${project.gwtVersion}", - "org.gwtproject:gwt-dev:${project.gwtVersion}" - } - if (project.hasProperty("validationJakartaApiVersion")) - project.dependencies { - gwtImplementation "jakarta.validation:jakarta.validation-api:${project.validationJakartaApiVersion}" - } - else if (project.hasProperty("validationApiVersion")) - project.dependencies { - gwtImplementation "javax.validation:validation-api:${project.validationApiVersion}" - } - } - - private void addSourceSets() - { - project.sourceSets { - gwt { - java { - srcDir project.gwt.srcDir - } - } - main { - java { - srcDir project.gwt.srcDir - } - } - } - } - - private void addTasks() - { - Map gwtModuleClasses = getGwtModuleClasses(project) - List gwtTasks = new ArrayList<>(gwtModuleClasses.size()) - gwtModuleClasses.entrySet().each { - gwtModuleClass -> - - String taskName ='compileGwt' + gwtModuleClass.getKey() - project.tasks.register(taskName, JavaExec) { - JavaExec java -> - java.outputs.cacheIf {true} - java.group = GroupNames.GWT - java.description = "compile GWT source files for " + gwtModuleClass.getKey() + " into JS" - - File extrasDir = BuildUtils.getBuildDirFile(project, project.gwt.extrasDir) - File outputDir = BuildUtils.getBuildDirFile(project, project.gwt.outputDir) - - java.inputs.files(project.sourceSets.gwt.java.srcDirs) - String extrasDirPath = extrasDir.getPath() - String outputDirPath = outputDir.getPath() - - java.outputs.dir extrasDir - java.outputs.dir outputDir - - // Workaround for incremental build (GRADLE-1483) - java.outputs.upToDateSpec = new AndSpec() - - java.doFirst { - extrasDir.mkdirs() - outputDir.mkdirs() - } - - if (!LabKeyExtension.isDevMode(project)) - { - java.doLast new GzipAction() - } - - java.setMainClass('com.google.gwt.dev.Compiler') - - def paths = [] - - paths += [ - project.sourceSets.gwt.compileClasspath, // Dep - project.sourceSets.gwt.java.srcDirs // Java source - ] - String apiProjectPath = BuildUtils.getApiProjectPath(project.gradle) - if (project.findProject(apiProjectPath) != null && project.project(apiProjectPath).file(project.gwt.srcDir).exists()) - paths += [project.project(apiProjectPath).file(project.gwt.srcDir)] - java.classpath paths - - java.args = - [ - '-war', outputDirPath, - '-style', project.gwt.style, - '-logLevel', project.gwt.logLevel, - '-extra', extrasDirPath, - '-deploy', extrasDirPath, - '-localWorkers', 4, - gwtModuleClass.getValue() - ] - if (project.gwt.draftCompile) - java.args.add('-draftCompile') - java.jvmArgs = - [ - '-Xss1024k', - '-Djava.awt.headless=true' - ] - - java.maxHeapSize = '512m' - - } - gwtTasks.add(project.tasks.named(taskName)) - } - project.tasks.register('compileGwt', Copy) { - Copy copy -> - copy.from gwtTasks - copy.into project.labkey.explodedModuleWebDir - copy.description = "compile all GWT source files into JS and copy them to the module's web directory" - copy.group = GroupNames.GWT - } - - project.tasks.named("classes").configure {dependsOn(project.tasks.compileGwt)} - } - - private static Map getGwtModuleClasses(Project project) - { - File gwtSrc = project.file(project.gwt.srcDir) - FileTree tree = project.fileTree(dir: gwtSrc, includes: ["**/*${GWT_EXTENSION}"]) - Map nameToClass = new HashMap<>() - String separator = System.getProperty("file.separator").equals("\\") ? "\\\\" : System.getProperty("file.separator") - for (File file : tree.getFiles()) - { - String className = file.getPath() - className = className.substring(gwtSrc.getPath().length() + 1) // lop off the part of the path before the package structure - className = className.replaceAll(separator, ".") // convert from path to class package - className = className.substring(0, className.indexOf(GWT_EXTENSION)) // remove suffix - nameToClass.put(file.getName().substring(0, file.getName().indexOf(GWT_EXTENSION)),className) - } - return nameToClass - } - -} - diff --git a/src/main/groovy/org/labkey/gradle/plugin/JavaModule.groovy b/src/main/groovy/org/labkey/gradle/plugin/JavaModule.groovy index 8940537a..de56128d 100644 --- a/src/main/groovy/org/labkey/gradle/plugin/JavaModule.groovy +++ b/src/main/groovy/org/labkey/gradle/plugin/JavaModule.groovy @@ -74,9 +74,6 @@ class JavaModule implements Plugin if (Jsp.isApplicable(project)) project.apply plugin: 'org.labkey.build.jsp' - if (Gwt.isApplicable(project)) - project.apply plugin: 'org.labkey.build.gwt' - if (UiTest.isApplicable(project)) { project.apply plugin: 'org.labkey.build.uiTest' diff --git a/src/main/groovy/org/labkey/gradle/plugin/extension/GwtExtension.groovy b/src/main/groovy/org/labkey/gradle/plugin/extension/GwtExtension.groovy deleted file mode 100644 index 9bfd1848..00000000 --- a/src/main/groovy/org/labkey/gradle/plugin/extension/GwtExtension.groovy +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2017 LabKey Corporation - * - * Licensed 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.labkey.gradle.plugin.extension - -import org.labkey.gradle.plugin.Gwt - -class GwtExtension -{ - String srcDir = Gwt.SOURCE_DIR - String style = "OBF" - String logLevel = "INFO" - String extrasDir = "gwtExtras" - String outputDir = "gwtOut" - Boolean draftCompile = false - Boolean allBrowserCompile = true -} diff --git a/src/main/groovy/org/labkey/gradle/task/ClientLibsCompress.groovy b/src/main/groovy/org/labkey/gradle/task/ClientLibsCompress.groovy index 0de7ea1c..ab08e2f1 100644 --- a/src/main/groovy/org/labkey/gradle/task/ClientLibsCompress.groovy +++ b/src/main/groovy/org/labkey/gradle/task/ClientLibsCompress.groovy @@ -22,8 +22,11 @@ import org.apache.tools.ant.util.FileUtils import org.gradle.api.DefaultTask import org.gradle.api.GradleException import org.gradle.api.Project +import org.gradle.api.file.FileCollection import org.gradle.api.file.FileTree +import org.gradle.api.provider.Property import org.gradle.api.tasks.CacheableTask +import org.gradle.api.tasks.Input import org.gradle.api.tasks.InputFiles import org.gradle.api.tasks.Internal import org.gradle.api.tasks.OutputDirectories @@ -31,6 +34,7 @@ import org.gradle.api.tasks.OutputFiles import org.gradle.api.tasks.PathSensitive import org.gradle.api.tasks.PathSensitivity import org.gradle.api.tasks.TaskAction +import org.gradle.work.DisableCachingByDefault import org.labkey.gradle.plugin.NpmRun import org.labkey.gradle.plugin.extension.LabKeyExtension import org.labkey.gradle.util.BuildUtils @@ -44,7 +48,7 @@ import java.nio.charset.StandardCharsets import java.util.stream.Collectors /** - * Class for compressing javascript and css files using the yuicompressor classes. + * Class for compressing javascript and css files */ @CacheableTask class ClientLibsCompress extends DefaultTask @@ -55,15 +59,34 @@ class ClientLibsCompress extends DefaultTask @InputFiles @PathSensitive(PathSensitivity.RELATIVE) FileTree xmlFiles + private List inputFiles = null private List outputFiles = null private List outputDirs = null + @Input + final abstract Property isDevMode = project.objects.property(Boolean).convention(LabKeyExtension.isDevMode(project)) + + @Input + final abstract Property nodeVersion = project.objects.property(String).convention(project.hasProperty("nodeVersion") ? project.nodeVersion : "") + @Internal String getWorkingDirPath() { return new File((String) project.labkey.explodedModuleWebDir).getAbsolutePath() } + @InputFiles + @PathSensitive(PathSensitivity.RELATIVE) + FileCollection getNpmPackageFiles() { + if (BuildUtils.haveMinificationProject(project.gradle)) { + Project minProject = project.project(BuildUtils.getMinificationProjectPath(project.gradle)) + return project.files( + "${minProject.projectDir}/package.json", + "${minProject.projectDir}/package-lock.json" + ).filter { it.exists() } + } + } + /** * Creates a map between the individual .lib.xml files and the importers used to parse these files and * extract the css and javascript files that are referenced. @@ -72,7 +95,7 @@ class ClientLibsCompress extends DefaultTask @Internal Map getImporterMap() { - Map importerMap = new HashMap<>() + Map importerMap = new LinkedHashMap<>() xmlFiles.files.each() { File file -> importerMap.put(file, parseXmlFile(getSourceDir(file), file)) @@ -104,28 +127,23 @@ class ClientLibsCompress extends DefaultTask } /** - * Input files include: - * - .lib.xml files + * Input files includes: * - css files referenced in the .lib.xml files * - js files referenced in the .lib.xml files - * @return list of all the .lib.xml files and the (internal) files referenced in the .lib.xml files + * @return list of all the (internal) files referenced in the .lib.xml files (the xml files are designated as input above) */ @InputFiles @PathSensitive(PathSensitivity.RELATIVE) List getInputFiles() { - if (inputFiles == null) - { - inputFiles = new ArrayList<>() - inputFiles.addAll(xmlFiles) + if (inputFiles == null) { + inputFiles = new ArrayList<>() getImporterMap().entrySet().each { Map.Entry entry -> - if (entry.value.getCssFiles().size() > 0) - { + if (entry.value.getCssFiles().size() > 0) { inputFiles.addAll(entry.value.getCssFiles()) } - if (entry.value.getJavascriptFiles().size() > 0) - { + if (entry.value.getJavascriptFiles().size() > 0) { inputFiles.addAll(entry.value.getJavascriptFiles()) } } @@ -146,23 +164,21 @@ class ClientLibsCompress extends DefaultTask outputFiles = new ArrayList<>() getImporterMap().entrySet().each { Map.Entry entry -> - // The output file will be in the working directory not in the source directory used when parsing the file. - String fileName = entry.key.getAbsolutePath() - fileName = fileName.replace(entry.value.sourceDir.getAbsolutePath(), getWorkingDirPath()) - File workingFile = project.file(fileName) - if (entry.value.getCssFiles().size() > 0) - { - outputFiles.add(getOutputFile(workingFile, "min", "css")) - if (LabKeyExtension.isDevMode(project)) - outputFiles.add(getOutputFile(workingFile, "min", "css.gz")) - outputFiles.add(getOutputFile(workingFile, "combined", "css")) - } - if (entry.value.getJavascriptFiles().size() > 0) - { - outputFiles.add(getOutputFile(workingFile, "min", "js")) - if (LabKeyExtension.isDevMode(project)) - outputFiles.add(getOutputFile(workingFile, "min", "js.gz")) - outputFiles.add(getOutputFile(workingFile, "combined", "js")) + if (entry.value.doCompile) { + // The output file will be in the working directory not in the source directory used when parsing the file. + String fileName = entry.key.getAbsolutePath() + fileName = fileName.replace(entry.value.sourceDir.getAbsolutePath(), getWorkingDirPath()) + File workingFile = project.file(fileName) + if (entry.value.getCssFiles().size() > 0) { + outputFiles.add(getOutputFile(workingFile, "min", "css")) + if (!isDevMode.get()) + outputFiles.add(getOutputFile(workingFile, "min", "css.gz")) + } + if (entry.value.getJavascriptFiles().size() > 0) { + outputFiles.add(getOutputFile(workingFile, "min", "js")) + if (!isDevMode.get()) + outputFiles.add(getOutputFile(workingFile, "min", "js.gz")) + } } } } @@ -234,7 +250,7 @@ class ClientLibsCompress extends DefaultTask String getNodeExecutableDir() { Project minProject = project.project(BuildUtils.getMinificationProjectPath(project.gradle)) - String nodeFilePrefix = "node-v${project.nodeVersion}-" + String nodeFilePrefix = "node-v${nodeVersion.get()}-" File nodeDir = new File("${minProject.projectDir}/.gradle/nodejs") File[] nodeFiles = nodeDir.listFiles({ File file -> file.name.startsWith(nodeFilePrefix) } as FileFilter) if (nodeFiles != null && nodeFiles.length > 0) @@ -387,7 +403,7 @@ class ClientLibsCompress extends DefaultTask void compressFile(File file) { - if (!LabKeyExtension.isDevMode(project)) + if (!isDevMode.get()) { this.logger.info("Compressing " + file) project.ant.gzip( diff --git a/src/main/groovy/org/labkey/gradle/task/RestoreFromTrash.groovy b/src/main/groovy/org/labkey/gradle/task/RestoreFromTrash.groovy index bd907930..3c40d9b5 100644 --- a/src/main/groovy/org/labkey/gradle/task/RestoreFromTrash.groovy +++ b/src/main/groovy/org/labkey/gradle/task/RestoreFromTrash.groovy @@ -181,6 +181,7 @@ class RestoreFromTrash extends DefaultTask { HttpPost httpPost = new HttpPost(endpoint) // N.B. Using Authorization Bearer with an API token does not currently work + // TODO JFrog claims to support bearer tokens now. https://docs.jfrog.com/administration/docs/access-tokens#authorization-headers httpPost.setHeader("Authorization", "Basic " + Base64.getEncoder().encodeToString("${project.property(BuildUtils.ARTIFACTORY_USER_PROP)}:${project.property(BuildUtils.ARTIFACTORY_PASSWORD_PROP)}".getBytes())) CloseableHttpResponse response = httpClient.execute(httpPost) int statusCode = response.getCode() diff --git a/src/main/groovy/org/labkey/gradle/util/GroupNames.groovy b/src/main/groovy/org/labkey/gradle/util/GroupNames.groovy index 9a00934a..ce821809 100644 --- a/src/main/groovy/org/labkey/gradle/util/GroupNames.groovy +++ b/src/main/groovy/org/labkey/gradle/util/GroupNames.groovy @@ -26,7 +26,6 @@ class GroupNames public static final String DEPLOY = "Deploy" public static final String DISTRIBUTION = "Distribution" public static final String DOCUMENTATION = "Documentation" - public static final String GWT = "gwt" public static final String HELP = "Help" public static final String JSP = "jsp" public static final String MODULE = "Module"