diff --git a/.idea/misc.xml b/.idea/misc.xml
index 2c6efcb6..5c1ea9b3 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -8,4 +8,4 @@
-
\ No newline at end of file
+
diff --git a/README.md b/README.md
index 519f8dc0..408b4196 100644
--- a/README.md
+++ b/README.md
@@ -13,11 +13,29 @@ on how to do that, including how to develop and test locally and the versioning
## Release Notes
-### TBD
-*Released*: TBD
-(Earliest compatible LabKeyversion: 25.2)
+### 7.0.0
+*Released*: 22 September 2025
+(Earliest compatible LabKey version: 25.10)
- Update `PurgeArtifacts` task to be compatible with configuration cache and to work with input files so archived projects can be kept up to date
- Update dependency versions
+- Update DoThenSetup and its relatives to be compatible with configuration cache
+- Update DeployApp for better configuration cache compatibility
+- Replace usages of project.javaexc with JavaExc task types or injected ExecOperations
+- Update CompressClientLibs, StopLabKey to work better with configuration cache
+- Remove `listNodeModules` task defined by `NpmRun` plugin
+- Remove `cleanOut` task defined by `FileModule` plugin
+- Remove `cleanAndDeploy` task from `ServerDeploy` plugin
+- Update `CopyAndInstallRPackage` Task class to use injected FileSystem instead of `project.copy`
+- Update `GzipAction` task to use task logger and ant builder objects
+- Update `StopLabKey` and `StartLabKey` with appropriate dependencies to mirror the deprecated `StopTomcat` and `StartTomcat`
+- Remove deprecated `getServerDeployDirectory` from `ServerDeployExtension`
+- Remove `createNlpConfig` task from `TeamCity` plugin since that module is no longer in use
+- Remove `setup` task from `ServerDeploy` plugin.
+- Update `DeployDistribution` and `StageDistribution` for configuration cache compatibility
+- Some updates for `ModuleDistribution` for better configuration cache compatibility
+- Update `PropertiesUtils` to remove code for supporting `labkey.xml`
+- Relocate the `.restartTriggerFile` to prevent creation of empty `build/deploy/modules` directory for embedded distribution deployment
+- Fix `bootstrap` task so it will update `application.properties`
### 6.3.0
*Released*: 3 July 2025
diff --git a/build.gradle b/build.gradle
index 7a6302cc..3ac65c89 100644
--- a/build.gradle
+++ b/build.gradle
@@ -30,6 +30,7 @@ repositories {
dependencies {
implementation gradleApi()
implementation localGroovy()
+ implementation "org.apache.groovy:groovy-sql:${groovySqlVersion}"
api "org.apache.commons:commons-lang3:${commonsLang3Version}"
api "org.apache.commons:commons-text:${commonsTextVersion}"
api "commons-io:commons-io:${commonsIoVersion}"
@@ -42,7 +43,7 @@ dependencies {
}
group = 'org.labkey.build'
-project.version = "6.4.0-SNAPSHOT"
+project.version = "7.1.0-SNAPSHOT"
gradlePlugin {
plugins {
diff --git a/gradle.properties b/gradle.properties
index e02ba1cb..d032ea94 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,6 +1,6 @@
artifactory_contextUrl=https://labkey.jfrog.io/artifactory
-artifactoryPluginVersion=5.2.5
+artifactoryPluginVersion=6.0.0
commonsIoVersion=2.20.0
commonsLang3Version=3.18.0
@@ -9,6 +9,8 @@ commonsTextVersion=1.14.0
grgitGradleVersion=5.3.2
graphqlJavaVersion=24.2
+groovySqlVersion=4.0.27
+
httpclientVersion=5.5
httpcoreVersion=5.3.4
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index d4081da4..2a84e188 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.14.3-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
diff --git a/src/main/groovy/org/labkey/gradle/plugin/Antlr.groovy b/src/main/groovy/org/labkey/gradle/plugin/Antlr.groovy
index 6034919a..6a6052be 100644
--- a/src/main/groovy/org/labkey/gradle/plugin/Antlr.groovy
+++ b/src/main/groovy/org/labkey/gradle/plugin/Antlr.groovy
@@ -26,6 +26,7 @@ import org.labkey.gradle.util.GroupNames
/**
* Used to compile antlr grammars into Java classes using the antlr executable.
+ * TODO: This could be converted to using the Antlr plugin https://docs.gradle.org/9.1.0/userguide/antlr_plugin.html
*/
class Antlr implements Plugin
{
diff --git a/src/main/groovy/org/labkey/gradle/plugin/ApplyLicenses.groovy b/src/main/groovy/org/labkey/gradle/plugin/ApplyLicenses.groovy
index 2f91ef0e..e6c61408 100644
--- a/src/main/groovy/org/labkey/gradle/plugin/ApplyLicenses.groovy
+++ b/src/main/groovy/org/labkey/gradle/plugin/ApplyLicenses.groovy
@@ -96,8 +96,9 @@ class ApplyLicenses implements Plugin
}
project.tasks.register('verifyLicensePatch') {
- dependsOn(patchApiTask)
- doLast {
+ it.group = GroupNames.TEST
+ it.dependsOn(patchApiTask)
+ it.doLast {
[project.configurations.extJs3Commercial, project.configurations.extJs4Commercial].forEach {
def commercialLicense = project.zipTree(it.singleFile).matching {
include '*/license.txt'
@@ -110,6 +111,7 @@ class ApplyLicenses implements Plugin
}
}
}
+ it.notCompatibleWithConfigurationCache("Needs to inject ArtifactOperations for zipTree usage")
}
}
}
diff --git a/src/main/groovy/org/labkey/gradle/plugin/ClientLibraries.groovy b/src/main/groovy/org/labkey/gradle/plugin/ClientLibraries.groovy
index 0a2e14d2..0889282f 100644
--- a/src/main/groovy/org/labkey/gradle/plugin/ClientLibraries.groovy
+++ b/src/main/groovy/org/labkey/gradle/plugin/ClientLibraries.groovy
@@ -51,6 +51,7 @@ class ClientLibraries
task.dependsOn(project.tasks.processResources)
task.dependsOn(project.project(minProjectPath).tasks.named("npmInstall"))
task.xmlFiles = getLibXmlFiles(project)
+ task.notCompatibleWithConfigurationCache("Class ClientLibsCompress needs more input and output properties declared")
}
project.evaluationDependsOn(minProjectPath)
diff --git a/src/main/groovy/org/labkey/gradle/plugin/Database.groovy b/src/main/groovy/org/labkey/gradle/plugin/Database.groovy
index 8856b63a..cbbc0f1f 100644
--- a/src/main/groovy/org/labkey/gradle/plugin/Database.groovy
+++ b/src/main/groovy/org/labkey/gradle/plugin/Database.groovy
@@ -52,6 +52,7 @@ class Database implements Plugin
task.group = GroupNames.DATABASE
task.description = "Switch to SQL Server configuration"
task.dbType = "mssql"
+ task.driverFiles.setFrom(project.configurations.driver)
}
}
@@ -62,6 +63,7 @@ class Database implements Plugin
task.group = GroupNames.DATABASE
task.description = "Switch to SQL Server configuration using jTDS driver"
task.dbType = "jtds"
+ task.driverFiles.setFrom(project.configurations.driver)
}
}
@@ -71,6 +73,9 @@ class Database implements Plugin
Bootstrap task ->
task.group = GroupNames.DATABASE
task.description = "Switch to bootstrap database properties as defined in current db.config file"
+ task.driverFiles.setFrom(project.configurations.driver)
+ task.dbPropertiesChanged = true
+ task.outputs.upToDateWhen({ return false })
}
}
}
diff --git a/src/main/groovy/org/labkey/gradle/plugin/Distribution.groovy b/src/main/groovy/org/labkey/gradle/plugin/Distribution.groovy
index a7fe0d96..9cec6412 100644
--- a/src/main/groovy/org/labkey/gradle/plugin/Distribution.groovy
+++ b/src/main/groovy/org/labkey/gradle/plugin/Distribution.groovy
@@ -23,15 +23,11 @@ import org.gradle.api.artifacts.ModuleDependency
import org.gradle.api.artifacts.ProjectDependency
import org.gradle.api.file.DeleteSpec
import org.gradle.api.internal.artifacts.dependencies.DefaultProjectDependency
-import org.gradle.api.publish.maven.MavenPublication
import org.gradle.api.tasks.Delete
import org.labkey.gradle.plugin.extension.DistributionExtension
-import org.labkey.gradle.plugin.extension.LabKeyExtension
import org.labkey.gradle.plugin.extension.TeamCityExtension
-import org.labkey.gradle.task.ModuleDistribution
import org.labkey.gradle.util.BuildUtils
import org.labkey.gradle.util.GroupNames
-import org.labkey.gradle.util.PomFileHelper
import org.labkey.gradle.util.TaskUtils
class Distribution implements Plugin
@@ -62,10 +58,6 @@ class Distribution implements Plugin
addDependencies(project)
addTasks(project)
addTaskDependencies(project)
-
- // commented out until we start publishing distribution artifacts, and then we'll examine the publications more closely
-// if (BuildUtils.shouldPublishDistribution(project))
-// addArtifacts(project)
}
private void addConfigurations(Project project)
@@ -169,74 +161,6 @@ class Distribution implements Plugin
project.dependencies.add("distribution", dep)
}
}
-
- private void addArtifacts(Project project)
- {
- project.apply plugin: 'maven-publish'
-
- // TODO this is really only an approximation of what's needed. We don't currently publish distribution artifacts
- // to artifactory
- project.afterEvaluate {
- String artifactId = getArtifactId(project)
- Properties pomProperties = LabKeyExtension.getApiPomProperties(artifactId, project.dist.description, project)
- project.publishing {
- publications {
- distributions(MavenPublication) { pub ->
- pub.artifactId(artifactId)
- project.tasks.each {
- if (it instanceof ModuleDistribution)
- {
- it.outputs.files.each {File file ->
- pub.artifact(file)
- {
- String fileName = file.getName()
- if (fileName.endsWith("gz"))
- extension "tar.gz"
- if (fileName.contains("-src."))
- classifier "src"
- }
- }
- }
- }
- pom {
- name = project.name
- description = pomProperties.getProperty("Description")
- url = PomFileHelper.LABKEY_ORG_URL
- developers PomFileHelper.getLabKeyTeamDevelopers()
- // TODO this should probably not always be Apache license
-// licenses pomUtil.getLicense()
- organization PomFileHelper.getLabKeyOrganization()
-// scm PomFileHelper.getLabKeyScm()
- // doesn't seem like these pom files will have any dependencies
- }
- }
- }
-
- project.artifactoryPublish {
- project.tasks.each {
- if (it instanceof ModuleDistribution)
- {
- dependsOn it
- }
- }
- publications('distributions')
- }
- }
- }
- }
-
- static String getArtifactId(Project project)
- {
- if (project.dist.artifactId != null)
- return project.dist.artifactId
- else
- {
- return TaskUtils.getOptionalTask(project, "distribution")
- .filter(task -> task.get() instanceof ModuleDistribution)
- .map(task -> ((ModuleDistribution)task.get()).getArtifactId())
- .orElse(project.name)
- }
- }
}
diff --git a/src/main/groovy/org/labkey/gradle/plugin/FileModule.groovy b/src/main/groovy/org/labkey/gradle/plugin/FileModule.groovy
index 61584ba8..e4f2bc51 100644
--- a/src/main/groovy/org/labkey/gradle/plugin/FileModule.groovy
+++ b/src/main/groovy/org/labkey/gradle/plugin/FileModule.groovy
@@ -137,19 +137,6 @@ class FileModule implements Plugin
task.outputs.cacheIf { false } // disable build caching. Has too many undeclared inputs.
}
- // This is added because Intellij started creating this "out" directory when you build through IntelliJ.
- // It copies files there that are actually input files to the build, which causes some problems when later
- // builds attempt to find their input files.
- project.tasks.register("cleanOut", Delete) {
- Delete task ->
- task.group = GroupNames.BUILD
- task.description = "removes the ${project.file('out')} directory created by Intellij builds"
- task.configure({ Delete delete ->
- if (project.file("out").isDirectory())
- project.delete project.file("out")
- })
- }
-
var moduleTask = project.tasks.register("module", Jar) {
Jar jar ->
jar.group = GroupNames.MODULE
@@ -207,8 +194,11 @@ class FileModule implements Plugin
}
BuildUtils.updateRestartTriggerFile(project)
}
+ task.notCompatibleWithConfigurationCache("Needs its own class to do the two copies (one to staging and one to deploy or possibly two Copy tasks chained together.")
}
+
+
project.tasks.register('undeployModule', Delete) {
Delete task ->
task.group = GroupNames.MODULE
@@ -230,9 +220,9 @@ class FileModule implements Plugin
task.doLast {
BuildUtils.updateRestartTriggerFile(project)
}
+ task.notCompatibleWithConfigurationCache("Does multiple deletes using project.delete. Should have its own class.")
}
-
project.tasks.register("reallyClean") {
Task task ->
task.group = GroupNames.BUILD
diff --git a/src/main/groovy/org/labkey/gradle/plugin/JavaModule.groovy b/src/main/groovy/org/labkey/gradle/plugin/JavaModule.groovy
index 0e6fe16d..8940537a 100644
--- a/src/main/groovy/org/labkey/gradle/plugin/JavaModule.groovy
+++ b/src/main/groovy/org/labkey/gradle/plugin/JavaModule.groovy
@@ -157,10 +157,6 @@ class JavaModule implements Plugin
})
}
- 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)}
// We do this afterEvaluate to allow all dependencies to be declared before checking
project.afterEvaluate({
diff --git a/src/main/groovy/org/labkey/gradle/plugin/JsDoc.groovy b/src/main/groovy/org/labkey/gradle/plugin/JsDoc.groovy
index f6f5b466..4bad3a01 100644
--- a/src/main/groovy/org/labkey/gradle/plugin/JsDoc.groovy
+++ b/src/main/groovy/org/labkey/gradle/plugin/JsDoc.groovy
@@ -15,13 +15,11 @@
*/
package org.labkey.gradle.plugin
-import org.gradle.api.DefaultTask
import org.gradle.api.Plugin
import org.gradle.api.Project
-import org.gradle.api.Task
-import org.gradle.api.file.CopySpec
-import org.gradle.api.file.Directory
+import org.gradle.api.file.DeleteSpec
import org.gradle.api.tasks.Copy
+import org.gradle.api.tasks.Delete
import org.gradle.api.tasks.bundling.Zip
import org.labkey.gradle.plugin.extension.JsDocExtension
import org.labkey.gradle.task.CreateJsDocs
@@ -29,6 +27,7 @@ import org.labkey.gradle.util.GroupNames
import org.labkey.gradle.util.PropertiesUtils
import java.util.regex.Matcher
+
/**
* Plugin that provides tasks for created JavaScript documentation using jsDoc tools
*/
@@ -39,43 +38,46 @@ class JsDoc implements Plugin
{
project.extensions.create("jsDoc", JsDocExtension)
project.jsDoc.root = "${project.rootDir}/tools/jsdoc-toolkit/"
- project.jsDoc.outputDir = getJsDocDirectory(project).file( "docs").asFile
addTasks(project)
}
- static Directory getJsDocDirectory(Project project)
+ public static List getJsFilesToProcess(Project project)
{
- return XsdDoc.getClientDocsBuildDir(project).get().dir("javascript")
+ List files = []
+ project.jsDoc.paths.each{ String path ->
+ files += project.file(path)
+ }
+ return files
}
private static void addTasks(Project project)
{
project.tasks.register('jsdocTemplate', Copy) {
- Copy task ->
- task.description = "insert the proper version number into the JavaScript documentation"
- task.configure
- { CopySpec copy ->
- copy.from project.file("${project.jsDoc.root}/templates/jsdoc")
- copy.filter( { String line ->
- Matcher matcher = PropertiesUtils.PROPERTY_PATTERN.matcher(line)
- String newLine = line;
- while (matcher.find())
- {
- if (matcher.group(1).equals("product.version"))
- newLine = newLine.replace(matcher.group(), (String) project.version)
- }
- return newLine;
+ Copy copy ->
+ copy.description = "insert the proper version number into the JavaScript documentation"
+ copy.from project.file("${project.jsDoc.root}/templates/jsdoc")
+ copy.inputs.property("version", project.version)
+ copy.filter( { String line ->
+ Matcher matcher = PropertiesUtils.PROPERTY_PATTERN.matcher(line)
+ String newLine = line;
+ while (matcher.find())
+ {
+ if (matcher.group(1).equals("product.version"))
+ newLine = newLine.replace(matcher.group(), (String) copy.inputs.properties.get("version"))
+ }
+ return newLine;
+
+ })
+ copy.destinationDir = new File((String) "${project.jsDoc.root}/templates/jsdoc_substituted")
- })
- copy.destinationDir = new File((String) "${project.jsDoc.root}/templates/jsdoc_substituted")
- }
}
project.tasks.register("jsdoc", CreateJsDocs) {
CreateJsDocs task ->
task.group = GroupNames.DOCUMENTATION
task.description = 'Generating Client API docs'
- task.dependsOn(project.tasks.jsdocTemplate)
+ task.inputs.files project.tasks.jsdocTemplate.outputs.files
+ task.getFilesToProcess().set(getJsFilesToProcess(project))
}
project.tasks.register("jsDocZip", Zip) {
@@ -86,17 +88,17 @@ class JsDoc implements Plugin
task.archiveVersion.set(project.getVersion().toString())
task.archiveExtension.set("zip")
task.from project.tasks.jsdoc
- task.destinationDirectory.set(getJsDocDirectory(project))
+ task.destinationDirectory.set(CreateJsDocs.getJsDocDirectory(project))
}
- project.tasks.register('cleanJsDoc', DefaultTask) {
- Task task ->
+ project.tasks.register('cleanJsDoc', Delete) {
+ Delete task ->
task.group = GroupNames.DOCUMENTATION
task.description = "Remove files created by jsdoc and jsDocZip tasks"
- task.doFirst( {
- project.delete(project.tasks.jsDocZip.outputs)
- project.delete(project.tasks.jsdoc.outputs)
- })
+ task.configure { DeleteSpec delete ->
+ delete.delete(project.tasks.jsDocZip.outputs)
+ delete.delete(project.tasks.jsdoc.outputs)
+ }
}
}
}
diff --git a/src/main/groovy/org/labkey/gradle/plugin/Jsp.groovy b/src/main/groovy/org/labkey/gradle/plugin/Jsp.groovy
index a2ebb656..be8c1535 100644
--- a/src/main/groovy/org/labkey/gradle/plugin/Jsp.groovy
+++ b/src/main/groovy/org/labkey/gradle/plugin/Jsp.groovy
@@ -109,17 +109,6 @@ class Jsp implements Plugin
private static void addJspTasks(Project project)
{
- project.tasks.register('listJsps') {
- Task task ->
- task.group = GroupNames.JSP
- task.doLast {
- getJspFileTree(project).each ({
- println it.absolutePath
- })
- }
- }
-
-
project.tasks.register('copyJsp', CopyJsp) {
CopyJsp task ->
task.group = GroupNames.JSP
diff --git a/src/main/groovy/org/labkey/gradle/plugin/NpmRun.groovy b/src/main/groovy/org/labkey/gradle/plugin/NpmRun.groovy
index 70d12891..b18821c2 100644
--- a/src/main/groovy/org/labkey/gradle/plugin/NpmRun.groovy
+++ b/src/main/groovy/org/labkey/gradle/plugin/NpmRun.groovy
@@ -170,24 +170,7 @@ class NpmRun implements Plugin
}
}
- project.tasks.register("listNodeProjects") {
- Task task ->
- task.group = GroupNames.NPM_RUN
- task.description = "List all projects that employ node in their build"
- task.doLast({
- List nodeProjects = []
- project.allprojects({Project p ->
- if (p.getPlugins().hasPlugin(NpmRun.class))
- nodeProjects.add(p.path)
- })
- if (nodeProjects.size() == 0)
- println("No projects found containing ${NPM_PROJECT_FILE}")
- else {
- println("The following projects use Node in their builds:\n\t${nodeProjects.join("\n\t")}\n")
- }
- })
- task.notCompatibleWithConfigurationCache("Needs to walk the project tree")
- }
+
}
diff --git a/src/main/groovy/org/labkey/gradle/plugin/ServerDeploy.groovy b/src/main/groovy/org/labkey/gradle/plugin/ServerDeploy.groovy
index a99a121b..0edd0813 100644
--- a/src/main/groovy/org/labkey/gradle/plugin/ServerDeploy.groovy
+++ b/src/main/groovy/org/labkey/gradle/plugin/ServerDeploy.groovy
@@ -62,7 +62,7 @@ class ServerDeploy implements Plugin
serverDeploy = project.extensions.create("serverDeploy", ServerDeployExtension)
deployDir = ServerDeployExtension.getServerDeployDirectoryPath(project)
- embeddedDir = ServerDeployExtension.getEmbeddedServerDeployDirectoryPath(project)
+ embeddedDir = ServerDeployExtension.getEmbeddedServerDeployDirectory(project).getAsFile().getAbsolutePath()
stagingDir = BuildUtils.getRootBuildDirFile(project, STAGING_DIR)
project.apply plugin: 'org.labkey.build.base'
@@ -86,13 +86,7 @@ class ServerDeploy implements Plugin
private void addTasks(Project project)
{
- project.tasks.register("deployApp", DeployApp) {
- DeployApp task ->
- task.group = GroupNames.DEPLOY
- 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")
- }
+
project.tasks.register("stageModules", StageModules) {
StageModules task ->
@@ -127,31 +121,6 @@ class ServerDeploy implements Plugin
task.dependsOn(project.tasks.checkModuleVersions)
}
- // Creating symbolic links on Windows requires elevated permissions. Even with these permissions, the createSymbolicLink method fails
- // with a message "required privilege is not held by the client". Using the ant.symlink task "succeeds", but it causes .sys files to be created in the
- // .node directory, which are not symbolic links and will cause failures with a message "java.nio.file.NotLinkException: The file or directory is not a reparse point."
- // the next time the command is run.
- //
- // So, for now, for Windows users, the symbolic links can be created manually using the following command when running as an administrator
- // MKLINK /D
- // or users can skip the link creation and put the target of the link in their path instead.
- //
- // At a later date, we can possibly make this task execute the mklink command (and its counterpart to remove the link).
- // This would require that the gradle tasks be run as an administrator, and that is possibly not ideal.
- if (!SystemUtils.IS_OS_WINDOWS && project.hasProperty('nodeVersion')) {
- project.tasks.register("symlinkNode") {
- Task task ->
- task.group = GroupNames.DEPLOY
- task.description = "Make a symbolic link to the npm directory for use in PATH environment variable"
- task.doFirst({
- if (project.hasProperty('npmVersion') && project.hasProperty('npmWorkDirectory'))
- linkBinaries(project, "npm", project.npmVersion, project.npmWorkDirectory)
- })
- task.dependsOn(project.tasks.npmSetup)
- }
- 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)
@@ -159,26 +128,23 @@ class ServerDeploy implements Plugin
Task task ->
task.group = GroupNames.DEPLOY
task.description = "Copy files needed for using remote pipeline jobs into ${stagingPipelineLibDir}"
+ task.inputs.files(project.configurations.remotePipelineJars.getFiles())
+ task.outputs.dir(BuildUtils.getRootBuildDirFile(project, STAGING_PIPELINE_DIR))
task.doLast({
- if (!project.configurations.remotePipelineJars.getFiles().isEmpty()) {
- task.ant.copy(
- todir: stagingPipelineLibDir,
+ if (!inputs.files.isEmpty()) {
+ ant.copy(
+ todir: outputs.files.singleFile,
preserveLastModified: true
)
{
- project.configurations.remotePipelineJars
- {
- Configuration collection ->
- collection.addToAntBuilder(project.ant, "fileset", FileCollection.AntType.FileSet)
- }
+ inputs.files.addToAntBuilder(ant, "fileset", FileCollection.AntType.FileSet)
}
}
})
}
project.tasks.named('stageRemotePipelineJars').configure {
- dependsOn project.configurations.remotePipelineJars
- notCompatibleWithConfigurationCache("TODO Needs dedicated task class with configuration as input")
+ it.dependsOn project.configurations.remotePipelineJars
}
project.tasks.register(
@@ -190,48 +156,64 @@ class ServerDeploy implements Plugin
task.dependsOn project.tasks.stageRemotePipelineJars
}
- project.tasks.register(
- "setup", DoThenSetup) {
- DoThenSetup task ->
+ project.tasks.register("deployApp", DeployApp) {
+ DeployApp task ->
task.group = GroupNames.DEPLOY
- task.description = "Installs application.properties into the tomcat configuration directory. Sets default database properties."
+ task.description = "Deploy the application locally into ${deployDir}"
+ task.binaries.setFrom(project.configurations.binaries)
+ task.bootJar.setFrom(project.project(BuildUtils.getEmbeddedProjectPath()).tasks.bootJar)
+ task.driverFiles.setFrom(project.configurations.driver)
// stage the application first to try to avoid multiple Tomcat restarts
- task.mustRunAfter(project.tasks.stageApp)
+ task.dependsOn(project.tasks.stageApp)
}
- project.tasks.named('deployApp').configure {
- dependsOn(project.tasks.setup)
- dependsOn(project.tasks.stageApp)
+ // Creating symbolic links on Windows requires elevated permissions. Even with these permissions, the createSymbolicLink method fails
+ // with a message "required privilege is not held by the client". Using the ant.symlink task "succeeds", but it causes .sys files to be created in the
+ // .node directory, which are not symbolic links and will cause failures with a message "java.nio.file.NotLinkException: The file or directory is not a reparse point."
+ // the next time the command is run.
+ //
+ // So, for now, for Windows users, the symbolic links can be created manually using the following command when running as an administrator
+ // MKLINK /D
+ // or users can skip the link creation and put the target of the link in their path instead.
+ //
+ // At a later date, we can possibly make this task execute the mklink command (and its counterpart to remove the link).
+ // This would require that the gradle tasks be run as an administrator, and that is possibly not ideal.
+ if (!SystemUtils.IS_OS_WINDOWS && project.hasProperty('nodeVersion')) {
+ project.tasks.register("symlinkNode") {
+ Task task ->
+ task.group = GroupNames.DEPLOY
+ task.description = "Make a symbolic link to the npm directory for use in PATH environment variable"
+ task.doFirst({
+ if (project.hasProperty('npmVersion') && project.hasProperty('npmWorkDirectory'))
+ linkBinaries(project, "npm", project.npmVersion, project.npmWorkDirectory)
+ })
+ task.dependsOn(project.tasks.npmSetup)
+ task.notCompatibleWithConfigurationCache("Needs its own class to declare proper input and output properties")
+ }
+ project.tasks.symlinkNode.notCompatibleWithConfigurationCache("References project properties. Need to add task class with input properties")
+ project.tasks.named('deployApp').configure {dependsOn(project.tasks.symlinkNode)}
}
+
if (BuildUtils.embeddedProjectExists(project)) {
def embeddedProject = project.project(BuildUtils.getEmbeddedProjectPath())
- project.tasks.register("cleanEmbeddedDeploy", DefaultTask) {
- DefaultTask task ->
+ project.tasks.register("cleanEmbeddedDeploy", Delete) {
+ Delete task ->
task.group = GroupNames.DEPLOY
task.description = "Remove the ${embeddedDir} directory"
- task.doLast {
- project.delete embeddedDir
+ task.configure { DeleteSpec delete ->
+ delete.delete embeddedDir
}
}
- project.tasks.named('deployApp').configure {
- mustRunAfter(project.tasks.cleanEmbeddedDeploy)
- doLast {
- project.copy {
- CopySpec copy ->
- copy.from embeddedProject.tasks.bootJar
- copy.into embeddedDir
- copy.setDuplicatesStrategy(DuplicatesStrategy.INCLUDE)
- }
- }
+ project.tasks.named('deployApp').configure {Task task ->
+ task.mustRunAfter(project.tasks.cleanEmbeddedDeploy)
}
TaskUtils.getOptionalTask(embeddedProject, 'checkVersionConflicts').ifPresent(task -> {
- project.tasks.named('deployApp').configure {dependsOn(task)}
+ project.tasks.named('deployApp').configure {it.dependsOn(task)}
})
- project.tasks.named('stageApp').configure {dependsOn(embeddedProject.tasks.build)}
- project.tasks.named('setup').configure {mustRunAfter(project.tasks.cleanEmbeddedDeploy)}
+ project.tasks.named('stageApp').configure {it.dependsOn(embeddedProject.tasks.build)}
}
@@ -245,21 +227,19 @@ class ServerDeploy implements Plugin
DeployDistribution task ->
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.dependsOn(project.tasks.cleanEmbeddedDeploy)
task.binaries.setFrom(project.configurations.binaries)
}
- // This may prevent multiple Tomcat restarts
- project.tasks.named('setup').configure {mustRunAfter(project.tasks.stageDistribution)}
-
- project.tasks.register('undeployModules',UndeployModules) {
+ project.tasks.register('undeployModules', UndeployModules) {
UndeployModules task ->
task.group = GroupNames.DEPLOY
task.description = "Removes all module files and directories from the deploy and staging directories"
+ task.notCompatibleWithConfigurationCache("Walks the project tree")
}
project.tasks.register(
- 'cleanStaging',Delete) {
+ 'cleanStaging', Delete) {
Delete task ->
task.group = GroupNames.DEPLOY
task.description = "Removes the staging directory ${stagingDir}"
@@ -280,15 +260,6 @@ class ServerDeploy implements Plugin
}
project.tasks.named('deployApp').configure {mustRunAfter(project.tasks.cleanDeploy)}
- project.tasks.register("cleanAndDeploy", DeployApp) {
- DeployApp task ->
- task.group = GroupNames.DEPLOY
- 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) {
Delete task ->
task.group = GroupNames.DEPLOY
@@ -297,13 +268,15 @@ class ServerDeploy implements Plugin
spec.delete project.rootProject.layout.buildDirectory
})
}
- project.tasks.named('deployApp').configure {mustRunAfter(project.tasks.cleanBuild)}
+ project.tasks.named('deployApp').configure {it.mustRunAfter(project.tasks.cleanBuild)}
project.tasks.named("cleanBuild").configure {
- it.dependsOn(project.tasks.stopTomcat)
+ it.dependsOn(project.tasks.stopLabKey)
+ it.mustRunAfter(project.tasks.stopTomcat)
}
project.tasks.named("cleanDeploy").configure {
- it.dependsOn(project.tasks.stopTomcat)
+ it.dependsOn(project.tasks.stopLabKey)
+ it.mustRunAfter(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 cc554af7..216f9513 100644
--- a/src/main/groovy/org/labkey/gradle/plugin/TeamCity.groovy
+++ b/src/main/groovy/org/labkey/gradle/plugin/TeamCity.groovy
@@ -15,20 +15,29 @@
*/
package org.labkey.gradle.plugin
-import com.sun.jdi.*
+import com.sun.jdi.AbsentInformationException
+import com.sun.jdi.Bootstrap
+import com.sun.jdi.IncompatibleThreadStateException
+import com.sun.jdi.ObjectReference
+import com.sun.jdi.StackFrame
+import com.sun.jdi.ThreadReference
+import com.sun.jdi.VMDisconnectedException
+import com.sun.jdi.VirtualMachine
import com.sun.jdi.connect.AttachingConnector
import com.sun.jdi.connect.Connector
import com.sun.jdi.connect.IllegalConnectorArgumentsException
import org.apache.commons.lang3.SystemUtils
+import org.gradle.api.AntBuilder
import org.gradle.api.GradleException
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.UnknownTaskException
+import org.gradle.api.file.DeleteSpec
+import org.gradle.api.logging.Logger
import org.gradle.api.provider.Provider
-import org.gradle.api.tasks.Copy
+import org.gradle.api.tasks.Delete
+import org.gradle.api.tasks.JavaExec
import org.gradle.api.tasks.TaskProvider
-import org.gradle.process.JavaExecSpec
-import org.labkey.gradle.plugin.extension.ServerDeployExtension
import org.labkey.gradle.plugin.extension.TeamCityExtension
import org.labkey.gradle.task.PickDb
import org.labkey.gradle.task.RunTestSuite
@@ -37,10 +46,8 @@ import org.labkey.gradle.task.UndeployModules
import org.labkey.gradle.util.BuildUtils
import org.labkey.gradle.util.DatabaseProperties
import org.labkey.gradle.util.GroupNames
-import org.labkey.gradle.util.PropertiesUtils
import java.time.Duration
-import java.util.regex.Matcher
/**
* Creates tasks for TeamCity to run its tests suites based on properties set in a build configuration (particularly for
@@ -49,8 +56,8 @@ import java.util.regex.Matcher
class TeamCity extends Tomcat
{
private static final String TEAMCITY_INFO_FILE = "teamcity-info.xml"
- private static final String TEST_CONFIGS_DIR = "configs/config-test"
- private static final String NLP_CONFIG_FILE = "nlpConfig.xml"
+ private static final String TEST_CONFIGS_DIR = "configs/config-test" // TODO remove once NLP is not conifgured on TC
+ private static final String NLP_CONFIG_FILE = "nlpConfig.xml" // TODO remove once NLP is not configured on TC
private static final String PIPELINE_CONFIG_FILE = "pipelineConfig.xml"
private static final Duration TOMCAT_SHUTDOWN_TIMEOUT = Duration.ofSeconds(15)
@@ -82,36 +89,36 @@ class TeamCity extends Tomcat
private void addTasks(Project project)
{
- project.tasks.register("setTeamCityAgentPassword") {
- Task task ->
+ project.tasks.register("setTeamCityAgentPassword", JavaExec) {
+ JavaExec task ->
task.group = GroupNames.TEST_SERVER
task.description = "Set the password for use in running tests"
task.dependsOn(project.tasks.jar)
- task.doLast {
- project.javaexec({ JavaExecSpec spec ->
- spec.mainClass = "org.labkey.test.util.PasswordUtil"
- spec.classpath {
- [project.configurations.uiTestRuntimeClasspath, project.tasks.jar]
- }
- spec.systemProperties["labkey.server"] = TeamCityExtension.getLabKeyServer(project)
- spec.args = ["set", TeamCityExtension.getLabKeyUsername(project), TeamCityExtension.getLabKeyPassword(project)]
- })
- }
+ task.mainClass.set("org.labkey.test.util.PasswordUtil")
+ task.classpath(project.configurations.uiTestRuntimeClasspath, project.tasks.jar)
+ task.systemProperty("labkey.server", TeamCityExtension.getLabKeyServer(project))
+ task.args("set", TeamCityExtension.getLabKeyUsername(project), TeamCityExtension.getLabKeyPassword(project))
}
- project.tasks.register("cleanTestLogs") {
- Task task ->
+ project.tasks.register("cleanTestLogs", Delete) {
+ Delete task ->
task.group = GroupNames.TEST_SERVER
task.description = "Removes log files from Tomcat and TeamCity"
task.dependsOn project.tasks.cleanLogs
- task.doLast {
- project.delete "${project.projectDir}/${TEAMCITY_INFO_FILE}"
+ task.configure { DeleteSpec delete ->
+ delete.delete "${project.projectDir}/${TEAMCITY_INFO_FILE}"
}
}
+ project.tasks.named("stopLabKey").configure {
+ it.doLast {
+ ensureShutdown(it.logger)
+ }
+ }
+
project.tasks.named("stopTomcat").configure {
- doLast {
- ensureShutdown(project)
+ it.doLast {
+ ensureShutdown(it.logger)
}
}
@@ -120,7 +127,7 @@ class TeamCity extends Tomcat
task.group = GroupNames.TEST_SERVER
task.description = "Kill Chrome processes"
task.doLast {
- killChrome(project)
+ killChrome(it.ant)
}
}
@@ -129,7 +136,7 @@ class TeamCity extends Tomcat
task.group = GroupNames.TEST_SERVER
task.description = "Kill Firefox processes"
task.doLast {
- killFirefox(project)
+ killFirefox(it.ant)
}
}
@@ -143,33 +150,12 @@ class TeamCity extends Tomcat
}
}
- project.tasks.named("startTomcat").configure {
+ project.tasks.named("startLabKey").configure {
dependsOn(project.tasks.createStartupPropertyFile)
}
- project.tasks.register("createNlpConfig", Copy) {
- Copy task ->
- task.group = GroupNames.TEST_SERVER
- task.description = "Create NLP engine configs for the test server"
- task.from BuildUtils.getServerProject(project).file(TEST_CONFIGS_DIR)
- task.include NLP_CONFIG_FILE
- task.filter({ String line ->
- Matcher matcher = PropertiesUtils.PROPERTY_PATTERN.matcher(line)
- String newLine = line
- while (matcher.find())
- {
- if (matcher.group(1).equals("enginePath"))
- newLine = newLine.replace(matcher.group(), new File((String) project.labkey.externalDir, "nlp/nlp_engine.py").getAbsolutePath())
- }
- return newLine
- }
- )
- task.destinationDir = new File("${ServerDeployExtension.getServerDeployDirectoryPath(project)}/config")
-
- }
-
project.tasks.named("startTomcat").configure {
- dependsOn(project.tasks.createNlpConfig)
+ dependsOn(project.tasks.createStartupPropertyFile)
}
project.tasks.register("validateConfiguration") {
@@ -201,18 +187,21 @@ class TeamCity extends Tomcat
task.description = "Copy properties file for running tests for ${shortType}"
task.dbType = "${shortType}"
task.dbPropertiesChanged = true
+ task.driverFiles.setFrom(project.configurations.driver)
}
pickDbTask = project.tasks.named(pickDbTaskName)
}
String suffix = properties.dbTypeAndVersion.capitalize()
String setUpTaskName = "setUp${suffix}"
- project.tasks.register(setUpTaskName,TeamCityDbSetup) {
+ project.tasks.register(setUpTaskName, TeamCityDbSetup) {
TeamCityDbSetup task ->
task.group = GroupNames.TEST_SERVER
task.description = "Get database properties set up for running tests for ${suffix}"
task.setDatabaseProperties(properties)
task.dropDatabase = extension.dropDatabase
+ task.dbPropertiesChanged = true
+ task.driverFiles.setFrom(project.configurations.driver)
task.testValidationOnly = Boolean.parseBoolean( extension.getTeamCityProperty("testValidationOnly"))
task.dependsOn (pickDbTask)
}
@@ -235,13 +224,19 @@ class TeamCity extends Tomcat
task.dbType = properties.shortType
task.mustRunAfter(BuildUtils.getServerProject(project).tasks.pickMSSQL)
task.mustRunAfter(BuildUtils.getServerProject(project).tasks.pickPg)
+ task.notCompatibleWithConfigurationCache("Walks the project tree")
}
}
undeployTask = project.tasks.named(undeployTaskName)
+ project.tasks.named("startLabKey").configure {
+ it.mustRunAfter(undeployTask)
+ }
+
project.tasks.named("startTomcat").configure {
- mustRunAfter(undeployTask)
+ it.mustRunAfter(undeployTask)
}
+ project.project(BuildUtils.getTestProjectPath(project.gradle)).tasks.startLabKey.mustRunAfter(setUpDbTask)
project.project(BuildUtils.getTestProjectPath(project.gradle)).tasks.startTomcat.mustRunAfter(setUpDbTask)
String ciTestTaskName = "ciTests" + properties.dbTypeAndVersion.capitalize()
project.tasks.register(ciTestTaskName, RunTestSuite) {
@@ -252,6 +247,7 @@ class TeamCity extends Tomcat
task.dbProperties = properties
task.mustRunAfter(project.tasks.validateConfiguration)
task.mustRunAfter(project.tasks.cleanTestLogs)
+ task.mustRunAfter(project.tasks.startLabKey)
task.mustRunAfter(project.tasks.startTomcat)
}
@@ -268,7 +264,7 @@ class TeamCity extends Tomcat
task.group = GroupNames.TEST_SERVER
task.description = "Generate server properties file to run with modules from a specified distribution"
task.doLast {
- project.logger.info("inheriting from distribution ${inheritedDistPath}")
+ task.logger.info("inheriting from distribution ${inheritedDistPath}")
Set includeModules = new HashSet<>()
project.project(inheritedDistPath).configurations.distribution.dependencies.each {
includeModules.add(it.getName())
@@ -279,10 +275,14 @@ class TeamCity extends Tomcat
extension.writeStartupProperties('00_modulesInclude.properties',
'ModuleLoader.include;startup=' + String.join(',', includeModules))
}
+ task.notCompatibleWithConfigurationCache("Needs the distribution configuration specified as an input ConfigurableFileCollection")
}
+ project.tasks.named("startLabKey").configure {
+ it.dependsOn(includeDistModulesTask)
+ }
project.tasks.named("startTomcat").configure {
- dependsOn(includeDistModulesTask)
+ it.dependsOn(includeDistModulesTask)
}
}
@@ -290,73 +290,76 @@ class TeamCity extends Tomcat
Task task ->
task.group = GroupNames.TEST_SERVER
task.dependsOn( ciTests )
- task.dependsOn( project.tasks.validateConfiguration, project.tasks.startTomcat, project.tasks.cleanTestLogs)
+ task.dependsOn( project.tasks.validateConfiguration, project.tasks.startLabKey, project.tasks.cleanTestLogs)
task.description = "Run a test suite on the TeamCity server"
task.doLast(
{
- killFirefox(project)
+ killFirefox(task.ant)
}
)
}
+ project.tasks.named("startLabKey").configure {
+ it.mustRunAfter(project.tasks.cleanTestLogs)
+ }
project.tasks.named("startTomcat").configure {
- mustRunAfter(project.tasks.cleanTestLogs)
+ it.mustRunAfter(project.tasks.cleanTestLogs)
}
}
- private static void killChrome(Project project)
+ private static void killChrome(AntBuilder ant)
{
if (SystemUtils.IS_OS_WINDOWS)
{
- project.ant.exec(executable: "taskkill")
+ ant.exec(executable: "taskkill")
{
arg(line:"/F /IM chromedriver.exe" )
}
- project.ant.exec(executable: "taskkill")
+ ant.exec(executable: "taskkill")
{
arg(line:"/F /IM chrome.exe" )
}
}
else if (SystemUtils.IS_OS_UNIX)
{
- project.ant.exec(executable: "killall")
+ ant.exec(executable: "killall")
{
arg(line: "-q -KILL chromedriver")
}
- project.ant.exec(executable: "killall")
+ ant.exec(executable: "killall")
{
arg(line: "-q -KILL chrome")
}
- project.ant.exec(executable: "killall")
+ ant.exec(executable: "killall")
{
arg(line: "-q KILL BrowserBlocking")
}
}
}
- private static void killFirefox(Project project)
+ private static void killFirefox(AntBuilder ant)
{
if (SystemUtils.IS_OS_WINDOWS)
{
- project.ant.exec(executable: "taskkill")
+ ant.exec(executable: "taskkill")
{
arg(line: "/F /IM firefox.exe")
}
- project.ant.exec(executable: "taskkill")
+ ant.exec(executable: "taskkill")
{
arg(line: "/F /IM geckodriver.exe")
}
}
else if (SystemUtils.IS_OS_UNIX)
{
- project.ant.exec(executable: "killall")
+ ant.exec(executable: "killall")
{
arg(line: "-q firefox")
}
- project.ant.exec(executable: "killall")
+ ant.exec(executable: "killall")
{
arg(line: "-q firefox-bin")
}
- project.ant.exec(executable: "killall")
+ ant.exec(executable: "killall")
{
arg(line: "-q geckodriver")
}
@@ -404,7 +407,6 @@ class TeamCity extends Tomcat
catch (VMDisconnectedException ignore)
{
println("VM at localhost:" + port + " exited normally")
- return
}
}
@@ -473,18 +475,18 @@ class TeamCity extends Tomcat
}
}
- private void ensureShutdown(Project project)
+ private void ensureShutdown(Logger logger)
{
String debugPort = extension.getTeamCityProperty("tomcat.debug")
if (!debugPort.isEmpty())
{
- project.logger.debug("Ensuring shutdown using port ${debugPort}")
+ logger.debug("Ensuring shutdown using port ${debugPort}")
try
{
AttachingConnector socketConnector = null
for (AttachingConnector connector : Bootstrap.virtualMachineManager().attachingConnectors())
{
- project.logger.debug("Found connector ${connector.name()} with class ${connector.getClass().getName()}")
+ logger.debug("Found connector ${connector.name()} with class ${connector.getClass().getName()}")
if ("com.sun.jdi.SocketAttach".equals(connector.name()))
{
socketConnector = connector
diff --git a/src/main/groovy/org/labkey/gradle/plugin/TestRunner.groovy b/src/main/groovy/org/labkey/gradle/plugin/TestRunner.groovy
index 61b9821c..aae0e2a7 100644
--- a/src/main/groovy/org/labkey/gradle/plugin/TestRunner.groovy
+++ b/src/main/groovy/org/labkey/gradle/plugin/TestRunner.groovy
@@ -17,6 +17,7 @@ package org.labkey.gradle.plugin
import org.gradle.api.Project
import org.gradle.api.Task
+import org.gradle.api.tasks.JavaExec
import org.labkey.gradle.plugin.extension.TeamCityExtension
import org.labkey.gradle.task.RunTestSuite
import org.labkey.gradle.util.BuildUtils
@@ -73,45 +74,30 @@ class TestRunner extends UiTest
}
-
- private void addPasswordTasks(Project project)
+ private static void addPasswordTasks(Project project)
{
-
- project.tasks.register("setPassword") {
- Task task ->
+ project.tasks.register("setPassword", JavaExec) {
+ JavaExec task ->
task.group = GroupNames.TEST
task.description = "Set the password for use in running tests"
- task.dependsOn(project.tasks.jar)
- task.doFirst({
- project.javaexec({
- mainClass = "org.labkey.test.util.PasswordUtil"
- classpath {
- [project.configurations.uiTestRuntimeClasspath, project.tasks.jar]
- }
- systemProperties["labkey.server"] = TeamCityExtension.getLabKeyServer(project)
- args = ["set"]
- standardInput = System.in
- })
- })
+ task.classpath(project.configurations.uiTestRuntimeClasspath, project.tasks.jar)
+ task.mainClass.set("org.labkey.test.util.PasswordUtil")
+ task.systemProperty("labkey.server", TeamCityExtension.getLabKeyServer(project))
+ task.args("set")
+ task.standardInput = System.in
}
- project.tasks.register("ensurePassword") {
- Task task ->
+ project.tasks.register("ensurePassword", JavaExec) {
+ JavaExec task ->
task.group = GroupNames.TEST
task.description = "Ensure that the password property used for running tests has been set"
task.dependsOn(project.tasks.jar)
- task.doFirst({
- project.javaexec({
- mainClass = "org.labkey.test.util.PasswordUtil"
- classpath {
- [project.configurations.uiTestRuntimeClasspath, project.tasks.jar]
- }
- systemProperties["labkey.server"] = TeamCityExtension.getLabKeyServer(project)
- args = ["ensure"]
- standardInput = System.in
- })
- })
+ task.mainClass.set("org.labkey.test.util.PasswordUtil")
+ task.classpath(project.configurations.uiTestRuntimeClasspath, project.tasks.jar)
+ task.systemProperty("labkey.server", TeamCityExtension.getLabKeyServer(project))
+ task.args("ensure")
+ task.standardInput = System.in
}
}
@@ -127,6 +113,9 @@ class TestRunner extends UiTest
}
})
+ directories.add(new File("${project.rootDir}/sampledata"))
+ directories.add(new File("${project.rootDir}/${BuildUtils.convertPathToRelativeDir(BuildUtils.getTestProjectPath(project.gradle))}/data"))
+
File sampleDataFile = BuildUtils.getBuildDirFile(project,"sampledata.dirs")
project.tasks.register("writeSampleDataFile") {
@@ -135,7 +124,7 @@ class TestRunner extends UiTest
task.description = "Produce the file with all sampledata directories for use in running tests"
task.inputs.files directories
task.outputs.file sampleDataFile
- task..doLast({
+ task.doLast({
List dirNames = new ArrayList<>()
directories.each({File file ->
@@ -147,8 +136,6 @@ class TestRunner extends UiTest
try
{
writer = new OutputStreamWriter(outputStream)
- dirNames.add("${project.rootDir}/sampledata")
- dirNames.add("${project.rootDir}/${BuildUtils.convertPathToRelativeDir(BuildUtils.getTestProjectPath(project.gradle))}/data")
writer.write(String.join(";", dirNames))
}
finally
@@ -163,8 +150,6 @@ class TestRunner extends UiTest
private void addTestSuiteTask(Project project)
{
project.logger.debug("TestRunner: addTestSuiteTask for ${project.path}")
- // Using project.tasks.register here cause an error:
- // Cannot add task 'uiTests' as a task with that name already exists
project.tasks.register("uiTests", RunTestSuite) {
RunTestSuite task ->
task.group = GroupNames.VERIFICATION
@@ -174,22 +159,25 @@ class TestRunner extends UiTest
private void addAspectJ(Project project)
{
- project.tasks.named('compileUiTestJava').configure {doLast {
- ant.taskdef(
- resource: "org/aspectj/tools/ant/taskdefs/aspectjTaskdefs.properties",
- classpath: project.configurations.aspectj.asPath
- )
- ant.iajc(
- destdir: BuildUtils.getBuildDirFile(project,"classes/java/uiTest/").getPath(),
- source: project.sourceCompatibility,
- target: project.targetCompatibility,
- classpath: project.configurations.uiTestRuntimeClasspath.asPath,
- {
- project.sourceSets.uiTest.java.srcDirs.each {
- src(path: it)
+ project.tasks.named('compileUiTestJava').configure {it ->
+ it.doLast {
+ ant.taskdef(
+ resource: "org/aspectj/tools/ant/taskdefs/aspectjTaskdefs.properties",
+ classpath: project.configurations.aspectj.asPath
+ )
+ ant.iajc(
+ destdir: BuildUtils.getBuildDirFile(project,"classes/java/uiTest/").getPath(),
+ source: project.sourceCompatibility,
+ target: project.targetCompatibility,
+ classpath: project.configurations.uiTestRuntimeClasspath.asPath,
+ {
+ project.sourceSets.uiTest.java.srcDirs.each {
+ src(path: it)
+ }
}
- }
- )
- }}
+ )
+ }
+ it.notCompatibleWithConfigurationCache("Needs configurations adn sourceSets specified as ConfigurableFileCollection.")
+ }
}
}
diff --git a/src/main/groovy/org/labkey/gradle/plugin/Tomcat.groovy b/src/main/groovy/org/labkey/gradle/plugin/Tomcat.groovy
index 4383d899..f6b4a7ee 100644
--- a/src/main/groovy/org/labkey/gradle/plugin/Tomcat.groovy
+++ b/src/main/groovy/org/labkey/gradle/plugin/Tomcat.groovy
@@ -25,6 +25,7 @@ import org.labkey.gradle.plugin.extension.TomcatExtension
import org.labkey.gradle.plugin.extension.UiTestExtension
import org.labkey.gradle.task.StartLabKey
import org.labkey.gradle.task.StopLabKey
+import org.labkey.gradle.util.BuildUtils
import org.labkey.gradle.util.GroupNames
/**
@@ -57,29 +58,33 @@ class Tomcat implements Plugin
StartLabKey task ->
task.group = GroupNames.WEB_APPLICATION
task.description = "Start the LabKey web application"
- }
-
- project.tasks.register("startTomcat", StartLabKey) {
- StartLabKey task ->
- task.group = GroupNames.WEB_APPLICATION
- task.description = "Start the LabKey web application (deprecated: use startLabKey)"
+ task.notCompatibleWithConfigurationCache("Needs some properties converted to inputs and outputs")
}
project.tasks.register("stopLabKey", StopLabKey) {
StopLabKey task ->
task.group = GroupNames.WEB_APPLICATION
task.description = "Stop the LabKey web application"
+ task.onlyIf{ BuildUtils.getApplicationPropertiesFile(project).exists() }
+ }
+
+ project.tasks.register("startTomcat", StartLabKey) {
+ StartLabKey task ->
+ task.group = GroupNames.WEB_APPLICATION
+ task.description = "Start the LabKey web application (deprecated: use startLabKey)"
+ task.notCompatibleWithConfigurationCache("Needs some properties converted to inputs and outputs")
}
project.tasks.register("stopTomcat", StopLabKey) {
StopLabKey task ->
task.group = GroupNames.WEB_APPLICATION
task.description = "Stop the LabKey web application (deprecated: use stopLabKey)"
+ task.onlyIf{ BuildUtils.getApplicationPropertiesFile(project).exists() }
}
project.tasks.register("cleanLogs", Delete) {
Delete task ->
- var logDir = "${ServerDeployExtension.getEmbeddedServerDeployDirectoryPath(project)}/logs"
+ var logDir = ServerDeployExtension.getEmbeddedServerDeployDirectory(project).dir("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/UiTest.groovy b/src/main/groovy/org/labkey/gradle/plugin/UiTest.groovy
index 1ce12a0f..1e3e247c 100644
--- a/src/main/groovy/org/labkey/gradle/plugin/UiTest.groovy
+++ b/src/main/groovy/org/labkey/gradle/plugin/UiTest.groovy
@@ -93,6 +93,7 @@ class UiTest implements Plugin
task.mustRunAfter(serverProject.tasks.pickPg)
task.mustRunAfter(serverProject.tasks.pickMSSQL)
}
+ task.notCompatibleWithConfigurationCache("Needs some properties set for various project references.")
}
}
diff --git a/src/main/groovy/org/labkey/gradle/plugin/XsdDoc.groovy b/src/main/groovy/org/labkey/gradle/plugin/XsdDoc.groovy
index 95ebca4d..ef8278b0 100644
--- a/src/main/groovy/org/labkey/gradle/plugin/XsdDoc.groovy
+++ b/src/main/groovy/org/labkey/gradle/plugin/XsdDoc.groovy
@@ -15,12 +15,13 @@
*/
package org.labkey.gradle.plugin
-import org.gradle.api.DefaultTask
+
import org.gradle.api.Plugin
import org.gradle.api.Project
-import org.gradle.api.Task
+import org.gradle.api.file.DeleteSpec
import org.gradle.api.file.Directory
import org.gradle.api.provider.Provider
+import org.gradle.api.tasks.Delete
import org.gradle.api.tasks.bundling.Zip
import org.labkey.gradle.plugin.extension.XsdDocExtension
import org.labkey.gradle.task.CreateXsdDocs
@@ -59,6 +60,8 @@ class XsdDoc implements Plugin
CreateXsdDocs task ->
task.group = GroupNames.DOCUMENTATION
task.description = 'Generating documentation for classes generated from XSD files'
+ task.xsdDocClasspath.setFrom(project.configurations.xsdDoc)
+ task.getFilesToProcess().set(Arrays.asList(project.xsdDoc.xsdFiles))
}
project.tasks.register("xsdDocZip", Zip) {
@@ -73,13 +76,13 @@ class XsdDoc implements Plugin
task.dependsOn(project.tasks.xsddoc)
}
- project.tasks.register("cleanXsdDoc", DefaultTask) {
- Task task ->
+ project.tasks.register("cleanXsdDoc", Delete) {
+ Delete task ->
task.group = GroupNames.DOCUMENTATION
task.description = "Remove files created by xsddoc and xsdDocZip tasks"
- task.doFirst({
- project.delete(project.tasks.xsdDocZip.outputs)
- project.delete(project.tasks.xsddoc.outputs)
+ task.configure({ DeleteSpec delete ->
+ delete.delete(project.tasks.xsdDocZip.outputs)
+ delete.delete(project.tasks.xsddoc.outputs)
})
}
}
diff --git a/src/main/groovy/org/labkey/gradle/plugin/extension/DistributionExtension.groovy b/src/main/groovy/org/labkey/gradle/plugin/extension/DistributionExtension.groovy
index 4e8e56bb..86315974 100644
--- a/src/main/groovy/org/labkey/gradle/plugin/extension/DistributionExtension.groovy
+++ b/src/main/groovy/org/labkey/gradle/plugin/extension/DistributionExtension.groovy
@@ -15,7 +15,7 @@
*/
package org.labkey.gradle.plugin.extension
-import org.gradle.api.GradleException
+
import org.gradle.api.Project
import java.nio.file.Paths
@@ -28,8 +28,6 @@ class DistributionExtension
public static final String TAR_ARCHIVE_EXTENSION = "tar.gz"
String dir = "${project.rootProject.projectDir}/dist"
- String artifactId
- String description
private Project project
@@ -38,27 +36,25 @@ class DistributionExtension
this.project = project
}
- static File getDistributionFile(Project project) {
- File distDir = new File(project.rootDir, "dist")
- if (project.hasProperty("distDir"))
- {
- if (Paths.get((String) project.property('distDir')).isAbsolute())
- distDir = new File((String) project.property('distDir'))
- else
- distDir = new File(project.rootDir, (String) project.property("distDir"))
+ static File getDistributionFile(Project project, String distDirName) {
+ File distDir
+ if (Paths.get(distDirName).isAbsolute())
+ distDir = new File((String) project.property('distDir'))
+ else
+ distDir = new File(project.rootDir, distDirName)
+ if (distDir.exists()) {
+ File[] distFiles = distDir.listFiles(new FilenameFilter() {
+ @Override
+ boolean accept(File dir, String name)
+ {
+ return name.endsWith(TAR_ARCHIVE_EXTENSION)
+ }
+ })
+ if (distFiles == null || distFiles.length == 0 || distFiles.length > 1)
+ return null
+
+ return distFiles[0]
}
- if (!distDir.exists())
- throw new GradleException("Distribution directory ${distDir} not found")
- File[] distFiles = distDir.listFiles(new FilenameFilter() {
- @Override
- boolean accept(File dir, String name) {
- return name.endsWith(TAR_ARCHIVE_EXTENSION)
- }
- })
- if (distFiles == null || distFiles.length == 0)
- throw new GradleException("No distribution found in directory ${distDir} with extension ${TAR_ARCHIVE_EXTENSION}")
- else if (distFiles.length > 1)
- throw new GradleException("${distDir} contains ${distFiles.length} files with extension ${TAR_ARCHIVE_EXTENSION}. Only one is allowed.")
- return distFiles[0]
+ return null
}
}
diff --git a/src/main/groovy/org/labkey/gradle/plugin/extension/JsDocExtension.groovy b/src/main/groovy/org/labkey/gradle/plugin/extension/JsDocExtension.groovy
index 4f15f663..d847da62 100644
--- a/src/main/groovy/org/labkey/gradle/plugin/extension/JsDocExtension.groovy
+++ b/src/main/groovy/org/labkey/gradle/plugin/extension/JsDocExtension.groovy
@@ -19,5 +19,4 @@ class JsDocExtension
{
String root
String[] paths = []
- File outputDir
}
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 5f91aa2a..85b94a56 100644
--- a/src/main/groovy/org/labkey/gradle/plugin/extension/ServerDeployExtension.groovy
+++ b/src/main/groovy/org/labkey/gradle/plugin/extension/ServerDeployExtension.groovy
@@ -17,22 +17,27 @@ package org.labkey.gradle.plugin.extension
import org.gradle.api.Project
import org.gradle.api.file.Directory
+import org.gradle.api.provider.Provider
import org.labkey.gradle.plugin.ServerDeploy
import org.labkey.gradle.util.BuildUtils
class ServerDeployExtension
{
- Map foundModules = new HashMap<>();
+ Map foundModules = new HashMap<>()
- @Deprecated(forRemoval=true)
- static String getServerDeployDirectory(Project project)
+ static String getServerDeployDirectoryPath(Project project)
{
- return getServerDeployDirectoryPath(project)
+ return BuildUtils.getRootBuildDirFile(project, ServerDeploy.DEPLOY_DIR).path
}
- static String getServerDeployDirectoryPath(Project project)
+ static Provider getServerDeployDirectoryProvider(Project project)
{
- return BuildUtils.getRootBuildDirFile(project, ServerDeploy.DEPLOY_DIR).path
+ return BuildUtils.getRootBuildDirectoryProvider(project, ServerDeploy.DEPLOY_DIR)
+ }
+
+ static Directory getEmbeddedServerDeployDirectory(Project project)
+ {
+ return getServerDeployDirectoryProvider(project).get().dir("embedded")
}
static String getEmbeddedServerDeployDirectoryPath(Project project)
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 6e551975..9d28f27b 100644
--- a/src/main/groovy/org/labkey/gradle/plugin/extension/TeamCityExtension.groovy
+++ b/src/main/groovy/org/labkey/gradle/plugin/extension/TeamCityExtension.groovy
@@ -36,6 +36,78 @@ class TeamCityExtension
setValidationMessages()
}
+ static boolean isOnTeamCity(Project project)
+ {
+ return project.hasProperty('teamcity')
+ }
+
+ static Object getTeamCityProperty(Project project, String name, Object defaultValue)
+ {
+ if (isOnTeamCity(project))
+ return project.teamcity[name] != null ? project.teamcity[name] : defaultValue
+ else if (project.hasProperty(name))
+ return project.property(name)
+ else
+ return defaultValue
+ }
+
+ static Properties getTeamCityProperties(Project project)
+ {
+ if (isOnTeamCity(project))
+ {
+ def tcProps = new Properties()
+ tcProps.putAll(project.teamcity)
+ return tcProps
+ }
+ else
+ return new Properties()
+ }
+
+ static String getLabKeyServer(Project project)
+ {
+ return getTeamCityProperty(project, "labkey.server", "http://localhost")
+ }
+
+ static String getLabKeyContextPath(Project project)
+ {
+ return getTeamCityProperty(project, "labkey.contextpath", null)
+ }
+
+ static String getLabKeyServerPort(Project project)
+ {
+ return getTeamCityProperty(project, 'tomcat.port', null)
+ }
+
+ static String getLabKeyServerShutdownPort(Project project)
+ {
+ return getTeamCityProperty(project, 'tomcat.shutdown', null)
+ }
+
+ static String getLabKeyServerKeystore(Project project)
+ {
+ return getTeamCityProperty(project, 'labkey.keystore', null)
+ }
+
+ static String getLabKeyServerKeystorePassword(Project project)
+ {
+ return getTeamCityProperty(project, 'labkey.keystore.password', null)
+ }
+
+ static String getLabKeyUsername(Project project)
+ {
+ return getTeamCityProperty(project, "labkey.server.email", "teamcity@labkey.test")
+ }
+
+ static String getLabKeyPassword(Project project)
+ {
+ return getTeamCityProperty(project, "labkey.server.password", "We'reSo\$tr0ng@yekbal1!")
+ }
+
+ static String getTomcatJavaHome(Project project)
+ {
+ return getTeamCityProperty(project, "tomcatJavaHome", System.getenv("JAVA_HOME"))
+ }
+
Boolean isValidForTestRun()
{
return validationMessages.isEmpty()
@@ -88,7 +160,7 @@ class TeamCityExtension
}
DatabaseProperties props = new DatabaseProperties(typeAndVersion, typeName, null)
- props.setProject(project)
+ props.setProjectPath(project.path)
props.jdbcDatabase = getDatabaseName()
if (!getTeamCityProperty("database.${typeAndVersion}.jdbcURL").isEmpty())
{
@@ -115,7 +187,7 @@ class TeamCityExtension
}
File startupPropertiesDir() {
- File startupDir = new File(new File(ServerDeployExtension.getEmbeddedServerDeployDirectoryPath(project)), 'startup')
+ File startupDir = ServerDeployExtension.getEmbeddedServerDeployDirectory(project).dir('startup').asFile
FileUtils.forceMkdir(startupDir)
return startupDir
}
@@ -136,10 +208,6 @@ class TeamCityExtension
(Boolean) getTeamCityProperty("teamcity.build.branch.is_default", true)
}
- static boolean isOnTeamCity(Project project)
- {
- return project.hasProperty('teamcity')
- }
String getTeamCityProperty(String name)
{
@@ -151,65 +219,5 @@ class TeamCityExtension
getTeamCityProperty(project, name, defaultValue)
}
- static Object getTeamCityProperty(Project project, String name, Object defaultValue)
- {
- if (isOnTeamCity(project))
- return project.teamcity[name] != null ? project.teamcity[name] : defaultValue
- else if (project.hasProperty(name))
- return project.property(name)
- else
- return defaultValue
- }
-
- static Properties getTeamCityProperties(Project project)
- {
- if (isOnTeamCity(project))
- {
- def tcProps = new Properties()
- tcProps.putAll(project.teamcity)
- return tcProps
- }
- else
- return new Properties()
- }
-
- static String getLabKeyServer(Project project)
- {
- return getTeamCityProperty(project, "labkey.server", "http://localhost")
- }
- static String getLabKeyContextPath(Project project)
- {
- return getTeamCityProperty(project, "labkey.contextpath", null)
- }
-
- static String getLabKeyServerPort(Project project)
- {
- return getTeamCityProperty(project, 'tomcat.port', null)
- }
-
- static String getLabKeyServerShutdownPort(Project project)
- {
- return getTeamCityProperty(project, 'tomcat.shutdown', null)
- }
-
- static String getLabKeyServerKeystore(Project project)
- {
- return getTeamCityProperty(project, 'labkey.keystore', null)
- }
-
- static String getLabKeyServerKeystorePassword(Project project)
- {
- return getTeamCityProperty(project, 'labkey.keystore.password', null)
- }
-
- static String getLabKeyUsername(Project project)
- {
- return getTeamCityProperty(project, "labkey.server.email", "teamcity@labkey.test")
- }
-
- static String getLabKeyPassword(Project project)
- {
- return getTeamCityProperty(project, "labkey.server.password", "We'reSo\$tr0ng@yekbal1!")
- }
}
diff --git a/src/main/groovy/org/labkey/gradle/plugin/extension/UiTestExtension.groovy b/src/main/groovy/org/labkey/gradle/plugin/extension/UiTestExtension.groovy
index a38aa248..5cb23d82 100644
--- a/src/main/groovy/org/labkey/gradle/plugin/extension/UiTestExtension.groovy
+++ b/src/main/groovy/org/labkey/gradle/plugin/extension/UiTestExtension.groovy
@@ -72,7 +72,7 @@ class UiTestExtension
else if (DatabaseProperties.getPickedConfigFile(project).exists())
{
project.logger.info("Found config file ${DatabaseProperties.getPickedConfigFile(project).getAbsolutePath()} to get db properties from")
- DatabaseProperties dbProperties = new DatabaseProperties(project, false)
+ DatabaseProperties dbProperties = new DatabaseProperties(project.path, DatabaseProperties.getPickedConfigFile(project), false)
// read database configuration, but don't include jdbcUrl and other non-"database"
// properties because they "cause problems" (quote from the test/build.xml file)
for (String name : dbProperties.getConfigProperties().stringPropertyNames())
diff --git a/src/main/groovy/org/labkey/gradle/task/Bootstrap.groovy b/src/main/groovy/org/labkey/gradle/task/Bootstrap.groovy
index b9d967f4..572e1173 100644
--- a/src/main/groovy/org/labkey/gradle/task/Bootstrap.groovy
+++ b/src/main/groovy/org/labkey/gradle/task/Bootstrap.groovy
@@ -16,24 +16,21 @@
package org.labkey.gradle.task
import org.labkey.gradle.util.DatabaseProperties
-import org.labkey.gradle.util.SqlUtils
-class Bootstrap extends DoThenSetup
+abstract class Bootstrap extends DoThenSetup
{
- boolean dbPropertiesChanged = true
-
@Override
protected void doDatabaseTask()
{
setDatabaseProperties()
- SqlUtils.dropDatabase(this.project, databaseProperties)
+ dropDatabase(getPath(), databaseProperties)
databaseProperties.interpolateCompositeProperties()
}
@Override
protected void setDatabaseProperties()
{
- databaseProperties = new DatabaseProperties(project, true)
+ databaseProperties = new DatabaseProperties(getPath(), chosenPropsFile.get().asFile, true)
}
}
diff --git a/src/main/groovy/org/labkey/gradle/task/ClientLibsCompress.groovy b/src/main/groovy/org/labkey/gradle/task/ClientLibsCompress.groovy
index 782fe1b6..816af2c1 100644
--- a/src/main/groovy/org/labkey/gradle/task/ClientLibsCompress.groovy
+++ b/src/main/groovy/org/labkey/gradle/task/ClientLibsCompress.groovy
@@ -61,7 +61,8 @@ class ClientLibsCompress extends DefaultTask
* extract the css and javascript files that are referenced.
* @return map between the file and the importer
*/
- private Map getImporterMap()
+ @Internal
+ Map getImporterMap()
{
Map importerMap = new HashMap<>()
xmlFiles.files.each() {
diff --git a/src/main/groovy/org/labkey/gradle/task/CopyAndInstallRPackage.groovy b/src/main/groovy/org/labkey/gradle/task/CopyAndInstallRPackage.groovy
index 8b888a56..f231447d 100644
--- a/src/main/groovy/org/labkey/gradle/task/CopyAndInstallRPackage.groovy
+++ b/src/main/groovy/org/labkey/gradle/task/CopyAndInstallRPackage.groovy
@@ -2,12 +2,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.InputDirectory
import org.gradle.api.tasks.TaskAction
import org.gradle.api.tasks.TaskExecutionException
-class CopyAndInstallRPackage extends InstallRPackage
+import javax.inject.Inject
+
+abstract class CopyAndInstallRPackage extends InstallRPackage
{
+ @Inject abstract FileSystemOperations getFs()
+
@InputDirectory
File packageLocation
@@ -22,7 +27,7 @@ class CopyAndInstallRPackage extends InstallRPackage
super.doInstall() // Install dependencies
File rLibsUserDir = getInstallDir()
- project.copy {
+ fs.copy {
CopySpec copy ->
copy.from packageLocation
copy.into(rLibsUserDir)
diff --git a/src/main/groovy/org/labkey/gradle/task/CreateJsDocs.groovy b/src/main/groovy/org/labkey/gradle/task/CreateJsDocs.groovy
index 211bf06c..a84c52a7 100644
--- a/src/main/groovy/org/labkey/gradle/task/CreateJsDocs.groovy
+++ b/src/main/groovy/org/labkey/gradle/task/CreateJsDocs.groovy
@@ -16,40 +16,57 @@
package org.labkey.gradle.task
import org.gradle.api.DefaultTask
-import org.gradle.api.tasks.InputFiles
+import org.gradle.api.Project
+import org.gradle.api.file.Directory
+import org.gradle.api.file.DirectoryProperty
+import org.gradle.api.provider.ListProperty
+import org.gradle.api.provider.Property
+import org.gradle.api.tasks.Input
+import org.gradle.api.tasks.InputDirectory
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.TaskAction
+import org.gradle.process.ExecOperations
+import org.labkey.gradle.plugin.XsdDoc
-class CreateJsDocs extends DefaultTask
+import javax.inject.Inject
+
+abstract class CreateJsDocs extends DefaultTask
{
- @InputFiles
- List getInputFiles()
- {
- List files = []
- project.jsDoc.paths.each{ String path ->
- files += project.file(path)
- }
- return files
- }
+ @Inject abstract ExecOperations getExec()
+
+ @InputDirectory
+ final abstract DirectoryProperty templateDir = project.objects.directoryProperty().convention(
+ project.rootProject.layout.projectDirectory.dir("tools/jsdoc-toolkit/templates/jsdoc_substituted")
+ )
+
+ @Input
+ final abstract Property jarPath = project.objects.property(String).convention("${project.jsDoc.root}/jsrun.jar")
+
+ @Input
+ final abstract Property jsPath = project.objects.property(String).convention("${project.jsDoc.root}/app/run.js")
@OutputDirectory
- File getOutputDirectory()
+ final abstract DirectoryProperty outputDir = project.objects.directoryProperty().convention(getJsDocDirectory(project).dir( "docs"))
+
+ @Input
+ abstract ListProperty getFilesToProcess()
+
+ static Directory getJsDocDirectory(Project project)
{
- return project.jsDoc.outputDir
+ return XsdDoc.getClientDocsBuildDir(project).get().dir("javascript")
}
@TaskAction
void createDocs()
{
- List inputPaths = getInputFiles()
- project.javaexec { exec ->
+ exec.javaexec { exec ->
exec.mainClass = "-jar"
- exec.args = ["${project.jsDoc.root}/jsrun.jar",
- "${project.jsDoc.root}/app/run.js",
- "--template=${project.tasks.jsdocTemplate.destinationDir}",
- "--directory=${getOutputDirectory()}",
+ exec.args = [jarPath.get(),
+ jsPath.get(),
+ "--template=${templateDir.get()}",
+ "--directory=${outputDir.get()}",
"--verbose"]
- inputPaths.each { File file ->
+ filesToProcess.get().each { File file ->
exec.args += file.path
}
}
diff --git a/src/main/groovy/org/labkey/gradle/task/CreateModule.groovy b/src/main/groovy/org/labkey/gradle/task/CreateModule.groovy
index 18af823e..8ca8fe23 100644
--- a/src/main/groovy/org/labkey/gradle/task/CreateModule.groovy
+++ b/src/main/groovy/org/labkey/gradle/task/CreateModule.groovy
@@ -47,7 +47,7 @@ class CreateModule extends DefaultTask
moduleName = project.moduleName
}
else {
- project.ant.input(
+ ant.input(
message: "\nEnter the (Java) name for your new module: ",
addProperty: "new_moduleName"
)
@@ -65,7 +65,7 @@ class CreateModule extends DefaultTask
moduleDestination = project.moduleDestination
}
else {
- project.ant.input(
+ ant.input(
message: "\nEnter the location for the new module (absolute or relative to '" + project.projectDir.getAbsolutePath() + "'): ",
addProperty: "new_moduleDestination"
)
@@ -93,19 +93,19 @@ class CreateModule extends DefaultTask
createApiFiles = ((String)project.createFiles).contains('api')
}
else {
- project.ant.input(
+ ant.input(
message: "\nWill this module create and manage a database schema? (Y/n)",
addProperty: "new_hasManagedSchema"
)
hasManagedSchema = !(ant.new_hasManagedSchema.trim().equalsIgnoreCase("n"))
- project.ant.input(
+ ant.input(
message: "\nCreate test stubs (y/N)",
addProperty: "new_createTestFiles"
)
createTestFiles = ant.new_createTestFiles.trim().equalsIgnoreCase("y")
- project.ant.input(
+ ant.input(
message: "\nCreate API stubs (y/N)",
addProperty: "new_createApiFiles"
)
diff --git a/src/main/groovy/org/labkey/gradle/task/CreateXsdDocs.groovy b/src/main/groovy/org/labkey/gradle/task/CreateXsdDocs.groovy
index eb7b84d0..6777d7c5 100644
--- a/src/main/groovy/org/labkey/gradle/task/CreateXsdDocs.groovy
+++ b/src/main/groovy/org/labkey/gradle/task/CreateXsdDocs.groovy
@@ -16,47 +16,59 @@
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.provider.ListProperty
+import org.gradle.api.provider.Property
+import org.gradle.api.tasks.Classpath
+import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.TaskAction
+import org.gradle.process.ExecOperations
import org.labkey.gradle.plugin.XsdDoc
-class CreateXsdDocs extends DefaultTask
+import javax.inject.Inject
+
+abstract class CreateXsdDocs extends DefaultTask
{
- @InputFiles
- List getXsdFiles()
- {
- return project.xsdDoc.xsdFiles
- }
+ @Inject abstract ExecOperations getExec()
+
+ @Input
+ abstract ListProperty getFilesToProcess()
@OutputDirectory
- File getOutputDirectory()
- {
- return XsdDoc.getXsdDocDirectory(project).file( "docs").asFile
- }
+ final abstract DirectoryProperty outputDir = project.objects.directoryProperty().convention(XsdDoc.getXsdDocDirectory(project).dir( "docs"))
+
+ @Input
+ final abstract Property templatePath = project.objects.property(String).convention("${project.rootDir}/tools/docflex-xml-re-${project.docflexXmlReVersion}/templates/XSDDoc/FramedDoc.tpl")
+
+ @InputFiles
+ @Classpath
+ abstract ConfigurableFileCollection getXsdDocClasspath()
@TaskAction
void createDocs()
{
- List xsdFiles = getXsdFiles()
- project.javaexec {exec ->
+ exec.javaexec {exec ->
exec.mainClass = "com.docflex.xml.Generator"
- exec.classpath project.configurations.xsdDoc
+ exec.classpath getXsdDocClasspath()
exec.args = [
- "-template", "${project.rootDir}/tools/docflex-xml-re-${project.docflexXmlReVersion}/templates/XSDDoc/FramedDoc.tpl",
+ "-template", templatePath.get(),
"-p:docTitle", "LabKey XML Schema Reference",
"-format", "HTML", // output format
- "-d", getOutputDirectory(), // output directory
+ "-d", outputDir.get().asFile.getAbsolutePath(),
"-nodialog", // do not launch the generator GUI
"-launchviewer=false", // do not launch the default viewer for the output file
]
- // Specify one or many data source XML files to be processed
- // by the specified template. (Both local pathnames and URLs
- // are allowed.)
- xsdFiles.each {File file ->
- exec.args += file.path
+ // Specify one or many data source XML files to be processed
+ // by the specified template. (Both local pathnames and URLs
+ // are allowed.)
+ filesToProcess.get().each {
+ File file ->
+ exec.args += file.path
}
}
}
diff --git a/src/main/groovy/org/labkey/gradle/task/DeployApp.groovy b/src/main/groovy/org/labkey/gradle/task/DeployApp.groovy
index 2506c12c..c6cd1dc9 100644
--- a/src/main/groovy/org/labkey/gradle/task/DeployApp.groovy
+++ b/src/main/groovy/org/labkey/gradle/task/DeployApp.groovy
@@ -15,13 +15,20 @@
*/
package org.labkey.gradle.task
+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.RegularFileProperty
+import org.gradle.api.provider.Property
+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.OutputFile
import org.gradle.api.tasks.TaskAction
import org.labkey.gradle.plugin.ServerDeploy
+import org.labkey.gradle.plugin.extension.ServerDeployExtension
import org.labkey.gradle.util.BuildUtils
abstract class DeployApp extends DeployAppBase
@@ -45,24 +52,39 @@ abstract class DeployApp extends DeployAppBase
@OutputDirectory
final abstract DirectoryProperty deployBinDir = BuildUtils.getRootBuildDirectoryProperty(project, ServerDeploy.DEPLOY_BIN_DIR)
+ @Input
+ final abstract Property useLocalBuild = project.objects.property(Boolean).convention(project.hasProperty("useLocalBuild") && "false" != project.property("useLocalBuild"))
+
+ @OutputFile
+ final abstract RegularFileProperty restartTriggerFile = project.objects.fileProperty().fileValue(BuildUtils.getRestartTriggerFile(project))
+
+ @OutputDirectory
+ final abstract DirectoryProperty embeddedDir = project.objects.directoryProperty().convention(ServerDeployExtension.getEmbeddedServerDeployDirectory(project))
+
+ @InputFiles
+ abstract ConfigurableFileCollection getBootJar()
+
@TaskAction
void action()
{
deployModules()
deployPipelineJars()
deployPlatformBinaries(deployBinDir.get().asFile)
- BuildUtils.updateRestartTriggerFile(project)
+ deployEmbeddedBootJar()
+ setDatabaseProperties()
+ setUpProperties()
+ BuildUtils.updateRestartTriggerFile(useLocalBuild.get(), restartTriggerFile.get().asFile)
}
private void deployModules()
{
ant.copy (
- todir: deployModulesDir.get().asFile,
- preserveLastModified: true,
+ todir: deployModulesDir.get().asFile,
+ preserveLastModified: true,
)
- {
- fileset(dir: stagingModulesDir.get().asFile)
- }
+ {
+ fileset(dir: stagingModulesDir.get().asFile)
+ }
}
private void deployPipelineJars()
@@ -73,4 +95,14 @@ abstract class DeployApp extends DeployAppBase
copy.setDuplicatesStrategy(DuplicatesStrategy.INCLUDE)
})
}
+
+ private void deployEmbeddedBootJar()
+ {
+ fs.copy {
+ CopySpec copy ->
+ copy.from bootJar
+ copy.into embeddedDir.get()
+ 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 a22082c9..2f2d86d8 100644
--- a/src/main/groovy/org/labkey/gradle/task/DeployAppBase.groovy
+++ b/src/main/groovy/org/labkey/gradle/task/DeployAppBase.groovy
@@ -1,7 +1,6 @@
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
@@ -13,7 +12,7 @@ import org.labkey.gradle.util.BuildUtils
import javax.inject.Inject
-abstract class DeployAppBase extends DefaultTask {
+abstract class DeployAppBase extends SetUpProperties {
@Inject abstract FileSystemOperations getFs()
diff --git a/src/main/groovy/org/labkey/gradle/task/DeployDistribution.groovy b/src/main/groovy/org/labkey/gradle/task/DeployDistribution.groovy
index a5ffaa7c..216a0ce1 100644
--- a/src/main/groovy/org/labkey/gradle/task/DeployDistribution.groovy
+++ b/src/main/groovy/org/labkey/gradle/task/DeployDistribution.groovy
@@ -1,15 +1,28 @@
package org.labkey.gradle.task
+import org.gradle.api.GradleException
+import org.gradle.api.file.ArchiveOperations
import org.gradle.api.file.CopySpec
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.DuplicatesStrategy
+import org.gradle.api.file.RegularFileProperty
+import org.gradle.api.provider.Property
+import org.gradle.api.tasks.Input
+import org.gradle.api.tasks.InputFile
+import org.gradle.api.tasks.Optional
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
-abstract class DeployDistribution extends DeployAppBase {
+import javax.inject.Inject
+
+abstract class DeployDistribution extends DeployAppBase
+{
+ @Inject abstract ArchiveOperations getArchiveOps()
+
+ @Input
+ final abstract Property distDir = project.objects.property(String).convention(project.hasProperty("distDir") ? (String) project.property("distDir") : "dist")
@OutputDirectory
final abstract DirectoryProperty deployDir = project.objects.directoryProperty().convention(ServerDeployExtension.getEmbeddedDir(project))
@@ -17,23 +30,30 @@ abstract class DeployDistribution extends DeployAppBase {
@OutputDirectory
final abstract DirectoryProperty deployBinDir = project.objects.directoryProperty().convention(ServerDeployExtension.getEmbeddedBinDir(project))
+ @InputFile @Optional
+ final abstract RegularFileProperty distributionFile = project.objects.fileProperty().fileValue(DistributionExtension.getDistributionFile(project, distDir.get()))
+
@TaskAction
void action()
{
+ if (!distributionFile.isPresent())
+ throw new GradleException("Distribution file not found in directory '${distDir.get()}'. Be sure the directory exists and contains exactly one file with extension ${DistributionExtension.TAR_ARCHIVE_EXTENSION}.")
+
deployExecutableJar()
deployPlatformBinaries(deployBinDir.get().asFile)
+ setDatabaseProperties()
+ setUpProperties()
}
private void deployExecutableJar() {
- File distributionFile = DistributionExtension.getDistributionFile(project)
fs.copy({ CopySpec copy ->
- copy.from project.tarTree(distributionFile).files
+ copy.from archiveOps.tarTree(distributionFile.get().asFile).files
copy.into deployDir.get()
copy.include "*.jar"
copy.setDuplicatesStrategy(DuplicatesStrategy.INCLUDE)
})
fs.copy({ CopySpec copy ->
- copy.from project.tarTree(distributionFile).files
+ copy.from archiveOps.tarTree(distributionFile.get().asFile).files
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 51799759..565069dc 100644
--- a/src/main/groovy/org/labkey/gradle/task/DoThenSetup.groovy
+++ b/src/main/groovy/org/labkey/gradle/task/DoThenSetup.groovy
@@ -15,32 +15,10 @@
*/
package org.labkey.gradle.task
-import org.gradle.api.DefaultTask
-import org.gradle.api.Project
-import org.gradle.api.file.CopySpec
-import org.gradle.api.file.DuplicatesStrategy
-import org.gradle.api.tasks.Input
-import org.gradle.api.tasks.Optional
import org.gradle.api.tasks.TaskAction
-import org.labkey.gradle.plugin.extension.TeamCityExtension
-import org.labkey.gradle.util.BuildUtils
-import org.labkey.gradle.util.DatabaseProperties
-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
+abstract class DoThenSetup extends SetUpProperties
{
- // TODO rethink this input declaration. Dependence on a file makes more sense
- @Optional @Input
- protected DatabaseProperties databaseProperties
-
- @Input
- boolean dbPropertiesChanged = false
-
protected void doDatabaseTask()
{
setDatabaseProperties()
@@ -49,136 +27,6 @@ class DoThenSetup extends DefaultTask
@TaskAction
void setup() {
doDatabaseTask()
- if (!embeddedConfigUpToDate()) {
- Properties configProperties = databaseProperties.getConfigProperties()
- configProperties.putAll(getExtraJdbcProperties())
- // in .properties files, backward slashes are seen as escape characters, so all paths must use forward slashes, even on Windows
- configProperties.setProperty("pathToServer", project.rootDir.getAbsolutePath().replaceAll("\\\\", "/"))
-
- configProperties.setProperty("serverPort", tcPropOrDefault(project,
- TeamCityExtension::getLabKeyServerPort,
- "serverPort",
- project.hasProperty("useSsl") ? "8443" : "8080"))
-
- configProperties.setProperty("contextPath", tcPropOrDefault(project,
- TeamCityExtension::getLabKeyContextPath,
- "contextPath",
- ""))
-
- configProperties.setProperty("shutdownPort", tcPropOrDefault(project,
- TeamCityExtension::getLabKeyServerShutdownPort,
- "shutdownPort",
- "8081"))
-
- if (project.hasProperty("useSsl")) {
- configProperties.setProperty("keyStore", tcPropOrDefault(project,
- TeamCityExtension::getLabKeyServerKeystore,
- "keyStore",
- "/opt/teamcity-agent/localhost.keystore"))
-
- configProperties.setProperty("keyStorePassword", tcPropOrDefault(project,
- TeamCityExtension::getLabKeyServerKeystorePassword,
- "keyStorePassword",
- "changeit"))
- }
-
- String embeddedDir = BuildUtils.getEmbeddedConfigPath(project)
- File configsDir = new File(BuildUtils.getConfigsProject(project).projectDir, "configs")
- project.copy({ CopySpec copy ->
- copy.from configsDir
- copy.into embeddedDir
- copy.include "application.properties"
- copy.setDuplicatesStrategy(DuplicatesStrategy.INCLUDE)
- copy.filter({ String line ->
- // Always uncomment properties prepended by '#setupTask#'
- line = line.replace("#setupTask#", "")
- if (project.hasProperty("useSsl")) {
- line = line.replace("#server.ssl", "server.ssl")
- }
- if (project.hasProperty("useLocalBuild") && "false" != project.property("useLocalBuild")) {
- // Enable properties that require 'useLocalBuild' (e.g. 'context.webAppLocation' and 'spring.devtools.restart.additional-paths')
- line = line.replace("#useLocalBuild#", "")
- }
- else {
- // Remove placeholder
- line = line.replace("#useLocalBuild#", "#")
- }
- if (configProperties.containsKey("extraJdbcDataSource") && line.contains("=@@extraJdbc"))
- {
- line = line.replace("#context.", "context.")
- }
- if (configProperties.containsKey("contextPath") && line.contains("=@@contextPath"))
- {
- line = line.replace("#context.", "context.")
- }
- if (line.startsWith("#")) {
- return line // Don't apply replacements to comments
- }
- return PropertiesUtils.replaceProps(line, configProperties, false)
- })
- })
- BuildUtils.updateRestartTriggerFile(project)
- }
- }
-
- /**
- * Get 'extraJdbc*' properties from TeamCity.
- * Used as string replacements when deploying 'labkey.xml' and 'application.properties'
- */
- private Properties getExtraJdbcProperties()
- {
- def extraJdbcProperties = new Properties()
- def tcProperties = TeamCityExtension.getTeamCityProperties(project)
- for (Map.Entry entry : tcProperties.entrySet())
- {
- if (entry.getKey().startsWith("extraJdbc"))
- {
- extraJdbcProperties.put(entry.getKey(), entry.getValue())
- }
- }
- return extraJdbcProperties
- }
-
- boolean embeddedConfigUpToDate()
- {
- if (this.dbPropertiesChanged)
- return false
-
- File dbPropFile = DatabaseProperties.getPickedConfigFile(project)
- File applicationPropsFile = new File(BuildUtils.getEmbeddedConfigPath(project), "application.properties")
- if (!dbPropFile.exists() || !applicationPropsFile.exists())
- return false
- if (dbPropFile.lastModified() < applicationPropsFile.lastModified())
- {
- return true
- }
- return false
- }
-
- protected void setDatabaseProperties()
- {
- databaseProperties = new DatabaseProperties(project, false)
- }
-
- void setDatabaseProperties(DatabaseProperties dbProperties)
- {
- this.databaseProperties = dbProperties
- }
-
- DatabaseProperties getDatabaseProperties()
- {
- return databaseProperties
- }
-
- private static String tcPropOrDefault(Project project, Function tcPropertyFunc, String projectPropertyName, String defaultValue)
- {
- String value = tcPropertyFunc.apply(project)
- if (value == null) {
- if (project.hasProperty(projectPropertyName))
- value = (String) project.property(projectPropertyName)
- else
- value = defaultValue
- }
- return value
+ setUpProperties()
}
}
diff --git a/src/main/groovy/org/labkey/gradle/task/GzipAction.groovy b/src/main/groovy/org/labkey/gradle/task/GzipAction.groovy
index e6d0148c..36a6cf97 100644
--- a/src/main/groovy/org/labkey/gradle/task/GzipAction.groovy
+++ b/src/main/groovy/org/labkey/gradle/task/GzipAction.groovy
@@ -40,11 +40,11 @@ class GzipAction implements Action
}
tree.each { File file ->
- task.project.ant.gzip(
+ task.ant.gzip(
src: file,
destfile: "${file.toString()}.gz"
)
- task.project.logger.info("zipping file " + file + " to ${file.toString()}.gz")
+ task.logger.info("zipping file " + file + " to ${file.toString()}.gz")
}
}
}
diff --git a/src/main/groovy/org/labkey/gradle/task/ModuleDistribution.groovy b/src/main/groovy/org/labkey/gradle/task/ModuleDistribution.groovy
index c94aed25..ed6aa155 100644
--- a/src/main/groovy/org/labkey/gradle/task/ModuleDistribution.groovy
+++ b/src/main/groovy/org/labkey/gradle/task/ModuleDistribution.groovy
@@ -21,15 +21,26 @@ import org.gradle.api.GradleException
import org.gradle.api.Project
import org.gradle.api.file.CopySpec
import org.gradle.api.file.DuplicatesStrategy
-import org.gradle.api.tasks.*
+import org.gradle.api.file.FileSystemOperations
+import org.gradle.api.provider.Property
+import org.gradle.api.tasks.Input
+import org.gradle.api.tasks.Optional
+import org.gradle.api.tasks.OutputDirectory
+import org.gradle.api.tasks.OutputFile
+import org.gradle.api.tasks.OutputFiles
+import org.gradle.api.tasks.TaskAction
import org.labkey.gradle.plugin.ApplyLicenses
import org.labkey.gradle.plugin.extension.DistributionExtension
import org.labkey.gradle.plugin.extension.LabKeyExtension
import org.labkey.gradle.util.BuildUtils
import org.labkey.gradle.util.GroupNames
-class ModuleDistribution extends DefaultTask
+import javax.inject.Inject
+
+abstract class ModuleDistribution extends DefaultTask
{
+ @Inject abstract FileSystemOperations getFs()
+
@Optional @Input
String extraFileIdentifier = null
@Optional @Input
@@ -45,6 +56,12 @@ class ModuleDistribution extends DefaultTask
@Optional @Input
Map extraProperties = [:]
+ @Input
+ final abstract Property isDevMode = project.objects.property(Boolean).convention(LabKeyExtension.isDevMode(project))
+
+ @Input
+ final abstract Property isDevDist = project.objects.property(Boolean).convention(project.hasProperty("devDistribution"))
+
private File distributionDir
private final DistributionExtension distExtension
@@ -90,7 +107,7 @@ class ModuleDistribution extends DefaultTask
@TaskAction
void doAction()
{
- if (LabKeyExtension.isDevMode(project) && !project.hasProperty("devDistribution"))
+ if (isDevMode.get() && !isDevDist.get())
throw new GradleException("Distributions should never be created with deployMode=dev as dev modules are not portable. " +
"Use -PdevDistribution if you need to override this exception for debugging.")
@@ -126,7 +143,7 @@ class ModuleDistribution extends DefaultTask
{
File modulesDir = getModulesDir()
modulesDir.deleteDir()
- project.copy {
+ fs.copy {
CopySpec copy ->
copy.from { project.configurations.distribution }
copy.setDuplicatesStrategy(DuplicatesStrategy.EXCLUDE)
@@ -134,7 +151,7 @@ class ModuleDistribution extends DefaultTask
}
if (!BuildUtils.isOpenSource(project))
{
- project.copy {
+ fs.copy {
CopySpec copy ->
copy.from(findLicensingProject().tasks.patchApiModule.outputs.files.singleFile)
copy.rename { String fileName ->
@@ -203,7 +220,7 @@ class ModuleDistribution extends DefaultTask
File utilsDir = getWindowsUtilDir()
if (project.configurations.findByName("utilities") != null && !utilsDir.exists())
{
- project.copy({
+ fs.copy({
CopySpec copy ->
copy.from(project.configurations.utilities.collect { project.zipTree(it) })
copy.into utilsDir
@@ -231,7 +248,7 @@ class ModuleDistribution extends DefaultTask
}
}
- project.copy {
+ fs.copy {
CopySpec copy ->
copy.from(embeddedJarFile)
copy.into(project.layout.buildDirectory)
@@ -276,7 +293,7 @@ class ModuleDistribution extends DefaultTask
// Prefer files from 'server/configs/webapps' if they exist
File serverConfigDir = project.rootProject.file("server/configs/webapps/")
if (serverConfigDir.exists()) {
- project.copy({ CopySpec copy ->
+ fs.copy({ CopySpec copy ->
copy.from(serverConfigDir)
copy.exclude "*.xml"
copy.into(project.layout.buildDirectory)
@@ -286,12 +303,12 @@ class ModuleDistribution extends DefaultTask
// Allow distributions to include custom README
File resources = project.file("resources")
if (resources.isDirectory()) {
- project.copy({ CopySpec copy ->
+ fs.copy({ CopySpec copy ->
copy.from(resources)
copy.into(project.layout.buildDirectory)
copy.setDuplicatesStrategy(DuplicatesStrategy.INCLUDE)
})
- project.copy({ CopySpec copy ->
+ fs.copy({ CopySpec copy ->
copy.from(resources)
copy.into(project.layout.buildDirectory.file("embedded"))
copy.setDuplicatesStrategy(DuplicatesStrategy.INCLUDE)
@@ -300,7 +317,7 @@ class ModuleDistribution extends DefaultTask
// This is necessary for reasons that are unclear. Without it, you get:
// -bash: ./manual-upgrade.sh: /bin/sh^M: bad interpreter: No such file or directory
// even though the original file has unix line endings. Dunno.
- project.ant.fixcrlf (srcdir: BuildUtils.getBuildDirPath(project), includes: "manual-upgrade.sh", eol: "unix")
+ this.ant.fixcrlf (srcdir: BuildUtils.getBuildDirPath(project), includes: "manual-upgrade.sh", eol: "unix")
}
@OutputFile
diff --git a/src/main/groovy/org/labkey/gradle/task/PurgeNpmAlphaVersions.groovy b/src/main/groovy/org/labkey/gradle/task/PurgeNpmAlphaVersions.groovy
index 52143c9b..c73db094 100644
--- a/src/main/groovy/org/labkey/gradle/task/PurgeNpmAlphaVersions.groovy
+++ b/src/main/groovy/org/labkey/gradle/task/PurgeNpmAlphaVersions.groovy
@@ -9,15 +9,18 @@ import org.apache.hc.client5.http.impl.classic.HttpClients
import org.apache.hc.core5.http.HttpStatus
import org.gradle.api.DefaultTask
import org.gradle.api.GradleException
+import org.gradle.api.provider.Property
+import org.gradle.api.tasks.Input
import org.gradle.api.tasks.TaskAction
import org.labkey.gradle.plugin.NpmRun
import java.util.stream.Collectors
-class PurgeNpmAlphaVersions extends DefaultTask
+abstract class PurgeNpmAlphaVersions extends DefaultTask
{
private static final String REPOSITORY_NAME = 'libs-client-local'
public static final String ALPHA_PREFIX_PROPERTY = 'alphaPrefix'
+ public static final String DRY_RUN_PROPERTY = 'dryRun'
public static final String[] PACKAGE_NAMES = [
'@labkey/api',
'@labkey/assayreport',
@@ -29,13 +32,23 @@ class PurgeNpmAlphaVersions extends DefaultTask
'@labkey/themes'
]
+ @Input
+ final abstract Property alphaPrefixProp = project.objects.property(String).convention((project.hasProperty(ALPHA_PREFIX_PROPERTY) ? (String) project.property(ALPHA_PREFIX_PROPERTY) : null))
+ @Input
+ final abstract Property isDryRun = project.objects.property(Boolean).convention(project.hasProperty(DRY_RUN_PROPERTY))
+ @Input
+ final abstract Property artifactoryUrl = project.objects.property(String).convention((String) project.property('artifactory_contextUrl'))
+ @Input
+ final abstract Property artifactoryUser = project.objects.property(String).convention((String) project.property('artifactory_user'))
+ @Input
+ final abstract Property artifactoryPassword = project.objects.property(String).convention((String) project.property('artifactory_password'))
+
@TaskAction
void purgeVersions()
{
- String alphaPrefix
- if (!project.hasProperty(ALPHA_PREFIX_PROPERTY))
+ if (!alphaPrefixProp.isPresent() || StringUtils.isEmpty(alphaPrefixProp.get().trim()))
throw new GradleException("No value provided for alphaPrefix.")
- alphaPrefix = project.property(ALPHA_PREFIX_PROPERTY)
+ String alphaPrefix = alphaPrefixProp.get()
String[] undeletedVersions = []
for (String packageName : PACKAGE_NAMES)
{
@@ -47,7 +60,7 @@ class PurgeNpmAlphaVersions extends DefaultTask
logger.quiet("Found ${alphaVersions.size()} versions with alpha prefix ${alphaPrefix} in package ${packageName}")
if (!alphaVersions.isEmpty()) {
alphaVersions.forEach(version -> {
- if (project.hasProperty("dryRun"))
+ if (isDryRun.get())
logger.quiet("Removing version ${version} of package ${packageName} -- Skipped for dry run")
else {
logger.quiet("Removing version ${version} of package ${packageName}")
@@ -109,7 +122,7 @@ class PurgeNpmAlphaVersions extends DefaultTask
boolean makeDeleteRequest(String packageName, String version)
{
CloseableHttpClient httpClient = HttpClients.createDefault()
- String endpoint = project.property('artifactory_contextUrl')
+ String endpoint = artifactoryUrl.get()
boolean success = true
if (!endpoint.endsWith("/"))
endpoint += "/"
@@ -121,7 +134,7 @@ class PurgeNpmAlphaVersions extends DefaultTask
{
HttpDelete httpDelete = new HttpDelete(endpoint)
// N.B. Using Authorization Bearer with an API token does not currently work
- httpDelete.setHeader("Authorization", "Basic " + Base64.getEncoder().encodeToString("${project.property('artifactory_user')}:${project.property('artifactory_password')}".getBytes()))
+ httpDelete.setHeader("Authorization", "Basic " + Base64.getEncoder().encodeToString("${artifactoryUser.get()}:${artifactoryPassword.get()}".getBytes()))
CloseableHttpResponse response = httpClient.execute(httpDelete)
int statusCode = response.getCode()
if (statusCode != HttpStatus.SC_OK && statusCode != HttpStatus.SC_NO_CONTENT) {
diff --git a/src/main/groovy/org/labkey/gradle/task/RestoreFromTrash.groovy b/src/main/groovy/org/labkey/gradle/task/RestoreFromTrash.groovy
index fa772b4f..9a14481b 100644
--- a/src/main/groovy/org/labkey/gradle/task/RestoreFromTrash.groovy
+++ b/src/main/groovy/org/labkey/gradle/task/RestoreFromTrash.groovy
@@ -18,6 +18,8 @@ import org.labkey.gradle.util.BuildUtils
import static org.labkey.gradle.task.PurgeArtifacts.Response
+// TODO for more flexibility and for configuration cache support, needs to be converted to read projects and versions
+// from a file, as for PurgeArtifacts
class RestoreFromTrash extends DefaultTask
{
public static final String VERSION_PROPERTY = "restoreVersion"
diff --git a/src/main/groovy/org/labkey/gradle/task/SetUpProperties.groovy b/src/main/groovy/org/labkey/gradle/task/SetUpProperties.groovy
new file mode 100644
index 00000000..f45762cc
--- /dev/null
+++ b/src/main/groovy/org/labkey/gradle/task/SetUpProperties.groovy
@@ -0,0 +1,228 @@
+/*
+ * Copyright (c) 2016-2025 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.task
+
+import groovy.sql.Sql
+import org.gradle.api.GradleException
+import org.gradle.api.file.ConfigurableFileCollection
+import org.gradle.api.file.CopySpec
+import org.gradle.api.file.DuplicatesStrategy
+import org.gradle.api.file.FileSystemOperations
+import org.gradle.api.file.RegularFileProperty
+import org.gradle.api.provider.Property
+import org.gradle.api.tasks.Input
+import org.gradle.api.tasks.InputDirectory
+import org.gradle.api.tasks.InputFiles
+import org.gradle.api.tasks.Internal
+import org.gradle.api.tasks.OutputFile
+import org.labkey.gradle.plugin.extension.TeamCityExtension
+import org.labkey.gradle.util.BuildUtils
+import org.labkey.gradle.util.DatabaseProperties
+import org.labkey.gradle.util.PropertiesUtils
+
+import javax.inject.Inject
+
+abstract class SetUpProperties extends TeamCityPropertiesTask
+{
+ @Internal
+ private DatabaseProperties databaseProperties
+
+ @InputFiles
+ abstract ConfigurableFileCollection getDriverFiles()
+
+ @Input
+ boolean dbPropertiesChanged = false
+
+ @OutputFile
+ final abstract RegularFileProperty chosenPropsFile = project.objects.fileProperty().fileValue(DatabaseProperties.getPickedConfigFile(project))
+
+ @OutputFile
+ final abstract RegularFileProperty applicationPropsFile = project.objects.fileProperty().fileValue(new File(BuildUtils.getEmbeddedConfigPath(project), "application.properties"))
+
+ @Input
+ final abstract Property useSsl = project.objects.property(Boolean).convention(project.hasProperty("useSsl"))
+ @Input
+ final abstract Property portNumber = project.objects.property(String).convention(project.hasProperty("useSsl") ? "8443" : "8080")
+ @Input
+ final abstract Property useLocalBuild = project.objects.property(Boolean).convention(project.hasProperty("useLocalBuild") && "false" != project.property("useLocalBuild"))
+
+ @Input // in .properties files, backward slashes are seen as escape characters, so all paths must use forward slashes, even on Windows
+ final abstract Property pathToServer = project.objects.property(String).convention(project.rootDir.getAbsolutePath().replaceAll("\\\\", "/"))
+
+ @Input
+ final abstract Property embeddedDir = project.objects.property(String).convention(BuildUtils.getEmbeddedConfigPath(project))
+ @InputDirectory
+ File configsDir = new File(BuildUtils.getConfigsProject(project).projectDir, "configs")
+
+ @OutputFile
+ final abstract RegularFileProperty restartTriggerFile = project.objects.fileProperty().fileValue(BuildUtils.getRestartTriggerFile(project))
+
+ @Inject abstract FileSystemOperations getFs()
+
+ void setUpProperties() {
+ if (!embeddedConfigUpToDate()) {
+ Properties configProperties = databaseProperties.getConfigProperties()
+ configProperties.putAll(getExtraJdbcProperties())
+ configProperties.setProperty("pathToServer", pathToServer.get())
+ configProperties.setProperty("serverPort", labKeyServerPort.get())
+ configProperties.setProperty("contextPath", contextPath.get())
+ configProperties.setProperty("shutdownPort", shutdownPort.get())
+ if (useSsl.get()) {
+ configProperties.setProperty("keyStore", keyStore.get())
+ configProperties.setProperty("keyStorePassword", keyStorePassword.get())
+ }
+
+ fs.copy({ CopySpec copy ->
+ copy.from configsDir
+ copy.into embeddedDir.get()
+ copy.include "application.properties"
+ copy.setDuplicatesStrategy(DuplicatesStrategy.INCLUDE)
+ copy.filter({ String line ->
+ // Always uncomment properties prepended by '#setupTask#'
+ line = line.replace("#setupTask#", "")
+ if (useSsl.get()) {
+ line = line.replace("#server.ssl", "server.ssl")
+ }
+ if (useLocalBuild.get()) {
+ // Enable properties that require 'useLocalBuild' (e.g. 'context.webAppLocation' and 'spring.devtools.restart.additional-paths')
+ line = line.replace("#useLocalBuild#", "")
+ }
+ else {
+ // Remove placeholder
+ line = line.replace("#useLocalBuild#", "#")
+ }
+ if (configProperties.containsKey("extraJdbcDataSource") && line.contains("=@@extraJdbc"))
+ {
+ line = line.replace("#context.", "context.")
+ }
+ if (configProperties.containsKey("contextPath") && line.contains("=@@contextPath"))
+ {
+ line = line.replace("#context.", "context.")
+ }
+ if (line.startsWith("#")) {
+ return line // Don't apply replacements to comments
+ }
+ return PropertiesUtils.replaceProps(line, configProperties)
+ })
+ })
+ BuildUtils.updateRestartTriggerFile(useLocalBuild.get(), restartTriggerFile.get().asFile)
+ }
+ }
+
+ /**
+ * Get 'extraJdbc*' properties from TeamCity.
+ * Used as string replacements when deploying 'application.properties'
+ */
+ @Input
+ Properties getExtraJdbcProperties()
+ {
+ def extraJdbcProperties = new Properties()
+ def tcProperties = isOnTeamCity.get() ? TeamCityExtension.getTeamCityProperties(project) : new Properties()
+ for (Map.Entry entry : tcProperties.entrySet())
+ {
+ if (entry.getKey().startsWith("extraJdbc"))
+ {
+ extraJdbcProperties.put(entry.getKey(), entry.getValue())
+ }
+ }
+ return extraJdbcProperties
+ }
+
+ boolean embeddedConfigUpToDate()
+ {
+ if (this.dbPropertiesChanged)
+ return false
+
+ File dbPropFile = chosenPropsFile.get().getAsFile()
+ File _applicationPropsFile = applicationPropsFile.get().getAsFile()
+ if (!dbPropFile.exists() || !_applicationPropsFile.exists())
+ return false
+ if (dbPropFile.lastModified() < _applicationPropsFile.lastModified())
+ {
+ return true
+ }
+ return false
+ }
+
+ protected void setDatabaseProperties()
+ {
+ databaseProperties = new DatabaseProperties(getPath(), chosenPropsFile.get().asFile, false)
+ }
+
+ void setDatabaseProperties(DatabaseProperties dbProperties)
+ {
+ this.databaseProperties = dbProperties
+ }
+
+ DatabaseProperties getDatabaseProperties()
+ {
+ return databaseProperties
+ }
+
+ void execSql(DatabaseProperties params, String sql)
+ {
+ params.interpolateCompositeProperties()
+ String url = params.getJdbcURL()
+ String user = params.getJdbcUser()
+ String password = params.getJdbcPassword()
+ String driverClassName = params.getJdbcDriverClassName()
+ logger.info("in execSql: url ${url} driverClassName ${driverClassName}")
+ logger.debug(" user ${user} password ${password}")
+
+ var sqlLoader = Sql.classLoader
+ // N.B. It seems like this modification of the loader classpath should not be necessary (or possible) and we
+ // should be able to declare the dependencies on the driver jars in the buildscript { dependencies { } } block,
+ // but see this (admittedly old) post about how the classloader is behaving and the suggested solution (pre Gradle 9, but still)
+ // https://stackoverflow.com/questions/44740416/drivermanager-doesnt-see-dependency-in-gradle-custom-plugins-task
+ getDriverFiles().each {File file ->
+ logger.info("adding classLoader URL " + file.toURI().toURL())
+ sqlLoader.addURL(file.toURI().toURL())
+ }
+
+ try
+ {
+ Sql.withInstance(url, user, password, driverClassName) {
+ it.execute sql
+ }
+ }
+ catch (Exception e)
+ {
+ logger.error(e.toString())
+ }
+ }
+
+ void dropDatabase(String projectPath, DatabaseProperties dbProperties)
+ {
+ Properties properties = dbProperties.getConfigProperties()
+ logger.info("in dropDatabase for ${projectPath}, properties are ${properties}")
+ String toDrop = dbProperties.getJdbcDatabase()
+ if (toDrop == null || toDrop.equals("labkey"))
+ {
+ throw new GradleException("Must specify a database that is not 'labkey'")
+ }
+ else
+ {
+ DatabaseProperties dropProps = new DatabaseProperties(projectPath, dbProperties)
+ // need to connect to the built-in default database in order to drop the database
+ dropProps.setJdbcDatabase(dbProperties.getDefaultDatabase())
+ dropProps.setJdbcUrlParams("")
+
+ logger.info("Attempting to drop database ${toDrop}")
+ execSql(dropProps, "DROP DATABASE \"${toDrop}\";")
+ }
+ }
+
+}
diff --git a/src/main/groovy/org/labkey/gradle/task/StageDistribution.groovy b/src/main/groovy/org/labkey/gradle/task/StageDistribution.groovy
index 2e4247cd..13362e6f 100644
--- a/src/main/groovy/org/labkey/gradle/task/StageDistribution.groovy
+++ b/src/main/groovy/org/labkey/gradle/task/StageDistribution.groovy
@@ -16,13 +16,18 @@
package org.labkey.gradle.task
import org.gradle.api.DefaultTask
+import org.gradle.api.file.ArchiveOperations
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.RegularFileProperty
import org.gradle.api.file.RelativePath
+import org.gradle.api.provider.Property
+import org.gradle.api.tasks.Input
+import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.TaskAction
import org.labkey.gradle.plugin.ServerDeploy
@@ -34,8 +39,10 @@ import javax.inject.Inject
abstract class StageDistribution extends DefaultTask
{
@Inject abstract FileSystemOperations getFs()
+ @Inject abstract ArchiveOperations getArchiveOps()
- protected File distributionFile = null
+ @Input
+ final abstract Property distDir = project.objects.property(String).convention(project.hasProperty("distDir") ? (String) project.property("distDir") : "dist")
@OutputDirectory
final abstract DirectoryProperty modulesStagingDir = BuildUtils.getRootBuildDirectoryProperty(project, ServerDeploy.STAGING_MODULES_DIR)
@@ -46,12 +53,15 @@ abstract class StageDistribution extends DefaultTask
@OutputDirectory
final abstract DirectoryProperty pipelineJarStagingDir = BuildUtils.getRootBuildDirectoryProperty(project, ServerDeploy.STAGING_PIPELINE_DIR)
+ @InputFile
+ final abstract RegularFileProperty distributionFileProp = project.objects.fileProperty().fileValue(DistributionExtension.getDistributionFile(project, distDir.get()))
+
@TaskAction
void action()
{
- distributionFile = DistributionExtension.getDistributionFile(project)
+ File distributionFile = distributionFileProp.get().asFile
String extension = DistributionExtension.TAR_ARCHIVE_EXTENSION
- FileTree distArchiveTree = project.tarTree(distributionFile)
+ FileTree distArchiveTree = archiveOps.tarTree(distributionFile)
// first clean out the staging directory so we don't pick up modules not in this distribution
fs.delete {
diff --git a/src/main/groovy/org/labkey/gradle/task/StartLabKey.groovy b/src/main/groovy/org/labkey/gradle/task/StartLabKey.groovy
index 680c59be..1a174c04 100644
--- a/src/main/groovy/org/labkey/gradle/task/StartLabKey.groovy
+++ b/src/main/groovy/org/labkey/gradle/task/StartLabKey.groovy
@@ -17,9 +17,12 @@ package org.labkey.gradle.task
import org.apache.commons.lang3.StringUtils
import org.apache.commons.lang3.SystemUtils
-import org.gradle.api.DefaultTask
import org.gradle.api.GradleException
import org.gradle.api.Project
+import org.gradle.api.file.DirectoryProperty
+import org.gradle.api.file.RegularFileProperty
+import org.gradle.api.tasks.InputDirectory
+import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.TaskAction
import org.labkey.gradle.plugin.Tomcat
import org.labkey.gradle.plugin.extension.LabKeyExtension
@@ -29,7 +32,7 @@ import org.labkey.gradle.util.BuildUtils
import java.util.stream.Collectors
-class StartLabKey extends DefaultTask
+abstract class StartLabKey extends TeamCityPropertiesTask
{
private static final String EMBEDDED_REFLECTION_PARAM = "embeddedReflectionArgs"
private static final List DEFAULT_EMBEDDED_REFLECTION_OPTS = [
@@ -41,17 +44,23 @@ class StartLabKey extends DefaultTask
"--add-opens=java.base/java.text=ALL-UNNAMED"
]
+ @InputDirectory
+ final abstract DirectoryProperty deployDir = project.objects.directoryProperty().convention(ServerDeployExtension.getEmbeddedServerDeployDirectory(project))
+
+ @OutputFile
+ final abstract RegularFileProperty logFileProp = project.objects.fileProperty().convention(ServerDeployExtension.getEmbeddedServerDeployDirectory(project).file(Tomcat.EMBEDDED_LOG_FILE_NAME))
+
@TaskAction
void action()
{
- File jarFile = BuildUtils.getExecutableServerJar(project)
+ File jarFile = BuildUtils.getExecutableServerJar(deployDir.get().asFile)
if (jarFile == null)
{
- throw new GradleException("No jar file found in ${ServerDeployExtension.getEmbeddedServerDeployDirectoryPath(project)}.")
+ throw new GradleException("No jar file found in ${deployDir.get().asFile}.")
}
else
{
- String javaHome = TeamCityExtension.getTeamCityProperty(project, "tomcatJavaHome", System.getenv("JAVA_HOME"))
+ String javaHome = tomcatJavaHome.get()
if (StringUtils.isEmpty(javaHome))
throw new GradleException("JAVA_HOME must be set in order to start your embedded tomcat server.")
File javaBin = new File(javaHome, "bin")
@@ -63,20 +72,21 @@ class StartLabKey extends DefaultTask
commandParts += getStartupOpts(project)
commandParts += ["-jar", jarFile.getName()]
- File logFile = new File(ServerDeployExtension.getEmbeddedServerDeployDirectoryPath(project), Tomcat.EMBEDDED_LOG_FILE_NAME)
+ File logFile = logFileProp.get().asFile
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.getEmbeddedServerDeployDirectoryPath(project)}/bin${File.pathSeparator}${System.getenv("PATH")}")
+ String deployDirPath = deployDir.get().asFile.getAbsolutePath()
+ envMap.put('PATH', "${deployDirPath}/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.getEmbeddedServerDeployDirectoryPath(project)}")
- Process process = commandParts.execute(env, new File(ServerDeployExtension.getEmbeddedServerDeployDirectoryPath(project)))
+ this.logger.info("Starting LabKey with command ${commandParts} and env ${env} in directory ${deployDirPath}")
+ Process process = commandParts.execute(env, deployDir.get().asFile)
process.consumeProcessOutput(outputStream, outputStream)
}
}
diff --git a/src/main/groovy/org/labkey/gradle/task/StopLabKey.groovy b/src/main/groovy/org/labkey/gradle/task/StopLabKey.groovy
index 2821e334..f0477966 100644
--- a/src/main/groovy/org/labkey/gradle/task/StopLabKey.groovy
+++ b/src/main/groovy/org/labkey/gradle/task/StopLabKey.groovy
@@ -16,20 +16,33 @@
package org.labkey.gradle.task
import org.gradle.api.DefaultTask
+import org.gradle.api.file.RegularFileProperty
+import org.gradle.api.provider.Property
+import org.gradle.api.tasks.Input
+import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.TaskAction
import org.labkey.gradle.util.PropertiesUtils
+import org.labkey.gradle.util.BuildUtils
/**
* Task for stopping a running LabKey instance
*/
class StopLabKey extends DefaultTask
{
+ @InputFile
+ final abstract RegularFileProperty propertiesFile = project.objects.fileProperty().fileValue(
+ BuildUtils.getApplicationPropertiesFile(project)
+ )
+
+ @Input
+ final abstract Property useSsl = project.objects.property(Boolean).convention(project.hasProperty("useSsl"))
+
@TaskAction
void action()
{
- def applicationProperties = PropertiesUtils.getApplicationProperties(project)
+ def applicationProperties = PropertiesUtils.getApplicationProperties(propertiesFile.get().asFile)
def port = applicationProperties.getProperty("management.server.port", applicationProperties.getProperty("server.port"))
- def endpoint = "${project.hasProperty("useSsl") ? "https" : "http"}://localhost:$port/actuator/shutdown"
+ def endpoint = "${useSsl.get() ? "https" : "http"}://localhost:$port/actuator/shutdown"
def command = "curl -X POST $endpoint"
this.logger.info("Sending command to $endpoint")
def proc = command.execute()
diff --git a/src/main/groovy/org/labkey/gradle/task/TeamCityDbSetup.groovy b/src/main/groovy/org/labkey/gradle/task/TeamCityDbSetup.groovy
index 5fc7f349..88f2d300 100644
--- a/src/main/groovy/org/labkey/gradle/task/TeamCityDbSetup.groovy
+++ b/src/main/groovy/org/labkey/gradle/task/TeamCityDbSetup.groovy
@@ -16,11 +16,10 @@
package org.labkey.gradle.task
import org.gradle.api.tasks.Input
-import org.labkey.gradle.util.SqlUtils
+import org.labkey.gradle.util.DatabaseProperties
-class TeamCityDbSetup extends DoThenSetup
+abstract class TeamCityDbSetup extends DoThenSetup
{
- boolean dbPropertiesChanged = true
@Input
boolean dropDatabase = false
@Input
@@ -29,17 +28,33 @@ class TeamCityDbSetup extends DoThenSetup
@Override
protected void doDatabaseTask()
{
- databaseProperties.mergePropertiesFromFile()
+ databaseProperties.mergePropertiesFromFile(chosenPropsFile.get().asFile)
if (dropDatabase) {
- if (testValidationOnly){
+ if (testValidationOnly) {
logger.info("The 'testValidationOnly' flag is true, not going to drop the database.")
}
else {
- SqlUtils.dropDatabase(project, databaseProperties)
+ dropDatabase(getPath(), databaseProperties)
}
}
databaseProperties.interpolateCompositeProperties()
- databaseProperties.writeDbProps()
+ writeDbProps()
}
+ void writeDbProps()
+ {
+ writeDatabaseProperty(DatabaseProperties.JDBC_URL_PROP, databaseProperties.getJdbcURL())
+ writeDatabaseProperty(DatabaseProperties.JDBC_USER_PROP, databaseProperties.getJdbcUser())
+ writeDatabaseProperty(DatabaseProperties.JDBC_PASSWORD_PROP, databaseProperties.getJdbcPassword())
+ }
+
+ private void writeDatabaseProperty(String name, String value)
+ {
+ this.ant.propertyfile(
+ file: chosenPropsFile.get().asFile
+ )
+ {
+ entry( key: name, value: value)
+ }
+ }
}
diff --git a/src/main/groovy/org/labkey/gradle/task/TeamCityPropertiesTask.groovy b/src/main/groovy/org/labkey/gradle/task/TeamCityPropertiesTask.groovy
new file mode 100644
index 00000000..dbd28b0b
--- /dev/null
+++ b/src/main/groovy/org/labkey/gradle/task/TeamCityPropertiesTask.groovy
@@ -0,0 +1,78 @@
+package org.labkey.gradle.task
+
+import org.gradle.api.DefaultTask
+import org.gradle.api.Project
+import org.gradle.api.provider.Property
+import org.gradle.api.tasks.Input
+import org.gradle.api.tasks.Optional
+import org.labkey.gradle.plugin.extension.TeamCityExtension
+
+import java.util.function.Function
+
+abstract class TeamCityPropertiesTask extends DefaultTask
+{
+ @Input
+ final abstract Property useSsl = project.objects.property(Boolean).convention(project.hasProperty("useSsl"))
+
+ @Optional @Input
+ final abstract Property isOnTeamCity = project.objects.property(Boolean).convention(project.hasProperty("teamCity"))
+
+ @Optional @Input
+ final abstract Property labKeyServerPort = project.objects.property(String).convention(
+ tcPropOrDefault(project,
+ TeamCityExtension::getLabKeyServerPort,
+ "serverPort", project.hasProperty("useSsl") ? "8443" : "8080"))
+
+ @Optional @Input
+ final abstract Property contextPath = project.objects.property(String).convention(
+ tcPropOrDefault(project,
+ TeamCityExtension::getLabKeyContextPath,
+ "contextPath",
+ ""))
+
+ @Optional @Input
+ final abstract Property shutdownPort = project.objects.property(String).convention(
+ tcPropOrDefault(project,
+ TeamCityExtension::getLabKeyServerShutdownPort,
+ "shutdownPort",
+ "8081"))
+ @Optional @Input
+ final abstract Property keyStore = project.objects.property(String).convention(
+ tcPropOrDefault(project,
+ TeamCityExtension::getLabKeyServerKeystore,
+ "keyStore",
+ "/opt/teamcity-agent/localhost.keystore"))
+
+ @Optional @Input
+ final abstract Property keyStorePassword = project.objects.property(String).convention(
+ tcPropOrDefault(project,
+ TeamCityExtension::getLabKeyServerKeystorePassword,
+ "keyStorePassword",
+ "changeit"))
+
+ @Optional @Input
+ final abstract Property labKeyServer = project.objects.property(String).convention(TeamCityExtension.getLabKeyServer(project))
+
+ @Optional @Input
+ final abstract Property tomcatJavaHome = project.objects.property(String).convention(
+ tcPropOrDefault(project,
+ TeamCityExtension::getTomcatJavaHome,
+ "tomcatJavaHome",
+ System.getenv("JAVA_HOME")
+ )
+ )
+
+ protected static String tcPropOrDefault(Project project, Function tcPropertyFunc, String projectPropertyName, String defaultValue)
+ {
+ String value = tcPropertyFunc.apply(project)
+ if (value == null) {
+ if (project.hasProperty(projectPropertyName))
+ value = (String) project.property(projectPropertyName)
+ else
+ value = defaultValue
+ }
+ return value
+ }
+
+
+}
diff --git a/src/main/groovy/org/labkey/gradle/util/BuildUtils.groovy b/src/main/groovy/org/labkey/gradle/util/BuildUtils.groovy
index 4878ebae..9aa6a709 100644
--- a/src/main/groovy/org/labkey/gradle/util/BuildUtils.groovy
+++ b/src/main/groovy/org/labkey/gradle/util/BuildUtils.groovy
@@ -848,12 +848,16 @@ class BuildUtils
static String getEmbeddedConfigPath(Project project)
{
- return new File(ServerDeployExtension.getEmbeddedServerDeployDirectoryPath(project), "config").absolutePath
+ return getEmbeddedConfigDirectory(project).asFile.absolutePath
}
- static File getExecutableServerJar(Project project)
+ static Directory getEmbeddedConfigDirectory(Project project)
+ {
+ return ServerDeployExtension.getEmbeddedServerDeployDirectory(project).dir("config")
+ }
+
+ static File getExecutableServerJar(File deployDir)
{
- File deployDir = new File(ServerDeployExtension.getEmbeddedServerDeployDirectoryPath(project))
File[] jarFiles = deployDir.listFiles(new FilenameFilter() {
@Override
boolean accept(File dir, String name) {
@@ -874,27 +878,35 @@ class BuildUtils
}
/**
- * Writes a file in the build/deploy/modules directory that can be used as a trigger file for restarting
+ * Writes a file in the build/deploy directory that can be used as a trigger file for restarting
* SpringBoot. Without this, restarts may happen before the full application deployment is done, resulting
* in a failed start. See
* https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#using.devtools.restart.triggerfile
- * We use build/deploy/modules because when using a local build it's added in the application.properties file as a
+ * We use build/deploy because when using a local build it's added in the application.properties file as a
* spring.devtools.restart.additional-paths
*
* @param project - for use in getting the rootProject's build directory
*/
static void updateRestartTriggerFile(Project project)
{
- if (!project.hasProperty('useLocalBuild') || "false" == project.property("useLocalBuild"))
- return
+ updateRestartTriggerFile(project.hasProperty('useLocalBuild') && "false" != project.property("useLocalBuild"), getRestartTriggerFile(project))
+ }
- File triggerFileDir = project.rootProject.layout.buildDirectory.file("deploy/modules").get().getAsFile()
- if (!triggerFileDir.exists())
+ static File getRestartTriggerFile(Project project)
+ {
+ return project.rootProject.layout.buildDirectory.file("deploy/" + RESTART_FILE_NAME).get().getAsFile()
+ }
+
+ static void updateRestartTriggerFile(boolean useLocalBuild, File triggerFile)
+ {
+ if (!useLocalBuild)
return
+ if (!triggerFile.getParentFile().exists())
+ triggerFile.getParentFile().mkdirs()
+
OutputStreamWriter writer = null
try {
- File triggerFile = new File(triggerFileDir, RESTART_FILE_NAME)
writer = new OutputStreamWriter(new FileOutputStream(triggerFile), StandardCharsets.UTF_8)
writer.write(SimpleDateFormat.getDateTimeInstance().format(new Date()))
}
@@ -954,6 +966,11 @@ class BuildUtils
return project.rootProject.layout.buildDirectory.get().asFile.path
}
+ static File getApplicationPropertiesFile(Project project)
+ {
+ return new File(getEmbeddedConfigPath(project), "application.properties")
+ }
+
static Provider getRootBuildDirectoryProvider(Project project, String directoryPath)
{
return project.rootProject.layout.buildDirectory.dir(directoryPath)
diff --git a/src/main/groovy/org/labkey/gradle/util/DatabaseProperties.groovy b/src/main/groovy/org/labkey/gradle/util/DatabaseProperties.groovy
index 47ed91a9..f89025ff 100644
--- a/src/main/groovy/org/labkey/gradle/util/DatabaseProperties.groovy
+++ b/src/main/groovy/org/labkey/gradle/util/DatabaseProperties.groovy
@@ -16,19 +16,22 @@
package org.labkey.gradle.util
import org.gradle.api.Project
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
class DatabaseProperties
{
+ Logger logger = LoggerFactory.getLogger(DatabaseProperties.class)
private static final String PICKED_DATABASE_CONFIG_FILE = "config.properties"
private static final String JDBC_DRIVER_CLASS_NAME_PROP = "jdbcDriverClassName"
- private static final String JDBC_URL_PROP = "jdbcURL"
+ public static final String JDBC_URL_PROP = "jdbcURL"
private static final String JDBC_PORT_PROP = "jdbcPort"
private static final String JDBC_DATABASE_PROP = "jdbcDatabase"
private static final String JDBC_HOST_PROP = "jdbcHost"
private static final String JDBC_URL_PARAMS_PROP = "jdbcURLParameters"
- private static final String JDBC_USER_PROP = "jdbcUser"
- private static final String JDBC_PASSWORD_PROP = "jdbcPassword"
+ public static final String JDBC_USER_PROP = "jdbcUser"
+ public static final String JDBC_PASSWORD_PROP = "jdbcPassword"
private static final String BOOTSTRAP_DB_PROP = "databaseBootstrap"
private static final String DEFAULT_DB_PROP = "databaseDefault"
private static final String DEFAULT_HOST_PROP = "databaseDefaultHost"
@@ -39,7 +42,7 @@ class DatabaseProperties
String version // database version, e.g. 9.2
Properties configProperties
- transient private Project _project
+ String projectPath
DatabaseProperties(String dbTypeAndVersion, String shortType, version)
{
@@ -49,10 +52,10 @@ class DatabaseProperties
this.configProperties = new Properties()
}
- DatabaseProperties(Project project, Boolean useBootstrap)
+ DatabaseProperties(String projectPath, File configFile, Boolean useBootstrap)
{
- this._project = project
- this.configProperties = readDatabaseProperties(project)
+ this.projectPath = projectPath
+ this.configProperties = readDatabaseProperties(configFile, logger)
if (!this.configProperties.isEmpty())
{
setDefaultJdbcProperties(useBootstrap)
@@ -61,9 +64,9 @@ class DatabaseProperties
}
}
- DatabaseProperties(Project project, DatabaseProperties copyProperties)
+ DatabaseProperties(String projectPath, DatabaseProperties copyProperties)
{
- this._project = project
+ this.projectPath = projectPath
this.configProperties = (Properties) copyProperties.configProperties.clone()
this.dbTypeAndVersion = copyProperties.dbTypeAndVersion
this.shortType = copyProperties.shortType
@@ -80,11 +83,6 @@ class DatabaseProperties
return BuildUtils.getConfigsProject(project).file(dbConfigFile)
}
- void setProject(Project project)
- {
- this._project = project
- }
-
String getJdbcDriverClassName()
{
return this.configProperties.get(JDBC_DRIVER_CLASS_NAME_PROP)
@@ -190,19 +188,19 @@ class DatabaseProperties
return (String) this.configProperties.get(property)
else
{
- _project.logger.info("Default database config property ${property} not defined; returning '${defaultValue}'.")
+ logger.info("Default database config property ${property} not defined; returning '${defaultValue}'.")
return defaultValue
}
}
void interpolateCompositeProperties()
{
- this.configProperties.setProperty(JDBC_URL_PROP, PropertiesUtils.parseCompositeProp(_project, this.configProperties, this.configProperties.getProperty(JDBC_URL_PROP)))
+ this.configProperties.setProperty(JDBC_URL_PROP, PropertiesUtils.parseCompositeProp(projectPath, this.configProperties, this.configProperties.getProperty(JDBC_URL_PROP), logger))
}
- void mergePropertiesFromFile()
+ void mergePropertiesFromFile(File chosenPropsFile)
{
- Properties fileProperties = readDatabaseProperties(_project)
+ Properties fileProperties = readDatabaseProperties(chosenPropsFile, logger)
for (String name : fileProperties.propertyNames())
{
if (this.configProperties.getProperty(name) == null)
@@ -213,39 +211,30 @@ class DatabaseProperties
setDefaultJdbcProperties(false)
}
- void writeDbProps()
+ void mergePropertiesFromFile(File file, Logger logger)
{
- writeDatabaseProperty(_project, JDBC_URL_PROP, PropertiesUtils.parseCompositeProp(_project, this.configProperties, this.configProperties.getProperty(JDBC_URL_PROP)))
- writeDatabaseProperty(_project, JDBC_USER_PROP, getJdbcUser())
- writeDatabaseProperty(_project, JDBC_PASSWORD_PROP, getJdbcPassword())
- }
-
- static Properties readDatabaseProperties(Project project)
- {
- return _readDatabaseProperties(project, PICKED_DATABASE_CONFIG_FILE)
+ Properties fileProperties = readDatabaseProperties(file, logger)
+ for (String name : fileProperties.propertyNames())
+ {
+ if (this.configProperties.getProperty(name) == null)
+ {
+ this.configProperties.setProperty(name, fileProperties.getProperty(name))
+ }
+ }
+ setDefaultJdbcProperties(false)
}
- private static Properties _readDatabaseProperties(Project project, String configFile)
+ static Properties readDatabaseProperties(File configFile, Logger logger)
{
- if (getConfigFile(project, configFile).exists())
+ if (configFile.exists())
{
- Properties props = PropertiesUtils.readFileProperties(BuildUtils.getConfigsProject(project), configFile)
+ Properties props = PropertiesUtils.readFileProperties(configFile)
return props
}
else
{
- project.logger.info("No file ${configFile} found. Returning empty properties.")
+ logger.info("No file ${configFile} found. Returning empty properties.")
return new Properties()
}
}
-
- private void writeDatabaseProperty(Project project, String name, String value)
- {
- project.ant.propertyfile(
- file: getPickedConfigFile(project)
- )
- {
- entry( key: name, value: value)
- }
- }
}
diff --git a/src/main/groovy/org/labkey/gradle/util/PropertiesUtils.groovy b/src/main/groovy/org/labkey/gradle/util/PropertiesUtils.groovy
index c1dacbea..b7a4dc78 100644
--- a/src/main/groovy/org/labkey/gradle/util/PropertiesUtils.groovy
+++ b/src/main/groovy/org/labkey/gradle/util/PropertiesUtils.groovy
@@ -15,8 +15,8 @@
*/
package org.labkey.gradle.util
-import org.apache.commons.text.StringEscapeUtils
import org.gradle.api.Project
+import org.slf4j.Logger
import java.util.regex.Matcher
import java.util.regex.Pattern
@@ -37,12 +37,20 @@ class PropertiesUtils
return props
}
- static String parseCompositeProp(Project project, Properties props, String prop)
+ static Properties readFileProperties(File propFile)
+ {
+ Properties props = new Properties()
+ if (propFile.exists())
+ props.load(new FileInputStream(propFile))
+ return props
+ }
+
+ static String parseCompositeProp(String projectPath, Properties props, String prop, Logger logger)
{
if (props == null)
- project.logger.error("${project.path} Properties is null")
+ logger.error("${projectPath} Properties is null")
else if (prop == null)
- project.logger.error("${project.path} Property is null; no parsing possible")
+ logger.error("${projectPath} Property is null; no parsing possible")
else
{
Matcher valMatcher = VALUE_PATTERN.matcher(prop)
@@ -52,39 +60,30 @@ class PropertiesUtils
if (props.getProperty(p) != null)
prop = prop.replace(valMatcher.group(1), (String) (props.getProperty(p)))
else
- project.logger.error("Unable to find value for ${p} in ${props}")
+ logger.error("Unable to find value for ${p} in ${props}")
}
}
return prop
}
- static String replacePropInLine(String line, String propName, Object val, Boolean xmlEncode)
+ static String replacePropInLine(String line, String propName, Object val)
{
if (val != null)
{
String stringVal = val.toString()
- if (xmlEncode)
- stringVal = StringEscapeUtils.escapeXml10(stringVal)
return line.replace("@@" + propName + "@@", stringVal)
}
return line
}
- static String replaceProps(String line, Properties props, Boolean xmlEncode = false)
+ static String replaceProps(String line, Properties props)
{
Matcher matcher = PROPERTY_PATTERN.matcher(line)
while (matcher.find())
{
String propName = matcher.group(1)
if (props.containsKey(propName))
- line = replacePropInLine(line, propName, props.get(propName), xmlEncode)
- // backward compatibility for labkey.xml having new prop name and config.properties having old one
- // TODO remove these cases once we move to a plugin version that doesn't need to support backward compatibility
- else if (propName.equals(ENCRYPTION_KEY_PROP_NAME) && props.containsKey(DEPRECATED_ENCRYPTION_KEY_PROP_NAME))
- line = replacePropInLine(line, propName, props.get(DEPRECATED_ENCRYPTION_KEY_PROP_NAME), xmlEncode)
- // backward compatibility for labkey.xml having old prop name and config.properties having new one
- else if (propName.equals(DEPRECATED_ENCRYPTION_KEY_PROP_NAME) && props.containsKey(ENCRYPTION_KEY_PROP_NAME))
- line = replacePropInLine(line, propName, props.get(ENCRYPTION_KEY_PROP_NAME), xmlEncode)
+ line = replacePropInLine(line, propName, props.get(propName))
}
return line
}
@@ -107,10 +106,10 @@ class PropertiesUtils
}
}
- static Properties getApplicationProperties(Project project)
+ static Properties getApplicationProperties(File propertiesFile)
{
def applicationProperties = new Properties()
- readProperties(new File(BuildUtils.getEmbeddedConfigPath(project), "application.properties"), applicationProperties)
+ readProperties(propertiesFile, applicationProperties)
return applicationProperties
}
}
diff --git a/src/main/groovy/org/labkey/gradle/util/SqlUtils.groovy b/src/main/groovy/org/labkey/gradle/util/SqlUtils.groovy
deleted file mode 100644
index 6f3d7dfd..00000000
--- a/src/main/groovy/org/labkey/gradle/util/SqlUtils.groovy
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (c) 2016-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.util
-
-import groovy.sql.Sql
-import org.gradle.api.GradleException
-import org.gradle.api.Project
-
-import java.sql.Driver
-import java.sql.DriverManager
-
-class SqlUtils
-{
- static void execSql(Project project, DatabaseProperties params, String sql)
- {
- params.interpolateCompositeProperties()
- String url = params.getJdbcURL()
- String user = params.getJdbcUser()
- String password = params.getJdbcPassword()
- String driverClassName = params.getJdbcDriverClassName()
- project.logger.info("in execSql: url ${url} user ${user} password ${password} driverClassName ${driverClassName}")
-
- //see http://gradle.1045684.n5.nabble.com/using-jdbc-driver-in-a-task-fails-td1435189.html
- URLClassLoader loader = GroovyObject.class.classLoader
-
- project.configurations.driver.each {File file ->
- loader.addURL(file.toURI().toURL())
- }
- Class driverClass = loader.loadClass(driverClassName)
- project.logger.info("driverClass is ${driverClass}")
-
- Driver driverInstance = (Driver) driverClass.newInstance()
- DriverManager.registerDriver(driverInstance)
-
- Sql db = null
- try
- {
- db = Sql.newInstance(url, user, password)
- db.execute(sql)
- }
- catch (Exception e)
- {
- project.logger.error(e.toString())
- }
- finally
- {
- if (db != null)
- db.close()
- }
- }
-
- static void dropDatabase(Project project, DatabaseProperties dbProperties)
- {
- Properties properties = dbProperties.getConfigProperties()
- project.logger.info("in dropDatabase for ${project.path}, properties are ${properties}")
- String toDrop = dbProperties.getJdbcDatabase()
- if (toDrop == null || toDrop.equals("labkey"))
- {
- throw new GradleException("Must specify a database that is not 'labkey'")
- }
- else
- {
- DatabaseProperties dropProps = new DatabaseProperties(project, dbProperties)
- // need to connect to the built-in default database in order to drop the database
- dropProps.setJdbcDatabase(dbProperties.getDefaultDatabase())
- dropProps.setJdbcUrlParams("")
-
- project.logger.info("Attempting to drop database ${toDrop}")
- execSql(project, dropProps, "DROP DATABASE \"${toDrop}\";")
- }
- }
-}
\ No newline at end of file