Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/twbs/savage.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorChris Rebert <code@rebertia.com>2014-11-21 09:11:09 +0300
committerChris Rebert <code@rebertia.com>2014-11-21 09:11:09 +0300
commit7deb6a16177ecc6681ed0c0909d443d64f0ef9f1 (patch)
tree9261b2ba808f9fa1836d03bc3529b62adbf61066 /src
parente4d8a307811a99e8fdaae3300b5192a6d2f1aa7c (diff)
Fixes #8
Diffstat (limited to 'src')
-rw-r--r--src/main/resources/application.conf1
-rw-r--r--src/main/scala/com/getbootstrap/savage/github/GitHubJsonProtocol.scala43
-rw-r--r--src/main/scala/com/getbootstrap/savage/server/GitHubWebHooksDirectives.scala (renamed from src/main/scala/com/getbootstrap/savage/server/GitHubPullRequestWebHooksDirectives.scala)14
-rw-r--r--src/main/scala/com/getbootstrap/savage/server/PullRequestEventHandler.scala36
-rw-r--r--src/main/scala/com/getbootstrap/savage/server/SavageWebService.scala8
-rw-r--r--src/main/scala/com/getbootstrap/savage/server/Settings.scala1
6 files changed, 99 insertions, 4 deletions
diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf
index 03bfcdc..e914dff 100644
--- a/src/main/resources/application.conf
+++ b/src/main/resources/application.conf
@@ -24,6 +24,7 @@ savage {
github-repo-to-watch = "twbs/bootstrap"
github-test-repo = "twbs-savage/bootstrap"
ignore-branches-from-watched-repo = true
+ trusted-orgs = [ "twbs" ]
whitelist = [
"**.md",
"/bower.json",
diff --git a/src/main/scala/com/getbootstrap/savage/github/GitHubJsonProtocol.scala b/src/main/scala/com/getbootstrap/savage/github/GitHubJsonProtocol.scala
new file mode 100644
index 0000000..ab5bd01
--- /dev/null
+++ b/src/main/scala/com/getbootstrap/savage/github/GitHubJsonProtocol.scala
@@ -0,0 +1,43 @@
+package com.getbootstrap.savage.github
+
+import spray.json._
+import org.eclipse.egit.github.core.RepositoryId
+
+case class GitHubRepository(fullName: String) extends AnyVal {
+ def id: RepositoryId = RepositoryId.createFromId(fullName)
+}
+case class GitHubUser(username: String) extends AnyVal
+case class IssueOrComment(
+ number: Option[Int], // issue number
+ body: String,
+ user: GitHubUser
+)
+case class IssueOrCommentEvent(
+ repository: GitHubRepository,
+ comment: Option[IssueOrComment],
+ issue: IssueOrComment
+) {
+ def prNumber: Option[PullRequestNumber] = issue.number.flatMap{ PullRequestNumber(_) }
+}
+
+object GitHubJsonProtocol extends DefaultJsonProtocol {
+ implicit object RepoJsonFormat extends JsonFormat[GitHubRepository] {
+ override def write(repo: GitHubRepository) = JsObject("full_name" -> JsString(repo.fullName))
+ override def read(value: JsValue) = {
+ value.asJsObject.getFields("full_name") match {
+ case Seq(JsString(fullName)) => new GitHubRepository(fullName)
+ case _ => throw new DeserializationException("GitHubRepository expected")
+ }
+ }
+ }
+ implicit object UserFormat extends JsonFormat[GitHubUser] {
+ override def write(user: GitHubUser) = JsObject("login" -> JsString(user.username))
+ override def read(value: JsValue) = {
+ value.asJsObject.getFields("login") match {
+ case Seq(JsString(username)) => new GitHubUser(username)
+ }
+ }
+ }
+ implicit val issueOrCommentFormat = jsonFormat3(IssueOrComment.apply)
+ implicit val issueOrCommentEventFormat = jsonFormat3(IssueOrCommentEvent.apply)
+}
diff --git a/src/main/scala/com/getbootstrap/savage/server/GitHubPullRequestWebHooksDirectives.scala b/src/main/scala/com/getbootstrap/savage/server/GitHubWebHooksDirectives.scala
index d61a294..9ef26ee 100644
--- a/src/main/scala/com/getbootstrap/savage/server/GitHubPullRequestWebHooksDirectives.scala
+++ b/src/main/scala/com/getbootstrap/savage/server/GitHubWebHooksDirectives.scala
@@ -1,15 +1,18 @@
package com.getbootstrap.savage.server
import scala.util.{Success, Failure, Try}
+import spray.json._
import spray.routing.{Directive1, ValidationRejection}
import spray.routing.directives.{BasicDirectives, RouteDirectives}
import org.eclipse.egit.github.core.event.PullRequestPayload
import org.eclipse.egit.github.core.client.GsonUtils
+import com.getbootstrap.savage.github.{GitHubJsonProtocol,IssueOrCommentEvent}
-trait GitHubPullRequestWebHooksDirectives {
+trait GitHubWebHooksDirectives {
import RouteDirectives.reject
import BasicDirectives.provide
import HubSignatureDirectives.stringEntityMatchingHubSignature
+ import GitHubJsonProtocol._
def authenticatedPullRequestEvent(secretKey: Array[Byte]): Directive1[PullRequestPayload] = stringEntityMatchingHubSignature(secretKey).flatMap{ entityJsonString =>
Try { GsonUtils.fromJson(entityJsonString, classOf[PullRequestPayload]) } match {
@@ -17,6 +20,13 @@ trait GitHubPullRequestWebHooksDirectives {
case Success(payload) => provide(payload)
}
}
+
+ def authenticatedIssueOrCommentEvent(secretKey: Array[Byte]): Directive1[IssueOrCommentEvent] = stringEntityMatchingHubSignature(secretKey).flatMap{ entityJsonString =>
+ Try{ entityJsonString.parseJson.convertTo[IssueOrCommentEvent] } match {
+ case Failure(err) => reject(ValidationRejection("JSON either malformed or does not match expected schema!"))
+ case Success(event) => provide(event)
+ }
+ }
}
-object GitHubPullRequestWebHooksDirectives extends GitHubPullRequestWebHooksDirectives
+object GitHubWebHooksDirectives extends GitHubWebHooksDirectives
diff --git a/src/main/scala/com/getbootstrap/savage/server/PullRequestEventHandler.scala b/src/main/scala/com/getbootstrap/savage/server/PullRequestEventHandler.scala
index 244b5d4..1a4ceff 100644
--- a/src/main/scala/com/getbootstrap/savage/server/PullRequestEventHandler.scala
+++ b/src/main/scala/com/getbootstrap/savage/server/PullRequestEventHandler.scala
@@ -1,11 +1,12 @@
package com.getbootstrap.savage.server
import java.nio.file.Path
+import java.util.regex.Pattern
import scala.collection.JavaConverters._
import scala.util.{Try,Success,Failure}
import akka.actor.ActorRef
import org.eclipse.egit.github.core._
-import org.eclipse.egit.github.core.service.CommitService
+import org.eclipse.egit.github.core.service.{CommitService, OrganizationService, PullRequestService}
import com.getbootstrap.savage.github._
import com.getbootstrap.savage.github.util._
import com.getbootstrap.savage.util.UnixFileSystemString
@@ -39,11 +40,44 @@ class PullRequestEventHandler(protected val pusher: ActorRef) extends GitHubActo
settings.Watchlist.anyInterestingIn(paths)
}
+ private def isTrusted(user: GitHubUser): Boolean = {
+ val orgService = new OrganizationService(gitHubClient)
+ settings.TrustedOrganizations.exists{ org => Try{ orgService.isPublicMember(org, user.username) }.toOption.getOrElse(false) }
+ }
+
private def logPrInfo(msg: String)(implicit prNum: PullRequestNumber) {
log.info(s"PR #${prNum.number} : ${msg}")
}
+ private val RetryCommentRegex = ("(?i)^" + Pattern.quote(s"@${settings.BotUsername}") + "\\s+retry").r
+
override def receive = {
+ case commentEvent: IssueOrCommentEvent => {
+ commentEvent.repository.id match {
+ case settings.MainRepoId => {
+ commentEvent.prNumber.foreach{ prNum => {
+ commentEvent.comment.foreach{ comment => {
+ if (isTrusted(comment.user)) {
+ comment.body match {
+ case RetryCommentRegex(_*) => {
+ val prService = new PullRequestService(gitHubClient)
+ Try{ prService.getPullRequest(settings.MainRepoId, prNum.number) } match {
+ case Failure(exc) => log.error(exc, s"Error getting ${prNum} for repo ${settings.MainRepoId}!")
+ case Success(pullReq) => {
+ log.info(s"Initiating retry of ${prNum} due to request from trusted user ${comment.user}")
+ self ! pullReq
+ }
+ }
+ }
+ case _ => {}
+ }
+ }
+ }}
+ }}
+ }
+ case otherRepo => log.error(s"Received event from GitHub about irrelevant repository: ${otherRepo}")
+ }
+ }
case pr: PullRequest => {
implicit val prNum = pr.number
val bsBase = pr.getBase
diff --git a/src/main/scala/com/getbootstrap/savage/server/SavageWebService.scala b/src/main/scala/com/getbootstrap/savage/server/SavageWebService.scala
index 0d3ec23..a300ff4 100644
--- a/src/main/scala/com/getbootstrap/savage/server/SavageWebService.scala
+++ b/src/main/scala/com/getbootstrap/savage/server/SavageWebService.scala
@@ -12,7 +12,7 @@ class SavageWebService(
protected val pullRequestCommenter: ActorRef,
protected val branchDeleter: ActorRef
) extends ActorWithLogging with HttpService {
- import GitHubPullRequestWebHooksDirectives.authenticatedPullRequestEvent
+ import GitHubWebHooksDirectives.{authenticatedPullRequestEvent,authenticatedIssueOrCommentEvent}
import TravisWebHookDirectives.authenticatedTravisEvent
private val settings = Settings(context.system)
@@ -35,6 +35,12 @@ class SavageWebService(
log.info("Successfully received GitHub webhook ping.")
complete(StatusCodes.OK)
}
+ case "issue_comment" => {
+ authenticatedIssueOrCommentEvent(settings.GitHubWebHookSecretKey.toArray) { event => {
+ pullRequestEventHandler ! event
+ complete(StatusCodes.OK)
+ }}
+ }
case "pull_request" => {
authenticatedPullRequestEvent(settings.GitHubWebHookSecretKey.toArray) { event =>
event.getAction match {
diff --git a/src/main/scala/com/getbootstrap/savage/server/Settings.scala b/src/main/scala/com/getbootstrap/savage/server/Settings.scala
index c6989d4..f647557 100644
--- a/src/main/scala/com/getbootstrap/savage/server/Settings.scala
+++ b/src/main/scala/com/getbootstrap/savage/server/Settings.scala
@@ -23,6 +23,7 @@ class SettingsImpl(config: Config) extends Extension {
val Watchlist: FilePathWatchlist = new FilePathWatchlist(config.getStringList("savage.file-watchlist").asScala)
val BranchPrefix: String = config.getString("savage.branch-prefix")
val IgnoreBranchesFromMainRepo: Boolean = config.getBoolean("savage.ignore-branches-from-watched-repo")
+ val TrustedOrganizations: Set[String] = config.getStringList("savage.trusted-orgs").asScala.toSet
}
object Settings extends ExtensionId[SettingsImpl] with ExtensionIdProvider {
override def lookup() = Settings