diff --git a/README.md b/README.md index 64ec401d..67854d63 100644 --- a/README.md +++ b/README.md @@ -13,11 +13,25 @@ on how to do that, including how to develop and test locally and the versioning ## Release Notes -### 4.3.0-SNAPSHOT +### 5.1.0-SNAPSHOT *Released*: TBD (Earliest compatible LabKey version: 24.11) + +### 5.0.0 +*Released*: 5 November 2024 +(Earliest compatible LabKey version: 24.11) - Stop adding the standalone `VERSION` file to distribution archives; the `distribution.properties` file now contains the `version` and `buildUrl` properties that `EmbeddedExtractor.java` reads. +- Remove `checkModuleTasks` tasks, added to get us through a transition from plugins being declared more centrally +- Update `stageModules` to remove use of deprecated `fileCollection` method and make compatible with configuration cache +- Remove `StagingExtension` +- Make `jsp2Java` compatible with configuration cache +- Update property in node plugin configuration for latest version +- Mark `copyExternalLibs` task as not compatible with configuration cache for now +- Update `ServerSideJS` task for configuration cache compatibility +- Update `DeployApp` and relatives for better configuration cache compatibility +- Update `PickDb` task for better configuration cache compatibility +- Upgrade to Gradle 8.10.2 ### 4.2.0 *Released*: 11 October 2024 diff --git a/build.gradle b/build.gradle index c971ad14..9f87c84e 100644 --- a/build.gradle +++ b/build.gradle @@ -42,7 +42,7 @@ dependencies { } group 'org.labkey.build' -project.version = "4.3.0-SNAPSHOT" +project.version = "5.1.0-SNAPSHOT" gradlePlugin { plugins { diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 0aaefbca..df97d72b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/src/main/groovy/org/labkey/gradle/plugin/FileModule.groovy b/src/main/groovy/org/labkey/gradle/plugin/FileModule.groovy index 37f30fef..8fc32eaf 100644 --- a/src/main/groovy/org/labkey/gradle/plugin/FileModule.groovy +++ b/src/main/groovy/org/labkey/gradle/plugin/FileModule.groovy @@ -19,6 +19,8 @@ import org.gradle.api.GradleException import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.Task +import org.gradle.api.UnknownDomainObjectException +import org.gradle.api.artifacts.Configuration import org.gradle.api.artifacts.Dependency import org.gradle.api.artifacts.ProjectDependency import org.gradle.api.file.CopySpec @@ -197,7 +199,7 @@ class FileModule implements Plugin project.copy { CopySpec copy -> copy.from moduleTask copy.from project.configurations.modules - copy.into project.staging.modulesDir + copy.into "${BuildUtils.getRootBuildDirPath(project)}/$ServerDeploy.STAGING_MODULES_DIR" copy.setDuplicatesStrategy(DuplicatesStrategy.INCLUDE) } project.copy { CopySpec copy -> @@ -303,7 +305,8 @@ class FileModule implements Plugin // staging has only the .modules files if (includeStaging) { - File stagingDir = new File((String) project.staging.modulesDir) + + File stagingDir = BuildUtils.getRootBuildDirFile(project, ServerDeploy.STAGING_MODULES_DIR) if (stagingDir.isDirectory()) { files.addAll(stagingDir.listFiles(new FilenameFilter() { @@ -431,24 +434,30 @@ class FileModule implements Plugin // This is done after the project is evaluated otherwise the dependencies for the modules configuration will not have been added yet. project.afterEvaluate({ BuildUtils.addLabKeyDependency(project: serverProject, config: 'modules', depProjectPath: project.path, depProjectConfig: 'published', depExtension: 'module') - if (project.configurations.findByName("modules") != null) - project.configurations.modules.dependencies.each { - Dependency dep -> - if (dep instanceof ProjectDependency) - { - ProjectDependency projectDep = (ProjectDependency) dep - if (shouldDoBuild(projectDep.dependencyProject, false)) { - BuildUtils.addLabKeyDependency(project: serverProject, config: 'modules', depProjectPath: projectDep.dependencyProject.getPath(), depProjectConfig: 'published', depExtension: 'module') - } - else { - serverProject.dependencies.add("modules", BuildUtils.getLabKeyArtifactName(project, projectDep.dependencyProject.getPath(), projectDep.version, "module")) - } - } - else - { - serverProject.dependencies.add("modules", dep) + BuildUtils.addLabKeyDependency(project: serverProject, config: 'builtModules', depProjectPath: project.path, depProjectConfig: 'published', depExtension: 'module') + try { + project.configurations.named("modules") { + Configuration config -> { + config.dependencies.each { + Dependency dep -> + if (dep instanceof ProjectDependency) { + ProjectDependency projectDep = (ProjectDependency) dep + if (shouldDoBuild(projectDep.dependencyProject, false)) { + BuildUtils.addLabKeyDependency(project: serverProject, config: 'modules', depProjectPath: projectDep.dependencyProject.getPath(), depProjectConfig: 'published', depExtension: 'module') + BuildUtils.addLabKeyDependency(project: serverProject, config: 'builtModules', depProjectPath: projectDep.dependencyProject.getPath(), depProjectConfig: 'published', depExtension: 'module') + } else { + serverProject.dependencies.add("modules", BuildUtils.getLabKeyArtifactName(project, projectDep.dependencyProject.getPath(), projectDep.version, "module")) + serverProject.dependencies.add("downloadedModules", BuildUtils.getLabKeyArtifactName(project, projectDep.dependencyProject.getPath(), projectDep.version, "module")) + } + } else { + serverProject.dependencies.add("modules", dep) + serverProject.dependencies.add("downloadedModules", dep) + } } + } } + + } catch (UnknownDomainObjectException ignore) { } }) } diff --git a/src/main/groovy/org/labkey/gradle/plugin/JavaModule.groovy b/src/main/groovy/org/labkey/gradle/plugin/JavaModule.groovy index 538726d1..0e6fe16d 100644 --- a/src/main/groovy/org/labkey/gradle/plugin/JavaModule.groovy +++ b/src/main/groovy/org/labkey/gradle/plugin/JavaModule.groovy @@ -156,12 +156,9 @@ class JavaModule implements Plugin copy.from task }) } - populateLib.configure { - it.doFirst { - File explodedLibDir = new File(project.labkey.explodedModuleLibDir) - if (explodedLibDir.exists()) - explodedLibDir.delete() - } + + project.tasks.named('populateExplodedLib') { + notCompatibleWithConfigurationCache("Need to figure out how to make the inputs from the optional tasks work.") } project.tasks.named('module').configure {dependsOn(populateLib)} diff --git a/src/main/groovy/org/labkey/gradle/plugin/Jsp.groovy b/src/main/groovy/org/labkey/gradle/plugin/Jsp.groovy index b6c39e55..a2ebb656 100644 --- a/src/main/groovy/org/labkey/gradle/plugin/Jsp.groovy +++ b/src/main/groovy/org/labkey/gradle/plugin/Jsp.groovy @@ -153,10 +153,6 @@ class Jsp implements Plugin task.dependsOn('jar') } - project.tasks.named('jsp2Java') { - notCompatibleWithConfigurationCache("ant.jasper doesn't seem completely compatible") - } - project.tasks.named('compileJspJava').configure { Task task -> task.dependsOn project.tasks.jsp2Java diff --git a/src/main/groovy/org/labkey/gradle/plugin/LabKey.groovy b/src/main/groovy/org/labkey/gradle/plugin/LabKey.groovy index 93321044..356d179c 100644 --- a/src/main/groovy/org/labkey/gradle/plugin/LabKey.groovy +++ b/src/main/groovy/org/labkey/gradle/plugin/LabKey.groovy @@ -18,7 +18,6 @@ package org.labkey.gradle.plugin import org.gradle.api.Plugin import org.gradle.api.Project import org.labkey.gradle.plugin.extension.LabKeyExtension -import org.labkey.gradle.plugin.extension.StagingExtension import org.labkey.gradle.util.ModuleFinder import org.labkey.gradle.util.BuildUtils @@ -56,9 +55,6 @@ class LabKey implements Plugin LabKeyExtension labKeyExt = project.extensions.create("labkey", LabKeyExtension) labKeyExt.setDirectories(project) - - StagingExtension stagingExt = project.extensions.create("staging", StagingExtension) - stagingExt.setDirectories(project) } // These configurations are used for deploying the app. We declare them here diff --git a/src/main/groovy/org/labkey/gradle/plugin/ModuleResources.groovy b/src/main/groovy/org/labkey/gradle/plugin/ModuleResources.groovy index ea8270f1..8e590c3a 100644 --- a/src/main/groovy/org/labkey/gradle/plugin/ModuleResources.groovy +++ b/src/main/groovy/org/labkey/gradle/plugin/ModuleResources.groovy @@ -41,9 +41,12 @@ class ModuleResources Provider> artifacts = config.getIncoming().getArtifacts().getResolvedArtifacts(); task.getArtifactIds().set(artifacts.map(new WriteDependenciesFile.IdExtractor())) } - task.externalDependencies.set(project.extensions.findByType(ModuleExtension.class).getExternalDependencies()) + def externals = project.extensions.findByType(ModuleExtension.class).getExternalDependencies() + task.externalDependencies.set(externals) + task.onlyIf { + return !externals.isEmpty() + } } catch (UnknownDomainObjectException ignore) { - } } diff --git a/src/main/groovy/org/labkey/gradle/plugin/NpmRun.groovy b/src/main/groovy/org/labkey/gradle/plugin/NpmRun.groovy index 8c9a1a7b..2f813558 100644 --- a/src/main/groovy/org/labkey/gradle/plugin/NpmRun.groovy +++ b/src/main/groovy/org/labkey/gradle/plugin/NpmRun.groovy @@ -91,7 +91,7 @@ class NpmRun implements Plugin download = project.hasProperty('nodeVersion') && project.hasProperty('npmVersion') // Set the work directory where node_modules should be located - nodeModulesDir = project.file("${project.projectDir}") + nodeProjectDir = project.file("${project.projectDir}") npmInstallCommand = project.hasProperty('npmInstallCommand') ? project.npmInstallCommand : 'ci' } diff --git a/src/main/groovy/org/labkey/gradle/plugin/ServerDeploy.groovy b/src/main/groovy/org/labkey/gradle/plugin/ServerDeploy.groovy index 72c007fb..958b11ed 100644 --- a/src/main/groovy/org/labkey/gradle/plugin/ServerDeploy.groovy +++ b/src/main/groovy/org/labkey/gradle/plugin/ServerDeploy.groovy @@ -16,22 +16,17 @@ package org.labkey.gradle.plugin import org.apache.commons.lang3.SystemUtils -import org.gradle.api.UnknownTaskException import org.gradle.api.file.DuplicatesStrategy import org.gradle.api.DefaultTask import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.Task import org.gradle.api.artifacts.Configuration -import org.gradle.api.artifacts.Dependency import org.gradle.api.file.CopySpec import org.gradle.api.file.DeleteSpec import org.gradle.api.file.FileCollection -import org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency -import org.gradle.api.internal.artifacts.dependencies.DefaultProjectDependency import org.gradle.api.tasks.Delete import org.labkey.gradle.plugin.extension.ServerDeployExtension -import org.labkey.gradle.plugin.extension.StagingExtension import org.labkey.gradle.task.* import org.labkey.gradle.util.BuildUtils import org.labkey.gradle.util.GroupNames @@ -46,19 +41,28 @@ import java.nio.file.Paths */ class ServerDeploy implements Plugin { + public static final String DEPLOY_DIR = "deploy" + public static final String MODULES_DIR = "${DEPLOY_DIR}/modules" + public static final String WEBAPP_DIR = "${DEPLOY_DIR}/labkeyWebapp" + public static final String PIPELINE_DIR = "${DEPLOY_DIR}/pipelineLib" + public static final String BIN_DIR = "${DEPLOY_DIR}/bin" + public static final String STAGING_DIR = "staging" + public static final String STAGING_MODULES_DIR = "${STAGING_DIR}/modules/" + public static final String STAGING_PIPELINE_DIR = "${STAGING_DIR}/pipelineLib" + private ServerDeployExtension serverDeploy + String deployDir + String embeddedDir + String stagingDir @Override void apply(Project project) { serverDeploy = project.extensions.create("serverDeploy", ServerDeployExtension) - serverDeploy.dir = ServerDeployExtension.getServerDeployDirectory(project) - serverDeploy.embeddedDir = ServerDeployExtension.getEmbeddedServerDeployDirectory(project) - serverDeploy.modulesDir = "${serverDeploy.dir}/modules" - serverDeploy.webappDir = "${serverDeploy.dir}/labkeyWebapp" - serverDeploy.binDir = "${serverDeploy.dir}/bin" - serverDeploy.pipelineLibDir = "${serverDeploy.dir}/pipelineLib" + deployDir = ServerDeployExtension.getServerDeployDirectoryPath(project) + embeddedDir = ServerDeployExtension.getEmbeddedServerDeployDirectoryPath(project) + stagingDir = BuildUtils.getRootBuildDirFile(project, STAGING_DIR) project.apply plugin: 'org.labkey.build.base' // we depend on the jar task from the embedded project, if available @@ -66,6 +70,17 @@ class ServerDeploy implements Plugin project.evaluationDependsOn(BuildUtils.getEmbeddedProjectPath(project.gradle)) addTasks(project) + addConfigurations(project) + } + + private void addConfigurations(project) + { + project.configurations + { + builtModules + downloadedModules + } + } private void addTasks(Project project) @@ -73,77 +88,32 @@ class ServerDeploy implements Plugin project.tasks.register("deployApp", DeployApp) { DeployApp task -> task.group = GroupNames.DEPLOY - task.description = "Deploy the application locally into ${serverDeploy.dir}" - task.doLast( { - BuildUtils.updateRestartTriggerFile(project) - } ) + task.description = "Deploy the application locally into ${deployDir}" + task.binaries.setFrom(project.configurations.binaries) + task.notCompatibleWithConfigurationCache("TODO 'cannot serialize project' error, but unclear where it comes from") } - StagingExtension staging = project.getExtensions().getByType(StagingExtension.class) - - // The staging step complicates things, but it is currently needed for the following reasons: - // - We want to make sure tomcat doesn't restart multiple times when deploying the application. - // (seems like it could be avoided as the copy being done here is just as atomic as the copy from deployModules) - project.tasks.register("stageModules") { - Task task -> + project.tasks.register("stageModules", StageModules) { + StageModules task -> task.group = GroupNames.DEPLOY - task.description = "Stage the modules for the application into ${staging.dir}" - task.doFirst({ - project.delete staging.modulesDir - }) - task.doLast( { - // copy over the module dependencies first (things not built from source that might bring in - // transitive dependencies) - FileCollection remoteModules = project.configurations.modules.fileCollection ({ - Dependency dependency -> dependency instanceof DefaultExternalModuleDependency - }) - if (!remoteModules.isEmpty()) - { - project.ant.copy( - todir: staging.modulesDir, - preserveLastModified: true // this is important so we don't re-explode modules that have not changed - ) - { - remoteModules.addToAntBuilder(project.ant, "fileset", FileCollection.AntType.FileSet) - } - } - - // Then copy over the project dependencies (things built from source) so they will replace - // any transitive dependencies that were brought in). - // One might like to do this overriding/overwriting using DependencySubstitution, as that is very much - // what it is designed for, but that allows substitution of a project for an ExternalModuleDependency - // and since a .module file is only one of the artifacts produced by our projects (e.g., :server:modules:platform:experiment) - // and is not the default artifact, DependencySubstitution does not seem to work. - FileCollection localModules = project.configurations.modules.fileCollection ({ - Dependency dependency -> dependency instanceof DefaultProjectDependency - }) - if (!localModules.isEmpty()) - { - project.ant.copy( - overwrite: true, // overwrite existing files even if the destination files are newer - todir: staging.modulesDir, - preserveLastModified: true // this is important so we don't re-explode modules that have not changed - ) - { - localModules.addToAntBuilder(project.ant, "fileset", FileCollection.AntType.FileSet) - } - } - }) + task.description = "Stage the modules for the application into ${stagingDir}" + task.downloadedModules.setFrom(project.configurations.downloadedModules) + task.builtModules.setFrom(project.configurations.builtModules) } - project.tasks.named('stageModules').configure {dependsOn project.configurations.modules} project.tasks.register("checkModuleVersions", CheckForVersionConflicts) { CheckForVersionConflicts task -> - task.directory = new File(serverDeploy.modulesDir) + String modulesDir = "${deployDir}/${MODULES_DIR}" + task.directory = new File(modulesDir) task.extension = "module" task.cleanTask = ":server:cleanDeploy" task.collection = project.configurations.modules task.group = GroupNames.DEPLOY task.description = "Check for conflicts in version numbers of module files to be deployed and files in the deploy directory. " + "Default action on detecting a conflict is to fail. Use -PversionConflictAction=[delete|fail|warn] to change this behavior. The value 'delete' will cause the " + - "conflicting version(s) in the ${serverDeploy.modulesDir} directory to be removed." + "conflicting version(s) in the ${modulesDir} directory to be removed." task.onlyIf({ - return new File(serverDeploy.modulesDir).exists() + return new File(modulesDir).exists() }) } @@ -180,17 +150,20 @@ class ServerDeploy implements Plugin linkBinaries(project, "yarn", project.yarnVersion, project.yarnWorkDirectory) }) } + project.tasks.symlinkNode.notCompatibleWithConfigurationCache("References project properties. Need to add task class with input properties") project.tasks.named('deployApp').configure {dependsOn(project.tasks.symlinkNode)} } + String stagingPipelineLibDir = BuildUtils.getRootBuildDirFile(project, STAGING_PIPELINE_DIR) + project.tasks.register("stageRemotePipelineJars") { Task task -> task.group = GroupNames.DEPLOY - task.description = "Copy files needed for using remote pipeline jobs into ${staging.pipelineLibDir}" + task.description = "Copy files needed for using remote pipeline jobs into ${stagingPipelineLibDir}" task.doLast({ if (!project.configurations.remotePipelineJars.getFiles().isEmpty()) { - project.ant.copy( - todir: staging.pipelineLibDir, + task.ant.copy( + todir: stagingPipelineLibDir, preserveLastModified: true ) { @@ -204,13 +177,16 @@ class ServerDeploy implements Plugin }) } - project.tasks.named('stageRemotePipelineJars').configure {dependsOn project.configurations.remotePipelineJars} + project.tasks.named('stageRemotePipelineJars').configure { + dependsOn project.configurations.remotePipelineJars + notCompatibleWithConfigurationCache("TODO Needs dedicated task class with configuration as input") + } project.tasks.register( "stageApp") { Task task -> task.group = GroupNames.DEPLOY - task.description = "Stage modules and jar files into ${staging.dir}" + task.description = "Stage modules and jar files into ${stagingDir}" task.dependsOn project.tasks.stageModules task.dependsOn project.tasks.stageRemotePipelineJars } @@ -235,9 +211,9 @@ class ServerDeploy implements Plugin project.tasks.register("cleanEmbeddedDeploy", DefaultTask) { DefaultTask task -> task.group = GroupNames.DEPLOY - task.description = "Remove the ${project.serverDeploy.embeddedDir} directory" + task.description = "Remove the ${embeddedDir} directory" task.doLast { - project.delete project.serverDeploy.embeddedDir + project.delete embeddedDir } } project.tasks.named('deployApp').configure { @@ -246,7 +222,7 @@ class ServerDeploy implements Plugin project.copy { CopySpec copy -> copy.from embeddedProject.tasks.bootJar - copy.into project.serverDeploy.embeddedDir + copy.into embeddedDir copy.setDuplicatesStrategy(DuplicatesStrategy.INCLUDE) } } @@ -271,6 +247,7 @@ class ServerDeploy implements Plugin task.group = GroupNames.DISTRIBUTION task.description = "Extract the executable jar from a distribution and put it and the included binaries in the appropriate deploy directory" task.dependsOn(project.tasks.cleanEmbeddedDeploy, project.tasks.setup) + task.binaries.setFrom(project.configurations.binaries) } // This may prevent multiple Tomcat restarts @@ -286,9 +263,9 @@ class ServerDeploy implements Plugin 'cleanStaging',Delete) { Delete task -> task.group = GroupNames.DEPLOY - task.description = "Removes the staging directory ${staging.dir}" + task.description = "Removes the staging directory ${stagingDir}" task.configure({ DeleteSpec spec -> - spec.delete staging.dir + spec.delete stagingDir }) } @@ -296,10 +273,10 @@ class ServerDeploy implements Plugin 'cleanDeploy', Delete) { Delete task -> task.group = GroupNames.DEPLOY - task.description = "Removes the deploy directory ${serverDeploy.dir}" + task.description = "Removes the deploy directory ${deployDir}" task.dependsOn (project.tasks.cleanStaging) task.configure({ DeleteSpec spec -> - spec.delete serverDeploy.dir + spec.delete deployDir }) } project.tasks.named('deployApp').configure {mustRunAfter(project.tasks.cleanDeploy)} @@ -307,8 +284,10 @@ class ServerDeploy implements Plugin project.tasks.register("cleanAndDeploy", DeployApp) { DeployApp task -> task.group = GroupNames.DEPLOY - task.description = "Removes the deploy directory ${serverDeploy.dir} then deploys the application locally" + task.binaries.setFrom(project.configurations.binaries) + task.description = "Removes the deploy directory ${deployDir} then deploys the application locally" task.dependsOn(project.tasks.cleanDeploy) + task.notCompatibleWithConfigurationCache("TODO 'cannot serialize project' error, but unclear where it comes from") } project.tasks.register("cleanBuild", Delete) { @@ -321,36 +300,6 @@ class ServerDeploy implements Plugin } project.tasks.named('deployApp').configure {mustRunAfter(project.tasks.cleanBuild)} - // TODO is this still useful? - project.tasks.register( - 'checkModuleTasks', DefaultTask) { - DefaultTask task -> - task.group = GroupNames.MODULE - task.description = "Verify that all modules with module.properties files have a module task" - task.doLast({ - String[] projectsMissingTasks = [] - project.subprojects({ - Project sub -> - if (sub.file("module.properties").exists()) { - try { - sub.tasks.named("module") - } catch (UnknownTaskException ignore) { - projectsMissingTasks += sub.path - } - } - }) - if (projectsMissingTasks.length > 0) - project.logger.quiet("Each of the following projects has a 'module.properties' file but no 'module' task. " + - "These modules will not be included in the deployed server. " + - "You should apply either the 'org.labkey.build.fileModule' or 'org.labkey.build.module' plugin in each project's 'build.gradle' file. " + - "See https://www.labkey.org/Documentation/wiki-page.view?name=gradleModules for more information.\n\t" + - "${projectsMissingTasks.join("\n\t")}") - - }) - task.notCompatibleWithConfigurationCache("Needs to walk the project tree") - } - project.tasks.named('deployApp').configure {dependsOn(project.tasks.named("checkModuleTasks"))} - project.tasks.named('checkModuleTasks').configure {mustRunAfter(project.tasks.stageApp)} // do this so the message appears at the bottom of the output project.tasks.named("cleanBuild").configure { it.dependsOn(project.tasks.stopTomcat) } diff --git a/src/main/groovy/org/labkey/gradle/plugin/TeamCity.groovy b/src/main/groovy/org/labkey/gradle/plugin/TeamCity.groovy index ad916dd3..240c0c28 100644 --- a/src/main/groovy/org/labkey/gradle/plugin/TeamCity.groovy +++ b/src/main/groovy/org/labkey/gradle/plugin/TeamCity.groovy @@ -164,7 +164,7 @@ class TeamCity extends Tomcat return newLine } ) - task.destinationDir = new File("${ServerDeployExtension.getServerDeployDirectory(project)}/config") + task.destinationDir = new File("${ServerDeployExtension.getServerDeployDirectoryPath(project)}/config") } diff --git a/src/main/groovy/org/labkey/gradle/plugin/Tomcat.groovy b/src/main/groovy/org/labkey/gradle/plugin/Tomcat.groovy index 9331e19f..4383d899 100644 --- a/src/main/groovy/org/labkey/gradle/plugin/Tomcat.groovy +++ b/src/main/groovy/org/labkey/gradle/plugin/Tomcat.groovy @@ -79,7 +79,7 @@ class Tomcat implements Plugin project.tasks.register("cleanLogs", Delete) { Delete task -> - var logDir = "${ServerDeployExtension.getEmbeddedServerDeployDirectory(project)}/logs" + var logDir = "${ServerDeployExtension.getEmbeddedServerDeployDirectoryPath(project)}/logs" task.group = GroupNames.WEB_APPLICATION task.description = "Delete logs from ${logDir}" task.configure { DeleteSpec spec -> spec.delete project.fileTree(logDir) } diff --git a/src/main/groovy/org/labkey/gradle/plugin/Webapp.groovy b/src/main/groovy/org/labkey/gradle/plugin/Webapp.groovy index d013bb88..13c68aeb 100644 --- a/src/main/groovy/org/labkey/gradle/plugin/Webapp.groovy +++ b/src/main/groovy/org/labkey/gradle/plugin/Webapp.groovy @@ -59,11 +59,11 @@ class Webapp implements Plugin else { // We should only redistribute the ExtJS resource files, not the full dev kit - exclude "${project.labkey.ext3Dir}/src/**" - exclude "${project.labkey.ext4Dir}/builds/**" - exclude "${project.labkey.ext4Dir}/cmd/**" - exclude "${project.labkey.ext4Dir}/locale/**" - exclude "${project.labkey.ext4Dir}/src/**" + exclude "${LabKeyExtension.ext3Dir}/src/**" + exclude "${LabKeyExtension.ext4Dir}/builds/**" + exclude "${LabKeyExtension.ext4Dir}/cmd/**" + exclude "${LabKeyExtension.ext4Dir}/locale/**" + exclude "${LabKeyExtension.ext4Dir}/src/**" exclude "d3/examples/**" exclude "d3/test/**" } diff --git a/src/main/groovy/org/labkey/gradle/plugin/extension/LabKeyExtension.groovy b/src/main/groovy/org/labkey/gradle/plugin/extension/LabKeyExtension.groovy index d0fac3a6..5740970b 100644 --- a/src/main/groovy/org/labkey/gradle/plugin/extension/LabKeyExtension.groovy +++ b/src/main/groovy/org/labkey/gradle/plugin/extension/LabKeyExtension.groovy @@ -58,8 +58,8 @@ class LabKeyExtension String srcGenDir String externalDir - String ext3Dir = "ext-3.4.1" - String ext4Dir = "ext-4.2.1" + public static final String ext3Dir = "ext-3.4.1" + public static final String ext4Dir = "ext-4.2.1" static String getDeployModeName(Project project) { diff --git a/src/main/groovy/org/labkey/gradle/plugin/extension/ServerDeployExtension.groovy b/src/main/groovy/org/labkey/gradle/plugin/extension/ServerDeployExtension.groovy index 8e693c0a..1a24fe54 100644 --- a/src/main/groovy/org/labkey/gradle/plugin/extension/ServerDeployExtension.groovy +++ b/src/main/groovy/org/labkey/gradle/plugin/extension/ServerDeployExtension.groovy @@ -16,31 +16,37 @@ package org.labkey.gradle.plugin.extension import org.gradle.api.Project +import org.gradle.api.file.Directory +import org.labkey.gradle.plugin.ServerDeploy import org.labkey.gradle.util.BuildUtils class ServerDeployExtension { - String dir - String embeddedDir - String modulesDir - String webappDir - String binDir - String pipelineLibDir Map foundModules = new HashMap<>(); - static String getServerDeployDirectory(Project project) + static String getServerDeployDirectoryPath(Project project) { - return BuildUtils.getRootBuildDirFile(project, "deploy").path + return BuildUtils.getRootBuildDirFile(project, ServerDeploy.DEPLOY_DIR).path } - static String getEmbeddedServerDeployDirectory(Project project) + static String getEmbeddedServerDeployDirectoryPath(Project project) { - return "${getServerDeployDirectory(project)}/embedded" + return "${getServerDeployDirectoryPath(project)}/embedded" + } + + static Directory getEmbeddedDir(Project project) + { + return project.rootProject.layout.buildDirectory.dir(ServerDeploy.DEPLOY_DIR + "/embedded").get() + } + + static Directory getEmbeddedBinDir(Project project) + { + return project.rootProject.layout.buildDirectory.dir(ServerDeploy.DEPLOY_DIR + "/embedded/bin").get() } static String getModulesDeployDirectory(Project project) { - return "${getServerDeployDirectory(project)}/modules" + return "${getServerDeployDirectoryPath(project)}/${ServerDeploy.MODULES_DIR}" } String getFoundModule(String key) diff --git a/src/main/groovy/org/labkey/gradle/plugin/extension/StagingExtension.groovy b/src/main/groovy/org/labkey/gradle/plugin/extension/StagingExtension.groovy deleted file mode 100644 index cf5f0a72..00000000 --- a/src/main/groovy/org/labkey/gradle/plugin/extension/StagingExtension.groovy +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2017-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.extension - -import org.gradle.api.Project -import org.labkey.gradle.util.BuildUtils - -class StagingExtension -{ - public static final String STAGING_DIR = "staging" - public static final String STAGING_MODULES_DIR = "${STAGING_DIR}/modules/" - public static final String STAGING_WEBAPP_DIR = "${STAGING_DIR}/labkeyWebapp" - public static final String STAGING_WEBINF_DIR = "${STAGING_WEBAPP_DIR}/WEB-INF/" - - String dir - String webappClassesDir - String jspDir - String webInfDir - String webappDir - String modulesDir - String pipelineLibDir - - void setDirectories(Project project) - { - String buildDirPath = BuildUtils.getRootBuildDirPath(project) - dir = "${buildDirPath}/${STAGING_DIR}" - webappClassesDir = "${buildDirPath}/${STAGING_WEBINF_DIR}/classes" - jspDir = "${buildDirPath}/${STAGING_WEBINF_DIR}/jsp" - webInfDir = "${buildDirPath}/${STAGING_WEBINF_DIR}" - webappDir = "${buildDirPath}/${STAGING_WEBAPP_DIR}" - modulesDir = "${buildDirPath}/${STAGING_MODULES_DIR}" - pipelineLibDir = "${dir}/pipelineLib" - } -} diff --git a/src/main/groovy/org/labkey/gradle/plugin/extension/TeamCityExtension.groovy b/src/main/groovy/org/labkey/gradle/plugin/extension/TeamCityExtension.groovy index b7241f96..0f27bf3c 100644 --- a/src/main/groovy/org/labkey/gradle/plugin/extension/TeamCityExtension.groovy +++ b/src/main/groovy/org/labkey/gradle/plugin/extension/TeamCityExtension.groovy @@ -17,7 +17,6 @@ package org.labkey.gradle.plugin.extension import org.apache.commons.io.FileUtils import org.gradle.api.Project -import org.labkey.gradle.util.BuildUtils import org.labkey.gradle.util.DatabaseProperties import java.nio.charset.StandardCharsets @@ -116,7 +115,7 @@ class TeamCityExtension } File startupPropertiesDir() { - File startupDir = new File(new File(ServerDeployExtension.getEmbeddedServerDeployDirectory(project)), 'startup') + File startupDir = new File(new File(ServerDeployExtension.getEmbeddedServerDeployDirectoryPath(project)), 'startup') FileUtils.forceMkdir(startupDir) return startupDir } diff --git a/src/main/groovy/org/labkey/gradle/task/DeployApp.groovy b/src/main/groovy/org/labkey/gradle/task/DeployApp.groovy index 442557e5..0c248636 100644 --- a/src/main/groovy/org/labkey/gradle/task/DeployApp.groovy +++ b/src/main/groovy/org/labkey/gradle/task/DeployApp.groovy @@ -16,55 +16,60 @@ package org.labkey.gradle.task import org.gradle.api.file.CopySpec +import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.DuplicatesStrategy import org.gradle.api.tasks.InputDirectory import org.gradle.api.tasks.OutputDirectory import org.gradle.api.tasks.TaskAction +import org.labkey.gradle.plugin.ServerDeploy +import org.labkey.gradle.util.BuildUtils -class DeployApp extends DeployAppBase +abstract class DeployApp extends DeployAppBase { @InputDirectory - File stagingModulesDir = new File((String) project.staging.modulesDir) + final abstract DirectoryProperty stagingModulesDir = BuildUtils.getRootBuildDirectoryProperty(project, ServerDeploy.STAGING_MODULES_DIR) @InputDirectory - File stagingPipelineJarDir = new File((String) project.staging.pipelineLibDir) + final abstract DirectoryProperty stagingPipelineJarDir = BuildUtils.getRootBuildDirectoryProperty(project, ServerDeploy.STAGING_PIPELINE_DIR) @OutputDirectory - File deployModulesDir = new File((String) project.serverDeploy.modulesDir) + final abstract DirectoryProperty deployModulesDir = BuildUtils.getRootBuildDirectoryProperty(project, ServerDeploy.MODULES_DIR) + // We declare this as an output so it will be created by this task, even though not actually populated here @OutputDirectory - File deployWebappDir = new File((String) project.serverDeploy.webappDir) + final abstract DirectoryProperty deployWebappDir = BuildUtils.getRootBuildDirectoryProperty(project, ServerDeploy.WEBAPP_DIR) @OutputDirectory - File deployPipelineLibDir = new File((String) project.serverDeploy.pipelineLibDir) + final abstract DirectoryProperty deployPipelineLibDir = BuildUtils.getRootBuildDirectoryProperty(project, ServerDeploy.PIPELINE_DIR) @OutputDirectory - File deployBinDir = new File((String) project.serverDeploy.binDir) + final abstract DirectoryProperty deployBinDir = BuildUtils.getRootBuildDirectoryProperty(project, ServerDeploy.BIN_DIR) @TaskAction void action() { deployModules() deployPipelineJars() - deployPlatformBinaries(deployBinDir) + deployPlatformBinaries(deployBinDir.get().asFile) + updateRestartTriggerFile() } private void deployModules() { ant.copy ( - todir: deployModulesDir, + todir: deployModulesDir.get().asFile, preserveLastModified: true, ) { - fileset(dir: stagingModulesDir) + fileset(dir: stagingModulesDir.get().asFile) } } private void deployPipelineJars() { - project.copy( { CopySpec copy -> - copy.from stagingPipelineJarDir - copy.into deployPipelineLibDir + fs.copy( { CopySpec copy -> + copy.from stagingPipelineJarDir.get().asFile + copy.into deployPipelineLibDir.get().asFile copy.setDuplicatesStrategy(DuplicatesStrategy.INCLUDE) }) } diff --git a/src/main/groovy/org/labkey/gradle/task/DeployAppBase.groovy b/src/main/groovy/org/labkey/gradle/task/DeployAppBase.groovy index 21f2ef10..f1efbc63 100644 --- a/src/main/groovy/org/labkey/gradle/task/DeployAppBase.groovy +++ b/src/main/groovy/org/labkey/gradle/task/DeployAppBase.groovy @@ -1,34 +1,47 @@ package org.labkey.gradle.task import org.apache.commons.lang3.SystemUtils -import org.gradle.api.DefaultTask +import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.file.CopySpec +import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.DuplicatesStrategy +import org.gradle.api.file.FileSystemOperations +import org.gradle.api.tasks.InputFiles +import org.gradle.api.tasks.OutputDirectory +import org.labkey.gradle.util.BuildUtils -class DeployAppBase extends DefaultTask { +import javax.inject.Inject - private File _externalDir = new File((String) project.labkey.externalDir) +abstract class DeployAppBase extends RestartTriggerTask { + + @Inject abstract FileSystemOperations getFs() + + @InputFiles + abstract ConfigurableFileCollection getBinaries() + + @OutputDirectory + final abstract DirectoryProperty _externalDir = BuildUtils.getRootBuildDirectoryProperty(project, "external") protected void deployPlatformBinaries(File deployBinDir) { deployBinDir.mkdirs() - if (project.configurations.findByName("binaries") != null) + if (getBinaries() != null && !getBinaries().isEmpty()) { - project.logger.debug("Copying from binaries configuration to ${deployBinDir}") - project.copy({ + this.logger.debug("Copying from binaries configuration to ${deployBinDir}") + fs.copy({ CopySpec copy -> copy.setDuplicatesStrategy(DuplicatesStrategy.EXCLUDE) - copy.from(project.configurations.binaries.collect { project.zipTree(it) }) + copy.from(getBinaries().collect { project.zipTree(it) }) copy.into deployBinDir.path }) - project.logger.debug("Contents of ${deployBinDir}\n" + deployBinDir.listFiles()) + this.logger.debug("Contents of ${deployBinDir}\n" + deployBinDir.listFiles()) } // For TC builds, we deposit the artifacts of the Linux TPP Tools and Windows Proteomics Tools into // the external directory, so we want to copy those over as well. // TODO: package the output of these builds into the Artifactory artifact to simplify - if (project.file(_externalDir).exists()) { - project.logger.info("Copying from ${_externalDir} to ${project.serverDeploy.binDir}") + if (_externalDir.get().asFile.exists()) { + this.logger.info("Copying from ${_externalDir.get()} to ${deployBinDir}") if (SystemUtils.IS_OS_MAC) deployBinariesViaProjectCopy("osx", deployBinDir) else if (SystemUtils.IS_OS_LINUX) @@ -41,7 +54,7 @@ class DeployAppBase extends DefaultTask { // Use this method to preserve file permissions, since ant.copy does not, but this does not preserve last modified times private void deployBinariesViaProjectCopy(String osDirectory, File deployBinDir) { - File parentDir = new File(_externalDir, "${osDirectory}") + File parentDir = new File(_externalDir.get().asFile, "${osDirectory}") if (parentDir.exists()) { List subDirs = parentDir.listFiles new FileFilter() { @@ -51,7 +64,7 @@ class DeployAppBase extends DefaultTask { } } for (File dir : subDirs) { - project.copy { CopySpec copy -> + fs.copy { CopySpec copy -> copy.from dir copy.into deployBinDir.getPath() copy.setDuplicatesStrategy(DuplicatesStrategy.EXCLUDE) @@ -62,8 +75,8 @@ class DeployAppBase extends DefaultTask { private void deployBinariesViaAntCopy(String osDirectory, File deployBinDir) { - def fromDir = "${_externalDir}/${osDirectory}" - if (project.file(fromDir).exists()) + File fromDir = _externalDir.get().file(osDirectory).asFile + if (fromDir.exists()) { ant.copy( todir: deployBinDir.getPath(), @@ -71,7 +84,7 @@ class DeployAppBase extends DefaultTask { ) { ant.cutdirsmapper(dirs: 1) - fileset(dir: fromDir) + fileset(dir: fromDir.path) { exclude(name: "**.*") } diff --git a/src/main/groovy/org/labkey/gradle/task/DeployDistribution.groovy b/src/main/groovy/org/labkey/gradle/task/DeployDistribution.groovy index e6ff80ca..a5ffaa7c 100644 --- a/src/main/groovy/org/labkey/gradle/task/DeployDistribution.groovy +++ b/src/main/groovy/org/labkey/gradle/task/DeployDistribution.groovy @@ -1,37 +1,40 @@ package org.labkey.gradle.task import org.gradle.api.file.CopySpec +import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.DuplicatesStrategy import org.gradle.api.tasks.OutputDirectory import org.gradle.api.tasks.TaskAction +import org.labkey.gradle.plugin.ServerDeploy import org.labkey.gradle.plugin.extension.DistributionExtension +import org.labkey.gradle.plugin.extension.ServerDeployExtension -class DeployDistribution extends DeployAppBase { +abstract class DeployDistribution extends DeployAppBase { @OutputDirectory - File deployDir = new File((String) project.serverDeploy.embeddedDir) + final abstract DirectoryProperty deployDir = project.objects.directoryProperty().convention(ServerDeployExtension.getEmbeddedDir(project)) @OutputDirectory - File deployBinDir = new File((String) project.serverDeploy.embeddedDir, "bin") + final abstract DirectoryProperty deployBinDir = project.objects.directoryProperty().convention(ServerDeployExtension.getEmbeddedBinDir(project)) @TaskAction void action() { deployExecutableJar() - deployPlatformBinaries(deployBinDir) + deployPlatformBinaries(deployBinDir.get().asFile) } private void deployExecutableJar() { File distributionFile = DistributionExtension.getDistributionFile(project) - project.copy({ CopySpec copy -> + fs.copy({ CopySpec copy -> copy.from project.tarTree(distributionFile).files - copy.into deployDir + copy.into deployDir.get() copy.include "*.jar" copy.setDuplicatesStrategy(DuplicatesStrategy.INCLUDE) }) - project.copy({ CopySpec copy -> + fs.copy({ CopySpec copy -> copy.from project.tarTree(distributionFile).files - copy.into deployBinDir + copy.into deployBinDir.get() copy.include "*.exe", "*.dll" copy.setDuplicatesStrategy(DuplicatesStrategy.INCLUDE) }) diff --git a/src/main/groovy/org/labkey/gradle/task/DoThenSetup.groovy b/src/main/groovy/org/labkey/gradle/task/DoThenSetup.groovy index 59a8ce45..6d18fc7c 100644 --- a/src/main/groovy/org/labkey/gradle/task/DoThenSetup.groovy +++ b/src/main/groovy/org/labkey/gradle/task/DoThenSetup.groovy @@ -29,28 +29,18 @@ import org.labkey.gradle.util.PropertiesUtils import java.util.function.Function +// TODO making this extend from RestartTriggerTask causes the following error on TeamCity: +// Cannot fingerprint input property 'databaseProperties': value 'org.labkey.gradle.util.DatabaseProperties@286cb41a' cannot be serialized. +// Even though RestartTriggerTask has nothing to do with the `databaseProperties` class DoThenSetup extends DefaultTask { + // TODO rethink this input declaration. Dependence on a file makes more sense @Optional @Input protected DatabaseProperties databaseProperties + @Input boolean dbPropertiesChanged = false - private static boolean canCreate(File file) - { - file = file.getParentFile() - - while (file != null) - { - if (file.exists()) - { - return file.canWrite() && file.canRead() - } - file = file.getParentFile() - } - return false - } - protected void doDatabaseTask() { setDatabaseProperties() diff --git a/src/main/groovy/org/labkey/gradle/task/JspCompile2Java.groovy b/src/main/groovy/org/labkey/gradle/task/JspCompile2Java.groovy index 660a1c0c..7a316189 100644 --- a/src/main/groovy/org/labkey/gradle/task/JspCompile2Java.groovy +++ b/src/main/groovy/org/labkey/gradle/task/JspCompile2Java.groovy @@ -21,8 +21,16 @@ import org.gradle.api.GradleException import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.FileSystemOperations -import org.gradle.api.tasks.* -import org.labkey.gradle.util.BuildUtils +import org.gradle.api.provider.Property +import org.gradle.api.tasks.CacheableTask +import org.gradle.api.tasks.CompileClasspath +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.InputDirectory +import org.gradle.api.tasks.InputFiles +import org.gradle.api.tasks.OutputDirectory +import org.gradle.api.tasks.PathSensitive +import org.gradle.api.tasks.PathSensitivity +import org.gradle.api.tasks.TaskAction import javax.inject.Inject @@ -33,6 +41,12 @@ abstract class JspCompile2Java extends DefaultTask private FileSystemOperations fileSystemOperations + @Input + final abstract Property targetCompatibility = project.objects.property(String).convention((String) project.property('targetCompatibility')) + + @Input + final abstract Property sourceCompatibility = project.objects.property(String).convention((String) project.property('sourceCompatibility')) + @PathSensitive(PathSensitivity.RELATIVE) @InputDirectory File webappDirectory @@ -51,11 +65,11 @@ abstract class JspCompile2Java extends DefaultTask @TaskAction - void compile() { - + void compile() + { if (!webappDirectory.exists()) { - project.logger.info("${webappDirectory.getAbsolutePath()}: no such file or directory. Nothing to do here.") + logger.info("${webappDirectory.getAbsolutePath()}: no such file or directory. Nothing to do here.") return } @@ -85,8 +99,8 @@ abstract class JspCompile2Java extends DefaultTask uriroot: "${webappDirectory.getAbsolutePath()}", outputDir: getClassesDirectory().get(), package: "org.labkey.jsp.compiled", - compilerTargetVM: project.targetCompatibility, - compilerSourceVM: project.sourceCompatibility, + compilerTargetVM: targetCompatibility.get(), + compilerSourceVM: sourceCompatibility.get(), compile: false, listErrors: true ) diff --git a/src/main/groovy/org/labkey/gradle/task/PickDb.groovy b/src/main/groovy/org/labkey/gradle/task/PickDb.groovy index d7b604f2..03972edd 100644 --- a/src/main/groovy/org/labkey/gradle/task/PickDb.groovy +++ b/src/main/groovy/org/labkey/gradle/task/PickDb.groovy @@ -17,12 +17,17 @@ package org.labkey.gradle.task import org.gradle.api.file.CopySpec import org.gradle.api.file.DuplicatesStrategy +import org.gradle.api.file.FileSystemOperations import org.gradle.api.tasks.Input import org.gradle.api.tasks.InputDirectory import org.labkey.gradle.util.BuildUtils -class PickDb extends DoThenSetup +import javax.inject.Inject + +abstract class PickDb extends DoThenSetup { + @Inject abstract FileSystemOperations getFs() + @Input String dbType @@ -33,7 +38,7 @@ class PickDb extends DoThenSetup protected void doDatabaseTask() { //copies the correct config file. - project.copy({ CopySpec copy -> + fs.copy({ CopySpec copy -> copy.from configsDir copy.into configsDir.parent copy.include "${dbType}.properties" diff --git a/src/main/groovy/org/labkey/gradle/task/RestartTriggerTask.groovy b/src/main/groovy/org/labkey/gradle/task/RestartTriggerTask.groovy new file mode 100644 index 00000000..891aec07 --- /dev/null +++ b/src/main/groovy/org/labkey/gradle/task/RestartTriggerTask.groovy @@ -0,0 +1,37 @@ +package org.labkey.gradle.task + +import org.gradle.api.DefaultTask +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.OutputDirectory +import org.labkey.gradle.util.BuildUtils + +import java.nio.charset.StandardCharsets +import java.text.SimpleDateFormat + +abstract class RestartTriggerTask extends DefaultTask +{ + public static final String RESTART_FILE_NAME = ".restartTrigger" + + @Input + final abstract Property useLocalBuild = project.objects.property(String).convention(project.hasProperty('useLocalBuild') ? (String) project.property('useLocalBuild') : null) + + @OutputDirectory + final abstract DirectoryProperty triggerFileDir = BuildUtils.getRootBuildDirectoryProperty(project, "deploy/modules") + + void updateRestartTriggerFile() + { + if (useLocalBuild.get() == null || "false" == useLocalBuild.get()) + return + + if (!triggerFileDir.get().asFile.exists()) + return + + File triggerFile = new File(triggerFileDir.get().asFile, RESTART_FILE_NAME) + try (OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(triggerFile), StandardCharsets.UTF_8)) + { + writer.write(SimpleDateFormat.getDateTimeInstance().format(new Date())) + } + } +} diff --git a/src/main/groovy/org/labkey/gradle/task/ServerSideJS.groovy b/src/main/groovy/org/labkey/gradle/task/ServerSideJS.groovy index 4c810545..77637574 100644 --- a/src/main/groovy/org/labkey/gradle/task/ServerSideJS.groovy +++ b/src/main/groovy/org/labkey/gradle/task/ServerSideJS.groovy @@ -17,15 +17,17 @@ package org.labkey.gradle.task import org.gradle.api.DefaultTask import org.gradle.api.GradleException +import org.gradle.api.file.DirectoryProperty import org.gradle.api.tasks.InputDirectory import org.gradle.api.tasks.OutputDirectory import org.gradle.api.tasks.TaskAction +import org.labkey.gradle.plugin.extension.LabKeyExtension import org.labkey.gradle.util.BuildUtils /** * N.B. This task requires that you have the platform/api project source as it needs access to directories in that project */ -class ServerSideJS extends DefaultTask +abstract class ServerSideJS extends DefaultTask { @InputDirectory File scriptFragmentsDir = project.file("script-fragments") @@ -33,6 +35,14 @@ class ServerSideJS extends DefaultTask @OutputDirectory File scriptsDir = project.file("resources/scripts") + @InputDirectory + final abstract DirectoryProperty ext3SrcDir = project.objects.directoryProperty() + .convention(project.project(BuildUtils.getApiProjectPath(project.gradle)).layout.projectDirectory.dir("webapp/${LabKeyExtension.ext3Dir}/src")) + + @InputDirectory + final abstract DirectoryProperty ext4SrcDir = project.objects.directoryProperty() + .convention(project.project(BuildUtils.getApiProjectPath(project.gradle)).layout.projectDirectory.dir("webapp/${LabKeyExtension.ext4Dir}/src")) + @TaskAction void action() { @@ -43,22 +53,21 @@ class ServerSideJS extends DefaultTask // create combined Ext.js usable by the core module's server-side scripts private void concatenateExt3JsFiles() { - - File ext3SrcDir = project.project(BuildUtils.getApiProjectPath(project.gradle)).file("webapp/${project.labkey.ext3Dir}/src") - if (!ext3SrcDir.exists()) - throw new GradleException("Unable to create server-side javascript files. Missing ext3 source directory: ${ext3SrcDir}") + File srcDir = ext3SrcDir.get().asFile + if (!srcDir.exists()) + throw new GradleException("Unable to create server-side javascript files. Missing ext3 source directory: ${srcDir}") if (!scriptsDir.canWrite()) throw new GradleException("Unable to create server-side javascript files. Output directory ${scriptsDir} not writable.") ant.concat(destFile: "${scriptsDir}/Ext.js", force: true) { header(file: "${scriptFragmentsDir}/Ext.header.js") - fileset(file: new File(ext3SrcDir, "Ext.js")) + fileset(file: new File(srcDir, "Ext.js")) fileset(file: "${scriptFragmentsDir}/Ext.middle.js") - fileset(file: new File(ext3SrcDir, "Observable.js")) - fileset(file: new File(ext3SrcDir, "JSON.js")) - fileset(file: new File(ext3SrcDir, "Connection.js")) - fileset(file: new File(ext3SrcDir, "Format.js")) + fileset(file: new File(srcDir, "Observable.js")) + fileset(file: new File(srcDir, "JSON.js")) + fileset(file: new File(srcDir, "Connection.js")) + fileset(file: new File(srcDir, "Format.js")) footer(file: "${scriptFragmentsDir}/Ext.footer.js") } File destFile = new File("${scriptsDir}/Ext.js") @@ -69,24 +78,24 @@ class ServerSideJS extends DefaultTask // create a combined Ext4.js usable by the core module's server-side scripts private void concatenateExt4JsFiles() { - File ext4SrcDir = project.project(BuildUtils.getApiProjectPath(project.gradle)).file("webapp/${project.labkey.ext4Dir}/src") - if (!ext4SrcDir.exists()) - throw new GradleException("Unable to create server-side javascript files. Missing ext4 source directory: ${ext4SrcDir}") + File srcDir = ext4SrcDir.get().asFile + if (!srcDir.exists()) + throw new GradleException("Unable to create server-side javascript files. Missing ext4 source directory: ${srcDir}") if (!scriptsDir.canWrite()) throw new GradleException("Unable to create server-side javascript files. Output directory ${scriptsDir} not writable.") ant.concat(destFile: "${scriptsDir}/Ext4.js", force: true) { header(file: "${scriptFragmentsDir}/Ext4.header.js") - fileset(file: new File(ext4SrcDir, "Ext.js")) - fileset(file: new File(ext4SrcDir, "lang/Array.js")) - fileset(file: new File(ext4SrcDir, "lang/Date.js")) - fileset(file: new File(ext4SrcDir, "lang/Number.js")) - fileset(file: new File(ext4SrcDir, "lang/Object.js")) - fileset(file: new File(ext4SrcDir, "lang/String.js")) - fileset(file: new File(ext4SrcDir, "lang/Error.js")) + fileset(file: new File(srcDir, "Ext.js")) + fileset(file: new File(srcDir, "lang/Array.js")) + fileset(file: new File(srcDir, "lang/Date.js")) + fileset(file: new File(srcDir, "lang/Number.js")) + fileset(file: new File(srcDir, "lang/Object.js")) + fileset(file: new File(srcDir, "lang/String.js")) + fileset(file: new File(srcDir, "lang/Error.js")) fileset(file: "${scriptFragmentsDir}/Ext4.middle.js") - fileset(file: new File(ext4SrcDir, "misc/JSON.js")) + fileset(file: new File(srcDir, "misc/JSON.js")) footer(file: "${scriptFragmentsDir}/Ext4.footer.js") } File destFile = new File("${scriptsDir}/Ext4.js") diff --git a/src/main/groovy/org/labkey/gradle/task/StageDistribution.groovy b/src/main/groovy/org/labkey/gradle/task/StageDistribution.groovy index 09727ef7..2e4247cd 100644 --- a/src/main/groovy/org/labkey/gradle/task/StageDistribution.groovy +++ b/src/main/groovy/org/labkey/gradle/task/StageDistribution.groovy @@ -17,26 +17,34 @@ package org.labkey.gradle.task import org.gradle.api.DefaultTask import org.gradle.api.file.CopySpec +import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.DuplicatesStrategy import org.gradle.api.file.FileCopyDetails +import org.gradle.api.file.FileSystemOperations import org.gradle.api.file.FileTree import org.gradle.api.file.RelativePath import org.gradle.api.tasks.OutputDirectory import org.gradle.api.tasks.TaskAction +import org.labkey.gradle.plugin.ServerDeploy import org.labkey.gradle.plugin.extension.DistributionExtension +import org.labkey.gradle.util.BuildUtils -class StageDistribution extends DefaultTask +import javax.inject.Inject + +abstract class StageDistribution extends DefaultTask { + @Inject abstract FileSystemOperations getFs() + protected File distributionFile = null @OutputDirectory - File modulesStagingDir = new File((String) project.staging.modulesDir) + final abstract DirectoryProperty modulesStagingDir = BuildUtils.getRootBuildDirectoryProperty(project, ServerDeploy.STAGING_MODULES_DIR) @OutputDirectory - File stagingDir = new File((String) project.staging.dir) + final abstract DirectoryProperty stagingDir = BuildUtils.getRootBuildDirectoryProperty(project, ServerDeploy.STAGING_DIR) @OutputDirectory - File pipelineJarStagingDir = new File((String) project.staging.pipelineLibDir) + final abstract DirectoryProperty pipelineJarStagingDir = BuildUtils.getRootBuildDirectoryProperty(project, ServerDeploy.STAGING_PIPELINE_DIR) @TaskAction void action() @@ -46,20 +54,22 @@ class StageDistribution extends DefaultTask FileTree distArchiveTree = project.tarTree(distributionFile) // first clean out the staging directory so we don't pick up modules not in this distribution - project.delete modulesStagingDir + fs.delete { + it.delete(modulesStagingDir.get()) + } - project.copy({ CopySpec spec -> + fs.copy({ CopySpec spec -> spec.from distArchiveTree.files - spec.into modulesStagingDir + spec.into modulesStagingDir.get() spec.include "**/*.module" spec.setDuplicatesStrategy(DuplicatesStrategy.INCLUDE) }) String baseName = distributionFile.getName().substring(0, distributionFile.getName().length() - (extension.length() + 1)) - project.copy({ CopySpec spec -> + fs.copy({ CopySpec spec -> spec.from distArchiveTree - spec.into stagingDir + spec.into stagingDir.get() spec.setDuplicatesStrategy(DuplicatesStrategy.INCLUDE) spec.eachFile { FileCopyDetails fcp -> @@ -79,9 +89,9 @@ class StageDistribution extends DefaultTask spec.includeEmptyDirs = false }) - project.copy({ CopySpec spec -> + fs.copy({ CopySpec spec -> spec.from distArchiveTree - spec.into pipelineJarStagingDir + spec.into pipelineJarStagingDir.get() spec.setDuplicatesStrategy(DuplicatesStrategy.INCLUDE) spec.eachFile { FileCopyDetails fcp -> diff --git a/src/main/groovy/org/labkey/gradle/task/StageModules.groovy b/src/main/groovy/org/labkey/gradle/task/StageModules.groovy new file mode 100644 index 00000000..0bb8a50b --- /dev/null +++ b/src/main/groovy/org/labkey/gradle/task/StageModules.groovy @@ -0,0 +1,67 @@ +package org.labkey.gradle.task + +import org.gradle.api.DefaultTask +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.FileCollection +import org.gradle.api.file.FileSystemOperations +import org.gradle.api.tasks.InputFiles +import org.gradle.api.tasks.OutputDirectory +import org.gradle.api.tasks.TaskAction +import org.labkey.gradle.plugin.ServerDeploy +import org.labkey.gradle.util.BuildUtils + +import javax.inject.Inject + +abstract class StageModules extends DefaultTask +{ + @Inject abstract FileSystemOperations getFs() + + @OutputDirectory + final abstract DirectoryProperty stagingModulesDir = BuildUtils.getRootBuildDirectoryProperty(project, ServerDeploy.STAGING_MODULES_DIR) + + @InputFiles + abstract ConfigurableFileCollection getDownloadedModules() + + @InputFiles + abstract ConfigurableFileCollection getBuiltModules() + + @TaskAction + void action() + { + fs.delete({ + it.delete(stagingModulesDir.get()) + }) + // copy over the module dependencies first (things not built from source that might bring in + // transitive dependencies) + if (!getDownloadedModules().isEmpty()) + { + ant.copy( + todir: stagingModulesDir.get(), + preserveLastModified: true // this is important so we don't re-explode modules that have not changed + ) + { + getDownloadedModules().addToAntBuilder(ant, "fileset", FileCollection.AntType.FileSet) + } + } + + // Then copy over the project dependencies (things built from source) so they will replace + // any transitive dependencies that were brought in). + // One might like to do this overriding/overwriting using DependencySubstitution, as that is very much + // what it is designed for, but that allows substitution of a project for an ExternalModuleDependency + // and since a .module file is only one of the artifacts produced by our projects (e.g., :server:modules:platform:experiment) + // and is not the default artifact, DependencySubstitution does not seem to work. + // See BuildUtils.substituteModuleDependencies for an almost-working attempt at this. + if (!getBuiltModules().isEmpty()) + { + ant.copy( + overwrite: true, // overwrite existing files even if the destination files are newer + todir: stagingModulesDir.get(), + preserveLastModified: true // this is important so we don't re-explode modules that have not changed + ) + { + getBuiltModules().addToAntBuilder(ant, "fileset", FileCollection.AntType.FileSet) + } + } + } +} diff --git a/src/main/groovy/org/labkey/gradle/task/StartLabKey.groovy b/src/main/groovy/org/labkey/gradle/task/StartLabKey.groovy index a1f0f444..680c59be 100644 --- a/src/main/groovy/org/labkey/gradle/task/StartLabKey.groovy +++ b/src/main/groovy/org/labkey/gradle/task/StartLabKey.groovy @@ -47,7 +47,7 @@ class StartLabKey extends DefaultTask File jarFile = BuildUtils.getExecutableServerJar(project) if (jarFile == null) { - throw new GradleException("No jar file found in ${ServerDeployExtension.getEmbeddedServerDeployDirectory(project)}.") + throw new GradleException("No jar file found in ${ServerDeployExtension.getEmbeddedServerDeployDirectoryPath(project)}.") } else { @@ -63,20 +63,20 @@ class StartLabKey extends DefaultTask commandParts += getStartupOpts(project) commandParts += ["-jar", jarFile.getName()] - File logFile = new File(ServerDeployExtension.getEmbeddedServerDeployDirectory(project), Tomcat.EMBEDDED_LOG_FILE_NAME) + File logFile = new File(ServerDeployExtension.getEmbeddedServerDeployDirectoryPath(project), Tomcat.EMBEDDED_LOG_FILE_NAME) if (!logFile.getParentFile().exists()) logFile.getParentFile().mkdirs() if (!logFile.exists()) logFile.createNewFile() FileOutputStream outputStream = new FileOutputStream(logFile) def envMap = new HashMap<>(System.getenv()) - envMap.put('PATH', "${ServerDeployExtension.getEmbeddedServerDeployDirectory(project)}/bin${File.pathSeparator}${System.getenv("PATH")}") + envMap.put('PATH', "${ServerDeployExtension.getEmbeddedServerDeployDirectoryPath(project)}/bin${File.pathSeparator}${System.getenv("PATH")}") def env = [] for (String key : envMap.keySet()) { env += "${key}=${envMap.get(key)}" } - this.logger.info("Starting LabKey with command ${commandParts} and env ${env} in directory ${ServerDeployExtension.getEmbeddedServerDeployDirectory(project)}") - Process process = commandParts.execute(env, new File(ServerDeployExtension.getEmbeddedServerDeployDirectory(project))) + this.logger.info("Starting LabKey with command ${commandParts} and env ${env} in directory ${ServerDeployExtension.getEmbeddedServerDeployDirectoryPath(project)}") + Process process = commandParts.execute(env, new File(ServerDeployExtension.getEmbeddedServerDeployDirectoryPath(project))) process.consumeProcessOutput(outputStream, outputStream) } } diff --git a/src/main/groovy/org/labkey/gradle/task/WriteDependenciesFile.groovy b/src/main/groovy/org/labkey/gradle/task/WriteDependenciesFile.groovy index 70f3d76b..4b32a59b 100644 --- a/src/main/groovy/org/labkey/gradle/task/WriteDependenciesFile.groovy +++ b/src/main/groovy/org/labkey/gradle/task/WriteDependenciesFile.groovy @@ -57,13 +57,13 @@ abstract class WriteDependenciesFile extends DefaultTask { this.inputs.file(project.file("gradle.properties")) } - onlyIf { - !externalDependencies.get().isEmpty() - } } private void writeDependencies(OutputStreamWriter writer) { + if (externalDependencies.get().isEmpty()) + return + List missing = [] List licenseMissing = [] Map dependencies = externalDependencies.get() diff --git a/src/main/groovy/org/labkey/gradle/util/BuildUtils.groovy b/src/main/groovy/org/labkey/gradle/util/BuildUtils.groovy index 17ae9ebb..9ad0f858 100644 --- a/src/main/groovy/org/labkey/gradle/util/BuildUtils.groovy +++ b/src/main/groovy/org/labkey/gradle/util/BuildUtils.groovy @@ -24,12 +24,16 @@ import org.gradle.api.UnknownDomainObjectException import org.gradle.api.artifacts.Configuration import org.gradle.api.artifacts.DependencySubstitutions import org.gradle.api.artifacts.ProjectDependency +import org.gradle.api.file.Directory +import org.gradle.api.file.DirectoryProperty import org.gradle.api.initialization.Settings import org.gradle.api.invocation.Gradle +import org.gradle.api.provider.Provider import org.labkey.gradle.plugin.extension.LabKeyExtension import org.labkey.gradle.plugin.extension.ModuleExtension import org.labkey.gradle.plugin.extension.ServerDeployExtension import org.labkey.gradle.plugin.extension.TeamCityExtension +import org.labkey.gradle.task.RestartTriggerTask import java.nio.charset.StandardCharsets import java.nio.file.Files @@ -49,7 +53,6 @@ class BuildUtils public static final String PLATFORM_MODULES_DIR = "server/modules/platform" public static final String COMMON_ASSAYS_MODULES_DIR = "server/modules/commonAssays" public static final String CUSTOM_MODULES_DIR = "server/modules/customModules" - private static final String RESTART_FILE_NAME = ".restartTrigger" public static final List EHR_MODULE_NAMES = [ "EHR_ComplianceDB", @@ -845,12 +848,12 @@ class BuildUtils static String getEmbeddedConfigPath(Project project) { - return new File(project.serverDeploy.embeddedDir, "config").absolutePath + return new File(ServerDeployExtension.getEmbeddedServerDeployDirectoryPath(project), "config").absolutePath } static File getExecutableServerJar(Project project) { - File deployDir = new File(ServerDeployExtension.getEmbeddedServerDeployDirectory(project)) + File deployDir = new File(ServerDeployExtension.getEmbeddedServerDeployDirectoryPath(project)) File[] jarFiles = deployDir.listFiles(new FilenameFilter() { @Override boolean accept(File dir, String name) { @@ -879,7 +882,9 @@ class BuildUtils * spring.devtools.restart.additional-paths * * @param project - for use in getting the rootProject's build directory + * @deprecated Use RestartTriggerTask as a base class for your task instead */ + @Deprecated(forRemoval=true) static void updateRestartTriggerFile(Project project) { if (!project.hasProperty('useLocalBuild') || "false" == project.property("useLocalBuild")) @@ -891,7 +896,7 @@ class BuildUtils OutputStreamWriter writer = null try { - File triggerFile = new File(triggerFileDir, RESTART_FILE_NAME) + File triggerFile = new File(triggerFileDir, RestartTriggerTask.RESTART_FILE_NAME) writer = new OutputStreamWriter(new FileOutputStream(triggerFile), StandardCharsets.UTF_8) writer.write(SimpleDateFormat.getDateTimeInstance().format(new Date())) } @@ -941,6 +946,17 @@ class BuildUtils return project.rootProject.layout.buildDirectory.get().asFile.path } + static Provider getRootBuildDirectoryProvider(Project project, String directoryPath) + { + return project.rootProject.layout.buildDirectory.dir(directoryPath) + } + + static DirectoryProperty getRootBuildDirectoryProperty(Project project, String defaultDirectoryPath) + { + return project.objects.directoryProperty().convention(getRootBuildDirectoryProvider(project, defaultDirectoryPath)) + } + + // See Issue 49316: https://www.labkey.org/home/Developer/issues/Secure/issues-details.view?issueId=49316 static void substituteModuleDependencies(Project project, String configName) { try {