Refactoring tasks with code duplication

Hello i currently have 4 tasks in my build script that essentially do the same thing except for a few parameters that are changed.

I was wondering what the best practice is for refactoring this is. I do not know of a way to pass parameters into tasks or anything like that so if any one could lead me in the right direction that would be great.

task buildDatabasesLevelD(type: Zip) {


File levelDdbProperties = file('configs/societies/LevelD/database/db.properties')
FileTree databases = fileTree('configs/databases/') { exclude 'db.properties' }

FileCollection levelDdatabases = files(levelDdbProperties, databases)

from(levelDdatabases)
include '*.*'
destinationDir file('build/databases')
archiveName 'databases-levelD.zip'
}

task buildDatabasesLevelT(type: Zip) {

File levelTdbProperties = file('configs/societies/LevelT/database/db.properties')

FileTree databases = fileTree('configs/databases/') { exclude 'db.properties' }

FileCollection levelTdatabases = files(levelTdbProperties, databases)


from(levelTdatabases)
include '*.*'
destinationDir file('build/databases')
archiveName 'databases-levelT.zip'
}

task buildDatabasesLevelP(type: Zip) {

File levelPdbProperties = file('configs/societies/LevelP/database/db.properties')

FileTree databases = fileTree('configs/databases/') { exclude 'db.properties' }

FileCollection levelPdatabases = files(levelPdbProperties, databases)


from(levelPdatabases)
include '*.*'
destinationDir file('build/databases')
archiveName 'databases-levelP.zip'
}

task buildDatabasesLevelL(type: Zip) {

File levelLdbProperties = file('configs/societies/LevelL/database/db.properties')

FileTree databases = fileTree('configs/databases/') { exclude 'db.properties' }

FileCollection levelLdatabases = files(levelLdbProperties, databases)

from(levelLdatabases)
include '*.*'
destinationDir file('build/databases')
archiveName 'databases-levelL.zip'
}

As you see the only thing that really changes is the fileLocation based on Level L, T,D,P.

Thanks in advance,

Brian S.indent preformatted text by 4 spaces

You should probably just use a custom task that extends the Zip task for this (see the user guide section on writing custom task classes). it probably wouldn’t take much - you would just expose a way to set the “level” (or something similar) which would then configure everything else in the task accordingly and then you would instantiate 4 instances of your custom task with each just setting a different “level”.

OK i will look into that.

Thanks alot.

So here is what I have now but am getting a compilation error.

task buildDatabasesLevelD(type: BuildDatabase) {
 level = 'D'
}

task buildDatabasesLevelT(type: BuildDatabase) {
 level = 'T'
}

task buildDatabasesLevelP(type: BuildDatabase) {
 level = 'P'
}

task buildDatabasesLevelL(type: BuildDatabase) {
 level = 'L'
}


class BuildDatabase extends Zip {
String level = '';

String dbPropsLocation = 'configs/societies/Level' + level + '/database/db.properties';

File leveldbProperties = file(dbPropsLocation);
FileTree databases = fileTree('configs/databases/') { exclude 'db.properties' }
FileCollection leveldatabases = files(leveldbProperties, databases)


from(leveldatabases)
include '*.*'
destinationDir file('build/databases')
archiveName 'databases-level' + level + '.zip'


}

The error im getting is this:

startup failed:
build file ‘C:\Users\bstoll\build.gradle’: 156: unexpected token: from @ line 156, column 5.
from(leveldatabases)
^
1 error

I guess im having trouble calling the methods to set up the Zip task from my extended Zip task. Am I missing something? In java terms im thinking i would do like super.from() but i know that doesnt exist.

Thanks in advance.

So i have made a little more progress. It seemed i was forgetting to put everything in the @TaskAction block.
I have fixed the compilation error but it still doesnt seem to actually run the zip task. I also made another buildDatabases task which just depends on the other ones.

task buildDatabases(dependsOn: [
    'buildDatabasesLevelD',
    'buildDatabasesLevelT',
    'buildDatabasesLevelP',
    'buildDatabasesLevelL']){

}


task buildDatabasesLevelD(type: BuildDatabase) {
 level 'D'
}

task buildDatabasesLevelT(type: BuildDatabase) {
 level 'T'
}

task buildDatabasesLevelP(type: BuildDatabase) {
 level 'P'
}

task buildDatabasesLevelL(type: BuildDatabase) {
 level 'L'
}


