mirror of
https://github.com/gitbucket/gitbucket.git
synced 2025-11-09 15:05:50 +01:00
Merge branch 'odz-closing-issues-via-commit-messages'
This commit is contained in:
@@ -79,7 +79,7 @@ trait PullRequestsControllerBase extends ControllerBase {
|
|||||||
pulls.html.pullreq(
|
pulls.html.pullreq(
|
||||||
issue, pullreq,
|
issue, pullreq,
|
||||||
getComments(owner, name, issueId),
|
getComments(owner, name, issueId),
|
||||||
getIssueLabels(owner, name, issueId.toInt),
|
getIssueLabels(owner, name, issueId),
|
||||||
(getCollaborators(owner, name) ::: (if(getAccountByUserName(owner).get.isGroupAccount) Nil else List(owner))).sorted,
|
(getCollaborators(owner, name) ::: (if(getAccountByUserName(owner).get.isGroupAccount) Nil else List(owner))).sorted,
|
||||||
getMilestonesWithIssueCount(owner, name),
|
getMilestonesWithIssueCount(owner, name),
|
||||||
getLabels(owner, name),
|
getLabels(owner, name),
|
||||||
@@ -183,6 +183,18 @@ trait PullRequestsControllerBase extends ControllerBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// close issue by content of pull request
|
||||||
|
val defaultBranch = getRepository(owner, name, baseUrl).get.repository.defaultBranch
|
||||||
|
if(pullreq.branch == defaultBranch){
|
||||||
|
commits.flatten.foreach { commit =>
|
||||||
|
closeIssuesFromMessage(commit.fullMessage, loginAccount.userName, owner, name)
|
||||||
|
}
|
||||||
|
issue.content match {
|
||||||
|
case Some(content) => closeIssuesFromMessage(content, loginAccount.userName, owner, name)
|
||||||
|
case _ =>
|
||||||
|
}
|
||||||
|
closeIssuesFromMessage(form.message, loginAccount.userName, owner, name)
|
||||||
|
}
|
||||||
// call web hook
|
// call web hook
|
||||||
getWebHookURLs(owner, name) match {
|
getWebHookURLs(owner, name) match {
|
||||||
case webHookURLs if(webHookURLs.nonEmpty) =>
|
case webHookURLs if(webHookURLs.nonEmpty) =>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import Q.interpolation
|
|||||||
import model._
|
import model._
|
||||||
import util.Implicits._
|
import util.Implicits._
|
||||||
import util.StringUtil._
|
import util.StringUtil._
|
||||||
|
import util.StringUtil
|
||||||
|
|
||||||
trait IssuesService {
|
trait IssuesService {
|
||||||
import IssuesService._
|
import IssuesService._
|
||||||
@@ -314,6 +315,14 @@ trait IssuesService {
|
|||||||
}.toList
|
}.toList
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def closeIssuesFromMessage(message: String, userName: String, owner: String, repository: String) = {
|
||||||
|
StringUtil.extractCloseId(message).foreach { issueId =>
|
||||||
|
for(issue <- getIssue(owner, repository, issueId) if !issue.closed){
|
||||||
|
createComment(owner, repository, userName, issue.issueId, "Close", "close")
|
||||||
|
updateClosed(owner, repository, issue.issueId, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object IssuesService {
|
object IssuesService {
|
||||||
|
|||||||
@@ -141,6 +141,14 @@ class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// close issues
|
||||||
|
val defaultBranch = getRepository(owner, repository, baseUrl).get.repository.defaultBranch
|
||||||
|
if(refName(1) == "heads" && branchName == defaultBranch && command.getType == ReceiveCommand.Type.UPDATE){
|
||||||
|
git.log.addRange(command.getOldId, command.getNewId).call.asScala.foreach { commit =>
|
||||||
|
closeIssuesFromMessage(commit.getFullMessage, pusher, owner, repository)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// call web hook
|
// call web hook
|
||||||
getWebHookURLs(owner, repository) match {
|
getWebHookURLs(owner, repository) match {
|
||||||
case webHookURLs if(webHookURLs.nonEmpty) =>
|
case webHookURLs if(webHookURLs.nonEmpty) =>
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ object StringUtil {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Make string from byte array. Character encoding is detected automatically by [[util.StringUtil.detectEncoding]].
|
* Make string from byte array. Character encoding is detected automatically by [[util.StringUtil.detectEncoding]].
|
||||||
* And if given bytes contains UTF-8 BOM, it's removed from returned string..
|
* And if given bytes contains UTF-8 BOM, it's removed from returned string.
|
||||||
*/
|
*/
|
||||||
def convertFromByteArray(content: Array[Byte]): String =
|
def convertFromByteArray(content: Array[Byte]): String =
|
||||||
IOUtils.toString(new BOMInputStream(new java.io.ByteArrayInputStream(content)), detectEncoding(content))
|
IOUtils.toString(new BOMInputStream(new java.io.ByteArrayInputStream(content)), detectEncoding(content))
|
||||||
@@ -47,12 +47,21 @@ object StringUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract issue id like ````#issueId``` from the given message.
|
* Extract issue id like ```#issueId``` from the given message.
|
||||||
*
|
*
|
||||||
*@param message the message which may contains issue id
|
*@param message the message which may contains issue id
|
||||||
* @return the iterator of issue id
|
* @return the iterator of issue id
|
||||||
*/
|
*/
|
||||||
def extractIssueId(message: String): Iterator[String] =
|
def extractIssueId(message: String): Iterator[String] =
|
||||||
"(^|\\W)#(\\d+)(\\W|$)".r.findAllIn(message).matchData.map { matchData => matchData.group(2) }
|
"(^|\\W)#(\\d+)(\\W|$)".r.findAllIn(message).matchData.map(_.group(2))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract close issue id like ```close #issueId ``` from the given message.
|
||||||
|
*
|
||||||
|
* @param message the message which may contains close command
|
||||||
|
* @return the iterator of issue id
|
||||||
|
*/
|
||||||
|
def extractCloseId(message: String): Iterator[String] =
|
||||||
|
"(?i)(?<!\\w)(?:fix(?:e[sd])?|resolve[sd]?|close[sd]?)\\s+#(\\d+)(?!\\w)".r.findAllIn(message).matchData.map(_.group(1))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,4 +35,22 @@ class StringUtilSpec extends Specification {
|
|||||||
StringUtil.sha1("abc") mustEqual "a9993e364706816aba3e25717850c26c9cd0d89d"
|
StringUtil.sha1("abc") mustEqual "a9993e364706816aba3e25717850c26c9cd0d89d"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"extractIssueId" should {
|
||||||
|
"extract '#xxx' and return extracted id" in {
|
||||||
|
StringUtil.extractIssueId("(refs #123)").toSeq mustEqual Seq("123")
|
||||||
|
}
|
||||||
|
"returns Nil from message which does not contain #xxx" in {
|
||||||
|
StringUtil.extractIssueId("this is test!").toSeq mustEqual Nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"extractCloseId" should {
|
||||||
|
"extract 'close #xxx' and return extracted id" in {
|
||||||
|
StringUtil.extractCloseId("(close #123)").toSeq mustEqual Seq("123")
|
||||||
|
}
|
||||||
|
"returns Nil from message which does not contain close command" in {
|
||||||
|
StringUtil.extractCloseId("(refs #123)").toSeq mustEqual Nil
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user