Added dry-run option; fixes gh-163

This commit is contained in:
Marcin Grzejszczak
2019-10-24 13:54:36 +02:00
parent df01ac1bf3
commit 2deb04af16
9 changed files with 160 additions and 19 deletions

View File

@@ -679,8 +679,7 @@ public class ReleaserProperties implements Serializable {
private List<String> ignoredPomRegex = Arrays.asList("^.*\\.git/.*$",
"^.*spring-cloud-contract-maven-plugin/src/test/projects/.*$",
"^.*spring-cloud-contract-maven-plugin/target/.*$",
"^.*src/test/bats/.*$",
"^.*samples/standalone/[a-z]+/.*$");
"^.*src/test/bats/.*$", "^.*samples/standalone/[a-z]+/.*$");
public String getBranch() {
return this.branch;
@@ -1002,8 +1001,7 @@ public class ReleaserProperties implements Serializable {
private List<String> ignoredGradleRegex = Arrays.asList(
"^.*spring-cloud-contract-maven-plugin/src/test/projects/.*$",
"^.*spring-cloud-contract-maven-plugin/target/.*$",
"^.*src/test/bats/.*$",
"^.*samples/standalone/[a-z]+/.*$");
"^.*src/test/bats/.*$", "^.*samples/standalone/[a-z]+/.*$");
/**
* Command to be executed to build the project.

View File

@@ -39,6 +39,11 @@ public class Options {
*/
public Boolean interactive;
/**
* Is dry run set.
*/
public Boolean dryRun;
/**
* List of task names to release.
*/
@@ -54,11 +59,12 @@ public class Options {
*/
public String range = "";
Options(Boolean metaRelease, Boolean fullRelease, Boolean interactive,
Options(Boolean metaRelease, Boolean fullRelease, Boolean interactive, Boolean dryRun,
List<String> taskNames, String startFrom, String range) {
this.metaRelease = metaRelease;
this.fullRelease = fullRelease;
this.interactive = interactive;
this.dryRun = dryRun;
this.taskNames = taskNames.stream().map(this::removeQuotingChars)
.collect(Collectors.toList());
this.startFrom = removeQuotingChars(startFrom);
@@ -75,9 +81,9 @@ public class Options {
@Override
public String toString() {
return "Options{" + "metaRelease=" + this.metaRelease + ", fullRelease="
+ this.fullRelease + ", interactive=" + this.interactive + ", taskNames="
+ this.taskNames + ", startFrom='" + this.startFrom + '\'' + ", range='"
+ this.range + '\'' + '}';
+ this.fullRelease + ", interactive=" + this.interactive + ", dryRun="
+ this.dryRun + ", taskNames=" + this.taskNames + ", startFrom='"
+ this.startFrom + '\'' + ", range='" + this.range + '\'' + '}';
}
}

View File

@@ -27,6 +27,8 @@ public class OptionsBuilder {
private Boolean interactive = true;
private Boolean dryRun = false;
private List<String> taskNames = new ArrayList<>();
private String startFrom = "";
@@ -48,6 +50,11 @@ public class OptionsBuilder {
return this;
}
public OptionsBuilder dryRun(Boolean dryRun) {
this.dryRun = dryRun;
return this;
}
public OptionsBuilder taskNames(List<String> taskNames) {
this.taskNames = taskNames;
return this;
@@ -65,7 +72,7 @@ public class OptionsBuilder {
public Options options() {
return new Options(this.metaRelease, this.fullRelease, this.interactive,
this.taskNames, this.startFrom, this.range);
this.dryRun, this.taskNames, this.startFrom, this.range);
}
}

View File

@@ -58,6 +58,10 @@ class OptionsParser implements Parser {
Arrays.asList("i", "interactive"),
"Do you want to set the properties from the command line of a single project?")
.withRequiredArg().ofType(Boolean.class).defaultsTo(true);
ArgumentAcceptingOptionSpec<Boolean> dryRunOpt = parser.acceptsAll(
Arrays.asList("dr", "dry-run"),
"Do you want to do the release / meta release with build and install projects locally only?")
.withRequiredArg().ofType(Boolean.class).defaultsTo(false);
Tasks.NON_COMPOSITE_TASKS.forEach(
task -> parser.acceptsAll(Arrays.asList(task.shortName, task.name),
task.description).withOptionalArg());
@@ -85,6 +89,7 @@ class OptionsParser implements Parser {
}
Boolean metaRelease = options.valueOf(metaReleaseOpt);
Boolean interactive = options.valueOf(interactiveOpt);
Boolean dryRun = options.valueOf(dryRunOpt);
Boolean fullRelease = options.has(fullReleaseOpt);
List<String> providedTaskNames = StringUtils.hasText(options
.valueOf(taskNamesOpt)) ? Arrays.asList(
@@ -107,7 +112,7 @@ class OptionsParser implements Parser {
String startFrom = options.valueOf(startFromOpt);
String range = options.valueOf(rangeOpt);
Options buildOptions = new OptionsBuilder().metaRelease(metaRelease)
.fullRelease(fullRelease).interactive(interactive)
.fullRelease(fullRelease).interactive(interactive).dryRun(dryRun)
.taskNames(taskNames).startFrom(startFrom).range(range).options();
log.info(
"\n\nWill use the following options to process the project\n\n{}\n\n",

View File

@@ -51,7 +51,7 @@ class ReleaserPropertiesUpdater {
ReleaserProperties props = updatePropertiesFromFile(properties,
clonedProjectFromOrg);
props.setWorkingDir(clonedProjectFromOrg.getAbsolutePath());
log.info("Updated properties [\n\n{}\n\n]", props);
log.trace("Updated properties [\n\n{}\n\n]", props);
updateProperties(props);
return props;
}

View File

@@ -121,7 +121,7 @@ public class SpringReleaser {
List<ProcessedProject> processedProjects = new ArrayList<>();
if (options.metaRelease) {
ReleaserProperties original = this.properties.copy();
log.debug("The following properties were found [{}]", original);
log.trace("The following properties were found [{}]", original);
processedProjects = metaReleaseProjects(options).stream()
.map(project -> processProjectForMetaRelease(original.copy(), options,
project))
@@ -133,8 +133,10 @@ public class SpringReleaser {
File projectFolder = projectFolder();
projectsAndVersion = processProject(options, projectFolder, TaskType.RELEASE);
}
this.optionsProcessor.postReleaseOptions(options,
postReleaseOptionsAgs(options, projectsAndVersion, processedProjects));
if (!options.dryRun) {
this.optionsProcessor.postReleaseOptions(options, postReleaseOptionsAgs(
options, projectsAndVersion, processedProjects));
}
}
private void prepareForMetaRelease(Options options) {
@@ -146,7 +148,7 @@ public class SpringReleaser {
ProcessedProject processProjectForMetaRelease(ReleaserProperties copy,
Options options, String project) {
log.info("Original properties [\n\n{}\n\n]", copy);
log.trace("Original properties [\n\n{}\n\n]", copy);
File clonedProjectFromOrg = this.releaser.clonedProjectFromOrg(project);
copy = updatePropertiesIfCustomConfigPresent(copy, clonedProjectFromOrg);
log.info("Successfully cloned the project [{}] to [{}]", project,
@@ -289,7 +291,7 @@ public class SpringReleaser {
projectsAndVersion.projectVersions, originalVersion,
projectsAndVersion.versionFromBom, this.properties, options.interactive,
taskType, this.applicationEventPublisher);
log.debug("Processing project [{}] with args [{}]", project, defaultArgs);
log.trace("Processing project [{}] with args [{}]", project, defaultArgs);
this.optionsProcessor.processOptions(options, defaultArgs);
return projectsAndVersion;
}

View File

@@ -116,6 +116,9 @@ final class Tasks {
Tasks.CLOSE_MILESTONE, Tasks.UPDATE_SAGAN)
.collect(Collectors.toList());
static final List<Task> DEFAULT_DRY_RUN_TASKS_PER_PROJECT = Stream
.of(Tasks.UPDATING_POMS, Tasks.BUILD_PROJECT).collect(Collectors.toList());
static final List<Task> DEFAULT_TASKS_PER_RELEASE = Stream
.of(Tasks.RUN_UPDATED_SAMPLES, Tasks.CREATE_TEMPLATES, Tasks.UPDATE_GUIDES,
Tasks.UPDATE_START_SPRING_IO,
@@ -133,6 +136,12 @@ final class Tasks {
static Task RELEASE = Tasks.task("release", "fr", "FULL RELEASE",
"Perform a full release of this project without interruptions",
args -> new CompositeConsumer(DEFAULT_TASKS_PER_PROJECT).accept(args));
static Task DRY_RUN = Tasks.task("dryRun", "dr", "DRY RUN",
"Perform a dry run release of a single project - bumps versions and installs them locally",
args -> new CompositeConsumer(DEFAULT_DRY_RUN_TASKS_PER_PROJECT)
.accept(args));
static Task POST_RELEASE = Tasks.task("postRelease", "pr", "POST RELEASE TASKS",
"Perform post release tasks for this release without interruptions",
args -> new CompositeConsumer(DEFAULT_TASKS_PER_RELEASE).accept(args),
@@ -146,9 +155,14 @@ final class Tasks {
args -> new CompositeConsumer(DEFAULT_TASKS_PER_PROJECT,
(args1 -> args.properties.getMetaRelease().setEnabled(true)))
.accept(args));
static Task META_RELEASE_DRY_RUN = Tasks.task("metaReleaseDryRun", "xdr",
"META RELEASE DRY RUN", "Perform a meta release dry run of projects",
args -> new CompositeConsumer(DEFAULT_DRY_RUN_TASKS_PER_PROJECT,
(args1 -> args.properties.getMetaRelease().setEnabled(true)))
.accept(args));
static final List<Task> COMPOSITE_TASKS = Stream
.of(RELEASE, RELEASE_VERBOSE, META_RELEASE, POST_RELEASE)
static final List<Task> COMPOSITE_TASKS = Stream.of(RELEASE, RELEASE_VERBOSE, DRY_RUN,
META_RELEASE, POST_RELEASE, META_RELEASE_DRY_RUN)
.collect(Collectors.toList());
static final List<Task> ALL_TASKS_PER_PROJECT = Stream

View File

@@ -270,16 +270,45 @@ public class AcceptanceTests {
thenUpdateReleaseTrainDocsWasCalled();
}
@Test
public void should_perform_a_meta_release_dry_run_of_sc_release_and_consul()
throws Exception {
// simulates an org
GitTestUtils.openGitProject(file("/projects/spring-cloud-release/")).checkout()
.setName("Edgware").call();
SpringReleaser releaser = metaReleaserDryRun(edgwareSr10());
releaser.release(new OptionsBuilder().metaRelease(true).dryRun(true).options());
// consul, release
then(this.nonAssertingGitHandler.clonedProjects).hasSize(2);
thenAllDryRunStepsWereExecutedForEachProject();
thenSaganWasNotCalled();
thenDocumentationWasNotUpdated();
BDDAssertions.then(clonedProject("spring-cloud-consul").tagList().call())
.extracting("name").doesNotContain("refs/tags/v5.3.5.RELEASE");
thenRunUpdatedTestsWereNotCalled();
thenUpdateReleaseTrainDocsWasNotCalled();
}
private void thenRunUpdatedTestsWereCalled() {
BDDMockito.then(this.postReleaseActions).should()
.runUpdatedTests(BDDMockito.any(Projects.class));
}
private void thenRunUpdatedTestsWereNotCalled() {
BDDMockito.then(this.postReleaseActions).shouldHaveNoInteractions();
}
private void thenUpdateReleaseTrainDocsWasCalled() {
BDDMockito.then(this.postReleaseActions).should()
.generateReleaseTrainDocumentation(BDDMockito.any(Projects.class));
}
private void thenUpdateReleaseTrainDocsWasNotCalled() {
BDDMockito.then(this.postReleaseActions).shouldHaveNoInteractions();
}
private Map<String, String> edgwareSr10() {
Map<String, String> versions = new LinkedHashMap<>();
versions.put("spring-boot", "5.5.16.RELEASE");
@@ -320,6 +349,10 @@ public class AcceptanceTests {
BDDMockito.any(ProjectVersion.class));
}
private void thenSaganWasNotCalled() {
BDDMockito.then(this.saganUpdater).shouldHaveNoInteractions();
}
private void thenAllStepsWereExecutedForEachProject() {
this.nonAssertingGitHandler.clonedProjects.stream()
.filter(f -> !f.getName().contains("angel")
@@ -333,6 +366,20 @@ public class AcceptanceTests {
});
}
private void thenAllDryRunStepsWereExecutedForEachProject() {
this.nonAssertingGitHandler.clonedProjects.stream()
.filter(f -> !f.getName().contains("angel")
&& !f.getName().equals("spring-cloud"))
.forEach(project -> {
then(Arrays.asList("spring-cloud-starter-build",
"spring-cloud-consul"))
.contains(pom(project).getArtifactId());
then(this.capture.toString()).contains("executed_build");
then(this.capture.toString()).doesNotContain("executed_deploy",
"executed_docs");
});
}
@Test
public void should_not_clone_any_projects_when_they_are_on_list_of_projects_to_skip()
throws Exception {
@@ -414,6 +461,10 @@ public class AcceptanceTests {
BDDMockito.any(ProjectVersion.class), BDDMockito.anyString());
}
private void thenDocumentationWasNotUpdated() {
BDDMockito.then(this.documentationUpdater).shouldHaveNoInteractions();
}
private void thenWikiPageWasUpdated() {
BDDMockito.then(this.documentationUpdater).should()
.updateReleaseTrainWiki(BDDMockito.any(Projects.class));
@@ -636,6 +687,12 @@ public class AcceptanceTests {
return metaReleaserWithFullDeployment(properties);
}
private SpringReleaser metaReleaserDryRun(Map<String, String> versions)
throws Exception {
ReleaserProperties properties = metaReleaserProperties(versions);
return metaReleaserWithDryRun(properties);
}
private SpringReleaser releaserWithFullDeployment(String expectedVersion,
String projectName, ReleaserProperties properties) throws Exception {
Releaser releaser = defaultReleaser(expectedVersion, projectName, properties);
@@ -672,6 +729,25 @@ public class AcceptanceTests {
}, this.updater, this.applicationEventPublisher);
}
private SpringReleaser metaReleaserWithDryRun(ReleaserProperties properties)
throws Exception {
Releaser releaser = defaultMetaReleaser(properties);
return new SpringReleaser(releaser, properties, new OptionsProcessor(releaser,
properties, this.applicationEventPublisher) {
@Override
String chosenOption() {
// meta release dry run
return String.valueOf(TaskUtils.indexOf(Tasks.META_RELEASE_DRY_RUN));
}
@Override
void postReleaseOptions(Options options, Args defaultArgs) {
options.interactive = false;
super.postReleaseOptions(options, defaultArgs);
}
}, this.updater, this.applicationEventPublisher);
}
private SpringReleaser releaserWithSnapshotScRelease(File projectFile,
String projectName, String branch, String expectedVersion) throws Exception {
ReleaserProperties properties = snapshotScReleaseReleaserProperties(projectFile,
@@ -687,7 +763,7 @@ public class AcceptanceTests {
properties, this.applicationEventPublisher) {
@Override
String chosenOption() {
return "13";
return String.valueOf(TaskUtils.indexOf(Tasks.CREATE_TEMPLATES));
}
@Override

View File

@@ -0,0 +1,33 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.release.internal.spring;
import java.util.List;
class TaskUtils {
static int indexOf(Task task) {
List<Task> compositeTasks = Tasks.ALL_TASKS_PER_PROJECT;
for (int i = 0; i < compositeTasks.size(); i++) {
if (task == compositeTasks.get(i)) {
return i;
}
}
return -1;
}
}