Skip to content

Commit cdc2e97

Browse files
GitHub Issue 774: Fix distribution file name for snapshot versions (#240)
1 parent 2117410 commit cdc2e97

File tree

6 files changed

+165
-69
lines changed

6 files changed

+165
-69
lines changed

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@ If you are making changes to the plugins, please see the [internal docs](https:/
1212
on how to do that, including how to develop and test locally and the versioning information.
1313

1414
## Release Notes
15+
### 7.3.0
16+
*Released* 8 January 2026
17+
(Earliest compatible LabKey version: 25.10)
18+
- Update `BuildUtils.getDistributionVersion` to get VCS properties from git commands instead of removed versioning plugin
19+
- Add some trimming and empty string checks for `PurgeArtficats` task
20+
- Modify `RestoreFromTrash` task to read modules and versions from files
21+
1522
### 7.2.1
1623
*Released* 23 December 2025
1724
(Earliest compatible LabKey version: 25.10)

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ dependencies {
4343
}
4444

4545
group = 'org.labkey.build'
46-
project.version = "7.3.0-SNAPSHOT"
46+
project.version = "7.4.0-SNAPSHOT"
4747

4848
gradlePlugin {
4949
plugins {

src/main/groovy/org/labkey/gradle/task/PurgeArtifacts.groovy

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,8 @@ class PurgeArtifacts extends DefaultTask
107107
logger.quiet("### Begin purge for module ${moduleName} for ${versions.size()} versions\n")
108108

109109
for (String version: versions) {
110-
makeRequests(moduleName, moduleName, version, deleteStats, true)
110+
if (!StringUtils.isEmpty(version.trim()))
111+
makeRequests(moduleName, moduleName, version, deleteStats, true)
111112
}
112113

113114
logger.quiet("Deleted ${deleteStats.get(NUM_DELETED)} artifacts; ${deleteStats.get(NUM_NOT_FOUND)} artifacts not found.")

src/main/groovy/org/labkey/gradle/task/RestoreFromTrash.groovy

Lines changed: 124 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -8,66 +8,149 @@ import org.apache.hc.client5.http.impl.classic.HttpClients
88
import org.apache.hc.core5.http.HttpStatus
99
import org.gradle.api.DefaultTask
1010
import org.gradle.api.GradleException
11-
import org.gradle.api.Project
11+
import org.gradle.api.provider.Property
12+
import org.gradle.api.tasks.Input
13+
import org.gradle.api.tasks.Optional
1214
import org.gradle.api.tasks.TaskAction
13-
import org.labkey.gradle.plugin.Api
14-
import org.labkey.gradle.plugin.FileModule
15-
import org.labkey.gradle.plugin.JavaModule
16-
import org.labkey.gradle.plugin.Module
17-
import org.labkey.gradle.util.BuildUtils
15+
import org.labkey.gradle.util.TaskUtils
1816

1917
import static org.labkey.gradle.task.PurgeArtifacts.Response
2018

21-
// TODO for more flexibility and for configuration cache support, needs to be converted to read projects and versions
22-
// from a file, as for PurgeArtifacts
2319
class RestoreFromTrash extends DefaultTask
2420
{
2521
public static final String VERSION_PROPERTY = "restoreVersion"
22+
public static final String VERSIONS_FILE_PROPERTY = 'restoreVersions'
23+
public static final String RESTORE_LIST_FILE_PROPERTY = 'restoreList';
24+
25+
@Input @Optional
26+
final abstract Property<String> restoreVersion = project.objects.property(String).convention(project.hasProperty(VERSION_PROPERTY) ? (String) project.property(VERSION_PROPERTY) : "")
27+
28+
@Input @Optional
29+
final abstract Property<String> restoreVersions = project.objects.property(String).convention(project.hasProperty(VERSIONS_FILE_PROPERTY) ? (String) project.property(VERSIONS_FILE_PROPERTY) : "")
30+
@Input @Optional
31+
final abstract Property<String> restoreListFile = project.objects.property(String).convention(project.hasProperty(RESTORE_LIST_FILE_PROPERTY) ? (String) project.property(RESTORE_LIST_FILE_PROPERTY) : "")
32+
@Input
33+
final abstract Property<Boolean> isDryRun = project.objects.property(Boolean).convention(project.hasProperty(PurgeArtifacts.DRY_RUN_PROPERTY))
34+
35+
@Input
36+
final abstract Property<String> artifactoryUrl = project.objects.property(String).convention((String) project.property('artifactory_contextUrl'))
37+
@Input
38+
final abstract Property<String> artifactoryUser = project.objects.property(String).convention((String) project.property('artifactory_user'))
39+
@Input
40+
final abstract Property<String> artifactoryPassword = project.objects.property(String).convention((String) project.property('artifactory_password'))
41+
42+
private static final String NUM_NOT_FOUND = "numNotFound"
43+
private static final String NUM_RESTORED = "numRestored"
44+
private static final String UNRESTORED_VERSIONS = "unrestoredVersions"
2645

2746
@TaskAction
2847
void restoreVersions()
2948
{
30-
String restoreVersion
31-
if (!project.hasProperty(VERSION_PROPERTY))
32-
throw new GradleException("No value provided for ${VERSION_PROPERTY}.")
33-
restoreVersion = project.property(VERSION_PROPERTY)
34-
String[] unrestoredVersions = []
35-
int numRestored = 0
36-
int numNotFound = 0
37-
project.allprojects({ Project p ->
38-
def plugins = p.getPlugins()
39-
if (plugins.hasPlugin(Module.class) || plugins.hasPlugin(JavaModule.class) || plugins.hasPlugin(FileModule.class)) {
40-
logger.quiet("Considering ${p.path}...")
41-
Response response = makeRestoreRequest(p.name, restoreVersion, "module")
42-
if (response == Response.NOT_FOUND)
43-
numNotFound++
44-
else if (response == Response.ERROR) {
45-
unrestoredVersions += "${p.path} - module: ${restoreVersion}"
46-
} else
47-
numRestored++
49+
String restoreModulesFileName = restoreListFile.get();
50+
if (StringUtils.isEmpty(restoreModulesFileName))
51+
throw new GradleException("Use -P${RESTORE_LIST_FILE_PROPERTY}=<moduleNames.txt> to provide a list of modules to work with.")
52+
List<String> moduleNames = TaskUtils.readInputFile(restoreModulesFileName, "modules", logger)
53+
if (moduleNames.isEmpty())
54+
throw new GradleException("No module names found in file ${restoreModulesFileName}")
55+
String version = this.restoreVersion.get()
56+
if (!StringUtils.isEmpty(version.trim()))
57+
this.restoreVersion(version, moduleNames)
58+
else
59+
{
60+
Map<String, Integer> overallStats = new HashMap<>()
61+
overallStats.put(NUM_NOT_FOUND, 0)
62+
overallStats.put(NUM_RESTORED, 0)
63+
String restoreVersionsFileName = restoreVersions.get()
64+
if (StringUtils.isEmpty(restoreVersionsFileName))
65+
throw new GradleException("Either -P${VERSION_PROPERTY}=<versionToPurge> or -P${VERSIONS_FILE_PROPERTY}=<versionsFile.txt> must be provided")
66+
List<String> versions = TaskUtils.readInputFile(restoreVersionsFileName, "versions", logger)
67+
if (versions.isEmpty())
68+
throw new GradleException("No versions found for file ${restoreVersionsFileName}.")
69+
if (versions.size() > 1) {
70+
for (String moduleName : moduleNames) {
71+
Map<String, Object> restoreStats = restoreModuleVersions(moduleName, versions)
72+
overallStats.put(NUM_NOT_FOUND, overallStats.get(NUM_NOT_FOUND) + (Integer) restoreStats.get(NUM_NOT_FOUND))
73+
overallStats.put(NUM_RESTORED, overallStats.get(NUM_RESTORED) + (Integer) restoreStats.get(NUM_RESTORED))
74+
}
75+
if (moduleNames.size() > 1)
76+
logger.quiet("\nSummary:\n\tRestored ${overallStats.get(NUM_RESTORED)} artifacts.\n\t${overallStats.get(NUM_NOT_FOUND)} artifacts not found.")
4877
}
49-
if (plugins.hasPlugin(Api.class) || project.path == BuildUtils.getApiProjectPath(project.gradle)) {
50-
Response response = makeRestoreRequest(p.name, restoreVersion, "api")
51-
if (response == Response.NOT_FOUND)
52-
numNotFound++
53-
else if (response == Response.ERROR) {
54-
unrestoredVersions += "${p.path} - api: ${restoreVersion}"
55-
} else
56-
numRestored++
78+
else {
79+
for (String v : versions) {
80+
if (!StringUtils.isEmpty(v.trim()))
81+
this.restoreVersion(v, moduleNames)
82+
}
5783
}
58-
})
84+
}
85+
}
86+
87+
Map<String, Object> restoreModuleVersions(String moduleName, List<String> versions)
88+
{
89+
Map<String, Object> restoreStats = new HashMap();
90+
restoreStats.put(NUM_RESTORED, 0)
91+
restoreStats.put(NUM_NOT_FOUND, 0)
92+
restoreStats.put(UNRESTORED_VERSIONS, new ArrayList<>())
93+
94+
logger.quiet("### Begin restore for module ${moduleName} for ${versions.size()} versions\n")
95+
96+
for (String version: versions) {
97+
if (!StringUtils.isEmpty(version.trim()))
98+
makeRequests(moduleName, moduleName, version, restoreStats)
99+
}
100+
logStats(restoreStats)
101+
logger.quiet("\n### End restore for module ${moduleName}\n")
102+
return restoreStats
103+
}
104+
105+
Map<String, Object> restoreVersion(String version, List<String> moduleNames)
106+
{
107+
if (StringUtils.isEmpty(version.trim()))
108+
return
109+
Map<String, Object> restoreStats = new HashMap();
110+
restoreStats.put(NUM_RESTORED, 0)
111+
restoreStats.put(NUM_NOT_FOUND, 0)
112+
restoreStats.put(UNRESTORED_VERSIONS, new ArrayList<>())
113+
for (String moduleName: moduleNames) {
114+
makeRequests(moduleName, moduleName, version, restoreStats)
115+
}
116+
logStats(restoreStats)
117+
return restoreStats
118+
}
59119

60-
logger.quiet("Restored ${numRestored} artifacts; ${numNotFound} artifacts not found.")
61-
if (unrestoredVersions.size() > 0 && !project.hasProperty("dryRun"))
62-
throw new GradleException("The following ${unrestoredVersions.size()} versions were not restored.\n${StringUtils.join(unrestoredVersions, "\n")}\nCheck the log for more information.")
120+
void logStats(Map<String, Object> restoreStats)
121+
{
122+
logger.quiet("Restored ${restoreStats.get(NUM_RESTORED)} artifacts; ${restoreStats.get(NUM_NOT_FOUND)} artifacts not found.")
123+
if (((List<String>) restoreStats.get(UNRESTORED_VERSIONS)).size() > 0 && !isDryRun.get())
124+
throw new GradleException("The following ${((List<String>) restoreStats.get(UNRESTORED_VERSIONS)).size()} versions were not restored.\n${StringUtils.join(restoreStats.get(UNRESTORED_VERSIONS), "\n")}\nCheck the log for more information.")
125+
}
126+
127+
void makeRequests(String moduleName, String loggingName, String restoreVersion, Map<String, Object> statsMap)
128+
{
129+
if (StringUtils.isEmpty(restoreVersion.trim()))
130+
return
131+
logger.quiet("Considering ${loggingName} ${restoreVersion}...")
132+
Response response = makeRestoreRequest(moduleName, restoreVersion, "module")
133+
if (response == Response.NOT_FOUND)
134+
statsMap.put(NUM_NOT_FOUND, statsMap.get(NUM_NOT_FOUND)+1);
135+
else if (response == Response.ERROR)
136+
statsMap.get(UNRESTORED_VERSIONS).add("${loggingName} - module: ${restoreVersion}")
137+
else
138+
statsMap.put(NUM_RESTORED, statsMap.get(NUM_RESTORED)+1)
63139

140+
response = makeRestoreRequest(moduleName, restoreVersion, "api")
141+
if (response == Response.NOT_FOUND)
142+
statsMap.put(NUM_NOT_FOUND, statsMap.get(NUM_NOT_FOUND)+1)
143+
else if (response == Response.ERROR)
144+
statsMap.get(UNRESTORED_VERSIONS).add("${loggingName} - api: ${restoreVersion}")
145+
else
146+
statsMap.put(NUM_RESTORED, statsMap.get(NUM_RESTORED)+1)
64147
}
65148

66149
/**
67150
* This uses the Artifactory REST Api to request a restoration of a particular "item" (https://jfrog.com/help/r/jfrog-rest-apis/restore-item-from-trash-can)
68151
*
69-
* @param artifactName the artifact whose version is to be deleted
70-
* @param version the version of the artifact to delete (e.g., 21.11-SNAPSHOT)
152+
* @param artifactName the artifact whose version is to be restored
153+
* @param version the version of the artifact to restore (e.g., 21.11-SNAPSHOT)
71154
* @param type either "api" or "module"
72155
* @return Response summarizing http status from request
73156
* @throws GradleException if the restoration request throws an exception
@@ -76,7 +159,7 @@ class RestoreFromTrash extends DefaultTask
76159
{
77160
if (project.hasProperty("dryRun")) {
78161
logger.quiet("\tRestoring version ${version} of ${artifactName} ${type} -- Skipped for dry run")
79-
return
162+
return null
80163
}
81164

82165
CloseableHttpClient httpClient = HttpClients.createDefault()

src/main/groovy/org/labkey/gradle/util/BuildUtils.groovy

Lines changed: 30 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -401,10 +401,11 @@ class BuildUtils
401401
{
402402
String version = project.labkeyVersion
403403

404-
if (project.hasProperty("versioning"))
404+
if (project.hasProperty("includeVcs"))
405405
{
406-
String branch = project.versioning.info.branchId
407-
if (!["trunk", "develop", "master", "main", "none", ""].contains(branch) &&
406+
var vcsProps = getStandardVCSProperties(project)
407+
String branch = vcsProps.get(VCS_BRANCH_PROP_NAME)
408+
if (branch != null && !["trunk", "develop", "master", "main", "none", ""].contains(branch) &&
408409
!branch.toLowerCase().matches("release.*-snapshot"))
409410
{
410411
Matcher matcher = Pattern.compile(".*fb_(.+)").matcher(branch)
@@ -452,9 +453,10 @@ class BuildUtils
452453
}
453454
}
454455

455-
if (project.hasProperty("versioning"))
456+
if (project.hasProperty("includeVcs"))
456457
{
457-
String rootBranch = project.rootProject.versioning.info.branchId
458+
Properties vcsProps = getStandardVCSProperties(project)
459+
String rootBranch = vcsProps.get(VCS_BRANCH_PROP_NAME)
458460
project.logger.info("${project.path} rootBranch ${rootBranch}")
459461
if (rootBranch.startsWith("release") && /* e.g. release20.11-SNAPSHOT */
460462
project.labkeyVersion.contains("-SNAPSHOT")) /* e.g. 20.11-SNAPSHOT */
@@ -490,41 +492,47 @@ class BuildUtils
490492
return version
491493
}
492494

495+
public static final String VCS_URL_PROP_NAME = "VcsURL"
496+
public static final String VCS_BRANCH_PROP_NAME = "VcsBranch"
497+
public static final String VCS_TAG_PROP_NAME = "VcsTag"
498+
public static final String VCS_REVISION_PROP_NAME = "VcsRevision"
499+
public static final String BUILD_NUMBER_PROP_NAME = "BuildNumber"
500+
493501
static Properties getStandardVCSProperties(project)
494502
{
495503
String buildNumber =
496504
(String) TeamCityExtension.getTeamCityProperty(project, "system.teamcity.agent.dotnet.build_id", // Unique build ID
497505
TeamCityExtension.getTeamCityProperty(project,"build.number", null))
498506
Properties ret = new Properties()
499507
def gitCmd = SystemUtils.IS_OS_WINDOWS ? "git.exe" : "git"
500-
if (project.hasProperty("includeVcs") && (!project.hasProperty("lkModule") || project.lkModule.getModProperties().get("VcsURL").isEmpty()))
508+
if (project.hasProperty("includeVcs") && (!project.hasProperty("lkModule") || project.lkModule.getModProperties().get(VCS_URL_PROP_NAME).isEmpty()))
501509
{
502510
def url = "${gitCmd} -C ${project.projectDir.absolutePath} config --get remote.origin.url".execute().text.trim()
503511
Matcher matcher = GIT_URL_WITH_TOKEN.matcher(url)
504512
if (matcher.matches()) // Strip out the token if included in the URL.
505513
url = matcher.group(1) + "@" + matcher.group(3)
506-
ret.setProperty("VcsURL", url)
514+
ret.setProperty(VCS_URL_PROP_NAME, url)
507515
project.logger.info("${project.path} git url: ${url}")
508516
def branch = "${gitCmd} -C ${project.projectDir.absolutePath} rev-parse --abbrev-ref HEAD".execute().text.trim()
509517
project.logger.info("${project.path} git branch: ${branch}")
510-
ret.setProperty("VcsBranch", branch)
518+
ret.setProperty(VCS_BRANCH_PROP_NAME, branch)
511519
def revision = "${gitCmd} -C ${project.projectDir.absolutePath} rev-parse @".execute().text.trim()
512520
project.logger.info("${project.path} git revision: ${revision}")
513-
ret.setProperty("VcsRevision", revision)
521+
ret.setProperty(VCS_REVISION_PROP_NAME, revision)
514522
def tag = "${gitCmd} -C ${project.projectDir.absolutePath} describe --tags --exact-match 2> /dev/null".execute().text.trim()
515523
project.logger.info("${project.path} git tag: ${revision}")
516524
if (!tag.isEmpty() && !tag.equals(revision))
517-
ret.setProperty("VcsTag", tag)
525+
ret.setProperty(VCS_TAG_PROP_NAME, tag)
518526
else
519-
ret.setProperty("VcsTag", "")}
520-
else
527+
ret.setProperty(VCS_TAG_PROP_NAME, "")}
528+
else if (!project.hasProperty("includeVcs"))
521529
{
522-
ret.setProperty("VcsBranch", "Unknown")
523-
ret.setProperty("VcsTag", "Unknown")
524-
ret.setProperty("VcsURL", "Unknown")
525-
ret.setProperty("VcsRevision", "Unknown")
530+
ret.setProperty(VCS_BRANCH_PROP_NAME, "Unknown")
531+
ret.setProperty(VCS_TAG_PROP_NAME, "Unknown")
532+
ret.setProperty(VCS_URL_PROP_NAME, "Unknown")
533+
ret.setProperty(VCS_REVISION_PROP_NAME, "Unknown")
526534
}
527-
ret.setProperty("BuildNumber", buildNumber != null ? buildNumber : "Unknown")
535+
ret.setProperty(BUILD_NUMBER_PROP_NAME, buildNumber != null ? buildNumber : "Unknown")
528536
return ret
529537
}
530538

@@ -594,10 +602,7 @@ class BuildUtils
594602

595603
static void addModuleDistributionDependency(Project distributionProject, String depProjectPath, String config, boolean addTransitive)
596604
{
597-
if (distributionProject.configurations.findByName(config) == null)
598-
distributionProject.configurations {
599-
config
600-
}
605+
distributionProject.configurations.maybeCreate(config)
601606
distributionProject.logger.info("${distributionProject.path}: adding ${depProjectPath} as dependency for config ${config}")
602607
addLabKeyDependency(project: distributionProject, config: config, depProjectPath: depProjectPath, depProjectConfig: "published", depExtension: "module", depVersion: distributionProject.labkeyVersion)
603608
if (addTransitive) {
@@ -618,11 +623,11 @@ class BuildUtils
618623
if (dep instanceof ProjectDependency)
619624
{
620625
if (!pathsAdded.contains(dep.getPath())) {
621-
distributionProject.logger.info("${distributionProject.path}: Adding '${config}' dependency on project ${dep}")
626+
distributionProject.logger.info("${distributionProject.path}: Adding '${config}' dependency on ${dep}")
622627
distributionProject.dependencies.add(config, dep)
623628
distributionProject.evaluationDependsOn(dep.getPath())
624629
pathsAdded.add(dep.getPath())
625-
distributionProject.logger.debug("${distributionProject.path}: Adding recursive '${config}' dependenices from ${dep.getPath()}")
630+
distributionProject.logger.info("${distributionProject.path}: Adding recursive '${config}' dependenices from ${dep.getPath()}")
626631
addTransitiveModuleDependencies(distributionProject, depProject.project(dep.getPath()), config, pathsAdded)
627632
}
628633
}
@@ -974,7 +979,7 @@ class BuildUtils
974979
{
975980
try {
976981
project.configurations.named(configName) { Configuration config ->
977-
resolutionStrategy.dependencySubstitution { DependencySubstitutions ds ->
982+
config.resolutionStrategy.dependencySubstitution { DependencySubstitutions ds ->
978983
project.rootProject.subprojects {
979984
Project p ->
980985
{
@@ -985,7 +990,7 @@ class BuildUtils
985990
p.plugins.hasPlugin('org.labkey.build.javaModule')
986991
) {
987992
ds.substitute ds.module("org.labkey.module:${p.name}") using ds.project(p.path)
988-
p.logger.debug("Substituting org.labkey.module:${p.name} with ${p.path}")
993+
p.logger.info("Substituting org.labkey.module:${p.name} with ${p.path}")
989994
}
990995
// if (p.plugins.hasPlugin('org.labkey.build.api'))
991996
// {

0 commit comments

Comments
 (0)