Added boot version retrieval

This commit is contained in:
Marcin Grzejszczak
2017-03-06 14:42:30 +01:00
parent e01aa4f1a2
commit bca1d2bbb4
7 changed files with 338 additions and 101 deletions

View File

@@ -31,6 +31,11 @@
<artifactId>org.eclipse.jgit</artifactId>
<version>4.6.0.201612231935-r</version>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-model</artifactId>
<version>3.3.9</version>
</dependency>
</dependencies>
</dependencyManagement>
@@ -43,6 +48,10 @@
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit</artifactId>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-model</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>

View File

@@ -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);
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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);
}
}
}
}

View File

@@ -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<String, String> projects = new HashMap<>();
}

View File

@@ -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");
}
}

View File

@@ -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("<version>Camden.SR3</version>"))).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);
}
}