Building a Dependency Object as part of a Plugin Extension and resolving it as part of a Detached Configuration

So I’m building a plugin(targeting Gradle 6.5.1) for my company that does a bunch of Avro-related tasks, such as asserting that the current subject->schema mappings are compatible with past mappings.

I’m publishing the zipped mappings as a publication with the classifier ‘schemaMapping’. This is working, I see it in my ~/.m2 when published:

    project.afterEvaluate( projectAfterEvaluate->{
        projectAfterEvaluate.getExtensions().configure(PublishingExtension.class, publishingExtension -> {

            publishingExtension.getPublications().create(
                    "avroSchemaMapping", MavenPublication.class,
                    mp -> {

                        mp.artifact(zip.get(),
                                ma -> {
                                    ma.setClassifier("schemaMapping");
                                    ma.setExtension("zip");
                                });
                    });
        });

    });

Like I said this is working as expected.

Then what I’d like to do is then refer to defined subsets of these artifacts(from my build.gradle):

avro {
    history {
        coordinates = 'com.corp.fake:avro-assembly-demo:0.0.1:schemaMapping@zip'
    }
   history {
    coordinates = 'com.corp.fake:avro-assembly-demo:0.0.5:schemaMapping@zip'
}

Then fetch and extract these zips.

This is the plugin code that sets this up:

         project.afterEvaluate(
            afterEvaluate -> {
                SchemaDirectorExtension extension = project.getExtensions().getByType(SchemaDirectorExtension.class);
                File historicalToplevel = new File(project.getBuildDir(), "historicalAvroSchemas");
                var unzipHistoricalMaster = project.getTasks().create("unzipHistorical", t -> {
                    t.setGroup("schemaDirector");
                });
                //create unzip tasks for each historical version of the schema
                extension.getSchemaHistory().forEach(version -> {
                    String configName = version.getName();
                    String coordinates = version.getCoordinates();
                    Closure versionConfig = version.getConfig();project.getTasks().create("unzipHistoricalSchema_" + configName, Copy.class, c -> {
                        c.setGroup("schemaDirector");
                        c.setDescription("I unzip "+coordinates);
                        unzipHistoricalMaster.dependsOn(c);
                        Dependency asDependency = project.getDependencies().create(coordinates, versionConfig);

                        var detachedConfig = project.getConfigurations().detachedConfiguration(asDependency);
                        detachedConfig.setTransitive(false);
                        var files = detachedConfig.getFiles();
                        System.out.println("fetched files, " + files);

                        c.from(detachedConfig.getFiles()
                                .stream().map(project::zipTree)
                                .collect(Collectors.toList()));
                        c.into(new File(historicalToplevel, version.getCoordinates()));
                        c.eachFile(cfd -> {
                            cfd.setRelativePath(new RelativePath(true, cfd.getRelativePath().getLastName()));
                        });
                        c.setIncludeEmptyDirs(false);
                    });
                });
            });

It works without a problem as part of the plugin’s functional tests but when I use it in a live project I get an error:

Caused by: org.gradle.api.internal.artifacts.ivyservice.DefaultLenientConfiguration$ArtifactResolveException: Could not resolve all files for configuration ':detachedConfiguration1'.
at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration.rethrowFailure(DefaultConfiguration.java:1268)
at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration.access$1800(DefaultConfiguration.java:142)
at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration$ConfigurationFileCollection.visitContents(DefaultConfiguration.java:1245)
at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration$ConfigurationFileCollection.getFiles(DefaultConfiguration.java:1232)
at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration.getFiles(DefaultConfiguration.java:484)
at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration_Decorated.getFiles(Unknown Source)
at com.bigco.platform.schemaDirector.SchemaCompatibilityPlugin.lambda$apply$2(SchemaCompatibilityPlugin.java:40)
at com.bigco.platform.schemaDirector.SchemaCompatibilityPlugin$$Lambda$424.000000005D0FF420.execute(Unknown Source)
at org.gradle.api.internal.tasks.DefaultTaskContainer.create(DefaultTaskContainer.java:366)
at com.bigco.platform.schemaDirector.SchemaCompatibilityPlugin.lambda$apply$3(SchemaCompatibilityPlugin.java:32)
at com.bigco.platform.schemaDirector.SchemaCompatibilityPlugin$$Lambda$423.000000005D001220.accept(Unknown Source)
at com.bigco.platform.schemaDirector.SchemaCompatibilityPlugin.lambda$apply$4(SchemaCompatibilityPlugin.java:27)

Line 40 of my code is when the configuration tries to resolve the files:
var files = detachedConfig.getFiles();

Here’s the outputs to the console other than the stacktrace:

* What went wrong:

A problem occurred configuring root project ‘avro-assembly-demo’.

Could not resolve all files for configuration ‘:detachedConfiguration1’.
Could not resolve com.corp.fake:avro-assembly-demo:0.0.1.
Required by:
project :
Project : declares a dependency from configuration ‘detachedConfiguration1’ to configuration ‘default’ which is not declared in the descriptor for project :.

It seems that the classifier is lost somehow.

Again, this works when I run this task as a functional test of the plugin; I see the files as part of the detached configuration. The differences between the build.gradle under test and under regular usage is using net.researchgate.release for release management.

What am I doing wrong? Is there some sort of race condition I’m experiencing? Also, I’m using detached configurations to try and do this. Is there some other process that might make more sense?

Secondly, on a semi related note, is there any low effort way I can use the DependencyHandler stuff as part of my Extension class, so that I don’t have this custom dependency+version transfer object to work with?

thanks
Mike

I ended up solving this problem by simply publishing the zip as a separate artifact(rather than using the classifier to differentiate it from the “main” jar artifact) with it’s own id.

That got me to a working state.

        project.afterEvaluate( projectAfterEvaluate->{
        projectAfterEvaluate.getExtensions().configure(PublishingExtension.class, publishingExtension -> {

            publishingExtension.getPublications().create(
                    "avroSchemaMapping", MavenPublication.class,
                    mp -> {
                        mp.setArtifactId(extension.getSchemaMappingArtifactId().get());
                        mp.pom(pom->{
                            pom.setPackaging("zip");
                        });
                        mp.artifact(zip.get(),
                                ma -> {
                                    //ma.setClassifier("schemaMapping");
                                    ma.setExtension("zip");
                                });
                    });
        });

    });