diff --git a/sonarReview/build.sbt b/sonarReview/build.sbt index 018f85584a85468a989607a099ff9f06b1763851..81788d212406c663ed7ae3bb905f50be79ff377c 100644 --- a/sonarReview/build.sbt +++ b/sonarReview/build.sbt @@ -30,7 +30,7 @@ libraryDependencies += "org.agse" % "gitclient_2.10" % "0.0.1" libraryDependencies += "com.typesafe" % "config" % "1.2.1" -libraryDependencies += "org.specs2" % "specs2_2.10" % "2.4.16" +libraryDependencies += "org.specs2" % "specs2_2.10" % "2.4.16" % "test" resolvers ++= Seq( "Scalaz Bintray Repo" at "http://dl.bintray.com/scalaz/releases", diff --git a/sonarReview/src/main/scala/GerritRestClient.scala b/sonarReview/src/main/scala/GerritRestClient.scala index b3ee861464478c08deeea90049cdcde72e07a96a..8d2e5e0321058c5415f8cea47f02ea0abbdb94ff 100644 --- a/sonarReview/src/main/scala/GerritRestClient.scala +++ b/sonarReview/src/main/scala/GerritRestClient.scala @@ -13,15 +13,16 @@ import duration._ import scala.xml._ import scala.util.{ Success, Failure } -object GerritRestClient extends LazyLogging { +import com.typesafe.config.Config + +class GerritRestClient(conf: Config) extends LazyLogging { - private val conf = XML.load("conf.xml") //required - private val defaultHost = (conf \ "host").text - private val defaultUser = (conf \ "user").text - private val defaultPass = (conf \ "pass").text + private val defaultHost = conf.getString("gerrit.host") + private val defaultUser = conf.getString("gerrit.user") + private val defaultPass = conf.getString("gerrit.pass") //optional - private val defaultPort = (conf \ "port").text + private val defaultPort = if (conf.hasPath("gerrit.port")) conf.getString("gerrit.port") else "" def setReview(changeId: String, patchsetRevision: String, comments: String, host: String = defaultHost, diff --git a/sonarReview/src/main/scala/IssuesParser.scala b/sonarReview/src/main/scala/IssuesParser.scala index 16c4defb3f3eb40250ffcbc30ea03d1bb9df24fa..8d6cbe7fcaa7a264c61aefca0bfab341d93e1f79 100644 --- a/sonarReview/src/main/scala/IssuesParser.scala +++ b/sonarReview/src/main/scala/IssuesParser.scala @@ -4,6 +4,7 @@ import com.typesafe.scalalogging.slf4j._ import net.liftweb.json._ import JsonDSL._ import scala.xml._ +import com.typesafe.config.Config /** * Parses Sonar Issues Reports. @@ -11,7 +12,7 @@ import scala.xml._ * https://github.com/SonarCommunity/sonar-issues-report/blob/1b38791/src/main/resources/org/sonar/issuesreport/printer/html/issuesreport.ftl */ -class IssuesParser(repo: GitRepo) extends LazyLogging { +class IssuesParser(repo: GitRepo, config: Config) extends LazyLogging { // this particular parser can parse "tag-soup" document, which old html is val xml = XML.withSAXParser(new org.ccil.cowan.tagsoup.jaxp.SAXFactoryImpl().newSAXParser()) @@ -24,17 +25,17 @@ class IssuesParser(repo: GitRepo) extends LazyLogging { def isMerge = repo.lastCommit.isMerge logger.debug(s"Last commit is a merge commit? $isMerge") - def parse(pathToReportsDir: String, changeId: String): String = + def parse(changeId: String): String = pretty( render( merged( - projects map (parseProject(pathToReportsDir, changeId, _))))) + projects map (parseProject(changeId, _))))) - def parse(pathToReportsDir: String, changeId: String, project: String): String = + def parse(changeId: String, project: String): String = if (!isMerge) pretty( render( - parseProject(pathToReportsDir, changeId, project))) + parseProject(changeId, project))) else pretty( render( @@ -42,9 +43,12 @@ class IssuesParser(repo: GitRepo) extends LazyLogging { val mergeCommitReply = ("message" -> "Nothing to analyze, since this is a merge commit.") - private def parseProject(pathToReportsDir: String, changeId: String, project: String): JValue = { + private def parseProject(changeId: String, project: String): JValue = { - val pathToReport = pathToReportsDir + "/" + changeId + "/" + project + "/issues-report-light.html" + val pathToReport = config.getString(s"projects.$project.issueReports") + + changeId + "/" + + project + + "/issues-report-light.html" val issuesReport = xml.load(pathToReport) // this element is always there val summaryPerFile = (issuesReport \\ "div") find { x => (x \ "@id").text == "summary-per-file" } @@ -61,8 +65,6 @@ class IssuesParser(repo: GitRepo) extends LazyLogging { ("comments" -> comments) } - val forProject = (project: String) => "for project ${project}" - private def commentsPerResource(resourceReport: Node): JValue = { val sources = (resourceReport \\ "table") find { x => (x \ "@class").text == "sources" } diff --git a/sonarReview/src/main/scala/sonarReview.scala b/sonarReview/src/main/scala/sonarReview.scala index 3e559d81b347e7c343189ce5789f8b9a13dc8349..54f3aadfe77be4a90a6a00bb8ccb9c19ea4b1bce 100644 --- a/sonarReview/src/main/scala/sonarReview.scala +++ b/sonarReview/src/main/scala/sonarReview.scala @@ -3,6 +3,7 @@ import org.rogach.scallop._ import scala.xml._ import org.agse.gitclient.{ GitRepo } import com.typesafe.config._ +import java.io.File object sonarReview { @@ -12,16 +13,24 @@ object sonarReview { banner("""Usage: sonarreview.jar --change|c GERRIT_CHANGE_NUMBER --revision|-r GERRIT_PATCHSET_NUMBER --project|-p PROJECT [--debug|-d] | |sonarReview uses a configuration file which must lie in the same directory from where it is being called - |and it must be named "conf.xml". It must contain the following (${port} being optional) - |<conf> - | <host>gerrit.host.com</host> - | <user>user</user> - | <pass>pass</pass> - | <port>app_server_port</port> - | <issuesReportDir>/issues/reports/dir/</issuesReportDir> - |</conf> + |and it must be named "application.conf". It must contain the following (${port} being optional) | - |It will look for issues reports under ${issuesReportDir}/GERRIT_CHANGE_NUMBER, parse them and post + |gerrit { + | host = the server on which gerrit is hosted + | user = the username, as which you want to post the review + | pass = the password for the username above + | port = the port on which to post to the gerrit server, omitting this defaults to port 80 + |} + | + |gitRepoLocation = absolute path to the location of the .git folder of your current project + | + |projects{ + | projectFolder { + | projectTitle = the human-readable version of the project title + | issueReports = absolute path to the location of the folder where issue reports should be found + | } + |} + |It will look for issues reports under ${issueReports}/GERRIT_CHANGE_NUMBER, parse them and post |the issues found therein as comments to ${host}:${port}/gerrit as the user identified by ${user} and |${pass} for the patchset revision GERRIT_PATCHSET_NUMBER. | @@ -48,20 +57,21 @@ object sonarReview { else root.setLevel(Level.INFO) - val conf = XML.load("conf.xml") - val pathToIssuesReport = (conf \ "issuesReportDir").text + val config = ConfigFactory.parseFile(new File("application.conf")) - val issuesParser = new IssuesParser(new GitRepo((XML.load("conf.xml") \ "gitRepoLocation").text)) + val git = new GitRepo(config.getString("gitRepoLocation")) - val comments = P.project.get match { + val issuesParser = new IssuesParser(git, config) + val comments = P.project.get match { case Some(project) => - issuesParser.parse(pathToIssuesReport, P.changeNumber.apply, project) + issuesParser.parse(P.changeNumber.apply, project) case None => - issuesParser.parse(pathToIssuesReport, P.changeNumber.apply) + issuesParser.parse(P.changeNumber.apply) } - GerritRestClient.setReview(P.changeNumber.apply, P.patchsetNumber.apply, comments) + val gerrit = new GerritRestClient(config) + gerrit.setReview(P.changeNumber.apply, P.patchsetNumber.apply, comments) System.exit(0) } diff --git a/sonarReview/src/test/scala/ParserTest.scala b/sonarReview/src/test/scala/ParserTest.scala index 51d8992ba0d7c2959d08f8d2491765898423345d..730d8ed8f9c68e15b59d0616924ac1afa057946a 100644 --- a/sonarReview/src/test/scala/ParserTest.scala +++ b/sonarReview/src/test/scala/ParserTest.scala @@ -4,6 +4,7 @@ import org.agse.gitclient.GitRepo import net.liftweb.json._ import org.specs2.mutable._ import org.specs2.mock.Mockito +import com.typesafe.config._ object ParserTest extends Specification with Mockito { sequential @@ -32,34 +33,37 @@ object ParserTest extends Specification with Mockito { val changeId = "sampleReport" val project = "de.fu_berlin.inf.dpp.core" - val issuesParser = new IssuesParser(mockRepo) + val mockConfig = mock[Config] + mockConfig.getString(s"projects.$project.issueReports") returns pathToReports + + val issuesParser = new IssuesParser(mockRepo, mockConfig) "IssuesParser" should { "not analyze merge commits" in { - val review = issuesParser.parse(pathToReports, changeId, project) + val review = issuesParser.parse(changeId, project) compact(render(parse(review) \ "message")) mustEqual "\"Nothing to analyze, since this is a merge commit.\"" } "produce a confirmatory review message when no issues are found" in { - val review = issuesParser.parse(pathToReports, changeId, project) + val review = issuesParser.parse(changeId, project) compact(render(parse(review) \ "message")) mustEqual "\"Congratulations, QA found no issues whatsoever!\"" } "produce a review message that prompts to review the issues found" in { - val review = issuesParser.parse(pathToReports, changeId, project) + val review = issuesParser.parse(changeId, project) compact(render(parse(review) \ "message")) mustEqual "\"Please review your QA analysis results\"" } "produce a comment for each line with an issue" in { val numberOfIssues = 12 - val review = issuesParser.parse(pathToReports, changeId, project) + val review = issuesParser.parse(changeId, project) (parse(review) \\ "line").children.length mustEqual numberOfIssues ((parse(review) \\ "message").children.length - 1) mustEqual numberOfIssues } "produce comments when issues are found" in { val lengthOfReview = 1942 - val review = issuesParser.parse(pathToReports, changeId, project) + val review = issuesParser.parse(changeId, project) compact(render(parse(review))).length mustEqual lengthOfReview } }