How to download a dependency inside a task in Gradle 9.0 and newer?

Hello,

I am trying to write a Task that downloads an artifact stored in a repository. This artifact is just a zip file but the version gets determined in a function. The zip is not required for the build itself but later for the setup creation.
The idea is that other teams can include this plugin and pass in their version number which will be used to do a mapping of their version number to the zip-dependency I want to download.

As of Gradle 9.0 calling Task.getProject() will be deprecated. The warnings got me to: Upgrading your build from Gradle 7.x to 8.0
There are some solutions but none fit my problem.

package org.example;

import org.gradle.api.DefaultTask;
import org.gradle.api.provider.Property;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.TaskAction;

public abstract class DownloadTask extends DefaultTask {

    @Input
    abstract Property<String> getProjectVersion();

    @TaskAction
    void action() {
        var zipVersion = Common.determineVersion(getProjectVersion().get());
        var dependency = getProject().getDependencies().create("com.example:zipArtifact:" + zipVersion);
        var configuration = getProject().getConfigurations().detachedConfiguration(dependency);
        configuration.resolve().forEach(file -> {
            System.out.println("RESOLVED FILE: " + file);
        });
    }
}

This is my current approach which works, but there are deprecation warnings because I use Task.getProject().

I tried several ways to inject Dependencies and Configuration but so far I have not found a way to do that. Or is there any other way to achieve my goal to dynamically load another dependency?

I have two other ideas to solve this issue:

  1. let the plugin update the version in libs.versions.toml. Each project needs to run this plugin themselves before to update their dependencies.
  2. expose Common.determineVersion(String projectVersion).

Exposing works fine:

import org.gradle.api.Project;
import org.gradle.api.Plugin;

public class CommonPlugin implements Plugin<Project> {
    public void apply(Project project) {
        // nothing to do
    }

    public static String determineVersion(String productVersion) {
        return "1.2.3.4";
    }
}

With that each team could define a dependency block:

dependencies {
    implementation "com.example:zipArtifact:" + CommonPlugin.determineVersion(project.version)
}

But I am not sure if that is the best solution, I’d prefer the dynamic dependency resolution to copy them in the right folder were they will be needed.

I did not extensively test, but I think you probably need something like

public abstract class DownloadTask extends DefaultTask {

   @Input
   abstract Property<String> getProjectVersion();

   @InputFiles
   @PathSensitive(PathSensitivity.RELATIVE)
   protected Provider<FileCollection> getZipFile() {
      getProjectVersion().map(zipVersion -> getProject().getConfigurations().detachedConfiguration(getProject().getDependencies().create("com.example:zipArtifact:" + zipVersion)))
   };

   @TaskAction
   void action() {
      getZipFile().get().forEach(file -> {
         System.out.println("RESOLVED FILE: " + file);
      });
   }
}

or similar.

1 Like

Thank you, but the issue is not the zip itself but the Deprecation of Task.getProject() which you also use. You example would also create 2 warnings and will not work with Gradle 9.

Here is the deprecation: Deprecate calling Task.getProject at execution time · Issue #30860 · gradle/gradle · GitHub

What I need is a dependency resolver which I can use inside my task.

which you also use

No I don’t. :slight_smile:
The point is “at execution time”.
I use it at configuration time. :slight_smile:

1 Like

Oh, okay, I see, because you placed it inside a provider?

No, because I don’t use it at execution time.

So kind-of yes, the provider value is calculated at the end of the configuration phase and the result is serialized into the configuration cache entry, so when the configuration cache entry is reused, there is also no resolving or downloading necessary as everything is already present and readily available.

1 Like

Great, thanks! I will try it :slight_smile:

Great, your solution works. (There is a return inside getZipFile missing, but other than that it works!)

I got it running with more than one dependency, that is what I needed. Thanks for your support!

@InputFiles
public Provider<FileCollection> getArtifacts() {
    var dependencies = getProject().getDependencies();
    return getVersion()
            .map(version -> {
                return getProject().getConfigurations()
                        .detachedConfiguration(
                                // use version somewhere here, those two are just for testing
                                dependencies.create("org.testng:testng:7.11.0"),
                                dependencies.create("org.junit.jupiter:junit-jupiter-params:5.13.1")
                        );
            });
};
1 Like