diff --git a/pom.xml b/pom.xml
index f4512614..e61e388a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -31,6 +31,11 @@
org.eclipse.jgit
4.6.0.201612231935-r
+
+ org.apache.maven
+ maven-model
+ 3.3.9
+
@@ -43,6 +48,10 @@
org.eclipse.jgit
org.eclipse.jgit
+
+ org.apache.maven
+ maven-model
+
org.springframework.boot
diff --git a/src/main/java/org/springframework/cloud/release/PomParser.java b/src/main/java/org/springframework/cloud/release/PomParser.java
new file mode 100644
index 00000000..26072798
--- /dev/null
+++ b/src/main/java/org/springframework/cloud/release/PomParser.java
@@ -0,0 +1,73 @@
+package org.springframework.cloud.release;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.lang.invoke.MethodHandles;
+
+import org.apache.maven.model.Model;
+import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
+import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Parses the poms for a given project and populates versions.
+ *
+ * @author Marcin Grzejszczak
+ */
+class PomParser {
+
+ private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+ private static final String STARTER_POM = "spring-cloud-starter-parent/pom.xml";
+ private static final String BOOT_STARTER_ARTIFACTID = "spring-boot-starter-parent";
+ private static final String DEPENDENCIES_POM = "spring-cloud-dependencies/pom.xml";
+
+ private final File projectRootDir;
+ private final String bootPom;
+ private final PomReader pomReader = new PomReader();
+
+ PomParser(File projectRootDir) {
+ this(projectRootDir, STARTER_POM);
+ }
+
+ PomParser(File projectRootDir, String bootPom) {
+ this.projectRootDir = projectRootDir;
+ this.bootPom = bootPom;
+ }
+
+ String bootVersion() {
+ File bootPom = new File(this.projectRootDir, this.bootPom);
+ if (!bootPom.exists()) {
+ throw new IllegalStateException("Pom with boot version is not present");
+ }
+ Model model = this.pomReader.readPom(bootPom);
+ String bootArtifactId = model.getParent().getArtifactId();
+ if (log.isDebugEnabled()) {
+ log.debug("Boot artifact id is equal to [{}]", bootArtifactId);
+ }
+ if (!BOOT_STARTER_ARTIFACTID.equals(bootArtifactId)) {
+ throw new IllegalStateException("The pom doesn't have a boot version");
+ }
+ String bootVersion = model.getParent().getVersion();
+ if (log.isDebugEnabled()) {
+ log.debug("Boot version is equal to [{}]", bootVersion);
+ }
+ return bootVersion;
+ }
+}
+
+class PomReader {
+
+ Model readPom(File pom) {
+ try(Reader reader = new FileReader(pom)) {
+ MavenXpp3Reader xpp3Reader = new MavenXpp3Reader();
+ return xpp3Reader.read(reader);
+ }
+ catch (XmlPullParserException | IOException e) {
+ throw new IllegalStateException("Failed to read file", e);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/springframework/cloud/release/ProjectCloner.java b/src/main/java/org/springframework/cloud/release/ProjectCloner.java
deleted file mode 100644
index 98582a2e..00000000
--- a/src/main/java/org/springframework/cloud/release/ProjectCloner.java
+++ /dev/null
@@ -1,90 +0,0 @@
-package org.springframework.cloud.release;
-
-import java.io.File;
-import java.io.IOException;
-import java.lang.invoke.MethodHandles;
-import java.net.URI;
-
-import org.eclipse.jgit.api.CloneCommand;
-import org.eclipse.jgit.api.Git;
-import org.eclipse.jgit.api.errors.GitAPIException;
-import org.eclipse.jgit.util.FileUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * @author Marcin Grzejszczak
- */
-class ProjectCloner {
-
- private static final Logger log = LoggerFactory
- .getLogger(MethodHandles.lookup().lookupClass());
-
- private final ProjectCloner.JGitFactory gitFactory;
-
- private final File basedir;
-
- ProjectCloner(File basedir) {
- this.basedir = basedir;
- this.gitFactory = new ProjectCloner.JGitFactory();
- }
-
- ProjectCloner(File basedir, ProjectCloner.JGitFactory factory) {
- this.basedir = basedir;
- this.gitFactory = factory;
- }
-
- File cloneProject(URI projectUrl) {
- try {
- if (log.isDebugEnabled()) {
- log.debug("Cloning repo from [{}] to [{}]", projectUrl, this.basedir);
- }
- Git git = cloneToBasedir(projectUrl, this.basedir);
- if (git != null) {
- git.close();
- }
- File clonedRepo = git.getRepository().getDirectory();
- if (log.isDebugEnabled()) {
- log.debug("Cloned repo to [{}]", clonedRepo);
- }
- return clonedRepo;
- }
- catch (Exception e) {
- throw new IllegalStateException(e);
- }
- }
-
- private Git cloneToBasedir(URI projectUrl, File destinationFolder)
- throws GitAPIException {
- CloneCommand clone = this.gitFactory.getCloneCommandByCloneRepository()
- .setURI(projectUrl.toString() + ".git").setDirectory(destinationFolder);
- try {
- return clone.call();
- }
- catch (GitAPIException e) {
- deleteBaseDirIfExists();
- throw e;
- }
- }
-
- private void deleteBaseDirIfExists() {
- if (this.basedir.exists()) {
- try {
- FileUtils.delete(this.basedir, FileUtils.RECURSIVE);
- }
- catch (IOException e) {
- throw new IllegalStateException("Failed to initialize base directory", e);
- }
- }
- }
-
- /**
- * Wraps the static method calls to {@link org.eclipse.jgit.api.Git} and
- * {@link org.eclipse.jgit.api.CloneCommand} allowing for easier unit testing.
- */
- static class JGitFactory {
- CloneCommand getCloneCommandByCloneRepository() {
- return Git.cloneRepository();
- }
- }
-}
\ No newline at end of file
diff --git a/src/main/java/org/springframework/cloud/release/ProjectRepo.java b/src/main/java/org/springframework/cloud/release/ProjectRepo.java
new file mode 100644
index 00000000..a27d32a8
--- /dev/null
+++ b/src/main/java/org/springframework/cloud/release/ProjectRepo.java
@@ -0,0 +1,144 @@
+package org.springframework.cloud.release;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+import java.net.URI;
+
+import org.eclipse.jgit.api.CheckoutCommand;
+import org.eclipse.jgit.api.CloneCommand;
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.util.FileUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Abstraction over a Git repo. Can clonea repo from a given location
+ * and check its branch.
+ *
+ * @author Marcin Grzejszczak
+ */
+class ProjectRepo {
+
+ private static final Logger log = LoggerFactory
+ .getLogger(MethodHandles.lookup().lookupClass());
+
+ private final ProjectRepo.JGitFactory gitFactory;
+
+ private final File basedir;
+
+ ProjectRepo(File basedir) {
+ this.basedir = basedir;
+ this.gitFactory = new ProjectRepo.JGitFactory();
+ }
+
+ ProjectRepo(File basedir, ProjectRepo.JGitFactory factory) {
+ this.basedir = basedir;
+ this.gitFactory = factory;
+ }
+
+ /**
+ * Clones the project
+ * @param projectUri - URI of the project
+ * @return file where the project was cloned
+ */
+ File cloneProject(URI projectUri) {
+ try {
+ if (log.isDebugEnabled()) {
+ log.debug("Cloning repo from [{}] to [{}]", projectUri, this.basedir);
+ }
+ Git git = cloneToBasedir(projectUri, this.basedir);
+ if (git != null) {
+ git.close();
+ }
+ File clonedRepo = git.getRepository().getDirectory();
+ if (log.isDebugEnabled()) {
+ log.debug("Cloned repo to [{}]", clonedRepo);
+ }
+ return clonedRepo;
+ }
+ catch (Exception e) {
+ throw new IllegalStateException("Exception occurred while cloning repo", e);
+ }
+ }
+
+ /**
+ * Checks out a branch for a project
+ * @param project - a Git project
+ * @param branch - branch to check out
+ */
+ void checkout(File project, String branch) {
+ try {
+ if (log.isDebugEnabled()) {
+ log.debug("Checking out branch [{}] for repo [{}] to [{}]", this.basedir, branch);
+ }
+ checkoutBranch(project, branch);
+ if (log.isDebugEnabled()) {
+ log.debug("Successfully checked out the branch [{}]", branch);
+ }
+ }
+ catch (Exception e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ private Git cloneToBasedir(URI projectUrl, File destinationFolder)
+ throws GitAPIException {
+ CloneCommand command = this.gitFactory.getCloneCommandByCloneRepository()
+ .setURI(projectUrl.toString() + ".git").setDirectory(destinationFolder);
+ try {
+ return command.call();
+ }
+ catch (GitAPIException e) {
+ deleteBaseDirIfExists();
+ throw e;
+ }
+ }
+
+ private Ref checkoutBranch(File projectDir, String branch)
+ throws GitAPIException {
+ Git git = this.gitFactory.open(projectDir);
+ CheckoutCommand command = git.checkout().setName(branch);
+ try {
+ return command.call();
+ }
+ catch (GitAPIException e) {
+ deleteBaseDirIfExists();
+ throw e;
+ } finally {
+ git.close();
+ }
+ }
+
+ private void deleteBaseDirIfExists() {
+ if (this.basedir.exists()) {
+ try {
+ FileUtils.delete(this.basedir, FileUtils.RECURSIVE);
+ }
+ catch (IOException e) {
+ throw new IllegalStateException("Failed to initialize base directory", e);
+ }
+ }
+ }
+
+ /**
+ * Wraps the static method calls to {@link org.eclipse.jgit.api.Git} and
+ * {@link org.eclipse.jgit.api.CloneCommand} allowing for easier unit testing.
+ */
+ static class JGitFactory {
+ CloneCommand getCloneCommandByCloneRepository() {
+ return Git.cloneRepository();
+ }
+
+ Git open(File file) {
+ try {
+ return Git.open(file);
+ }
+ catch (IOException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/springframework/cloud/release/Versions.java b/src/main/java/org/springframework/cloud/release/Versions.java
new file mode 100644
index 00000000..45ec55b0
--- /dev/null
+++ b/src/main/java/org/springframework/cloud/release/Versions.java
@@ -0,0 +1,16 @@
+package org.springframework.cloud.release;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Represents versions taken out from Spring Cloud Release pom
+ *
+ * @author Marcin Grzejszczak
+ */
+class Versions {
+
+ String boot;
+ String build;
+ Map projects = new HashMap<>();
+}
\ No newline at end of file
diff --git a/src/test/java/org/springframework/cloud/release/PomParserTests.java b/src/test/java/org/springframework/cloud/release/PomParserTests.java
new file mode 100644
index 00000000..cf175c8f
--- /dev/null
+++ b/src/test/java/org/springframework/cloud/release/PomParserTests.java
@@ -0,0 +1,51 @@
+package org.springframework.cloud.release;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URISyntaxException;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.assertj.core.api.BDDAssertions.then;
+import static org.assertj.core.api.BDDAssertions.thenThrownBy;
+
+/**
+ * @author Marcin Grzejszczak
+ */
+public class PomParserTests {
+
+ File springCloudReleaseProject;
+
+ @Before
+ public void setup() throws IOException, URISyntaxException {
+ this.springCloudReleaseProject = new File(ProjectClonerTests.class.getResource("/projects/spring-cloud-release").toURI());
+ }
+
+ @Test
+ public void should_throw_exception_when_boot_pom_is_missing() {
+ PomParser parser = new PomParser(new File("."));
+
+ thenThrownBy(parser::bootVersion)
+ .isInstanceOf(IllegalStateException.class)
+ .hasMessageContaining("Pom with boot version is not present");
+ }
+
+ @Test
+ public void should_throw_exception_when_boot_version_is_missing_in_pom() {
+ PomParser parser = new PomParser(this.springCloudReleaseProject, "pom.xml");
+
+ thenThrownBy(parser::bootVersion)
+ .isInstanceOf(IllegalStateException.class)
+ .hasMessageContaining("The pom doesn't have a boot version");
+ }
+
+ @Test
+ public void should_populate_boot_version() {
+ PomParser parser = new PomParser(this.springCloudReleaseProject);
+
+ String bootVersion = parser.bootVersion();
+
+ then(bootVersion).isEqualTo("1.5.1.BUILD-SNAPSHOT");
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/org/springframework/cloud/release/ProjectClonerTests.java b/src/test/java/org/springframework/cloud/release/ProjectClonerTests.java
index 7c64aa23..2a793227 100644
--- a/src/test/java/org/springframework/cloud/release/ProjectClonerTests.java
+++ b/src/test/java/org/springframework/cloud/release/ProjectClonerTests.java
@@ -3,6 +3,7 @@ package org.springframework.cloud.release;
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;
@@ -10,7 +11,9 @@ 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
@@ -20,38 +23,69 @@ public class ProjectClonerTests {
@Rule public TemporaryFolder tmp = new TemporaryFolder();
File springCloudReleaseProject;
File tmpFolder;
- ProjectCloner projectCloner;
+ ProjectRepo projectRepo;
@Before
public void setup() throws IOException, URISyntaxException {
this.tmpFolder = this.tmp.newFolder();
this.springCloudReleaseProject = new File(ProjectClonerTests.class.getResource("/projects/spring-cloud-release").toURI());
TestUtils.prepareLocalRepo();
- this.projectCloner = new ProjectCloner(this.tmpFolder);
+ this.projectRepo = new ProjectRepo(this.tmpFolder);
}
@Test
public void should_clone_the_project_from_a_given_location() throws IOException {
- this.projectCloner.cloneProject(this.springCloudReleaseProject.toURI());
+ this.projectRepo.cloneProject(this.springCloudReleaseProject.toURI());
then(new File(this.tmpFolder, ".git")).exists();
}
- @Test(expected = IllegalStateException.class)
- public void should_throw_exception_when_there_is_no_repo()
- throws IOException, URISyntaxException {
- this.projectCloner.cloneProject(ProjectClonerTests.class.getResource("/projects/").toURI());
+ @Test
+ public void should_throw_exception_when_there_is_no_repo() throws IOException, URISyntaxException {
+ thenThrownBy(() -> this.projectRepo.cloneProject(ProjectClonerTests.class.getResource("/projects/").toURI()))
+ .isInstanceOf(IllegalStateException.class)
+ .hasMessageContaining("Exception occurred while cloning repo");
}
- @Test(expected = IllegalStateException.class)
+ @Test
public void should_throw_an_exception_when_failed_to_initialize_the_repo() throws IOException {
- new ProjectCloner(this.tmpFolder, new ExceptionThrowingJGitFactory()).cloneProject(this.springCloudReleaseProject.toURI());
+ thenThrownBy(() -> new ProjectRepo(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.projectRepo.cloneProject(this.springCloudReleaseProject.toURI());
+ this.projectRepo.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_throw_an_exception_when_checking_out_nonexisting_branch() throws IOException {
+ File project = this.projectRepo.cloneProject(this.springCloudReleaseProject.toURI());
+ try {
+ this.projectRepo.checkout(project, "nonExistingBranch");
+ fail("should throw an exception");
+ } catch (IllegalStateException e) {
+ then(e).hasMessageContaining("Ref nonExistingBranch can not be resolved");
+ }
}
}
-class ExceptionThrowingJGitFactory extends ProjectCloner.JGitFactory {
+class ExceptionThrowingJGitFactory extends ProjectRepo.JGitFactory {
@Override CloneCommand getCloneCommandByCloneRepository() {
- throw new RuntimeException("foo");
+ throw new CustomException("foo");
+ }
+}
+
+class CustomException extends RuntimeException {
+ public CustomException(String message) {
+ super(message);
}
}
\ No newline at end of file