Setting zip version at task execution and not at task configuration

For the Zip task, version is a String and it is normally set within the configuration DSL.

I want to set it later when it is needed within the execution phase. The reason is that the version string depends on the action of another task. What I have to do now is invoke this task with a separate gradle run first.

Would like to just run the zip task with the other task as a dependency, but because the zip version is set at configuration, it is as if the first task is not run at all.

You can use a “configure” task for this:

task otherTask // does the work to determine version
task configureZip {
   dependsOn otherTask
   doLast {
      zip.version = otherTask.determinedVersion
   }
}
task zip(type: Zip) {
   dependsOn configureZip
   // other configuration
}

Thanks, Sterling. I was hoping to find something that would get this “late binding” behavior by changing the Zip task implementation, and keep everything at the client level still in the configuration phase.

I came across the use of the PropertyState interface (in the guides section), and adapted it in the example below. In the example, the zipRepo version is set at configuration but the actual version string isn’t set until the execution phase. This allows the version of the zip to be the hash of the repo that’s pulled down when the getRepo task is executed. If you commented out the guts of the ZipRepo class, you’ll see it fail at line 69 because the setting of the version has moved to the configuration phase, before the repo has been retrieved.

buildscript {
  repositories {
    maven { url "https://plugins.gradle.org/m2/" }
  }
  dependencies {
    classpath 'org.ajoberstar:grgit:2.0.1'
  }
}

apply plugin: 'base'

task getRepo (type: GitGetTask) {
  repoName 'dslexamples'
  baseUrl 'https://github.com/donalhenry'
}

task zipRepo (type: ZipRepo) {
  dependsOn getRepo

  from getRepo.destinationDir
  version = new Version(getRepo.destinationDir)
}

clean.dependsOn cleanGetRepo


// lifted from https://guides.gradle.org/implementing-gradle-plugins/ - LatestArtifactVersion.java
class ZipRepo extends Zip {
  PropertyState<String> versionZip

  @Input String getVersion() {
    return this.versionZip
  }

  void setVersion(String version) {
    this.versionZip.set(version)
  }

  void setVersion(Provider<String> versionZip) {
    this.versionZip.set(versionZip)
  }
}

import org.ajoberstar.grgit.Grgit

class GitGetTask extends DefaultTask {
  @Input String repoName
  @Input String baseUrl
  @Input String branchName = 'master'
  @OutputDirectory File getDestinationDir() {
    project.file(repoName)
  }

  @TaskAction doIt() {
    def gitUrl = "$baseUrl/$repoName"
    println "Checking out $gitUrl(origin/$branchName)..."
    Grgit.clone(dir: destinationDir, uri: gitUrl, checkout: true, refToCheckout: branchName)
  }
}

class Version {
  File repoDir

  Version(File repoDir) {
    this.repoDir = repoDir
  }

  String toString() {
    def gitRepo = Grgit.open(dir: repoDir)
    gitRepo.head().abbreviatedId
  }
}

As a heads-up, PropertyState is deprecated in 4.3 (to be removed in 5.0) for Property, which is the same interface with a better name.

We’ll also be updating the guide you found to suggest the idiomatic ways use Propertys in a custom task (this is supposed to happen in the next ~week).

If your ZipRepo task is very specific to just building zips of git repos, you could also simplify your task to not extend Zip and just use ant.zip() in the task action instead. This is a better choice if you don’t need to expose the full API of CopySpec.

Here are some suggestions to make your tasks a little more idiomatic and lazy:

buildscript {
  repositories {
    maven { url "https://plugins.gradle.org/m2/" }
  }
  dependencies {
    classpath 'org.ajoberstar:grgit:2.0.1'
  }
}

apply plugin: 'base'

task getRepo (type: GitGetTask) {
  repoName = 'dslexamples'
  baseUrl = 'https://github.com/donalhenry'
}

task zipRepo (type: ZipRepo) {
  from getRepo
  versionZip = getRepo.version
}

clean.dependsOn cleanGetRepo

// lifted from https://guides.gradle.org/implementing-gradle-plugins/ - LatestArtifactVersion.java
class ZipRepo extends Zip {
  final PropertyState<String> versionZip = project.property(String)

  String getVersion() {
    return this.versionZip.getOrElse(super.getVersion())
  }
}

import org.ajoberstar.grgit.Grgit

class GitGetTask extends DefaultTask {
  @Input final Property<String> repoName = project.property(String)
  @Input final Property<String> baseUrl = project.property(String)
  @Input final Property<String> branchName = project.property(String)
  @OutputDirectory final DirectoryProperty destinationDir = newOutputDirectory()

  GitGetTask() {
    destinationDir.set(project.layout.projectDirectory.dir(repoName))
    branchName.set('master')
  }

  @TaskAction doIt() {
    def gitUrl = baseUrl.get() + "/" + repoName.get()
    println "Checking out $gitUrl(origin/$branchName)..."
    Grgit.clone(dir: destinationDir.get().getAsFile(), uri: gitUrl, checkout: true, refToCheckout: branchName.get())
  }

  Provider<String> getVersion() {
    project.provider { 
        def gitRepo = Grgit.open(dir: destinationDir.get().getAsFile())
        try {
            return gitRepo.head().abbreviatedId
        } finally {
            gitRepo.close()        
        }
    }
  }
}

Your Version class was only working because Gradle was calling toString() under the covers. It wasn’t really using the new Provider API.

There’s also a problem with GitGetTask running twice in a row, but I didn’t try to fix that.

Thanks for the suggested improvements and the heads-up on the upcoming deprecation. I’ll incorporate your suggestions. Looks like some are not in 4.1, so I’ll have to wait till we upgrade to 4.3 to use.

Think this fixes the issues.

buildscript {
  repositories {
    maven { url "https://plugins.gradle.org/m2/" }
  }
  dependencies {
    classpath 'org.ajoberstar:grgit:2.0.1'
  }
}

apply plugin: 'base'

task getRepo (type: GitGetTask) {
  repoName = 'dslexamples'
  baseUrl = 'https://github.com/donalhenry'
}

task zipRepo (type: ZipRepo) {
  from getRepo
  version = getRepo.version
}

clean.dependsOn cleanGetRepo

// lifted from https://guides.gradle.org/implementing-gradle-plugins/ - LatestArtifactVersion.java
class ZipRepo extends Zip {
  final Property<String> versionZip = project.property(String)

  String getVersion() {
    return this.versionZip.getOrElse(super.getVersion())
  }

  void setVersion(Provider<String> versionZip) {
    this.versionZip.set(versionZip)
  }
}

import org.ajoberstar.grgit.Grgit

class GitGetTask extends DefaultTask {
  @Input final Property<String> repoName = project.property(String)
  @Input final Property<String> baseUrl = project.property(String)
  @Input final Property<String> branchName = project.property(String)
  @OutputDirectory final DirectoryProperty destinationDir = newOutputDirectory()

  GitGetTask() {
    destinationDir.set(project.layout.projectDirectory.dir(repoName))
    branchName.set('master')
  }

  @TaskAction doIt() {
    def gitUrl = baseUrl.get() + "/" + repoName.get()
    println "Checking out $gitUrl(origin/$branchName)..."
    Grgit.clone(dir: destinationDir.get().getAsFile(), uri: gitUrl, checkout: true, refToCheckout: branchName.get())
  }

  Provider<String> getVersion() {
    project.provider {
      def gitRepo = Grgit.open(dir: destinationDir.get().getAsFile())
      try {
        return gitRepo.head().abbreviatedId
      } finally {
        gitRepo.close()
      }
    }
  }
}