class BuildDatabase extends Zip {

 String level = '';

@TaskAction
def setupLocations() {
String dbPropsLocation = 'configs/societies/Level' + level + '/database/db.properties'

File leveldbProperties = file(dbPropsLocation)
FileTree databases = fileTree('configs/databases/') { exclude 'db.properties' }
FileCollection leveldatabases = files(leveldbProperties, databases)

println leveldbProperties
println databases
println leveldatabases

from leveldatabases
include '*.*'
destinationDir file('build/databases')
archiveName 'databases-level' + level + '.zip'

}

}

Is there something I need to do to trigger the parent Zip task to run?

Thanks again.

By setting a @TaskAction you actually prevent the regular @TaskAction action from the super class to be called.

You need only to configure the super class, based on your level value.
Your initial hint of using from() is probably right, as seen in https://github.com/gradle/gradle/blob/478a891a864c8749c194d42c52973cb1c8dfa8a7/subprojects/core/src/main/groovy/org/gradle/api/tasks/AbstractCopyTask.java, which is the mother class of interest for the zip task.
I cannot play around right now, but that’s definitely the way to go.

You can create a method named ‘level’, and set your level attribute there, along with the ‘from’ location, etc

class BuildDatabase extends Zip {
void level(String aLevel) {
String dbPropsLocation = ‘configs/societies/Level’ + aLevel + ‘/database/db.properties’;
File leveldbProperties = getProject(). file(dbPropsLocation);
FileTree databases = getProject(). fileTree(‘configs/databases/’) { exclude ‘db.properties’ }
FileCollection leveldatabases = files(leveldbProperties, databases)
from(leveldatabases)

}
}

When configuring your custom task, you actually configure the extended tasks as well. Then, you simply let gradle work. When executing your custom task, it will call the @TaskAction of the extended AbstractCopyTask.

Sorry for the mess, I’m writing this on my phone, with no easy way to test, but you should see the point.

Thanks for all the help, That worked perfectly. Here is the final product.

task buildDatabases(dependsOn: [
    'buildDatabasesLevelD',
    'buildDatabasesLevelT',
    'buildDatabasesLevelP',
    'buildDatabasesLevelL']){

}


task buildDatabasesLevelD(type: BuildDatabase) {
 level('D')
}

task buildDatabasesLevelT(type: BuildDatabase) {
 level('T')
}

task buildDatabasesLevelP(type: BuildDatabase) {
 level('P')
}

task buildDatabasesLevelL(type: BuildDatabase) {
 level('L')
}


class BuildDatabase extends Zip {

void level(String aLevel) {
String dbPropsLocation = 'configs/societies/Level' + aLevel + '/database/db.properties'

File leveldbProperties = getProject().file(dbPropsLocation)
FileTree databases = getProject().fileTree('configs/databases/') { exclude 'db.properties' }
FileCollection leveldatabases = getProject().files(leveldbProperties, databases)

println leveldbProperties
println databases
println leveldatabases

from leveldatabases
include '*.*'
destinationDir getProject().file('build/databases')
archiveName 'databases-level' + aLevel + '.zip'

 }
}

Thanks again.

Note that in Groovy, you can omit parenthesis if there is at least one parameter to a method, and no ambiguity
e.g.

task buildDatabasesLevelD(type: BuildDatabase) {
 level 'D'
}

I see.

Is there any way to make the above code any cleaner?

I was thinking of something like

task buildDatabases() {
      def levels = ['D','L','P','T']

for(String aLevel : levels){
    BuildDatabase buildDb = new BuildDatabase();
    buildDb.level(aLevel )
  }
}

option 2: inline tasks? dont know if this is possible and havent found much info online.

task buildDatabases(){
   
def levels = ['D','L','P','T']

for(String aLevel : levels){
  task buildDatabasesLevel(type: BuildDatabase) {
     level aLevel
   }
 }
}

Again I am not sure if either are these are possible. I am really just curious at this point. I like to keep code duplication to a minimum and it bothers me that i need to create individual tasks for each level.

Is the way I am doing it now the standard(or best practice) way?

If i should move this to a new thread let me know.

Thanks, Brian

For groovy beauty, one thing that comes to mind is

['D','L','P','T'].each{
    task "buildDatabasesLevel$it"(type: BuildDatabase) {
     level "$it"
   }
}