Skip to content

Commit

Permalink
Make publishing incompatible with CC in some cases
Browse files Browse the repository at this point in the history
When using unsafe credentials, we mark the publication task as
incompatible with CC. This helps user experience instead of having weird
 failures on CC load.

Issue #24122
  • Loading branch information
ljacomet committed Mar 3, 2023
1 parent dd7a3ef commit eaacb62
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 7 deletions.
Expand Up @@ -18,7 +18,7 @@ package org.gradle.api.publish.maven

import org.gradle.api.credentials.Credentials
import org.gradle.api.credentials.PasswordCredentials
import org.gradle.integtests.fixtures.UnsupportedWithConfigurationCache
import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
import org.gradle.integtests.fixtures.publish.maven.AbstractMavenPublishIntegTest
import org.gradle.internal.credentials.DefaultPasswordCredentials
import org.gradle.test.fixtures.server.http.AuthScheme
Expand Down Expand Up @@ -325,7 +325,6 @@ class MavenPublishHttpIntegTest extends AbstractMavenPublishIntegTest {
/**
* @see org.gradle.configurationcache.ConfigurationCachePublishingIntegrationTest
*/
@UnsupportedWithConfigurationCache(because="Unsafe/inline credentials not supported with CC")
def "cannot publish to authenticated repository using credentials Provider with inferred identity if repo has incompatible name"() {
given:
buildFile << publicationBuild(version, group, mavenRemoteRepo.uri, "incompatible_repo_name")
Expand All @@ -335,11 +334,14 @@ class MavenPublishHttpIntegTest extends AbstractMavenPublishIntegTest {
fails 'publish'
then:
failure.assertHasDescription("Execution failed for task ':publishMavenPublicationToIncompatible_repo_nameRepository'.")
if (GradleContextualExecuter.isConfigCache()) {
failure.assertHasDescription("Configuration cache state could not be cached:")
} else {
failure.assertHasDescription("Execution failed for task ':publishMavenPublicationToIncompatible_repo_nameRepository'.")
}
failure.assertHasCause("Identity may contain only letters and digits, received: incompatible_repo_name")
}
@UnsupportedWithConfigurationCache(because="Unsafe/inline credentials not supported with CC")
def "can publish to authenticated repository using inlined credentials"() {
given:
PasswordCredentials credentials = new DefaultPasswordCredentials('username', 'password')
Expand All @@ -357,9 +359,11 @@ class MavenPublishHttpIntegTest extends AbstractMavenPublishIntegTest {
then:
module.assertPublishedAsJavaModule()
if (GradleContextualExecuter.isConfigCache()) {
postBuildOutputContains("Configuration cache entry discarded")
}
}
@UnsupportedWithConfigurationCache(because="Unsafe/inline credentials not supported with CC")
def "can publish to authenticated repository with name not valid as identity as long as one uses inlined credentials "() {
given:
PasswordCredentials credentials = new DefaultPasswordCredentials('username', 'password')
Expand All @@ -380,6 +384,9 @@ class MavenPublishHttpIntegTest extends AbstractMavenPublishIntegTest {
then:
module.assertPublishedAsJavaModule()
if (GradleContextualExecuter.isConfigCache()) {
postBuildOutputContains("Configuration cache entry discarded")
}
}
def "fails at configuration time with helpful error message when username and password provider has no value"() {
Expand Down
Expand Up @@ -16,6 +16,7 @@

package org.gradle.api.publish.maven.plugins;

import org.apache.commons.lang.builder.EqualsBuilder;
import org.gradle.api.NamedDomainObjectFactory;
import org.gradle.api.NamedDomainObjectList;
import org.gradle.api.NamedDomainObjectSet;
Expand All @@ -24,13 +25,18 @@
import org.gradle.api.Task;
import org.gradle.api.artifacts.repositories.MavenArtifactRepository;
import org.gradle.api.attributes.Usage;
import org.gradle.api.credentials.Credentials;
import org.gradle.api.file.DirectoryProperty;
import org.gradle.api.internal.artifacts.Module;
import org.gradle.api.internal.artifacts.configurations.DependencyMetaDataProvider;
import org.gradle.api.internal.artifacts.repositories.DefaultMavenArtifactRepository;
import org.gradle.api.internal.attributes.ImmutableAttributesFactory;
import org.gradle.api.internal.file.FileResolver;
import org.gradle.api.internal.project.ProjectInternal;
import org.gradle.api.internal.provider.MissingValueException;
import org.gradle.api.internal.tasks.TaskDependencyFactory;
import org.gradle.api.model.ObjectFactory;
import org.gradle.api.provider.Provider;
import org.gradle.api.provider.ProviderFactory;
import org.gradle.api.publish.PublishingExtension;
import org.gradle.api.publish.internal.versionmapping.DefaultVersionMappingStrategy;
Expand Down Expand Up @@ -126,15 +132,15 @@ private void realizePublishingTasksLater(final Project project, final Publishing
createGenerateMetadataTask(tasks, publication, mavenPublications, buildDirectory);
createGeneratePomTask(tasks, publication, buildDirectory, project);
createLocalInstallTask(tasks, publishLocalLifecycleTask, publication);
createPublishTasksForEachMavenRepo(tasks, publishLifecycleTask, publication, repositories);
createPublishTasksForEachMavenRepo(project, tasks, publishLifecycleTask, publication, repositories);
});
}

private String publishAllToSingleRepoTaskName(MavenArtifactRepository repository) {
return "publishAllPublicationsTo" + capitalize(repository.getName()) + "Repository";
}

private void createPublishTasksForEachMavenRepo(final TaskContainer tasks, final TaskProvider<Task> publishLifecycleTask, final MavenPublicationInternal publication, final NamedDomainObjectList<MavenArtifactRepository> repositories) {
private void createPublishTasksForEachMavenRepo(final Project project, final TaskContainer tasks, final TaskProvider<Task> publishLifecycleTask, final MavenPublicationInternal publication, final NamedDomainObjectList<MavenArtifactRepository> repositories) {
final String publicationName = publication.getName();
repositories.all(repository -> {
final String repositoryName = repository.getName();
Expand All @@ -144,12 +150,51 @@ private void createPublishTasksForEachMavenRepo(final TaskContainer tasks, final
publishTask.setRepository(repository);
publishTask.setGroup(PublishingPlugin.PUBLISH_TASK_GROUP);
publishTask.setDescription("Publishes Maven publication '" + publicationName + "' to Maven repository '" + repositoryName + "'.");
project.getGradle().getTaskGraph().whenReady(graph -> {
if (graph.hasTask(publishTask)) {
validateCredentialsSetup(project, publishTask);
}
});
});

publishLifecycleTask.configure(task -> task.dependsOn(publishTaskName));
tasks.named(publishAllToSingleRepoTaskName(repository), publish -> publish.dependsOn(publishTaskName));
});
}

private static void validateCredentialsSetup(Project project, PublishToMavenRepository publishToMavenRepository) {
DefaultMavenArtifactRepository repository = (DefaultMavenArtifactRepository) publishToMavenRepository.getRepository();
Credentials creds;
try {
creds = repository.getConfiguredCredentials().getOrNull();
} catch (Exception e) {
// In case of exception, we assume compatibility as this will fail later as well
creds = null;
}
if (creds != null && !isUsingCredentialsProvider((ProjectInternal) project, repository.getName(), creds)) {
publishToMavenRepository.notCompatibleWithConfigurationCache("Publishing to a repository without a credentials provider is not yet supported for the configuration cache");
}
}

private static boolean isUsingCredentialsProvider(ProjectInternal project, String identity, Credentials toCheck) {
ProviderFactory providerFactory = project.getServices().get(ProviderFactory.class);
Credentials referenceCredentials;
try {
Provider<? extends Credentials> credentialsProvider;
try {
credentialsProvider = providerFactory.credentials(toCheck.getClass(), identity);
} catch (IllegalArgumentException e) {
// some possibilities are invalid repository names and invalid credential types
// either way, this is not the place to validate that
return false;
}
referenceCredentials = credentialsProvider.get();
} catch (MissingValueException e) {
return false;
}
return EqualsBuilder.reflectionEquals(toCheck, referenceCredentials);
}

private void createLocalInstallTask(TaskContainer tasks, final TaskProvider<Task> publishLocalLifecycleTask, final MavenPublicationInternal publication) {
final String publicationName = publication.getName();
final String installTaskName = "publish" + capitalize(publicationName) + "PublicationToMavenLocal";
Expand Down

0 comments on commit eaacb62

Please sign in to comment.