Skip to content

Commit

Permalink
Merge pull request #24128 Make publishing incompatible with CC in som…
Browse files Browse the repository at this point in the history
…e cases

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

Co-authored-by: Louis Jacomet <louis@gradle.com>
  • Loading branch information
bot-gradle and ljacomet committed Mar 3, 2023
2 parents dd7a3ef + eaacb62 commit dee6053
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 dee6053

Please sign in to comment.