From 35439d59dd13ceabc53c60bcecc382a4b9bf6270 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Wed, 19 Jun 2024 14:03:45 +0200 Subject: [PATCH] Prepare multipart upload. See #86 --- .../release/build/BuildConfiguration.java | 1 + .../data/release/build/MavenBuildSystem.java | 35 +++++++++++++++++-- .../deployment/DeploymentConfiguration.java | 7 +--- .../deployment/DeploymentProperties.java | 15 +++++++- .../release/utils/HttpBasicCredentials.java | 17 +++++---- 5 files changed, 59 insertions(+), 16 deletions(-) diff --git a/src/main/java/org/springframework/data/release/build/BuildConfiguration.java b/src/main/java/org/springframework/data/release/build/BuildConfiguration.java index ba6ec6d..43b46bf 100644 --- a/src/main/java/org/springframework/data/release/build/BuildConfiguration.java +++ b/src/main/java/org/springframework/data/release/build/BuildConfiguration.java @@ -21,6 +21,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.release.model.SupportedProject; import org.springframework.plugin.core.PluginRegistry; + import org.xmlbeam.XBProjector; import org.xmlbeam.XBProjector.Flags; import org.xmlbeam.config.DefaultXMLFactoriesConfig; diff --git a/src/main/java/org/springframework/data/release/build/MavenBuildSystem.java b/src/main/java/org/springframework/data/release/build/MavenBuildSystem.java index 19a1e43..abcd0db 100644 --- a/src/main/java/org/springframework/data/release/build/MavenBuildSystem.java +++ b/src/main/java/org/springframework/data/release/build/MavenBuildSystem.java @@ -57,11 +57,19 @@ import org.springframework.data.release.deployment.DeploymentProperties.MavenCen import org.springframework.data.release.deployment.StagingRepository; import org.springframework.data.release.io.Workspace; import org.springframework.data.release.model.*; +import org.springframework.data.release.utils.HttpBasicCredentials; import org.springframework.data.release.utils.Logger; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; import org.springframework.util.Assert; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; +import org.springframework.web.client.RestTemplate; import org.xmlbeam.ProjectionFactory; import org.xmlbeam.XBProjector; @@ -90,6 +98,7 @@ class MavenBuildSystem implements BuildSystem { Gpg gpg; Environment env; DeploymentProperties deploymentProperties; + RestTemplate restTemplate; static final String REPO_OPENING_TAG = ""; static final String REPO_CLOSING_TAG = ""; @@ -97,7 +106,7 @@ class MavenBuildSystem implements BuildSystem { @Override public BuildSystem withJavaVersion(JavaVersion javaVersion) { return new MavenBuildSystem(workspace, projectionFactory, logger, mvn.withJavaVersion(javaVersion), mavenProperties, - properties, gpg, env, deploymentProperties); + properties, gpg, env, deploymentProperties, restTemplate); } /* @@ -449,7 +458,7 @@ class MavenBuildSystem implements BuildSystem { } if (process == MavenCentral.Publishing.PUBLISHER && stagingRepository.isFile()) { - publishRelease(stagingRepository); + publishRelease(train, stagingRepository); return; } @@ -457,14 +466,34 @@ class MavenBuildSystem implements BuildSystem { String.format("Cannot release train using %s and staging repository %s", process, stagingRepository)); } - private void publishRelease(StagingRepository stagingRepository) { + private void publishRelease(Train train, StagingRepository stagingRepository) { File root = new File(mavenProperties.getLocalStaging(), stagingRepository.getId()); Assert.isTrue(root.exists(), "StagingRepository " + root + " does not exist"); + logger.log(train, "Creating release bundle from %s…", stagingRepository); try { File releaseBundle = createReleaseBundle(root); + + logger.log(train, "Release bundle created. About to upload release bundle …"); + + MavenCentral mavenCentral = deploymentProperties.getMavenCentral(); + HttpBasicCredentials httpCredentials = mavenCentral.getAuthentication().getHttpCredentials(); + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.MULTIPART_FORM_DATA); + headers.add(HttpHeaders.AUTHORIZATION, "Bearer " + httpCredentials.encode()); + + MultiValueMap body = new LinkedMultiValueMap<>(); + body.add("bundle", releaseBundle); + + HttpEntity> requestEntity = new HttpEntity<>(body, headers); + ResponseEntity response = restTemplate.postForEntity(mavenCentral.getPublisherApi() + "publisher/upload", + requestEntity, String.class); + + logger.log(train, "Bundle published with deployment Id %s", response.getBody()); + } catch (IOException e) { throw new RuntimeException(e); } diff --git a/src/main/java/org/springframework/data/release/deployment/DeploymentConfiguration.java b/src/main/java/org/springframework/data/release/deployment/DeploymentConfiguration.java index e631032..29a820b 100644 --- a/src/main/java/org/springframework/data/release/deployment/DeploymentConfiguration.java +++ b/src/main/java/org/springframework/data/release/deployment/DeploymentConfiguration.java @@ -19,8 +19,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.release.deployment.DeploymentProperties.Authentication; -import org.springframework.data.release.model.Password; -import org.springframework.data.release.utils.HttpBasicCredentials; import org.springframework.data.release.utils.HttpComponentsClientHttpRequestFactoryBuilder; import org.springframework.data.release.utils.Logger; import org.springframework.web.client.RestOperations; @@ -51,10 +49,7 @@ class DeploymentConfiguration { String uri = authentication.getServer().getUri(); if (authentication.hasCredentials()) { - - HttpBasicCredentials credentials = new HttpBasicCredentials(authentication.getUsername(), - Password.of(authentication.getApiKey())); - builder = builder.withAuthentication(uri, credentials); + builder = builder.withAuthentication(uri, authentication.getHttpCredentials()); } else { logger.warn("Infrastructure", "No credentials configured for repository %s!", uri); diff --git a/src/main/java/org/springframework/data/release/deployment/DeploymentProperties.java b/src/main/java/org/springframework/data/release/deployment/DeploymentProperties.java index 32c6e82..defede3 100644 --- a/src/main/java/org/springframework/data/release/deployment/DeploymentProperties.java +++ b/src/main/java/org/springframework/data/release/deployment/DeploymentProperties.java @@ -24,6 +24,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.data.release.model.Gpg; import org.springframework.data.release.model.Password; import org.springframework.data.release.model.SupportStatusAware; +import org.springframework.data.release.utils.HttpBasicCredentials; import org.springframework.data.util.Streamable; import org.springframework.stereotype.Component; import org.springframework.util.Assert; @@ -103,17 +104,25 @@ public class DeploymentProperties implements InitializingBean { private Publishing process; + private Authentication authentication; + private Gpg gpg; + private String publisherApi = "https://central.sonatype.com/api/v1/"; + public boolean hasGpgConfiguration() { return gpg != null && gpg.isGpgAvailable(); } public void validate() { - if (!StringUtils.hasText(stagingProfileId)) { + if (process == Publishing.OSSRH && !StringUtils.hasText(stagingProfileId)) { throw new IllegalArgumentException("No staging profile Id for Maven Central"); } + + if (process == Publishing.PUBLISHER && (authentication == null || !authentication.hasCredentials())) { + throw new IllegalArgumentException("No publisher authentication for Maven Central"); + } } public enum Publishing { @@ -137,6 +146,10 @@ public class DeploymentProperties implements InitializingBean { return StringUtils.hasText(username) && password != null; } + public HttpBasicCredentials getHttpCredentials() { + return new HttpBasicCredentials(username, password); + } + public void validate() { if (!StringUtils.hasText(stagingRepository)) { diff --git a/src/main/java/org/springframework/data/release/utils/HttpBasicCredentials.java b/src/main/java/org/springframework/data/release/utils/HttpBasicCredentials.java index ba487bc..810aad6 100644 --- a/src/main/java/org/springframework/data/release/utils/HttpBasicCredentials.java +++ b/src/main/java/org/springframework/data/release/utils/HttpBasicCredentials.java @@ -29,8 +29,16 @@ import org.springframework.data.release.model.Password; @Value public class HttpBasicCredentials { - private final @NonNull String username; - private final @NonNull Password password; + @NonNull String username; + @NonNull Password password; + + public String encode() { + + String header = username.concat(":").concat(password.toString()); + byte[] encodedAuth = Base64.getEncoder().encode(header.getBytes(StandardCharsets.US_ASCII)); + + return new String(encodedAuth); + } /* * (non-Javadoc) @@ -38,9 +46,6 @@ public class HttpBasicCredentials { */ public String toString() { - String header = username.concat(":").concat(password.toString()); - byte[] encodedAuth = Base64.getEncoder().encode(header.getBytes(StandardCharsets.US_ASCII)); - - return "Basic ".concat(new String(encodedAuth)); + return "Basic ".concat(encode()); } }