Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,15 @@ If you are making changes to the plugins, please see the [internal docs](https:/
on how to do that, including how to develop and test locally and the versioning information.

## Release Notes
### 8.0.0
*Released*: 3 April 2026
(Earliest compatible LabKey version: 26.3.3)
- [GitHub Issue 464](https://github.com/LabKey/internal-issues/issues/464) Update `Distribution.substituteModuleDependencies` (moved from `BuildUtils`) to incorporate variant usage so it will pick up the module file
- Update to Gradle 9.4.1
- Update various dependency versions
- [Github Issue 1015](https://github.com/LabKey/internal-issues/issues/1015) Update `CheckForVersionConflicts` to account for using some jars that differ by classifier only but should not coexist.
- Remove setting of VcsTag property since our TC builds do not check out tags and will never populate this with a value

### 7.3.1
*Released* 11 February 2026
(Earliest compatible LabKey version: 25.10)
Expand Down
6 changes: 3 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,15 @@ dependencies {
api "org.apache.commons:commons-text:${commonsTextVersion}"
api "commons-io:commons-io:${commonsIoVersion}"
api "com.fasterxml.jackson.core:jackson-databind:${jacksonVersion}"
implementation "org.apache.httpcomponents.core5:httpcore5:${httpcoreVersion}"
api "org.apache.httpcomponents.client5:httpclient5:${httpclientVersion}"
implementation "org.apache.httpcomponents.core5:httpcore5:${httpcore5Version}"
api "org.apache.httpcomponents.client5:httpclient5:${httpclient5Version}"
api "org.json:json:${jsonVersion}"
implementation "com.graphql-java:graphql-java:${graphqlJavaVersion}"
api "org.ajoberstar.grgit:grgit-gradle:${grgitGradleVersion}"
}

group = 'org.labkey.build'
project.version = "7.4.0-SNAPSHOT"
project.version = "8.1.0-SNAPSHOT"

gradlePlugin {
plugins {
Expand Down
22 changes: 11 additions & 11 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
artifactory_contextUrl=https://labkey.jfrog.io/artifactory

artifactoryPluginVersion=6.0.0
artifactoryPluginVersion=6.0.4

commonsIoVersion=2.20.0
commonsLang3Version=3.18.0
commonsTextVersion=1.14.0
commonsIoVersion=2.21.0
commonsLang3Version=3.20.0
commonsTextVersion=1.15.0

grgitGradleVersion=5.3.2
graphqlJavaVersion=24.2
grgitGradleVersion=5.3.3
graphqlJavaVersion=25.0

groovySqlVersion=4.0.27
groovySqlVersion=5.0.4

httpclientVersion=5.5
httpcoreVersion=5.3.4
httpclient5Version=5.6
httpcore5Version=5.4.2

jacksonVersion=2.19.2
jsonVersion=20250517
jacksonVersion=2.21.2
jsonVersion=20251224
Binary file modified gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Expand Down
3 changes: 0 additions & 3 deletions gradlew

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions gradlew.bat

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 9 additions & 1 deletion src/main/groovy/org/labkey/gradle/plugin/Api.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package org.labkey.gradle.plugin

import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.attributes.Usage
import org.gradle.api.file.DuplicatesStrategy
import org.gradle.api.tasks.Copy
import org.gradle.api.tasks.bundling.Jar
Expand Down Expand Up @@ -52,7 +53,14 @@ class Api implements Plugin<Project>
private void addConfigurations(Project project)
{
project.configurations {
apiJarFile // used by other project to declare dependencies to this project api jar
apiJarFile { // used by other project to declare dependencies to this project's api jar
canBeConsumed = true
canBeResolved = false
attributes.attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage, Usage.JAVA_RUNTIME))
// The second attribute is needed to be able to distinguish from the module jar file when doing dependency substitution for distributions
attributes.attribute(FileModule.ARTIFACT_TYPE, FileModule.API_JAR_ARTIFACT_TYPE)
}

}
project.configurations.apiJarFile.setDescription("Configuration that depends on the task that generates the api jar file. Projects that depend on this project's api jar file should use this configuration in their dependency declaration.")
}
Expand Down
39 changes: 38 additions & 1 deletion src/main/groovy/org/labkey/gradle/plugin/Distribution.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ package org.labkey.gradle.plugin
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.UnknownDomainObjectException
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.Dependency
import org.gradle.api.artifacts.DependencySubstitutions
import org.gradle.api.artifacts.ModuleDependency
import org.gradle.api.artifacts.ProjectDependency
import org.gradle.api.file.DeleteSpec
Expand Down Expand Up @@ -95,7 +98,7 @@ class Distribution implements Plugin<Project>

BuildUtils.addLabKeyDependency(project: project, config: "embedded", depProjectPath: BuildUtils.getEmbeddedProjectPath(project.gradle), depVersion: project.labkeyVersion, depProjectConfig: "embedded", transitive: false)
TaskUtils.configureTaskIfPresent(project, 'artifactoryDeploy', { dependsOn(project.tasks.distribution) })

substituteModuleDependencies(project, "distribution")
}

private static void addTasks(Project project)
Expand Down Expand Up @@ -161,6 +164,40 @@ class Distribution implements Plugin<Project>
project.dependencies.add("distribution", dep)
}
}

