Subproject task(s) saying UP-TO-DATE on execution

I have this subproject code:

project(':app') {

  task copy(type: Copy) {
    doLast {
      includeEmptyDirs = true
      //In subproject context, we call files with rootProject path
      def fromDir = rootProject.file('services/website/app/') 
      def toDir = rootProject.file('dist/app/')
      from fromDir
      into toDir
    }
  }

  task zip(type: Zip, dependsOn: copy) {
    doLast {
      includeEmptyDirs = true
      def fromZipDir = rootProject.file('dist/app/') 
      def toZipDir = rootProject.file('dist/') 

      println fromZipDir
      println toZipDir

      baseName = 'app'
      from fromZipDir
      destinationDir toZipDir
    }
  }

  task upload(dependsOn: zip) {
    doLast {
      ssh.run {
        session(remotes.webServer) {
          put from: rootProject.file('dist/app.zip'), into: upload_path 
        }
      }
    }
  }

  task deploy(dependsOn: upload) {
    doLast {
      ssh.run {
        session(remotes.webServer) << {
          //execute 'mkdir ' + upload_path
          execute 'unzip ' + upload_path + '/app.zip'
          executeSudo 'cp ' + upload_path + ' /app/* /var/www/' + extProperties['domain_name'] + '/app/ -Rf'
        }
      }
    }
  }
}

When I execute app:deploy in project context, I am expecting the code to execute from top to bottom e.g. 1. copy, 2. zip, 3. upload and 4. deploy. While I see it executes in this order but somehow the copy and zip task don’t perform their actions every time as expected. It shows UP-TO-DATE for both these tasks and due to this my task #3 fails and script execution stops. And some time it performs copy task correctly but fails in zip.

Can any one let me know what am I missing this flow? Thanks

In task copy and zip, configuration of from and into is configured in the doLast{} block. This means that Gradle cannot determine input files at configuration phase. Thus Gradle considers task copy and zip as UP-TO-DATE.

To fix this, you have to move from and into outside of doLast{} block.

for example.

task copy(type: Copy) {
  // these code will be executed in configuration phase
  includeEmptyDirs = true
  def fromDir = rootProject.file('services/website/app/')
  def toDir = rootProject.file('dist/app/')
  from fromDir
  into toDir
}
1 Like

Thank you for the help.

Moving the code out of doLast{} block works fine. I was putting it in doLast{} block so it get executed only when explicitly called. In this case, it will execute the task during the configuration phase. Do we have any other way using which we can perform the copy action only when the task is explicitly called?

If you want to configure input files only when the task is called, use gradle.taskGraph.whenReady.

gradle.taskGraph.whenReady {TaskExecutionGraph graph ->
  if(graph.hasTask(tasks.copy)) {
    def fromDir = rootProject.file('services/website/app/')
    def toDir = rootProject.file('dist/app/')
    tasks.copy.from = fromDir
    tasks.copy.into = toDir
  }
}

The argument of taskGraph.whenReady is taskGraph itself. If copy task is not called, this taskGraph doesn’t have task copy. So this configuration is not performed.

I think the following topic will be helpful for your problem.

Zip task with lazy from property

1 Like

Spoiler Alert :slight_smile: This kind of construct will also be dealt more elegantly by the new configuration model.
You won’t have to use tricks like that to ensure

  • lazy configuration of your task
    AND
  • incremental builds

I hope that you, as well, can’t wait for it :smile:

1 Like

Thanks. I will look into this feature.

It doesn’t work quite like this. You may be trying to overcomplicate based on an incorrect assumption. The Copy task implements the task action for you. You are just configuring it.

The original code did not do anything during the configuration phase. It configured the task and ran it during the execution phase, breaking the UP-TO-DATE check.

The original suggestion by @Shinya_Mochida of just removing the doLast {...} meets the expectations you state above. The configuration of the task would occur in the configuration phase, but the actual copying of the files (the action of the copy task) would not occur until the execution phase. By removing the doLast {...}, it only changes when your configuration code listed executes. It does not cause the Copy task’s task action to run in the configuration phase instead of the execution phase.

This is different from your upload and deploy tasks which you are directly implementing the task action yourself to call ssh.run {...}. Those definitely need the doLast {...} to not run the ssh operation during the configuration phase.

1 Like