From 442a7234ebe71a1bc68361b234f0997d93f0a7ac Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Thu, 9 Mar 2017 12:38:34 +0100 Subject: [PATCH] Added committing / tagging and pushing --- .../cloud/release/internal/Releaser.java | 55 +++-- .../GitProjectRepo.java => git/GitRepo.java} | 77 ++++++- .../internal/git/ProjectGitUpdater.java | 39 ++++ .../cloud/release/internal/pom/PomReader.java | 6 +- .../release/internal/pom/ProjectUpdater.java | 9 +- .../release/internal/pom/ProjectVersion.java | 43 ++++ .../internal/git/GitProjectRepoTests.java | 217 ++++++++++++++++++ .../internal/git/ProjectGitUpdaterTests.java | 47 ++++ .../internal/pom/GitProjectRepoTests.java | 102 -------- .../release/internal/pom/PomReaderTests.java | 19 +- .../release/internal/pom/PomUpdaterTests.java | 1 + .../internal/pom/ProjectVersionTests.java | 64 ++++++ .../internal/pom/SCReleasePomParserTests.java | 1 + .../release/spring/ReleaserConfiguration.java | 4 +- 14 files changed, 548 insertions(+), 136 deletions(-) rename spring-cloud-release-tools-core/src/main/java/org/springframework/cloud/release/internal/{pom/GitProjectRepo.java => git/GitRepo.java} (66%) create mode 100644 spring-cloud-release-tools-core/src/main/java/org/springframework/cloud/release/internal/git/ProjectGitUpdater.java create mode 100644 spring-cloud-release-tools-core/src/main/java/org/springframework/cloud/release/internal/pom/ProjectVersion.java create mode 100644 spring-cloud-release-tools-core/src/test/java/org/springframework/cloud/release/internal/git/GitProjectRepoTests.java create mode 100644 spring-cloud-release-tools-core/src/test/java/org/springframework/cloud/release/internal/git/ProjectGitUpdaterTests.java delete mode 100644 spring-cloud-release-tools-core/src/test/java/org/springframework/cloud/release/internal/pom/GitProjectRepoTests.java create mode 100644 spring-cloud-release-tools-core/src/test/java/org/springframework/cloud/release/internal/pom/ProjectVersionTests.java diff --git a/spring-cloud-release-tools-core/src/main/java/org/springframework/cloud/release/internal/Releaser.java b/spring-cloud-release-tools-core/src/main/java/org/springframework/cloud/release/internal/Releaser.java index 8bd0b564..97a96a2f 100644 --- a/spring-cloud-release-tools-core/src/main/java/org/springframework/cloud/release/internal/Releaser.java +++ b/spring-cloud-release-tools-core/src/main/java/org/springframework/cloud/release/internal/Releaser.java @@ -1,13 +1,15 @@ package org.springframework.cloud.release.internal; import java.io.File; -import java.io.IOException; import java.lang.invoke.MethodHandles; +import java.util.Scanner; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.release.internal.builder.ProjectBuilder; +import org.springframework.cloud.release.internal.git.ProjectGitUpdater; import org.springframework.cloud.release.internal.pom.ProjectUpdater; +import org.springframework.cloud.release.internal.pom.ProjectVersion; import org.springframework.util.StringUtils; /** @@ -15,33 +17,58 @@ import org.springframework.util.StringUtils; */ public class Releaser { private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + private static final String MSG = "'q' to quit and 's' to skip\n\n"; + private static final String SKIP = "s"; + private static final String QUIT = "q"; private final ReleaserProperties properties; private final ProjectUpdater projectUpdater; private final ProjectBuilder projectBuilder; + private final ProjectGitUpdater projectGitUpdater; - public Releaser(ReleaserProperties properties, - ProjectUpdater projectUpdater, ProjectBuilder projectBuilder) { + public Releaser(ReleaserProperties properties, ProjectUpdater projectUpdater, + ProjectBuilder projectBuilder, ProjectGitUpdater projectGitUpdater) { this.properties = properties; this.projectUpdater = projectUpdater; this.projectBuilder = projectBuilder; + this.projectGitUpdater = projectGitUpdater; } public void release() { - try { - String workingDir = StringUtils.hasText(this.properties.getWorkingDir()) ? - this.properties.getWorkingDir() : System.getProperty("user.dir"); - log.info("\n\n\n=== UPDATING POMS ===\n\nWill run the application for root folder [{}]", workingDir); - this.projectUpdater.updateProject(new File(workingDir)); + String workingDir = StringUtils.hasText(this.properties.getWorkingDir()) ? + this.properties.getWorkingDir() : System.getProperty("user.dir"); + File project = new File(workingDir); + log.info("\n\n\n=== UPDATING POMS ===\n\nWill run the application " + + "for root folder [{}]. \n\nPress ENTER to continue {}", workingDir, MSG); + boolean skipPoms = skipStep(); + ProjectVersion version = new ProjectVersion(project); + if (!skipPoms) { + this.projectUpdater.updateProject(project); log.info("\n\nProject was successfully updated"); - log.info("\n\n\n=== BUILD PROJECT ===\n\nPress ENTER to build the project\n\n"); - System.in.read(); + } + log.info("\n\n\n=== BUILD PROJECT ===\n\nPress ENTER to build the project {}", MSG); + boolean skipBuild = skipStep(); + if (!skipBuild) { this.projectBuilder.build(); log.info("\nProject was successfully built"); - log.info("\n\n\n=== COMMITTING AND PUSHING TAGS ===\n\nPress ENTER to commit, tag and push the tag\n\n"); - System.in.read(); - } catch (IOException e) { - throw new IllegalStateException(e); + } + log.info("\n\n\n=== COMMITTING AND PUSHING TAGS ===\n\nPress ENTER to commit, tag and push the tag {}", MSG); + boolean skipCommit = skipStep(); + if (!skipCommit) { + this.projectGitUpdater.commitAndTagIfApplicable(project, version); + } + } + + boolean skipStep() { + String input = System.console().readLine(); + switch (input.toLowerCase()) { + case SKIP: + return true; + case QUIT: + System.exit(0); + return true; + default: + return false; } } } diff --git a/spring-cloud-release-tools-core/src/main/java/org/springframework/cloud/release/internal/pom/GitProjectRepo.java b/spring-cloud-release-tools-core/src/main/java/org/springframework/cloud/release/internal/git/GitRepo.java similarity index 66% rename from spring-cloud-release-tools-core/src/main/java/org/springframework/cloud/release/internal/pom/GitProjectRepo.java rename to spring-cloud-release-tools-core/src/main/java/org/springframework/cloud/release/internal/git/GitRepo.java index f0205a79..a6d30b8d 100644 --- a/spring-cloud-release-tools-core/src/main/java/org/springframework/cloud/release/internal/pom/GitProjectRepo.java +++ b/spring-cloud-release-tools-core/src/main/java/org/springframework/cloud/release/internal/git/GitRepo.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.cloud.release.internal.pom; +package org.springframework.cloud.release.internal.git; import java.io.File; import java.io.IOException; @@ -28,9 +28,11 @@ import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.ListBranchCommand; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.lib.Ref; +import org.eclipse.jgit.transport.RefSpec; import org.eclipse.jgit.util.FileUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.util.ResourceUtils; /** * Abstraction over a Git repo. Can clonea repo from a given location @@ -38,21 +40,20 @@ import org.slf4j.LoggerFactory; * * @author Marcin Grzejszczak */ -class GitProjectRepo { +public class GitRepo { - private static final Logger log = LoggerFactory - .getLogger(MethodHandles.lookup().lookupClass()); + private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - private final GitProjectRepo.JGitFactory gitFactory; + private final GitRepo.JGitFactory gitFactory; private final File basedir; - GitProjectRepo(File basedir) { + public GitRepo(File basedir) { this.basedir = basedir; - this.gitFactory = new GitProjectRepo.JGitFactory(); + this.gitFactory = new GitRepo.JGitFactory(); } - GitProjectRepo(File basedir, GitProjectRepo.JGitFactory factory) { + GitRepo(File basedir, GitRepo.JGitFactory factory) { this.basedir = basedir; this.gitFactory = factory; } @@ -62,7 +63,7 @@ class GitProjectRepo { * @param projectUri - URI of the project * @return file where the project was cloned */ - File cloneProject(URI projectUri) { + public File cloneProject(URI projectUri) { try { log.info("Cloning repo from [{}] to [{}]", projectUri, this.basedir); Git git = cloneToBasedir(projectUri, this.basedir); @@ -83,7 +84,7 @@ class GitProjectRepo { * @param project - a Git project * @param branch - branch to check out */ - void checkout(File project, String branch) { + public void checkout(File project, String branch) { try { log.info("Checking out branch [{}] for repo [{}] to [{}]", this.basedir, branch); checkoutBranch(project, branch); @@ -94,6 +95,62 @@ class GitProjectRepo { } } + /** + * Performs a commit + * @param project - a Git project + * @param message - commit message + */ + public void commit(File project, String message) { + try(Git git = this.gitFactory.open(ResourceUtils.getFile(project.toURI()).getAbsoluteFile())) { + git.commit().setMessage(message).call(); + } catch (Exception e) { + throw new IllegalStateException(e); + } + } + + /** + * Creates a tag with a given name + * @param project + * @param tagName + */ + public void tag(File project, String tagName) { + try(Git git = this.gitFactory.open(ResourceUtils.getFile(project.toURI()).getAbsoluteFile())) { + git.tag().setName(tagName).call(); + } catch (Exception e) { + throw new IllegalStateException(e); + } + } + + /** + * Pushes the commits to {@code origin} remote branch + * @param project - Git project + * @param branch - remote branch to which the code should be pushed + */ + public void pushBranch(File project, String branch) { + try(Git git = this.gitFactory.open(ResourceUtils.getFile(project.toURI()).getAbsoluteFile())) { + String localBranch = git.getRepository().getFullBranch(); + RefSpec refSpec = new RefSpec(localBranch + ":" + branch); + git.push().setPushTags().setRefSpecs(refSpec).call(); + } catch (Exception e) { + throw new IllegalStateException(e); + } + } + + /** + * Pushes the commits to {@code origin} remote tag + * @param project - Git project + * @param tagName - remote tag to which the code should be pushed + */ + public void pushTag(File project, String tagName) { + try(Git git = this.gitFactory.open(ResourceUtils.getFile(project.toURI()).getAbsoluteFile())) { + String localBranch = git.getRepository().getFullBranch(); + RefSpec refSpec = new RefSpec(localBranch + ":" + "refs/tags/" + tagName); + git.push().setPushTags().setRefSpecs(refSpec).call(); + } catch (Exception e) { + throw new IllegalStateException(e); + } + } + private Git cloneToBasedir(URI projectUrl, File destinationFolder) throws GitAPIException { CloneCommand command = this.gitFactory.getCloneCommandByCloneRepository() diff --git a/spring-cloud-release-tools-core/src/main/java/org/springframework/cloud/release/internal/git/ProjectGitUpdater.java b/spring-cloud-release-tools-core/src/main/java/org/springframework/cloud/release/internal/git/ProjectGitUpdater.java new file mode 100644 index 00000000..95beca99 --- /dev/null +++ b/spring-cloud-release-tools-core/src/main/java/org/springframework/cloud/release/internal/git/ProjectGitUpdater.java @@ -0,0 +1,39 @@ +package org.springframework.cloud.release.internal.git; + +import java.io.File; +import java.lang.invoke.MethodHandles; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.cloud.release.internal.pom.ProjectVersion; + +/** + * Contains business logic around Git operations + * + * @author Marcin Grzejszczak + */ +public class ProjectGitUpdater { + + private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + + private static final String MSG = "Bumping versions"; + private static final String PRE_RELEASE_MSG = "Bumping versions before release"; + + public void commitAndTagIfApplicable(File project, ProjectVersion version) { + GitRepo gitRepo = gitRepo(project); + if (version.isSnapshot()) { + log.info("Snapshot version [{}] found. Will only commit the changed poms", version); + gitRepo.commit(project, MSG); + } else { + log.info("NON-snapshot version [{}] found. Will commit the changed poms, tag the version and push the tag", version); + gitRepo.commit(project, PRE_RELEASE_MSG); + String tagName = "v" + version.version; + gitRepo.tag(project, tagName); + gitRepo.pushTag(project, tagName); + } + } + + GitRepo gitRepo(File workingDir) { + return new GitRepo(workingDir); + } +} diff --git a/spring-cloud-release-tools-core/src/main/java/org/springframework/cloud/release/internal/pom/PomReader.java b/spring-cloud-release-tools-core/src/main/java/org/springframework/cloud/release/internal/pom/PomReader.java index 4ad0e7a0..e81ce18f 100644 --- a/spring-cloud-release-tools-core/src/main/java/org/springframework/cloud/release/internal/pom/PomReader.java +++ b/spring-cloud-release-tools-core/src/main/java/org/springframework/cloud/release/internal/pom/PomReader.java @@ -32,7 +32,11 @@ class PomReader { /** * Returns a parsed POM */ - Model readPom(File pom) { + Model readPom(File file) { + File pom = file; + if (file.isDirectory()) { + pom = new File(file,"pom.xml"); + } try(Reader reader = new FileReader(pom)) { MavenXpp3Reader xpp3Reader = new MavenXpp3Reader(); return xpp3Reader.read(reader); diff --git a/spring-cloud-release-tools-core/src/main/java/org/springframework/cloud/release/internal/pom/ProjectUpdater.java b/spring-cloud-release-tools-core/src/main/java/org/springframework/cloud/release/internal/pom/ProjectUpdater.java index 312feb62..5272bce0 100644 --- a/spring-cloud-release-tools-core/src/main/java/org/springframework/cloud/release/internal/pom/ProjectUpdater.java +++ b/spring-cloud-release-tools-core/src/main/java/org/springframework/cloud/release/internal/pom/ProjectUpdater.java @@ -28,6 +28,7 @@ import java.nio.file.attribute.BasicFileAttributes; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.release.internal.ReleaserProperties; +import org.springframework.cloud.release.internal.git.GitRepo; /** * @author Marcin Grzejszczak @@ -38,7 +39,7 @@ public class ProjectUpdater { private final File destinationDir; private final ReleaserProperties properties; - private final GitProjectRepo gitProjectRepo; + private final GitRepo gitRepo; private final PomUpdater pomUpdater = new PomUpdater(); public ProjectUpdater(ReleaserProperties properties) { @@ -47,7 +48,7 @@ public class ProjectUpdater { new File(properties.getPom().getCloneDestinationDir()) : Files.createTempDirectory("releaser").toFile(); this.properties = properties; - this.gitProjectRepo = new GitProjectRepo(this.destinationDir); + this.gitRepo = new GitRepo(this.destinationDir); } catch (IOException e) { throw new IllegalStateException("Failed to create a temporary folder", e); @@ -61,9 +62,9 @@ public class ProjectUpdater { * @param projectRoot - root folder with project to update */ public void updateProject(File projectRoot) { - File clonedScRelease = this.gitProjectRepo.cloneProject( + File clonedScRelease = this.gitRepo.cloneProject( URI.create(this.properties.getPom().getSpringCloudReleaseGitUrl())); - this.gitProjectRepo.checkout(clonedScRelease, this.properties.getPom().getBranch()); + this.gitRepo.checkout(clonedScRelease, this.properties.getPom().getBranch()); SCReleasePomParser sCReleasePomParser = new SCReleasePomParser(clonedScRelease); Versions versions = sCReleasePomParser.allVersions(); log.info("Retrieved the following versions\n{}", versions); diff --git a/spring-cloud-release-tools-core/src/main/java/org/springframework/cloud/release/internal/pom/ProjectVersion.java b/spring-cloud-release-tools-core/src/main/java/org/springframework/cloud/release/internal/pom/ProjectVersion.java new file mode 100644 index 00000000..bb62f5c1 --- /dev/null +++ b/spring-cloud-release-tools-core/src/main/java/org/springframework/cloud/release/internal/pom/ProjectVersion.java @@ -0,0 +1,43 @@ +package org.springframework.cloud.release.internal.pom; + +import java.io.File; + +/** + * Object representing a root project's version. + * Knows how to provide a minor bumped version; + * + * @author Marcin Grzejszczak + */ +public class ProjectVersion { + + public static ProjectVersion NO_VERSION = new ProjectVersion(""); + + public final String version; + private final PomReader pomReader = new PomReader(); + + public ProjectVersion(String version) { + this.version = version; + } + + public ProjectVersion(File project) { + this.version = this.pomReader.readPom(project).getVersion(); + } + + public String bumpedVersion() { + // 1.0.0.BUILD-SNAPSHOT + String[] splitVersion = this.version.split("\\."); + if (splitVersion.length < 4) { + throw new IllegalStateException("Version is invalid. Should be of format [1.2.3.A]"); + } + Integer incrementedPatch = Integer.valueOf(splitVersion[2]) + 1; + return String.format("%s.%s.%s.%s", splitVersion[0], splitVersion[1], incrementedPatch, splitVersion[3]); + } + + public boolean isSnapshot() { + return this.version.contains("SNAPSHOT"); + } + + @Override public String toString() { + return this.version; + } +} diff --git a/spring-cloud-release-tools-core/src/test/java/org/springframework/cloud/release/internal/git/GitProjectRepoTests.java b/spring-cloud-release-tools-core/src/test/java/org/springframework/cloud/release/internal/git/GitProjectRepoTests.java new file mode 100644 index 00000000..e5eeb5bd --- /dev/null +++ b/spring-cloud-release-tools-core/src/test/java/org/springframework/cloud/release/internal/git/GitProjectRepoTests.java @@ -0,0 +1,217 @@ +package org.springframework.cloud.release.internal.git; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.nio.file.Files; +import java.util.List; + +import org.eclipse.jgit.api.CloneCommand; +import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.api.RemoteRemoveCommand; +import org.eclipse.jgit.api.RemoteSetUrlCommand; +import org.eclipse.jgit.api.errors.GitAPIException; +import org.eclipse.jgit.lib.Ref; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.transport.URIish; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.springframework.cloud.release.internal.pom.TestUtils; + +import static org.assertj.core.api.Assertions.fail; +import static org.assertj.core.api.BDDAssertions.then; +import static org.assertj.core.api.BDDAssertions.thenThrownBy; + +/** + * @author Marcin Grzejszczak + */ +public class GitProjectRepoTests { + + @Rule public TemporaryFolder tmp = new TemporaryFolder(); + File springCloudReleaseProject; + File tmpFolder; + GitRepo gitRepo; + + @Before + public void setup() throws IOException, URISyntaxException { + this.tmpFolder = this.tmp.newFolder(); + this.springCloudReleaseProject = new File(GitProjectRepoTests.class.getResource("/projects/spring-cloud-release").toURI()); + TestUtils.prepareLocalRepo(); + this.gitRepo = new GitRepo(this.tmpFolder); + } + + @Test + public void should_clone_the_project_from_a_given_location() throws IOException { + this.gitRepo.cloneProject(this.springCloudReleaseProject.toURI()); + + then(new File(this.tmpFolder, ".git")).exists(); + } + + @Test + public void should_throw_exception_when_there_is_no_repo() throws IOException, URISyntaxException { + thenThrownBy(() -> this.gitRepo + .cloneProject(GitProjectRepoTests.class.getResource("/projects/").toURI())) + .isInstanceOf(IllegalStateException.class) + .hasMessageContaining("Exception occurred while cloning repo"); + } + + @Test + public void should_throw_an_exception_when_failed_to_initialize_the_repo() throws IOException { + thenThrownBy(() -> new GitRepo(this.tmpFolder, new ExceptionThrowingJGitFactory()).cloneProject(this.springCloudReleaseProject.toURI())) + .isInstanceOf(IllegalStateException.class) + .hasMessageContaining("Exception occurred while cloning repo") + .hasCauseInstanceOf(CustomException.class); + } + + @Test + public void should_check_out_a_branch_on_cloned_repo() throws IOException { + File project = this.gitRepo.cloneProject(this.springCloudReleaseProject.toURI()); + this.gitRepo.checkout(project, "vCamden.SR3"); + + File pom = new File(this.tmpFolder, "pom.xml"); + then(pom).exists(); + then(Files.lines(pom.toPath()).anyMatch(s -> s.contains("Camden.SR3"))).isTrue(); + } + + @Test + public void should_check_out_a_branch_on_cloned_repo2() throws IOException { + File project = this.gitRepo.cloneProject(this.springCloudReleaseProject.toURI()); + this.gitRepo.checkout(project, "Camden.x"); + + File pom = new File(this.tmpFolder, "pom.xml"); + then(pom).exists(); + then(Files.lines(pom.toPath()).anyMatch(s -> s.contains("Camden.BUILD-SNAPSHOT"))).isTrue(); + } + + @Test + public void should_throw_an_exception_when_checking_out_nonexisting_branch() throws IOException { + File project = this.gitRepo.cloneProject(this.springCloudReleaseProject.toURI()); + try { + this.gitRepo.checkout(project, "nonExistingBranch"); + fail("should throw an exception"); + } catch (IllegalStateException e) { + then(e).hasMessageContaining("Ref nonExistingBranch can not be resolved"); + } + } + + @Test + public void should_commit_changes() throws Exception { + File project = this.gitRepo.cloneProject(this.springCloudReleaseProject.toURI()); + createNewFile(project); + + this.gitRepo.commit(project, "some message"); + + try(Git git = openGitProject(project)) { + RevCommit revCommit = git.log().call().iterator().next(); + then(revCommit.getShortMessage()).isEqualTo("some message"); + } + } + + @Test + public void should_create_a_tag() throws Exception { + File project = this.gitRepo.cloneProject(this.springCloudReleaseProject.toURI()); + createNewFile(project); + this.gitRepo.commit(project, "some message"); + + this.gitRepo.tag(project, "v1.0.0"); + + try(Git git = openGitProject(project)) { + tagIsPresent(git, "v1.0.0"); + } + } + + private void tagIsPresent(Git git, String tag) throws GitAPIException { + List refs = git.tagList().call(); + System.out.println("All tags" + refs); + then(refs.stream().anyMatch(ref -> ref.getName().startsWith("refs/tags/" + tag))).isTrue(); + } + + @Test + public void should_push_changes_to_master_branch() throws Exception { + File origin = clonedProject(); + File project = this.gitRepo.cloneProject(this.springCloudReleaseProject.toURI()); + setOriginOnProjectToTmp(origin, project); + createNewFile(project); + this.gitRepo.commit(project, "some message"); + + this.gitRepo.pushBranch(project, "master"); + + try(Git git = openGitProject(origin)) { + RevCommit revCommit = git.log().call().iterator().next(); + then(revCommit.getShortMessage()).isEqualTo("some message"); + } + } + + @Test + public void should_push_a_tag_to_new_branch_in_origin() throws Exception { + File origin = clonedProject(); + File project = this.gitRepo.cloneProject(this.springCloudReleaseProject.toURI()); + setOriginOnProjectToTmp(origin, project); + createNewFile(project); + this.gitRepo.commit(project, "some message"); + this.gitRepo.tag(project, "v5.6.7.RELEASE"); + + this.gitRepo.pushTag(project, "v5.6.7.RELEASE"); + + try(Git git = openGitProject(origin)) { + tagIsPresent(git, "v5.6.7"); + git.checkout().setName("v5.6.7.RELEASE").call(); + RevCommit revCommit = git.log().call().iterator().next(); + then(revCommit.getShortMessage()).isEqualTo("some message"); + } + } + + private void setOriginOnProjectToTmp(File origin, File project) + throws GitAPIException, MalformedURLException { + try(Git git = openGitProject(project)) { + RemoteRemoveCommand remove = git.remoteRemove(); + remove.setName("origin"); + remove.call(); + RemoteSetUrlCommand command = git.remoteSetUrl(); + command.setUri(new URIish(origin.toURI().toURL())); + command.setName("origin"); + command.setPush(true); + command.call(); + } + } + + private Git openGitProject(File project) { + return new GitRepo.JGitFactory().open(project); + } + + private File clonedProject() throws IOException { + File anotherFolder = this.tmp.newFolder(); + GitRepo projectRepo = new GitRepo(anotherFolder); + projectRepo.cloneProject(this.springCloudReleaseProject.toURI()); + return anotherFolder; + } + + private void createNewFile(File project) throws Exception { + File newFile = new File(this.tmpFolder, "newFile"); + newFile.createNewFile(); + try (PrintStream out = new PrintStream(new FileOutputStream(newFile))) { + out.print("foo"); + } + try(Git git = openGitProject(project)) { + git.add().addFilepattern("newFile").call(); + } + } + +} + +class ExceptionThrowingJGitFactory extends GitRepo.JGitFactory { + @Override CloneCommand getCloneCommandByCloneRepository() { + throw new CustomException("foo"); + } +} + +class CustomException extends RuntimeException { + public CustomException(String message) { + super(message); + } +} \ No newline at end of file diff --git a/spring-cloud-release-tools-core/src/test/java/org/springframework/cloud/release/internal/git/ProjectGitUpdaterTests.java b/spring-cloud-release-tools-core/src/test/java/org/springframework/cloud/release/internal/git/ProjectGitUpdaterTests.java new file mode 100644 index 00000000..c1db8874 --- /dev/null +++ b/spring-cloud-release-tools-core/src/test/java/org/springframework/cloud/release/internal/git/ProjectGitUpdaterTests.java @@ -0,0 +1,47 @@ +package org.springframework.cloud.release.internal.git; + +import java.io.File; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.springframework.cloud.release.internal.pom.ProjectVersion; + +import static org.mockito.BDDMockito.then; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.never; + +/** + * @author Marcin Grzejszczak + */ +@RunWith(MockitoJUnitRunner.class) +public class ProjectGitUpdaterTests { + + @Mock GitRepo gitRepo; + ProjectGitUpdater updater = new ProjectGitUpdater() { + @Override GitRepo gitRepo(File workingDir) { + return ProjectGitUpdaterTests.this.gitRepo; + } + }; + File file = new File(""); + + @Test + public void should_only_commit_without_pushing_changes_when_version_is_snapshot() { + this.updater.commitAndTagIfApplicable(file, new ProjectVersion("1.0.0.BUILD-SNAPSHOT")); + + then(this.gitRepo).should().commit(any(File.class), anyString()); + then(this.gitRepo).should(never()).tag(any(File.class), anyString()); + } + + @Test + public void should_commit_tag_and_push_tag_when_version_is_not_snapshot() { + this.updater.commitAndTagIfApplicable(file, new ProjectVersion("1.0.0.RELEASE")); + + then(this.gitRepo).should().commit(any(File.class), anyString()); + then(this.gitRepo).should().tag(any(File.class), eq("v1.0.0.RELEASE")); + then(this.gitRepo).should().pushTag(any(File.class), eq("v1.0.0.RELEASE")); + } +} \ No newline at end of file diff --git a/spring-cloud-release-tools-core/src/test/java/org/springframework/cloud/release/internal/pom/GitProjectRepoTests.java b/spring-cloud-release-tools-core/src/test/java/org/springframework/cloud/release/internal/pom/GitProjectRepoTests.java deleted file mode 100644 index 5cdd1c41..00000000 --- a/spring-cloud-release-tools-core/src/test/java/org/springframework/cloud/release/internal/pom/GitProjectRepoTests.java +++ /dev/null @@ -1,102 +0,0 @@ -package org.springframework.cloud.release.internal.pom; - -import java.io.File; -import java.io.IOException; -import java.net.URISyntaxException; -import java.nio.file.Files; - -import org.eclipse.jgit.api.CloneCommand; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; - -import static org.assertj.core.api.Assertions.fail; -import static org.assertj.core.api.BDDAssertions.then; -import static org.assertj.core.api.BDDAssertions.thenThrownBy; - -/** - * @author Marcin Grzejszczak - */ -public class GitProjectRepoTests { - - @Rule public TemporaryFolder tmp = new TemporaryFolder(); - File springCloudReleaseProject; - File tmpFolder; - GitProjectRepo gitProjectRepo; - - @Before - public void setup() throws IOException, URISyntaxException { - this.tmpFolder = this.tmp.newFolder(); - this.springCloudReleaseProject = new File(GitProjectRepoTests.class.getResource("/projects/spring-cloud-release").toURI()); - TestUtils.prepareLocalRepo(); - this.gitProjectRepo = new GitProjectRepo(this.tmpFolder); - } - - @Test - public void should_clone_the_project_from_a_given_location() throws IOException { - this.gitProjectRepo.cloneProject(this.springCloudReleaseProject.toURI()); - - then(new File(this.tmpFolder, ".git")).exists(); - } - - @Test - public void should_throw_exception_when_there_is_no_repo() throws IOException, URISyntaxException { - thenThrownBy(() -> this.gitProjectRepo - .cloneProject(GitProjectRepoTests.class.getResource("/projects/").toURI())) - .isInstanceOf(IllegalStateException.class) - .hasMessageContaining("Exception occurred while cloning repo"); - } - - @Test - public void should_throw_an_exception_when_failed_to_initialize_the_repo() throws IOException { - thenThrownBy(() -> new GitProjectRepo(this.tmpFolder, new ExceptionThrowingJGitFactory()).cloneProject(this.springCloudReleaseProject.toURI())) - .isInstanceOf(IllegalStateException.class) - .hasMessageContaining("Exception occurred while cloning repo") - .hasCauseInstanceOf(CustomException.class); - } - - @Test - public void should_check_out_a_branch_on_cloned_repo() throws IOException { - File project = this.gitProjectRepo.cloneProject(this.springCloudReleaseProject.toURI()); - this.gitProjectRepo.checkout(project, "vCamden.SR3"); - - File pom = new File(this.tmpFolder, "pom.xml"); - then(pom).exists(); - then(Files.lines(pom.toPath()).anyMatch(s -> s.contains("Camden.SR3"))).isTrue(); - } - - @Test - public void should_check_out_a_branch_on_cloned_repo2() throws IOException { - File project = this.gitProjectRepo.cloneProject(this.springCloudReleaseProject.toURI()); - this.gitProjectRepo.checkout(project, "Camden.x"); - - File pom = new File(this.tmpFolder, "pom.xml"); - then(pom).exists(); - then(Files.lines(pom.toPath()).anyMatch(s -> s.contains("Camden.BUILD-SNAPSHOT"))).isTrue(); - } - - @Test - public void should_throw_an_exception_when_checking_out_nonexisting_branch() throws IOException { - File project = this.gitProjectRepo.cloneProject(this.springCloudReleaseProject.toURI()); - try { - this.gitProjectRepo.checkout(project, "nonExistingBranch"); - fail("should throw an exception"); - } catch (IllegalStateException e) { - then(e).hasMessageContaining("Ref nonExistingBranch can not be resolved"); - } - } - -} - -class ExceptionThrowingJGitFactory extends GitProjectRepo.JGitFactory { - @Override CloneCommand getCloneCommandByCloneRepository() { - throw new CustomException("foo"); - } -} - -class CustomException extends RuntimeException { - public CustomException(String message) { - super(message); - } -} \ No newline at end of file diff --git a/spring-cloud-release-tools-core/src/test/java/org/springframework/cloud/release/internal/pom/PomReaderTests.java b/spring-cloud-release-tools-core/src/test/java/org/springframework/cloud/release/internal/pom/PomReaderTests.java index 59c5e1c1..cc869596 100644 --- a/spring-cloud-release-tools-core/src/test/java/org/springframework/cloud/release/internal/pom/PomReaderTests.java +++ b/spring-cloud-release-tools-core/src/test/java/org/springframework/cloud/release/internal/pom/PomReaderTests.java @@ -16,9 +16,6 @@ package org.springframework.cloud.release.internal.pom; -import static org.assertj.core.api.BDDAssertions.then; -import static org.assertj.core.api.BDDAssertions.thenThrownBy; - import java.io.File; import java.io.IOException; import java.net.URI; @@ -28,6 +25,10 @@ import org.apache.maven.model.Model; import org.codehaus.plexus.util.xml.pull.XmlPullParserException; import org.junit.Before; import org.junit.Test; +import org.springframework.cloud.release.internal.git.GitProjectRepoTests; + +import static org.assertj.core.api.BDDAssertions.then; +import static org.assertj.core.api.BDDAssertions.thenThrownBy; /** * @author Marcin Grzejszczak @@ -35,18 +36,28 @@ import org.junit.Test; public class PomReaderTests { PomReader pomReader = new PomReader(); + File springCloudReleaseProjectPom; File springCloudReleaseProject; File licenseFile; @Before public void setup() throws URISyntaxException { URI scRelease = GitProjectRepoTests.class.getResource("/projects/spring-cloud-release").toURI(); - this.springCloudReleaseProject = new File(scRelease.getPath(), "pom.xml"); + this.springCloudReleaseProject = new File(scRelease); + this.springCloudReleaseProjectPom = new File(scRelease.getPath(), "pom.xml"); this.licenseFile = new File(scRelease.getPath(), "LICENSE.txt"); } @Test public void should_parse_a_valid_pom() { + Model pom = this.pomReader.readPom(this.springCloudReleaseProjectPom); + + then(pom).isNotNull(); + then(pom.getArtifactId()).isEqualTo("spring-cloud-starter-build"); + } + + @Test + public void should_parse_a_valid_pom_when_passing_direcory() { Model pom = this.pomReader.readPom(this.springCloudReleaseProject); then(pom).isNotNull(); diff --git a/spring-cloud-release-tools-core/src/test/java/org/springframework/cloud/release/internal/pom/PomUpdaterTests.java b/spring-cloud-release-tools-core/src/test/java/org/springframework/cloud/release/internal/pom/PomUpdaterTests.java index 8209eff5..021d6bde 100644 --- a/spring-cloud-release-tools-core/src/test/java/org/springframework/cloud/release/internal/pom/PomUpdaterTests.java +++ b/spring-cloud-release-tools-core/src/test/java/org/springframework/cloud/release/internal/pom/PomUpdaterTests.java @@ -30,6 +30,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.springframework.boot.test.rule.OutputCapture; +import org.springframework.cloud.release.internal.git.GitProjectRepoTests; import org.springframework.util.FileSystemUtils; /** diff --git a/spring-cloud-release-tools-core/src/test/java/org/springframework/cloud/release/internal/pom/ProjectVersionTests.java b/spring-cloud-release-tools-core/src/test/java/org/springframework/cloud/release/internal/pom/ProjectVersionTests.java new file mode 100644 index 00000000..60d2711c --- /dev/null +++ b/spring-cloud-release-tools-core/src/test/java/org/springframework/cloud/release/internal/pom/ProjectVersionTests.java @@ -0,0 +1,64 @@ +package org.springframework.cloud.release.internal.pom; + +import java.io.File; +import java.net.URI; +import java.net.URISyntaxException; + +import org.junit.Before; +import org.junit.Test; +import org.springframework.cloud.release.internal.git.GitProjectRepoTests; + +import static org.assertj.core.api.BDDAssertions.then; +import static org.assertj.core.api.BDDAssertions.thenThrownBy; + +/** + * @author Marcin Grzejszczak + */ +public class ProjectVersionTests { + + File springCloudReleaseProject; + + @Before + public void setup() throws URISyntaxException { + URI scRelease = GitProjectRepoTests.class.getResource("/projects/spring-cloud-release").toURI(); + this.springCloudReleaseProject = new File(scRelease.getPath(), "pom.xml"); + } + + @Test + public void should_build_version_from_file() { + ProjectVersion projectVersion = new ProjectVersion(this.springCloudReleaseProject); + + then(projectVersion.version).isEqualTo("Dalston.BUILD-SNAPSHOT"); + } + + @Test + public void should_throw_exception_if_version_is_not_long_enough() { + String version = "1.0"; + + thenThrownBy(() -> new ProjectVersion(version).bumpedVersion()) + .isInstanceOf(IllegalStateException.class) + .hasMessageContaining("Version is invalid"); + } + + @Test + public void should_bump_version_by_patch_version() { + String version = "1.0.1.BUILD-SNAPSHOT"; + + then(new ProjectVersion(version).bumpedVersion()).isEqualTo("1.0.2.BUILD-SNAPSHOT"); + } + + @Test + public void should_return_true_for_snapshot_version() { + String version = "1.0.1.BUILD-SNAPSHOT"; + + then(new ProjectVersion(version).isSnapshot()).isTrue(); + } + + @Test + public void should_return_false_for_snapshot_version() { + String version = "1.0.1.RELEASE"; + + then(new ProjectVersion(version).isSnapshot()).isFalse(); + } + +} \ No newline at end of file diff --git a/spring-cloud-release-tools-core/src/test/java/org/springframework/cloud/release/internal/pom/SCReleasePomParserTests.java b/spring-cloud-release-tools-core/src/test/java/org/springframework/cloud/release/internal/pom/SCReleasePomParserTests.java index 64db70ef..3ca488fe 100644 --- a/spring-cloud-release-tools-core/src/test/java/org/springframework/cloud/release/internal/pom/SCReleasePomParserTests.java +++ b/spring-cloud-release-tools-core/src/test/java/org/springframework/cloud/release/internal/pom/SCReleasePomParserTests.java @@ -6,6 +6,7 @@ import java.net.URISyntaxException; import org.junit.Before; import org.junit.Test; +import org.springframework.cloud.release.internal.git.GitProjectRepoTests; import static org.assertj.core.api.BDDAssertions.then; import static org.assertj.core.api.BDDAssertions.thenThrownBy; diff --git a/spring-cloud-release-tools-spring/src/main/java/org/springframework/cloud/release/spring/ReleaserConfiguration.java b/spring-cloud-release-tools-spring/src/main/java/org/springframework/cloud/release/spring/ReleaserConfiguration.java index 5c4d54a9..eec26c65 100644 --- a/spring-cloud-release-tools-spring/src/main/java/org/springframework/cloud/release/spring/ReleaserConfiguration.java +++ b/spring-cloud-release-tools-spring/src/main/java/org/springframework/cloud/release/spring/ReleaserConfiguration.java @@ -19,6 +19,7 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties import org.springframework.cloud.release.internal.Releaser; import org.springframework.cloud.release.internal.ReleaserProperties; import org.springframework.cloud.release.internal.builder.ProjectBuilder; +import org.springframework.cloud.release.internal.git.ProjectGitUpdater; import org.springframework.cloud.release.internal.pom.ProjectUpdater; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -28,6 +29,7 @@ import org.springframework.context.annotation.Configuration; class ReleaserConfiguration { @Bean Releaser releaser(ReleaserProperties properties) { - return new Releaser(properties, new ProjectUpdater(properties), new ProjectBuilder(properties)); + return new Releaser(properties, new ProjectUpdater(properties), + new ProjectBuilder(properties), new ProjectGitUpdater()); } }