diff --git a/core b/core index ac900b7..90b387e 160000 --- a/core +++ b/core @@ -1 +1 @@ -Subproject commit ac900b7496ced0a2a38b3ceeba6ad57155e4c508 +Subproject commit 90b387e6b0b98db568adefd9d505b4c8be643415 diff --git a/src/main/java/rife/bld/dependencies/Repository.java b/src/main/java/rife/bld/dependencies/Repository.java index 55ee6dc..b02a4be 100644 --- a/src/main/java/rife/bld/dependencies/Repository.java +++ b/src/main/java/rife/bld/dependencies/Repository.java @@ -32,7 +32,8 @@ public record Repository(String location, String username, String password) { public static final Repository SONATYPE_RELEASES_LEGACY = new Repository("https://oss.sonatype.org/service/local/staging/deploy/maven2/"); public static final Repository SONATYPE_SNAPSHOTS = new Repository("https://s01.oss.sonatype.org/content/repositories/snapshots/"); public static final Repository SONATYPE_SNAPSHOTS_LEGACY = new Repository("https://oss.sonatype.org/content/repositories/snapshots/"); - public static final Repository CENTRAL_RELEASES = new Repository("https://ossrh-staging-api.central.sonatype.com/service/local/staging/deploy/maven2/"); + public static final String OSSRH_STAGING_API_DOMAIN = "ossrh-staging-api.central.sonatype.com"; + public static final Repository CENTRAL_RELEASES = new Repository("https://" + OSSRH_STAGING_API_DOMAIN + "/service/local/staging/deploy/maven2/"); public static final Repository CENTRAL_SNAPSHOTS = new Repository("https://central.sonatype.com/repository/maven-snapshots/"); public static final Repository RIFE2_RELEASES = new Repository("https://repo.rife2.com/releases/"); public static final Repository RIFE2_SNAPSHOTS = new Repository("https://repo.rife2.com/snapshots/"); diff --git a/src/main/java/rife/bld/operations/PublishOperation.java b/src/main/java/rife/bld/operations/PublishOperation.java index 90040a2..3d030d7 100644 --- a/src/main/java/rife/bld/operations/PublishOperation.java +++ b/src/main/java/rife/bld/operations/PublishOperation.java @@ -9,6 +9,7 @@ import rife.bld.BldVersion; import rife.bld.dependencies.*; import rife.bld.dependencies.exceptions.DependencyException; import rife.bld.operations.exceptions.OperationOptionException; +import rife.bld.operations.exceptions.RestApiException; import rife.bld.operations.exceptions.SignException; import rife.bld.operations.exceptions.UploadException; import rife.bld.publish.*; @@ -27,6 +28,7 @@ import java.time.ZoneId; import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.List; +import java.util.regex.Pattern; import static rife.bld.dependencies.Dependency.*; import static rife.bld.publish.MetadataBuilder.SNAPSHOT_TIMESTAMP_FORMATTER; @@ -40,6 +42,9 @@ import static rife.tools.StringUtils.encodeHexLower; * @since 1.5.7 */ public class PublishOperation extends AbstractOperation { + private static final String OSSRH_STAGING_MANUAL_SEARCH = "https://" + Repository.OSSRH_STAGING_API_DOMAIN + "/manual/search/repositories"; + private static final String OSSRH_STAGING_MANUAL_UPLOAD = "https://" + Repository.OSSRH_STAGING_API_DOMAIN + "/manual/upload/repository/"; + private boolean offline_ = false; private HierarchicalProperties properties_ = null; private ArtifactRetriever retriever_ = null; @@ -87,6 +92,11 @@ public class PublishOperation extends AbstractOperation { executePublishArtifacts(repository, actual_version); executePublishPom(repository, actual_version); executePublishMetadata(repository, moment); + + if (!info().version().isSnapshot() && + repository.location().contains(Repository.OSSRH_STAGING_API_DOMAIN)) { + executeCloseOSSRHStagingRepository(repository); + } } if (!silent()) { System.out.println("Publishing finished successfully."); @@ -474,12 +484,8 @@ public class PublishOperation extends AbstractOperation { var builder = HttpRequest.newBuilder() .PUT(body) .uri(URI.create(url)) - .header(HEADER_USER_AGENT, "bld/" + BldVersion.getVersion() + - " (" + System.getProperty("os.name") + "; " + System.getProperty("os.version") + "; " + System.getProperty("os.arch") + ") " + - "(" + System.getProperty("java.vendor") + " " + System.getProperty("java.vm.name") + "; " + System.getProperty("java.version") + "; " + System.getProperty("java.vm.version") + ")"); - if (repository.username() != null && repository.password() != null) { - builder.header(HEADER_AUTHORIZATION, basicAuthorizationHeader(repository.username(), repository.password())); - } + .header(HEADER_USER_AGENT, constructBldUserAgent()); + applyAuthorization(repository, builder); var request = builder.build(); HttpResponse response; @@ -505,6 +511,100 @@ public class PublishOperation extends AbstractOperation { } } + private static String constructBldUserAgent() { + return "bld/" + BldVersion.getVersion() + + " (" + System.getProperty("os.name") + "; " + System.getProperty("os.version") + "; " + System.getProperty("os.arch") + ") " + + "(" + System.getProperty("java.vendor") + " " + System.getProperty("java.vm.name") + "; " + System.getProperty("java.version") + "; " + System.getProperty("java.vm.version") + ")"; + } + + + private static void applyAuthorization(Repository repository, HttpRequest.Builder builder) { + if (repository.username() != null && repository.password() != null) { + builder.header(HEADER_AUTHORIZATION, basicAuthorizationHeader(repository.username(), repository.password())); + } + } + + /** + * Part of the {@link #execute} operation, closes the OSSRH staging API repository. + * + * @param repository the repository to close a staging repository in + * @since 2.2.2 + */ + protected void executeCloseOSSRHStagingRepository(Repository repository) { + var url_search = OSSRH_STAGING_MANUAL_SEARCH; + System.out.print("Finding open staging repositories at: " + url_search + " ... "); + System.out.flush(); + try { + var builder_search = HttpRequest.newBuilder() + .GET() + .uri(URI.create(url_search)) + .header(HEADER_USER_AGENT, constructBldUserAgent()); + applyAuthorization(repository, builder_search); + var request_list = builder_search.build(); + + HttpResponse response_search; + try { + response_search = client_.send(request_list, HttpResponse.BodyHandlers.ofString()); + } catch (IOException e) { + System.out.print("I/O error"); + throw new RestApiException(url_search, e); + } catch (InterruptedException e) { + System.out.print("interrupted"); + throw new RestApiException(url_search, e); + } + + if (response_search.statusCode() >= 200 && + response_search.statusCode() < 300) { + System.out.println("done"); + + var pattern_key = Pattern.compile("\\{\\s*\"key\"\\s*:\\s*\"([^\"]+)\"\\s*,\\s*\"state\"\\s*:\\s*\"open\""); + var matcher_key = pattern_key.matcher(response_search.body()); + if (matcher_key.find()) { + var key = matcher_key.group(1); + System.out.println("Found open staging repository with key: " + key); + + var url_close = OSSRH_STAGING_MANUAL_UPLOAD + key; + System.out.print("Closing the staging repository at: " + url_close + " ... "); + System.out.flush(); + var builder_close = HttpRequest.newBuilder() + .POST(BodyPublishers.ofString("")) + .uri(URI.create(url_close)) + .header(HEADER_USER_AGENT, constructBldUserAgent()); + applyAuthorization(repository, builder_close); + var request_close = builder_close.build(); + + HttpResponse response_close; + try { + response_close = client_.send(request_close, HttpResponse.BodyHandlers.ofString()); + } catch (IOException e) { + System.out.print("I/O error"); + throw new RestApiException(url_close, e); + } catch (InterruptedException e) { + System.out.print("interrupted"); + throw new RestApiException(url_close, e); + } + + if (response_close.statusCode() >= 200 && + response_close.statusCode() < 300) { + System.out.print("done"); + } else { + System.out.print("failed"); + throw new RestApiException(url_close, response_close.statusCode()); + } + } + else { + System.out.print("No open staging repository found."); + throw new RestApiException(url_search); + } + } else { + System.out.print("failed"); + throw new RestApiException(url_search, response_search.statusCode()); + } + } finally { + System.out.println(); + } + } + /** * Configures a publish operation from a {@link BaseProject}. * diff --git a/src/main/java/rife/bld/operations/exceptions/RestApiException.java b/src/main/java/rife/bld/operations/exceptions/RestApiException.java new file mode 100644 index 0000000..36d52ec --- /dev/null +++ b/src/main/java/rife/bld/operations/exceptions/RestApiException.java @@ -0,0 +1,40 @@ +/* + * Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com) + * Licensed under the Apache License, Version 2.0 (the "License") + */ +package rife.bld.operations.exceptions; + +import rife.tools.HttpUtils; + +import java.io.Serial; + +/** + * When thrown, indicates that something went wrong during the use of a rest API call. + * + * @author Geert Bevin (gbevin[remove] at uwyn dot com) + * @since 2.2.2 + */ +public class RestApiException extends RuntimeException { + @Serial private static final long serialVersionUID = -6753423938407177328L; + + private final String url_; + + public RestApiException(String url, int status) { + super("An error occurred while using rest API at '" + url + "'\nHTTP status code " + status + " : " + HttpUtils.statusReason(status)); + url_ = url; + } + + public RestApiException(String url, Throwable cause) { + super("An error occurred while using rest API at '" + url + "'", cause); + url_ = url; + } + + public RestApiException(String url) { + super("An error occurred while using rest API at '" + url + "'"); + url_ = url; + } + + public String getUrl() { + return url_; + } +} \ No newline at end of file