// See GH Issue 464
static void substituteModuleDependencies(Project project, String configName)
{
try {
project.configurations.named(configName) { Configuration config ->
config.resolutionStrategy.dependencySubstitution { DependencySubstitutions ds ->
project.rootProject.subprojects {
Project p ->
{
p.logger.debug("Considering substitution for ${p.path}.")
if (BuildUtils.shouldBuildFromSource(p)) {
if (p.plugins.hasPlugin('org.labkey.build.module') ||
p.plugins.hasPlugin('org.labkey.build.fileModule') ||
p.plugins.hasPlugin('org.labkey.build.javaModule')
) {
ds.substitute(ds.module("org.labkey.module:${p.name}"))
.using(variant(ds.project(p.path)) {
attributes {
attribute(FileModule.ARTIFACT_TYPE, FileModule.MODULE_ARTIFACT_TYPE)
}
})

p.logger.info("Substituting org.labkey.module:${p.name} with ${p.path} module file")
}
}
}
}
}
}
} catch (UnknownDomainObjectException ignore) {
project.logger.debug("No ${configName} configuration found for ${project.path}.")
}
}
}


17 changes: 14 additions & 3 deletions src/main/groovy/org/labkey/gradle/plugin/FileModule.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import org.gradle.api.UnknownDomainObjectException
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.Dependency
import org.gradle.api.artifacts.ProjectDependency
import org.gradle.api.attributes.Attribute
import org.gradle.api.attributes.Usage
import org.gradle.api.file.CopySpec
import org.gradle.api.file.DuplicatesStrategy
import org.gradle.api.java.archives.Manifest
Expand All @@ -44,6 +46,10 @@ import org.labkey.gradle.util.TaskUtils
*/
class FileModule implements Plugin<Project>
{
public static final Attribute ARTIFACT_TYPE = Attribute.of('artifactType', String)
public static final String MODULE_ARTIFACT_TYPE = "module"
public static final String API_JAR_ARTIFACT_TYPE = "apiJar"

static boolean shouldDoBuild(Project project, boolean logMessages)
{
List<String> indicators = new ArrayList<>()
Expand Down Expand Up @@ -115,7 +121,13 @@ class FileModule implements Plugin<Project>
{
project.configurations
{
published
published {
canBeConsumed = true
canBeResolved = false
attributes.attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage, Usage.JAVA_RUNTIME))
// The second attribute is needed to be able to distinguish from the API jar file when doing dependency substitution for distributions
attributes.attribute(ARTIFACT_TYPE, MODULE_ARTIFACT_TYPE)
}
}
}

Expand Down Expand Up @@ -168,8 +180,7 @@ class FileModule implements Plugin<Project>

project.artifacts
{
// TODO: Figure out how to add this artifact without resolving 'module' task
published moduleTask.get()
published(moduleTask)
}

project.tasks.register('deployModule')
Expand Down
11 changes: 6 additions & 5 deletions src/main/groovy/org/labkey/gradle/plugin/ServerDeploy.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,19 @@
package org.labkey.gradle.plugin

