#124 - Polishing.

Add additional logging. Skip staging repository drop on Sonatype failures (e.g. when running into timeouts on repository close).

Fix Artifactory error parsing.
This commit is contained in:
Mark Paluch
2019-09-06 10:44:22 +02:00
parent d8381cb4fc
commit 25bc0ef400
7 changed files with 143 additions and 37 deletions

View File

@@ -10,6 +10,7 @@ maven.mavenHome=
# Deployment
deployment.repository-prefix=
# Must be always the encrypted password taken from the Artifactory GUI/Profile view
deployment.password=
deployment.api-key=

View File

@@ -25,21 +25,25 @@ import java.util.Set;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.function.BiFunction;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Collector;
import javax.annotation.PreDestroy;
import org.springframework.data.release.Streamable;
import org.springframework.data.release.model.Project;
import org.springframework.data.release.model.ProjectAware;
import org.springframework.data.release.utils.ListWrapperCollector;
import org.springframework.data.release.utils.Logger;
import org.springframework.plugin.core.PluginRegistry;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import com.google.common.util.concurrent.MoreExecutors;
@@ -52,17 +56,20 @@ import com.google.common.util.concurrent.MoreExecutors;
class BuildExecutor {
private final @NonNull PluginRegistry<BuildSystem, Project> buildSystems;
private final Logger logger;
private final MavenProperties mavenProperties;
private final ExecutorService executor;
public BuildExecutor(PluginRegistry<BuildSystem, Project> buildSystems, MavenProperties mavenProperties) {
public BuildExecutor(PluginRegistry<BuildSystem, Project> buildSystems, Logger logger,
MavenProperties mavenProperties) {
this.buildSystems = buildSystems;
this.logger = logger;
this.mavenProperties = mavenProperties;
if (this.mavenProperties.isParllelize()) {
int processors = Runtime.getRuntime().availableProcessors();
int parallelity = Math.max(2, processors - 2);
int parallelity = Math.max(2, (processors / 2));
executor = new ThreadPoolExecutor(parallelity, parallelity, 10, TimeUnit.MINUTES, new ArrayBlockingQueue<>(256));
} else {
executor = MoreExecutors.newDirectExecutorService();
@@ -82,7 +89,7 @@ class BuildExecutor {
* @param function must not be {@literal null}.
* @return
*/
public <T, M extends ProjectAware> List<T> doWithBuildSystemOrdered(Streamable<M> iteration,
public <T, M extends ProjectAware> Summary<T> doWithBuildSystemOrdered(Streamable<M> iteration,
BiFunction<BuildSystem, M, T> function) {
return doWithBuildSystem(iteration, function, true);
}
@@ -95,12 +102,12 @@ class BuildExecutor {
* @param function must not be {@literal null}.
* @return
*/
public <T, M extends ProjectAware> List<T> doWithBuildSystemAnyOrder(Streamable<M> iteration,
public <T, M extends ProjectAware> Summary<T> doWithBuildSystemAnyOrder(Streamable<M> iteration,
BiFunction<BuildSystem, M, T> function) {
return doWithBuildSystem(iteration, function, false);
}
private <T, M extends ProjectAware> List<T> doWithBuildSystem(Streamable<M> iteration,
private <T, M extends ProjectAware> Summary<T> doWithBuildSystem(Streamable<M> iteration,
BiFunction<BuildSystem, M, T> function, boolean considerDependencyOrder) {
Map<Project, CompletableFuture<T>> results = new ConcurrentHashMap<>();
@@ -136,8 +143,20 @@ class BuildExecutor {
}
return iteration.stream()//
.map(module -> results.get(module.getProject()).join()) //
.collect(Collectors.toList());
.map(module -> {
CompletableFuture<T> future = results.get(module.getProject());
try {
return new ExecutionResult<T>(module.getProject(), future.get());
}
catch (InterruptedException | ExecutionException e) {
return new ExecutionResult<T>(module.getProject(), e.getCause());
}
}) //
.collect(toSummaryCollector());
}
private <T, M extends ProjectAware> CompletableFuture<T> run(M module, BiFunction<BuildSystem, M, T> function) {
@@ -165,4 +184,86 @@ class BuildExecutor {
return result;
}
/**
* Returns a new collector to toSummaryCollector {@link ExecutionResult} as {@link Summary} using the {@link Stream}
* API.
*
* @return
*/
public static <T> Collector<ExecutionResult<T>, ?, Summary<T>> toSummaryCollector() {
return ListWrapperCollector.collectInto(Summary::new);
}
public static class ExecutionResult<T> {
private final Project project;
private final T result;
private final Throwable failure;
public ExecutionResult(Project project, Throwable failure) {
this.project = project;
this.result = null;
this.failure = failure;
}
public ExecutionResult(Project project, T result) {
this.project = project;
this.result = result;
this.failure = null;
}
public T getResult() {
return result;
}
@Override
public String toString() {
return String.format("%20s - %s", project.getName(),
isSuccessful() ? "Successful" : "Error: " + failure.getMessage());
}
public boolean isSuccessful() {
return this.failure == null;
}
}
public static class Summary<T> {
private final List<ExecutionResult<T>> executions;
public Summary(List<ExecutionResult<T>> executions) {
this.executions = executions;
if (!isSuccessful()) {
throw new BuildFailed(this);
}
}
public List<ExecutionResult<T>> getExecutions() {
return executions;
}
public boolean isSuccessful() {
return this.executions.stream().allMatch(ExecutionResult::isSuccessful);
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("Execution summary");
builder.append("\n");
builder.append(StringUtils.collectionToDelimitedString(executions, "\n"));
return builder.toString();
}
}
static class BuildFailed extends RuntimeException {
public BuildFailed(Summary<?> summary) {
super(summary.toString());
}
}
}

View File

@@ -22,13 +22,16 @@ import java.nio.file.Path;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.springframework.data.release.deployment.DeploymentInformation;
import org.springframework.data.release.model.Module;
import org.springframework.data.release.model.ModuleIteration;
import org.springframework.data.release.model.Phase;
import org.springframework.data.release.model.Project;
import org.springframework.data.release.model.Train;
import org.springframework.data.release.model.TrainIteration;
import org.springframework.data.release.utils.Logger;
import org.springframework.plugin.core.PluginRegistry;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
@@ -44,6 +47,7 @@ import com.google.common.annotations.VisibleForTesting;
public class BuildOperations {
private final @NonNull PluginRegistry<BuildSystem, Project> buildSystems;
private final @NonNull Logger logger;
private final @NonNull MavenProperties properties;
private final @NonNull BuildExecutor executor;
@@ -61,8 +65,10 @@ public class BuildOperations {
UpdateInformation updateInformation = UpdateInformation.of(iteration, phase);
executor.doWithBuildSystemOrdered(iteration,
BuildExecutor.Summary<ModuleIteration> summary = executor.doWithBuildSystemOrdered(iteration,
(system, it) -> system.updateProjectDescriptors(it, updateInformation));
logger.log(iteration, "Update Project Descriptors done: %s", summary);
}
/**
@@ -86,7 +92,10 @@ public class BuildOperations {
Assert.notNull(train, "Train must not be null!");
executor.doWithBuildSystemAnyOrder(train, BuildSystem::triggerDistributionBuild);
BuildExecutor.Summary<Module> summary = executor.doWithBuildSystemAnyOrder(train,
BuildSystem::triggerDistributionBuild);
logger.log(train, "Distribution build: %s", summary);
}
/**
@@ -96,8 +105,13 @@ public class BuildOperations {
* @return
*/
public List<DeploymentInformation> performRelease(TrainIteration iteration) {
return executor.doWithBuildSystemOrdered(iteration,
BuildExecutor.Summary<DeploymentInformation> summary = executor.doWithBuildSystemOrdered(iteration,
(buildSystem, moduleIteration) -> performRelease(moduleIteration));
logger.log(iteration, "Release: %s", summary);
return summary.getExecutions().stream().map(BuildExecutor.ExecutionResult::getResult).collect(Collectors.toList());
}
/**
@@ -121,7 +135,10 @@ public class BuildOperations {
Assert.notNull(iteration, "Train iteration must not be null!");
Assert.notNull(phase, "Phase must not be null!");
executor.doWithBuildSystemOrdered(iteration, (system, module) -> system.prepareVersion(module, phase));
BuildExecutor.Summary<ModuleIteration> summary = executor.doWithBuildSystemOrdered(iteration,
(system, module) -> system.prepareVersion(module, phase));
logger.log(iteration, "Prepare versions: %s", summary);
}
/**

View File

@@ -338,7 +338,7 @@ class MavenBuildSystem implements BuildSystem {
SKIP_TESTS, //
arg("gpg.executable").withValue(gpg.getExecutable()), //
arg("gpg.keyname").withValue(gpg.getKeyname()), //
arg("gpg.password").withValue(gpg.getPassword()));
arg("gpg.password").withValue(gpg.getPassword()), arg("skipStagingRepositoryClose").withValue("true"));
mvn.execute(module.getProject(), arguments);
}

View File

@@ -20,6 +20,7 @@ import lombok.Value;
import java.io.IOException;
import java.net.URI;
import java.util.function.Consumer;
import org.springframework.data.release.model.ModuleIteration;
import org.springframework.data.release.utils.Logger;
@@ -61,7 +62,7 @@ class ArtifactoryClient {
template.postForEntity(uri,
new PromotionRequest(information.getTargetRepository(), properties.getStagingRepository()), String.class);
} catch (HttpClientErrorException o_O) {
handle("Promotion failed!", o_O, module);
handle(message -> logger.warn(information.getModule(), message), "Promotion failed!", o_O);
}
}
@@ -78,38 +79,24 @@ class ArtifactoryClient {
logger.log("Artifactory", "Authentication verified!");
} catch (HttpClientErrorException o_O) {
handle("Authentication verification failed!", o_O);
handle(message -> logger.log("Artifactory Client", message), "Authentication verification failed!", o_O);
throw new IllegalStateException("Authentication verification failed!");
}
}
void handle(String log, HttpClientErrorException o_O, ModuleIteration module) {
private void handle(Consumer<Object> logger, String message, HttpClientErrorException o_O) {
try {
logger.log(module, log);
logger.accept(message);
Errors errors = new ObjectMapper().readValue(o_O.getResponseBodyAsByteArray(), Errors.class);
errors.getErrors().forEach(error -> logger.log(module, error));
errors.getMessages().forEach(message -> logger.log(module, message));
errors.getErrors().forEach(logger);
errors.getMessages().forEach(logger);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private void handle(String log, HttpClientErrorException o_O) {
try {
logger.log("Artifactory Client", log);
Errors errors = new ObjectMapper().readValue(o_O.getResponseBodyAsByteArray(), Errors.class);
errors.getErrors().forEach(error -> logger.log("Artifactory Client", error));
errors.getMessages().forEach(message -> logger.log("Artifactory Client", message));
} catch (IOException e) {
throw new RuntimeException(e);
o_O.addSuppressed(e);
throw new RuntimeException(o_O.getResponseBodyAsString(), o_O);
}
}

View File

@@ -92,7 +92,7 @@ public class DeploymentProperties {
private static final String PROMOTION_RESOURCE = "/api/build/promote/{buildName}/{buildNumber}";
private static final String DELETE_BUILD_RESOURCE = "/api/build/{buildName}?buildNumbers={buildNumber}&artifacts=1";
private static final String VERIFICATION_RESOURCE = "/api/storage/test-libs-staging-local";
private static final String VERIFICATION_RESOURCE = "/api/storage/temp-private-local";
private String uri;

View File

@@ -55,7 +55,7 @@ public class ReleaseTrains {
LOVELACE = KAY.next("Lovelace", Transition.MINOR, new Module(JDBC, "1.0"), new Module(SOLR, "4.0"));
MOORE = LOVELACE.next("Moore", Transition.MINOR).withIterations(new Iterations(M1, M2, M3, M4, RC1, RC2, GA));
MOORE = LOVELACE.next("Moore", Transition.MINOR).withIterations(new Iterations(M1, M2, M3, M4, RC1, RC2, RC3, GA));
// Trains