Added dry-run option; fixes gh-163
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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 + '\'' + '}';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user