import org.apache.commons.lang3.SystemUtils
import org.gradle.api.file.DuplicatesStrategy
import org.gradle.api.DefaultTask
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.artifacts.Configuration
import org.gradle.api.file.CopySpec
import org.gradle.api.file.DeleteSpec
import org.gradle.api.file.FileCollection
import org.gradle.api.tasks.Delete
import org.labkey.gradle.plugin.extension.ServerDeployExtension
import org.labkey.gradle.task.*
import org.labkey.gradle.task.CheckForVersionConflicts
import org.labkey.gradle.task.DeployApp
import org.labkey.gradle.task.DeployDistribution
import org.labkey.gradle.task.StageDistribution
import org.labkey.gradle.task.StageModules
import org.labkey.gradle.task.UndeployModules
import org.labkey.gradle.util.BuildUtils
import org.labkey.gradle.util.GroupNames
import org.labkey.gradle.util.TaskUtils
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@ import java.util.regex.Matcher
*/
class CheckForVersionConflicts extends DefaultTask
{
// GH Issue 1015: We are using milestone versions of spring-ai jars, which use classifiers like -M2 to distinguish the different versions.
// We want to have the later milestones replace the earlier ones, so we want to exclude the milestone classifier from the name when
// comparing for conflicts. This is rather sketchy and I hope it goes away soon. We match on the spring-ai- prefix to try to guard against
// additional jars that might be included in later milestones, but it's entirely possible that, say, a jar with milestone M2 will not have
// the same name with M3 or M4 and won't get cleaned up during the conflict checking.
private static final String SPRING_AI_ARTIFACT_PREFIX = "spring-ai-";

@Input
Set<String> MULTIPLE_VERSIONS_ALLOWED = Set.of("jackson-core", "jackson-databind")

Expand Down Expand Up @@ -87,8 +94,10 @@ class CheckForVersionConflicts extends DefaultTask
if (matcher.matches())
{
// we support artifacts with different classifiers (e.g., activeio-core-3.1.0-tests.jar should not be in conflict with activeio-core-3.1.0.jar)
String nameWithClassifier = matcher.group(BuildUtils.ARTIFACT_NAME_INDEX)
if (matcher.group(BuildUtils.ARTIFACT_CLASSIFIER_INDEX) != null)
String nameWithoutClassifier = matcher.group(BuildUtils.ARTIFACT_NAME_INDEX)
String nameWithClassifier = nameWithoutClassifier
boolean useClassifierInName = !nameWithoutClassifier.startsWith(SPRING_AI_ARTIFACT_PREFIX)
if (matcher.group(BuildUtils.ARTIFACT_CLASSIFIER_INDEX) != null && useClassifierInName)
nameWithClassifier += matcher.group(BuildUtils.ARTIFACT_CLASSIFIER_INDEX)
if (nameVersionMap.containsKey(nameWithClassifier))
{
Expand All @@ -98,7 +107,7 @@ class CheckForVersionConflicts extends DefaultTask
}
else
{
haveMultiples = true
haveMultiples = haveMultiples || useClassifierInName
conflictMessages += "Multiple existing ${matcher.group(BuildUtils.ARTIFACT_NAME_INDEX)} ${extension} files."
}
}
Expand All @@ -119,9 +128,9 @@ class CheckForVersionConflicts extends DefaultTask
if (matcher.matches())
{
String name = matcher.group(BuildUtils.ARTIFACT_NAME_INDEX)
if (matcher.group(BuildUtils.ARTIFACT_CLASSIFIER_INDEX) != null)
if (matcher.group(BuildUtils.ARTIFACT_CLASSIFIER_INDEX) != null && !name.startsWith(SPRING_AI_ARTIFACT_PREFIX))
name += matcher.group(BuildUtils.ARTIFACT_CLASSIFIER_INDEX)
if (nameVersionMap.containsKey(name))
if (nameVersionMap.containsKey(name)) // with match for spring-ai
{
String version = matcher.group(BuildUtils.ARTIFACT_VERSION_INDEX)
if (version != null)
Expand Down
Loading
Loading