mirror of
https://github.com/gitbucket/gitbucket.git
synced 2025-11-08 22:45:51 +01:00
Merge branch 'master' into asciidoctorj
This commit is contained in:
@@ -42,7 +42,6 @@ or you can start GitBucket by `java -jar gitbucket.war` without servlet containe
|
||||
- --port=[NUMBER]
|
||||
- --prefix=[CONTEXTPATH]
|
||||
- --host=[HOSTNAME]
|
||||
- --https=true
|
||||
- --gitbucket.home=[DATA_DIR]
|
||||
|
||||
To upgrade GitBucket, only replace gitbucket.war. All GitBucket data is stored in HOME/.gitbucket. So if you want to back up GitBucket data, copy this directory to the other disk.
|
||||
@@ -59,8 +58,9 @@ Run the following commands in `Terminal` to
|
||||
|
||||
Release Notes
|
||||
--------
|
||||
### 1.11 - End of Feb 2014
|
||||
- Base URL for redirect, notification and repository URL box is configurable
|
||||
### 1.11 - 01 Mar 2014
|
||||
- Base URL for redirection, notification and repository URL box is configurable
|
||||
- Remove ```--https``` option because it's possible to substitute in the base url
|
||||
- Headline anchor is available for Markdown contents such as Wiki page
|
||||
- Improve H2 connectivity
|
||||
- Label is available for pull requests not only issues
|
||||
|
||||
@@ -4,9 +4,6 @@
|
||||
# Server port
|
||||
#GITBUCKET_PORT=8080
|
||||
|
||||
# Force HTTPS scheme
|
||||
#GITBUCKET_HTTPS=false
|
||||
|
||||
# Data directory (GITBUCKET_HOME/gitbucket)
|
||||
#GITBUCKET_HOME=/var/lib/gitbucket
|
||||
|
||||
|
||||
@@ -39,9 +39,6 @@ start() {
|
||||
if [ $GITBUCKET_HOST ]; then
|
||||
START_OPTS="${START_OPTS} --host=${GITBUCKET_HOST}"
|
||||
fi
|
||||
if [ $GITBUCKET_HTTPS ]; then
|
||||
START_OPTS="${START_OPTS} --https=true"
|
||||
fi
|
||||
|
||||
# Run the Java process
|
||||
GITBUCKET_HOME="${GITBUCKET_HOME}" java $GITBUCKET_JVM_OPTS -jar $GITBUCKET_WAR_FILE $START_OPTS >>$LOG_FILE 2>&1 &
|
||||
|
||||
@@ -25,8 +25,6 @@ public class JettyLauncher {
|
||||
port = Integer.parseInt(dim[1]);
|
||||
} else if(dim[0].equals("--prefix")) {
|
||||
contextPath = dim[1];
|
||||
} else if(dim[0].equals("--https") && (dim[1].equals("1") || dim[1].equals("true"))) {
|
||||
forceHttps = true;
|
||||
} else if(dim[0].equals("--gitbucket.home")){
|
||||
System.setProperty("gitbucket.home", dim[1]);
|
||||
}
|
||||
@@ -36,7 +34,7 @@ public class JettyLauncher {
|
||||
|
||||
Server server = new Server();
|
||||
|
||||
HttpsSupportConnector connector = new HttpsSupportConnector(forceHttps);
|
||||
SelectChannelConnector connector = new SelectChannelConnector();
|
||||
if(host != null) {
|
||||
connector.setHost(host);
|
||||
}
|
||||
@@ -62,19 +60,3 @@ public class JettyLauncher {
|
||||
server.join();
|
||||
}
|
||||
}
|
||||
|
||||
class HttpsSupportConnector extends SelectChannelConnector {
|
||||
private boolean forceHttps;
|
||||
|
||||
public HttpsSupportConnector(boolean forceHttps) {
|
||||
this.forceHttps = forceHttps;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void customize(final EndPoint endpoint, final Request request) throws IOException {
|
||||
if (this.forceHttps) {
|
||||
request.setScheme("https");
|
||||
super.customize(endpoint, request);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,13 +5,12 @@ import util.{FileUtil, OneselfAuthenticator}
|
||||
import util.StringUtil._
|
||||
import util.Directory._
|
||||
import jp.sf.amateras.scalatra.forms._
|
||||
import org.scalatra.FlashMapSupport
|
||||
import org.apache.commons.io.FileUtils
|
||||
|
||||
class AccountController extends AccountControllerBase
|
||||
with AccountService with RepositoryService with ActivityService with OneselfAuthenticator
|
||||
|
||||
trait AccountControllerBase extends AccountManagementControllerBase with FlashMapSupport {
|
||||
trait AccountControllerBase extends AccountManagementControllerBase {
|
||||
self: AccountService with RepositoryService with ActivityService with OneselfAuthenticator =>
|
||||
|
||||
case class AccountNewForm(userName: String, password: String, fullName: String, mailAddress: String,
|
||||
|
||||
@@ -20,7 +20,8 @@ import org.scalatra.i18n._
|
||||
* Provides generic features for controller implementations.
|
||||
*/
|
||||
abstract class ControllerBase extends ScalatraFilter
|
||||
with ClientSideValidationFormSupport with JacksonJsonSupport with I18nSupport with Validations with SystemSettingsService {
|
||||
with ClientSideValidationFormSupport with JacksonJsonSupport with I18nSupport with FlashMapSupport with Validations
|
||||
with SystemSettingsService {
|
||||
|
||||
implicit val jsonFormats = DefaultFormats
|
||||
|
||||
@@ -102,20 +103,20 @@ abstract class ControllerBase extends ScalatraFilter
|
||||
if(request.getMethod.toUpperCase == "POST"){
|
||||
org.scalatra.Unauthorized(redirect("/signin"))
|
||||
} else {
|
||||
val currentUrl = baseUrl + defining(request.getQueryString){ queryString =>
|
||||
request.getRequestURI.substring(request.getContextPath.length) + (if(queryString != null) "?" + queryString else "")
|
||||
}
|
||||
session.setAttribute(Keys.Session.Redirect, currentUrl)
|
||||
org.scalatra.Unauthorized(redirect("/signin"))
|
||||
org.scalatra.Unauthorized(redirect("/signin?redirect=" + StringUtil.urlEncode(
|
||||
defining(request.getQueryString){ queryString =>
|
||||
request.getRequestURI.substring(request.getContextPath.length) + (if(queryString != null) "?" + queryString else "")
|
||||
}
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected def baseUrl = loadSystemSettings().baseUrl.getOrElse {
|
||||
defining(request.getRequestURL.toString){ url =>
|
||||
url.substring(0, url.length - (request.getRequestURI.length - request.getContextPath.length))
|
||||
}
|
||||
}.replaceFirst("/$", "")
|
||||
override def fullUrl(path: String, params: Iterable[(String, Any)] = Iterable.empty,
|
||||
includeContextPath: Boolean = true, includeServletPath: Boolean = true)
|
||||
(implicit request: HttpServletRequest, response: HttpServletResponse) =
|
||||
if (path.startsWith("http")) path
|
||||
else baseUrl + url(path, params, includeContextPath, includeServletPath)
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -12,8 +12,7 @@ import org.apache.commons.io.FileUtils
|
||||
* This servlet saves uploaded file as temporary file and returns the unique id.
|
||||
* You can get uploaded file using [[app.FileUploadControllerBase#getTemporaryFile()]] with this id.
|
||||
*/
|
||||
class FileUploadController extends ScalatraServlet
|
||||
with FileUploadSupport with FlashMapSupport with FileUploadControllerBase {
|
||||
class FileUploadController extends ScalatraServlet with FileUploadSupport with FileUploadControllerBase {
|
||||
|
||||
configureMultipartHandling(MultipartConfig(maxFileSize = Some(3 * 1024 * 1024)))
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package app
|
||||
|
||||
import util._
|
||||
import util.Implicits._
|
||||
import service._
|
||||
import jp.sf.amateras.scalatra.forms._
|
||||
|
||||
@@ -31,7 +30,7 @@ trait IndexControllerBase extends ControllerBase {
|
||||
get("/signin"){
|
||||
val redirect = params.get("redirect")
|
||||
if(redirect.isDefined && redirect.get.startsWith("/")){
|
||||
session.setAttribute(Keys.Session.Redirect, redirect.get)
|
||||
flash += Keys.Flash.Redirect -> redirect.get
|
||||
}
|
||||
html.signin(loadSystemSettings())
|
||||
}
|
||||
@@ -55,7 +54,7 @@ trait IndexControllerBase extends ControllerBase {
|
||||
session.setAttribute(Keys.Session.LoginAccount, account)
|
||||
updateLastLoginDate(account.userName)
|
||||
|
||||
session.getAndRemove[String](Keys.Session.Redirect).map { redirectUrl =>
|
||||
flash.get(Keys.Flash.Redirect).asInstanceOf[Option[String]].map { redirectUrl =>
|
||||
if(redirectUrl.replaceFirst("/$", "") == request.getContextPath){
|
||||
redirect("/")
|
||||
} else {
|
||||
|
||||
@@ -79,7 +79,7 @@ trait PullRequestsControllerBase extends ControllerBase {
|
||||
pulls.html.pullreq(
|
||||
issue, pullreq,
|
||||
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,
|
||||
getMilestonesWithIssueCount(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
|
||||
getWebHookURLs(owner, name) match {
|
||||
case webHookURLs if(webHookURLs.nonEmpty) =>
|
||||
|
||||
@@ -5,7 +5,6 @@ import util.Directory._
|
||||
import util.{UsersAuthenticator, OwnerAuthenticator}
|
||||
import jp.sf.amateras.scalatra.forms._
|
||||
import org.apache.commons.io.FileUtils
|
||||
import org.scalatra.FlashMapSupport
|
||||
import org.scalatra.i18n.Messages
|
||||
import service.WebHookService.WebHookPayload
|
||||
import util.JGitUtil.CommitInfo
|
||||
@@ -16,7 +15,7 @@ class RepositorySettingsController extends RepositorySettingsControllerBase
|
||||
with RepositoryService with AccountService with WebHookService
|
||||
with OwnerAuthenticator with UsersAuthenticator
|
||||
|
||||
trait RepositorySettingsControllerBase extends ControllerBase with FlashMapSupport {
|
||||
trait RepositorySettingsControllerBase extends ControllerBase {
|
||||
self: RepositoryService with AccountService with WebHookService
|
||||
with OwnerAuthenticator with UsersAuthenticator =>
|
||||
|
||||
|
||||
@@ -4,12 +4,11 @@ import service.{AccountService, SystemSettingsService}
|
||||
import SystemSettingsService._
|
||||
import util.AdminAuthenticator
|
||||
import jp.sf.amateras.scalatra.forms._
|
||||
import org.scalatra.FlashMapSupport
|
||||
|
||||
class SystemSettingsController extends SystemSettingsControllerBase
|
||||
with SystemSettingsService with AccountService with AdminAuthenticator
|
||||
|
||||
trait SystemSettingsControllerBase extends ControllerBase with FlashMapSupport {
|
||||
trait SystemSettingsControllerBase extends ControllerBase {
|
||||
self: SystemSettingsService with AccountService with AdminAuthenticator =>
|
||||
|
||||
private val form = mapping(
|
||||
|
||||
@@ -6,18 +6,15 @@ import util.Directory._
|
||||
import util.ControlUtil._
|
||||
import jp.sf.amateras.scalatra.forms._
|
||||
import org.eclipse.jgit.api.Git
|
||||
import org.scalatra.FlashMapSupport
|
||||
import org.scalatra.i18n.Messages
|
||||
import scala.Some
|
||||
import java.util.ResourceBundle
|
||||
|
||||
class WikiController extends WikiControllerBase
|
||||
with WikiService with RepositoryService with AccountService with ActivityService
|
||||
with CollaboratorsAuthenticator with ReferrerAuthenticator
|
||||
with WikiService with RepositoryService with AccountService with ActivityService with CollaboratorsAuthenticator with ReferrerAuthenticator
|
||||
|
||||
trait WikiControllerBase extends ControllerBase with FlashMapSupport {
|
||||
self: WikiService with RepositoryService with ActivityService
|
||||
with CollaboratorsAuthenticator with ReferrerAuthenticator =>
|
||||
trait WikiControllerBase extends ControllerBase {
|
||||
self: WikiService with RepositoryService with ActivityService with CollaboratorsAuthenticator with ReferrerAuthenticator =>
|
||||
|
||||
case class WikiPageEditForm(pageName: String, content: String, message: Option[String], currentPageName: String, id: String)
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ trait AccountService {
|
||||
Query(Accounts) filter(t => (t.userName is userName.bind) && (t.removed is false.bind, !includeRemoved)) firstOption
|
||||
|
||||
def getAccountByMailAddress(mailAddress: String, includeRemoved: Boolean = false): Option[Account] =
|
||||
Query(Accounts) filter(t => (t.mailAddress is mailAddress.bind) && (t.removed is false.bind, !includeRemoved)) firstOption
|
||||
Query(Accounts) filter(t => (t.mailAddress.toLowerCase is mailAddress.toLowerCase.bind) && (t.removed is false.bind, !includeRemoved)) firstOption
|
||||
|
||||
def getAllUsers(includeRemoved: Boolean = true): List[Account] =
|
||||
if(includeRemoved){
|
||||
|
||||
@@ -8,6 +8,7 @@ import Q.interpolation
|
||||
import model._
|
||||
import util.Implicits._
|
||||
import util.StringUtil._
|
||||
import util.StringUtil
|
||||
|
||||
trait IssuesService {
|
||||
import IssuesService._
|
||||
@@ -314,6 +315,14 @@ trait IssuesService {
|
||||
}.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 {
|
||||
|
||||
@@ -3,9 +3,16 @@ package service
|
||||
import util.Directory._
|
||||
import util.ControlUtil._
|
||||
import SystemSettingsService._
|
||||
import javax.servlet.http.HttpServletRequest
|
||||
|
||||
trait SystemSettingsService {
|
||||
|
||||
def baseUrl(implicit request: HttpServletRequest): String = loadSystemSettings().baseUrl.getOrElse {
|
||||
defining(request.getRequestURL.toString){ url =>
|
||||
url.substring(0, url.length - (request.getRequestURI.length - request.getContextPath.length))
|
||||
}
|
||||
}.replaceFirst("/$", "")
|
||||
|
||||
def saveSystemSettings(settings: SystemSettings): Unit = {
|
||||
defining(new java.util.Properties()){ props =>
|
||||
settings.baseUrl.foreach(props.setProperty(BaseURL, _))
|
||||
|
||||
@@ -50,10 +50,10 @@ class GitRepositoryServlet extends GitServlet {
|
||||
|
||||
}
|
||||
|
||||
class GitBucketReceivePackFactory extends ReceivePackFactory[HttpServletRequest] {
|
||||
class GitBucketReceivePackFactory extends ReceivePackFactory[HttpServletRequest] with SystemSettingsService {
|
||||
|
||||
private val logger = LoggerFactory.getLogger(classOf[GitBucketReceivePackFactory])
|
||||
|
||||
|
||||
override def create(request: HttpServletRequest, db: Repository): ReceivePack = {
|
||||
val receivePack = new ReceivePack(db)
|
||||
val pusher = request.getAttribute(Keys.Request.UserName).asInstanceOf[String]
|
||||
@@ -64,13 +64,11 @@ class GitBucketReceivePackFactory extends ReceivePackFactory[HttpServletRequest]
|
||||
defining(request.paths){ paths =>
|
||||
val owner = paths(1)
|
||||
val repository = paths(2).replaceFirst("\\.git$", "")
|
||||
val baseURL = request.getRequestURL.toString.replaceFirst("/git/.*", "")
|
||||
|
||||
logger.debug("repository:" + owner + "/" + repository)
|
||||
logger.debug("baseURL:" + baseURL)
|
||||
|
||||
if(!repository.endsWith(".wiki")){
|
||||
receivePack.setPostReceiveHook(new CommitLogHook(owner, repository, pusher, baseURL))
|
||||
receivePack.setPostReceiveHook(new CommitLogHook(owner, repository, pusher, baseUrl(request)))
|
||||
}
|
||||
receivePack
|
||||
}
|
||||
@@ -79,7 +77,7 @@ class GitBucketReceivePackFactory extends ReceivePackFactory[HttpServletRequest]
|
||||
|
||||
import scala.collection.JavaConverters._
|
||||
|
||||
class CommitLogHook(owner: String, repository: String, pusher: String, baseURL: String) extends PostReceiveHook
|
||||
class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl: String) extends PostReceiveHook
|
||||
with RepositoryService with AccountService with IssuesService with ActivityService with PullRequestService with WebHookService {
|
||||
|
||||
private val logger = LoggerFactory.getLogger(classOf[CommitLogHook])
|
||||
@@ -143,12 +141,20 @@ 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
|
||||
getWebHookURLs(owner, repository) match {
|
||||
case webHookURLs if(webHookURLs.nonEmpty) =>
|
||||
for(pusherAccount <- getAccountByUserName(pusher);
|
||||
ownerAccount <- getAccountByUserName(owner);
|
||||
repositoryInfo <- getRepository(owner, repository, baseURL)){
|
||||
repositoryInfo <- getRepository(owner, repository, baseUrl)){
|
||||
callWebHook(owner, repository, webHookURLs,
|
||||
WebHookPayload(git, pusherAccount, command.getRefName, repositoryInfo, newCommits, ownerAccount))
|
||||
}
|
||||
@@ -181,7 +187,7 @@ class CommitLogHook(owner: String, repository: String, pusher: String, baseURL:
|
||||
*/
|
||||
private def updatePullRequests(branch: String) =
|
||||
getPullRequestsByRequest(owner, repository, branch, false).foreach { pullreq =>
|
||||
if(getRepository(pullreq.userName, pullreq.repositoryName, baseURL).isDefined){
|
||||
if(getRepository(pullreq.userName, pullreq.repositoryName, baseUrl).isDefined){
|
||||
using(Git.open(Directory.getRepositoryDir(pullreq.userName, pullreq.repositoryName))){ git =>
|
||||
git.fetch
|
||||
.setRemote(Directory.getRepositoryDir(owner, repository).toURI.toString)
|
||||
|
||||
@@ -3,7 +3,7 @@ package util
|
||||
import org.eclipse.jgit.api.Git
|
||||
import org.eclipse.jgit.revwalk.RevWalk
|
||||
import org.eclipse.jgit.treewalk.TreeWalk
|
||||
import org.eclipse.jgit.transport.RefSpec
|
||||
import scala.util.control.Exception._
|
||||
import scala.language.reflectiveCalls
|
||||
|
||||
/**
|
||||
@@ -16,10 +16,8 @@ object ControlUtil {
|
||||
def using[A <% { def close(): Unit }, B](resource: A)(f: A => B): B =
|
||||
try f(resource) finally {
|
||||
if(resource != null){
|
||||
try {
|
||||
ignoring(classOf[Throwable]) {
|
||||
resource.close()
|
||||
} catch {
|
||||
case e: Throwable => // ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package util
|
||||
|
||||
import scala.util.matching.Regex
|
||||
import scala.util.control.Exception._
|
||||
import javax.servlet.http.{HttpSession, HttpServletRequest}
|
||||
|
||||
/**
|
||||
@@ -42,10 +43,8 @@ object Implicits {
|
||||
sb.toString
|
||||
}
|
||||
|
||||
def toIntOpt: Option[Int] = try {
|
||||
Option(Integer.parseInt(value))
|
||||
} catch {
|
||||
case e: NumberFormatException => None
|
||||
def toIntOpt: Option[Int] = catching(classOf[NumberFormatException]) opt {
|
||||
Integer.parseInt(value)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -128,7 +128,7 @@ object JGitUtil {
|
||||
using(Git.open(getRepositoryDir(owner, repository))){ git =>
|
||||
try {
|
||||
// get commit count
|
||||
val commitCount = git.log.all.call.iterator.asScala.map(_ => 1).take(1000).sum
|
||||
val commitCount = git.log.all.call.iterator.asScala.map(_ => 1).take(10000).sum
|
||||
|
||||
RepositoryInfo(
|
||||
owner, repository, s"${baseUrl}/git/${owner}/${repository}.git",
|
||||
|
||||
@@ -13,12 +13,7 @@ object Keys {
|
||||
/**
|
||||
* Session key for the logged in account information.
|
||||
*/
|
||||
val LoginAccount = "LOGIN_ACCOUNT"
|
||||
|
||||
/**
|
||||
* Session key for the redirect URL.
|
||||
*/
|
||||
val Redirect = "REDIRECT"
|
||||
val LoginAccount = "loginAccount"
|
||||
|
||||
/**
|
||||
* Session key for the issue search condition in dashboard.
|
||||
@@ -47,6 +42,20 @@ object Keys {
|
||||
|
||||
}
|
||||
|
||||
object Flash {
|
||||
|
||||
/**
|
||||
* Flash key for the redirect URL.
|
||||
*/
|
||||
val Redirect = "redirect"
|
||||
|
||||
/**
|
||||
* Flash key for the information message.
|
||||
*/
|
||||
val Info = "info"
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Define request keys.
|
||||
*/
|
||||
|
||||
@@ -31,7 +31,7 @@ object StringUtil {
|
||||
|
||||
/**
|
||||
* 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 =
|
||||
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
|
||||
* @return the iterator of issue id
|
||||
*/
|
||||
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))
|
||||
|
||||
}
|
||||
|
||||
@@ -30,6 +30,9 @@
|
||||
}
|
||||
}
|
||||
</div>
|
||||
@repository.repository.description.map { description =>
|
||||
<p>@description</p>
|
||||
}
|
||||
<table class="global-nav box-header">
|
||||
<tr>
|
||||
<th class="box-header@if(active=="code"){ active}">
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<div class="head">
|
||||
<div class="pull-right">
|
||||
@defining(repository.commitCount){ commitCount =>
|
||||
<a href="@url(repository)/commits/@encodeRefName(branch)">@if(commitCount > 1000){ @commitCount+ } else { @commitCount } @plural(commitCount, "commit")</a>
|
||||
<a href="@url(repository)/commits/@encodeRefName(branch)">@if(commitCount > 10000){ @commitCount+ } else { @commitCount } @plural(commitCount, "commit")</a>
|
||||
}
|
||||
</div>
|
||||
<a href="@url(repository)/tree/@encodeRefName(branch)">@repository.name</a> /
|
||||
|
||||
@@ -35,4 +35,22 @@ class StringUtilSpec extends Specification {
|
||||
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