I’d like a ‘clean’ task that is version aware so that I could ensure that no artifacts of a previous version exists in the build/ directory. Our build slaves currently always do a ‘clean’ before building which costs us any incremental build gains. Without the clean we would end up with multiple artifacts in the build directory that would be archived together in Jenkins. ie “common-1.0.0.jar” and “common-1.0.1.jar”. I could do this with a custom task but handling multiple output types (Jar, Zip, Application…), custom tasks (executable jars, jnlp…) and multiple classifications of these it gets pretty harry.
Is it possible to parse the expected outputs of all tasks, detect if the version is part of the name and based on that cleanup any files within the build directory that shouldn’t exist without handling all of the cases above individually?
Unfortunately metadata like what the project version was at the time a particular archive was created isn’t available in your build. You could do some nasty stuff like use regular expressions to parse the version number in the archive name and compare it to the current project version.Basically, we don’t want “residual” artifacts in our build directory. One way to accomplish this is simply to delete any artifacts not produced by our build. Here is a very simple (read: dumb) implementation. You’ll likely want to adapt it for your build to look in specific folders or for files with particular extensions.
task lazyClean << {
fileTree("${buildDir}/libs").files.each { file ->
if (!tasks.any { it.outputs.files.contains(file) }) {
delete(file)
}
}
}
By using this task, doing something like ‘lazyClean build’ after a project version change should cause the old jars to be deleted (assuming the version number is part of the archive name).
That’s a pretty good approach. I’m not going to have time until next week but I’ll try and see if I can get that working off the archives listing so it can support things that aren’t just in the lib dir. I’ll post it back here if I get it working. Thanks!
Yeah like I said, you’ll likely have to change that bit. I limited it to the ‘libs’ directory because ‘Task.getOuputs()’ is not always inclusive of every file placed in the build directory, so applying it broadly to say, then entire project build directory, would cause a lot of output to be mistakenly deleted.
I found a working but not totally elegant solution. This gets all artifacts that are not directories and collections their parent directories. This list then use’s Mark’s lazyClean implementation to detect when to remove files. Its a little long but will remove stale artifacts, even when the version has change but no build has been done for it yet. This also handled any artifact specified via the ‘artifacts’ handler so distribution and application plugin artifacts are handled as well.
task cleanStaleArtifacts << {
def artifactOutputDirs = new HashSet()
configurations.findAll().each { config ->
config.allArtifacts.getFiles().each { file ->
// If this is an existing file we will search its parent directory
if(file.isFile()) {
artifactOutputDirs << file.getParentFile()
// If the file doesn't exist we still want to search its parent dir,
// but not if its the build dir itself. This covers the case that
// the first build of a version still removed stale artifacts.
} else if(!file.exists() && !file.getParentFile().equals(buildDir)) {
artifactOutputDirs << file.getParentFile()
}
}
}
artifactOutputDirs.each { folder ->
fileTree(folder).files.each { file ->
if (!tasks.any { it.outputs.files.contains(file) }) {
delete(file)
}
}
}
}