mirror of
https://github.com/gitbucket/gitbucket.git
synced 2025-11-01 19:15:59 +01:00
Merge remote-tracking branch 'upstream/master' into pr-implemt-apis
This commit is contained in:
28
CHANGELOG.md
28
CHANGELOG.md
@@ -1,24 +1,24 @@
|
||||
# Changelog
|
||||
All changes to the project will be documented in this file.
|
||||
|
||||
### 4.29.0 - 29 Sep 2018
|
||||
## 4.29.0 - 29 Sep 2018
|
||||
- Official Docker image has been available
|
||||
- Enhance file edit and delete buttons of the repository viewer
|
||||
- Fix Patch button to generate patches for all files in the commit
|
||||
- Display confirmation dialog for Transfer Ownership and Garbage collection
|
||||
- Fix wrong url encoding in "Compare & pull request"
|
||||
|
||||
### 4.28.0 - 1 Sep 2018
|
||||
## 4.28.0 - 1 Sep 2018
|
||||
- Proxy support for plugin installation
|
||||
- Fix some bugs around pull requests
|
||||
|
||||
### 4.27.0 - 29 Jul 2018
|
||||
## 4.27.0 - 29 Jul 2018
|
||||
- Create new tag on the browser
|
||||
- EditorConfig support
|
||||
- Improve issues / pull requests search
|
||||
- Some improvements and bug fixes for plugin installation via internet and pull request commenting
|
||||
|
||||
### 4.26.0 - 30 Jun 2018
|
||||
## 4.26.0 - 30 Jun 2018
|
||||
- Installing plugins from the central registry
|
||||
- Repositories tab in the dashboard
|
||||
- Fork dialog enhancement
|
||||
@@ -26,7 +26,7 @@ All changes to the project will be documented in this file.
|
||||
- Keep showing incompleted task list
|
||||
- New notification hooks
|
||||
|
||||
### 4.25.0 - 29 May 2018
|
||||
## 4.25.0 - 29 May 2018
|
||||
- Security improvements
|
||||
- Show mail address at the profile page
|
||||
- Task list on commit comments
|
||||
@@ -34,10 +34,10 @@ All changes to the project will be documented in this file.
|
||||
- Expose user public keys
|
||||
- Download repository improvements
|
||||
|
||||
### 4.24.1 - 1 May 2018
|
||||
## 4.24.1 - 1 May 2018
|
||||
- Fix bug in Web API authentication
|
||||
|
||||
### 4.24.0 - 30 Apr 2018
|
||||
## 4.24.0 - 30 Apr 2018
|
||||
- Diff for each review comment on pull requests
|
||||
- Extra mail addresses support
|
||||
- Show tags at the commit list
|
||||
@@ -45,12 +45,12 @@ All changes to the project will be documented in this file.
|
||||
- Renew layout of gitbucket-gist-plugin
|
||||
- Web API of gitbucket-ci-plugin
|
||||
|
||||
### 4.23.1 - 10 Apr 2018
|
||||
## 4.23.1 - 10 Apr 2018
|
||||
- Fix bug that the contents API doesn't work for the repository root
|
||||
- Fix shutdown problem in Tomcat deployment
|
||||
- Render by plugins at the blob view even if it's a binary file
|
||||
|
||||
### 4.23.0 - 31 Mar 2018
|
||||
## 4.23.0 - 31 Mar 2018
|
||||
- Allow tail slash in URL
|
||||
- Display commit message of tags at the releases page
|
||||
- Add labels property to issues and pull requests API response
|
||||
@@ -58,26 +58,26 @@ All changes to the project will be documented in this file.
|
||||
- Git authentication with personal access token
|
||||
- Max parallel builds and max stored history in CI plugin became configurable
|
||||
|
||||
### 4.22.0 - 3 Mar 2018
|
||||
## 4.22.0 - 3 Mar 2018
|
||||
- Pull request merge strategy settings
|
||||
- Create repository with an empty commit
|
||||
- Improve database viewer
|
||||
- Update maven-repository-plugin
|
||||
|
||||
### 4.21.2 - 27 Jan 2018
|
||||
## 4.21.2 - 27 Jan 2018
|
||||
- Bugfix
|
||||
|
||||
### 4.21.1 - 27 Jan 2018
|
||||
## 4.21.1 - 27 Jan 2018
|
||||
- Bugfix
|
||||
|
||||
### 4.21.0 - 27 Jan 2018
|
||||
## 4.21.0 - 27 Jan 2018
|
||||
- Release page
|
||||
- OpenID Connect support
|
||||
- New database viewer
|
||||
- Submodule links to web page
|
||||
- Clarify close/reopen button
|
||||
|
||||
## 4.20.0 - 23 Dec 2017
|
||||
# 4.20.0 - 23 Dec 2017
|
||||
- Squash and rebase merge strategy for pull requests
|
||||
- Quick pull request creation
|
||||
- Download patch from the diff view
|
||||
|
||||
30
build.sbt
30
build.sbt
@@ -6,7 +6,7 @@ val Name = "gitbucket"
|
||||
val GitBucketVersion = "4.30.0-SNAPSHOT"
|
||||
val ScalatraVersion = "2.6.3"
|
||||
val JettyVersion = "9.4.11.v20180605"
|
||||
val JgitVersion = "5.1.2.201810061102-r"
|
||||
val JgitVersion = "5.1.3.201810200350-r"
|
||||
|
||||
lazy val root = (project in file("."))
|
||||
.enablePlugins(SbtTwirl, ScalatraPlugin)
|
||||
@@ -36,40 +36,40 @@ libraryDependencies ++= Seq(
|
||||
"org.scalatra" %% "scalatra" % ScalatraVersion,
|
||||
"org.scalatra" %% "scalatra-json" % ScalatraVersion,
|
||||
"org.scalatra" %% "scalatra-forms" % ScalatraVersion,
|
||||
"org.json4s" %% "json4s-jackson" % "3.5.3",
|
||||
"org.json4s" %% "json4s-jackson" % "3.5.4",
|
||||
"commons-io" % "commons-io" % "2.6",
|
||||
"io.github.gitbucket" % "solidbase" % "1.0.2",
|
||||
"io.github.gitbucket" % "markedj" % "1.0.15",
|
||||
"org.apache.commons" % "commons-compress" % "1.18",
|
||||
"org.apache.commons" % "commons-email" % "1.5",
|
||||
"org.apache.httpcomponents" % "httpclient" % "4.5.6",
|
||||
"org.apache.sshd" % "apache-sshd" % "2.1.0" exclude ("org.slf4j", "slf4j-jdk14"),
|
||||
"org.apache.sshd" % "apache-sshd" % "1.7.0" exclude ("org.slf4j", "slf4j-jdk14"),
|
||||
"org.apache.tika" % "tika-core" % "1.19.1",
|
||||
"com.github.takezoe" %% "blocking-slick-32" % "0.0.10",
|
||||
"com.github.takezoe" %% "blocking-slick-32" % "0.0.11",
|
||||
"com.novell.ldap" % "jldap" % "2009-10-07",
|
||||
"com.h2database" % "h2" % "1.4.196",
|
||||
"com.h2database" % "h2" % "1.4.197",
|
||||
"org.mariadb.jdbc" % "mariadb-java-client" % "2.3.0",
|
||||
"org.postgresql" % "postgresql" % "42.2.5",
|
||||
"ch.qos.logback" % "logback-classic" % "1.2.3",
|
||||
"com.zaxxer" % "HikariCP" % "2.7.4",
|
||||
"com.typesafe" % "config" % "1.3.2",
|
||||
"com.typesafe.akka" %% "akka-actor" % "2.5.8",
|
||||
"com.zaxxer" % "HikariCP" % "2.7.9",
|
||||
"com.typesafe" % "config" % "1.3.3",
|
||||
"com.typesafe.akka" %% "akka-actor" % "2.5.17",
|
||||
"fr.brouillard.oss.security.xhub" % "xhub4j-core" % "1.0.0",
|
||||
"com.github.bkromhout" % "java-diff-utils" % "2.1.1",
|
||||
"org.cache2k" % "cache2k-all" % "1.0.1.Final",
|
||||
"com.enragedginger" %% "akka-quartz-scheduler" % "1.6.1-akka-2.5.x" exclude ("c3p0", "c3p0"),
|
||||
"org.cache2k" % "cache2k-all" % "1.0.2.Final",
|
||||
"com.enragedginger" %% "akka-quartz-scheduler" % "1.7.0-akka-2.5.x" exclude ("c3p0", "c3p0"),
|
||||
"net.coobird" % "thumbnailator" % "0.4.8",
|
||||
"com.github.zafarkhaja" % "java-semver" % "0.9.0",
|
||||
"com.nimbusds" % "oauth2-oidc-sdk" % "5.45",
|
||||
"com.nimbusds" % "oauth2-oidc-sdk" % "5.64.4",
|
||||
"org.eclipse.jetty" % "jetty-webapp" % JettyVersion % "provided",
|
||||
"javax.servlet" % "javax.servlet-api" % "3.1.0" % "provided",
|
||||
"junit" % "junit" % "4.12" % "test",
|
||||
"org.scalatra" %% "scalatra-scalatest" % ScalatraVersion % "test",
|
||||
"org.mockito" % "mockito-core" % "2.19.1" % "test",
|
||||
"org.mockito" % "mockito-core" % "2.23.0" % "test",
|
||||
"com.wix" % "wix-embedded-mysql" % "3.0.0" % "test",
|
||||
"ru.yandex.qatools.embed" % "postgresql-embedded" % "2.6" % "test",
|
||||
"net.i2p.crypto" % "eddsa" % "0.2.0",
|
||||
"is.tagomor.woothee" % "woothee-java" % "1.7.0",
|
||||
"ru.yandex.qatools.embed" % "postgresql-embedded" % "2.9" % "test",
|
||||
"net.i2p.crypto" % "eddsa" % "0.3.0",
|
||||
"is.tagomor.woothee" % "woothee-java" % "1.8.0",
|
||||
"org.ec4j.core" % "ec4j-core" % "0.0.1"
|
||||
)
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
sbt.version=1.2.3
|
||||
sbt.version=1.2.6
|
||||
|
||||
@@ -1 +1 @@
|
||||
libraryDependencies += "com.eclipsesource.minimal-json" % "minimal-json" % "0.9.4"
|
||||
libraryDependencies += "com.eclipsesource.minimal-json" % "minimal-json" % "0.9.5"
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
scalacOptions ++= Seq("-unchecked", "-deprecation", "-feature")
|
||||
|
||||
addSbtPlugin("com.geirsson" % "sbt-scalafmt" % "1.5.0")
|
||||
addSbtPlugin("com.typesafe.sbt" % "sbt-twirl" % "1.3.13")
|
||||
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.5")
|
||||
addSbtPlugin("com.typesafe.sbt" % "sbt-twirl" % "1.3.15")
|
||||
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.8")
|
||||
addSbtPlugin("org.scalatra.sbt" % "sbt-scalatra" % "1.0.1")
|
||||
addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.1.0")
|
||||
addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.1.2")
|
||||
addSbtCoursier
|
||||
addSbtPlugin("com.typesafe.sbt" % "sbt-license-report" % "1.2.0")
|
||||
|
||||
@@ -360,13 +360,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
||||
// FileUtils.deleteDirectory(getWikiRepositoryDir(userName, repositoryName))
|
||||
// FileUtils.deleteDirectory(getTemporaryDir(userName, repositoryName))
|
||||
// }
|
||||
// Remove from GROUP_MEMBER and COLLABORATOR
|
||||
removeUserRelatedData(userName)
|
||||
updateAccount(account.copy(isRemoved = true))
|
||||
|
||||
// call hooks
|
||||
PluginRegistry().getAccountHooks.foreach(_.deleted(userName))
|
||||
|
||||
suspendAccount(account)
|
||||
session.invalidate
|
||||
redirect("/")
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ class ApiController
|
||||
with WebHookService
|
||||
with WebHookPullRequestService
|
||||
with WebHookIssueCommentService
|
||||
with WebHookPullRequestReviewCommentService
|
||||
with WikiService
|
||||
with ActivityService
|
||||
with PrioritiesService
|
||||
|
||||
@@ -12,9 +12,13 @@ class DashboardController
|
||||
with PullRequestService
|
||||
with RepositoryService
|
||||
with AccountService
|
||||
with ActivityService
|
||||
with CommitsService
|
||||
with LabelsService
|
||||
with PrioritiesService
|
||||
with WebHookService
|
||||
with WebHookPullRequestService
|
||||
with WebHookPullRequestReviewCommentService
|
||||
with MilestonesService
|
||||
with UsersAuthenticator
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ class IssuesController
|
||||
with WritableUsersAuthenticator
|
||||
with PullRequestService
|
||||
with WebHookIssueCommentService
|
||||
with WebHookPullRequestReviewCommentService
|
||||
with CommitsService
|
||||
with PrioritiesService
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ class PullRequestsController
|
||||
with CommitsService
|
||||
with ActivityService
|
||||
with WebHookPullRequestService
|
||||
with WebHookPullRequestReviewCommentService
|
||||
with ReadableUsersAuthenticator
|
||||
with ReferrerAuthenticator
|
||||
with WritableUsersAuthenticator
|
||||
@@ -294,11 +295,12 @@ trait PullRequestsControllerBase extends ControllerBase {
|
||||
issueId <- params("id").toIntOpt
|
||||
loginAccount <- context.loginAccount
|
||||
(issue, pullreq) <- getPullRequest(baseRepository.owner, baseRepository.name, issueId)
|
||||
repository <- getRepository(pullreq.requestUserName, pullreq.requestRepositoryName)
|
||||
remoteRepository <- getRepository(pullreq.userName, pullreq.repositoryName)
|
||||
owner = pullreq.requestUserName
|
||||
name = pullreq.requestRepositoryName
|
||||
if hasDeveloperRole(owner, name, context.loginAccount)
|
||||
} yield {
|
||||
val repository = getRepository(owner, name).get
|
||||
val branchProtection = getProtectedBranchInfo(owner, name, pullreq.requestBranch)
|
||||
if (branchProtection.needStatusCheck(loginAccount.userName)) {
|
||||
flash += "error" -> s"branch ${pullreq.requestBranch} is protected need status check."
|
||||
@@ -314,83 +316,19 @@ trait PullRequestsControllerBase extends ControllerBase {
|
||||
JGitUtil.getAllCommitIds(git)
|
||||
}.toSet
|
||||
pullRemote(
|
||||
owner,
|
||||
name,
|
||||
repository,
|
||||
pullreq.requestBranch,
|
||||
pullreq.userName,
|
||||
pullreq.repositoryName,
|
||||
remoteRepository,
|
||||
pullreq.branch,
|
||||
loginAccount,
|
||||
s"Merge branch '${alias}' into ${pullreq.requestBranch}"
|
||||
s"Merge branch '${alias}' into ${pullreq.requestBranch}",
|
||||
Some(pullreq)
|
||||
) match {
|
||||
case None => // conflict
|
||||
flash += "error" -> s"Can't automatic merging branch '${alias}' into ${pullreq.requestBranch}."
|
||||
case Some(oldId) =>
|
||||
// update pull request
|
||||
updatePullRequests(owner, name, pullreq.requestBranch)
|
||||
|
||||
using(Git.open(Directory.getRepositoryDir(owner, name))) {
|
||||
git =>
|
||||
// after update branch
|
||||
val newCommitId = git.getRepository.resolve(s"refs/heads/${pullreq.requestBranch}")
|
||||
val commits = git.log
|
||||
.addRange(oldId, newCommitId)
|
||||
.call
|
||||
.iterator
|
||||
.asScala
|
||||
.map(c => new JGitUtil.CommitInfo(c))
|
||||
.toList
|
||||
|
||||
commits.foreach { commit =>
|
||||
if (!existIds.contains(commit.id)) {
|
||||
createIssueComment(owner, name, commit)
|
||||
}
|
||||
}
|
||||
|
||||
// record activity
|
||||
recordPushActivity(owner, name, loginAccount.userName, pullreq.branch, commits)
|
||||
|
||||
// close issue by commit message
|
||||
if (pullreq.requestBranch == repository.repository.defaultBranch) {
|
||||
commits.foreach { commit =>
|
||||
closeIssuesFromMessage(commit.fullMessage, loginAccount.userName, owner, name).foreach {
|
||||
issueId =>
|
||||
getIssue(repository.owner, repository.name, issueId.toString).foreach { issue =>
|
||||
callIssuesWebHook("closed", repository, issue, baseUrl, loginAccount)
|
||||
PluginRegistry().getIssueHooks
|
||||
.foreach(
|
||||
_.closedByCommitComment(issue, repository, commit.fullMessage, loginAccount)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// call web hook
|
||||
callPullRequestWebHookByRequestBranch(
|
||||
"synchronize",
|
||||
repository,
|
||||
pullreq.requestBranch,
|
||||
baseUrl,
|
||||
loginAccount
|
||||
)
|
||||
callWebHookOf(owner, name, WebHook.Push) {
|
||||
for {
|
||||
ownerAccount <- getAccountByUserName(owner)
|
||||
} yield {
|
||||
WebHookService.WebHookPushPayload(
|
||||
git,
|
||||
loginAccount,
|
||||
pullreq.requestBranch,
|
||||
repository,
|
||||
commits,
|
||||
ownerAccount,
|
||||
oldId = oldId,
|
||||
newId = newCommitId
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
updatePullRequests(owner, name, pullreq.requestBranch, loginAccount, "synchronize")
|
||||
flash += "info" -> s"Merge branch '${alias}' into ${pullreq.requestBranch}"
|
||||
}
|
||||
}
|
||||
@@ -401,119 +339,14 @@ trait PullRequestsControllerBase extends ControllerBase {
|
||||
})
|
||||
|
||||
post("/:owner/:repository/pull/:id/merge", mergeForm)(writableUsersOnly { (form, repository) =>
|
||||
params("id").toIntOpt.flatMap {
|
||||
issueId =>
|
||||
val owner = repository.owner
|
||||
val name = repository.name
|
||||
if (repository.repository.options.mergeOptions.split(",").contains(form.strategy)) {
|
||||
LockUtil.lock(s"${owner}/${name}") {
|
||||
getPullRequest(owner, name, issueId).map {
|
||||
case (issue, pullreq) =>
|
||||
using(Git.open(getRepositoryDir(owner, name))) {
|
||||
git =>
|
||||
// mark issue as merged and close.
|
||||
val loginAccount = context.loginAccount.get
|
||||
val commentId = createComment(owner, name, loginAccount.userName, issueId, form.message, "merge")
|
||||
createComment(owner, name, loginAccount.userName, issueId, "Close", "close")
|
||||
updateClosed(owner, name, issueId, true)
|
||||
params("id").toIntOpt.flatMap { issueId =>
|
||||
val owner = repository.owner
|
||||
val name = repository.name
|
||||
|
||||
// record activity
|
||||
recordMergeActivity(owner, name, loginAccount.userName, issueId, form.message)
|
||||
|
||||
val (commits, _) = getRequestCompareInfo(
|
||||
owner,
|
||||
name,
|
||||
pullreq.commitIdFrom,
|
||||
pullreq.requestUserName,
|
||||
pullreq.requestRepositoryName,
|
||||
pullreq.commitIdTo
|
||||
)
|
||||
|
||||
val revCommits = using(new RevWalk(git.getRepository)) { revWalk =>
|
||||
commits.flatten.map { commit =>
|
||||
revWalk.parseCommit(git.getRepository.resolve(commit.id))
|
||||
}
|
||||
}.reverse
|
||||
|
||||
// merge git repository
|
||||
form.strategy match {
|
||||
case "merge-commit" =>
|
||||
mergePullRequest(
|
||||
git,
|
||||
pullreq.branch,
|
||||
issueId,
|
||||
s"Merge pull request #${issueId} from ${pullreq.requestUserName}/${pullreq.requestBranch}\n\n" + form.message,
|
||||
new PersonIdent(loginAccount.fullName, loginAccount.mailAddress)
|
||||
)
|
||||
case "rebase" =>
|
||||
rebasePullRequest(
|
||||
git,
|
||||
pullreq.branch,
|
||||
issueId,
|
||||
revCommits,
|
||||
new PersonIdent(loginAccount.fullName, loginAccount.mailAddress)
|
||||
)
|
||||
case "squash" =>
|
||||
squashPullRequest(
|
||||
git,
|
||||
pullreq.branch,
|
||||
issueId,
|
||||
s"${issue.title} (#${issueId})\n\n" + form.message,
|
||||
new PersonIdent(loginAccount.fullName, loginAccount.mailAddress)
|
||||
)
|
||||
}
|
||||
|
||||
// close issue by content of pull request
|
||||
val defaultBranch = getRepository(owner, name).get.repository.defaultBranch
|
||||
if (pullreq.branch == defaultBranch) {
|
||||
commits.flatten.foreach { commit =>
|
||||
closeIssuesFromMessage(commit.fullMessage, loginAccount.userName, owner, name).foreach {
|
||||
issueId =>
|
||||
getIssue(owner, name, issueId.toString).foreach { issue =>
|
||||
callIssuesWebHook("closed", repository, issue, baseUrl, loginAccount)
|
||||
PluginRegistry().getIssueHooks
|
||||
.foreach(_.closedByCommitComment(issue, repository, commit.fullMessage, loginAccount))
|
||||
}
|
||||
}
|
||||
}
|
||||
val issueContent = issue.title + " " + issue.content.getOrElse("")
|
||||
closeIssuesFromMessage(
|
||||
issueContent,
|
||||
loginAccount.userName,
|
||||
owner,
|
||||
name
|
||||
).foreach { issueId =>
|
||||
getIssue(owner, name, issueId.toString).foreach { issue =>
|
||||
callIssuesWebHook("closed", repository, issue, baseUrl, loginAccount)
|
||||
PluginRegistry().getIssueHooks
|
||||
.foreach(_.closedByCommitComment(issue, repository, issueContent, loginAccount))
|
||||
}
|
||||
}
|
||||
closeIssuesFromMessage(form.message, loginAccount.userName, owner, name).foreach { issueId =>
|
||||
getIssue(owner, name, issueId.toString).foreach { issue =>
|
||||
callIssuesWebHook("closed", repository, issue, baseUrl, loginAccount)
|
||||
PluginRegistry().getIssueHooks
|
||||
.foreach(_.closedByCommitComment(issue, repository, issueContent, loginAccount))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updatePullRequests(owner, name, pullreq.branch)
|
||||
|
||||
// call web hook
|
||||
callPullRequestWebHook("closed", repository, issueId, context.baseUrl, context.loginAccount.get)
|
||||
|
||||
// call hooks
|
||||
PluginRegistry().getPullRequestHooks.foreach { h =>
|
||||
h.addedComment(commentId, form.message, issue, repository)
|
||||
h.merged(issue, repository)
|
||||
}
|
||||
|
||||
redirect(s"/${owner}/${name}/pull/${issueId}")
|
||||
}
|
||||
}
|
||||
}
|
||||
} else Some(BadRequest())
|
||||
mergePullRequest(repository, issueId, context.loginAccount.get, form.message, form.strategy) match {
|
||||
case Right(objectId) => redirect(s"/${owner}/${name}/pull/${issueId}")
|
||||
case Left(message) => Some(BadRequest())
|
||||
}
|
||||
} getOrElse NotFound()
|
||||
})
|
||||
|
||||
@@ -731,7 +564,7 @@ trait PullRequestsControllerBase extends ControllerBase {
|
||||
recordPullRequestActivity(owner, name, loginUserName, issueId, form.title)
|
||||
|
||||
// call web hook
|
||||
callPullRequestWebHook("opened", repository, issueId, context.baseUrl, context.loginAccount.get)
|
||||
callPullRequestWebHook("opened", repository, issueId, context.loginAccount.get)
|
||||
|
||||
getIssue(owner, name, issueId.toString) foreach { issue =>
|
||||
// extract references and create refer comment
|
||||
|
||||
@@ -13,13 +13,11 @@ import gitbucket.core.util.SyntaxSugars._
|
||||
import gitbucket.core.util.Implicits._
|
||||
import gitbucket.core.util.Directory._
|
||||
import org.scalatra.forms._
|
||||
import org.apache.commons.io.FileUtils
|
||||
import org.scalatra.i18n.Messages
|
||||
import org.eclipse.jgit.api.Git
|
||||
import org.eclipse.jgit.lib.Constants
|
||||
import org.eclipse.jgit.lib.ObjectId
|
||||
import gitbucket.core.model.WebHookContentType
|
||||
import gitbucket.core.plugin.PluginRegistry
|
||||
|
||||
class RepositorySettingsController
|
||||
extends RepositorySettingsControllerBase
|
||||
@@ -148,29 +146,6 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
||||
if (repository.name != form.repositoryName) {
|
||||
// Update database
|
||||
renameRepository(repository.owner, repository.name, repository.owner, form.repositoryName)
|
||||
// Move git repository
|
||||
defining(getRepositoryDir(repository.owner, repository.name)) { dir =>
|
||||
if (dir.isDirectory) {
|
||||
FileUtils.moveDirectory(dir, getRepositoryDir(repository.owner, form.repositoryName))
|
||||
}
|
||||
}
|
||||
// Move wiki repository
|
||||
defining(getWikiRepositoryDir(repository.owner, repository.name)) { dir =>
|
||||
if (dir.isDirectory) {
|
||||
FileUtils.moveDirectory(dir, getWikiRepositoryDir(repository.owner, form.repositoryName))
|
||||
}
|
||||
}
|
||||
// Move files directory
|
||||
defining(getRepositoryFilesDir(repository.owner, repository.name)) { dir =>
|
||||
if (dir.isDirectory) {
|
||||
FileUtils.moveDirectory(dir, getRepositoryFilesDir(repository.owner, form.repositoryName))
|
||||
}
|
||||
}
|
||||
// Delete parent directory
|
||||
FileUtil.deleteDirectoryIfEmpty(getRepositoryFilesDir(repository.owner, repository.name))
|
||||
|
||||
// Call hooks
|
||||
PluginRegistry().getRepositoryHooks.foreach(_.renamed(repository.owner, repository.name, form.repositoryName))
|
||||
}
|
||||
flash += "info" -> "Repository settings has been updated."
|
||||
redirect(s"/${repository.owner}/${form.repositoryName}/settings/options")
|
||||
@@ -392,31 +367,7 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
||||
post("/:owner/:repository/settings/transfer", transferForm)(ownerOnly { (form, repository) =>
|
||||
// Change repository owner
|
||||
if (repository.owner != form.newOwner) {
|
||||
LockUtil.lock(s"${repository.owner}/${repository.name}") {
|
||||
// Update database
|
||||
renameRepository(repository.owner, repository.name, form.newOwner, repository.name)
|
||||
// Move git repository
|
||||
defining(getRepositoryDir(repository.owner, repository.name)) { dir =>
|
||||
if (dir.isDirectory) {
|
||||
FileUtils.moveDirectory(dir, getRepositoryDir(form.newOwner, repository.name))
|
||||
}
|
||||
}
|
||||
// Move wiki repository
|
||||
defining(getWikiRepositoryDir(repository.owner, repository.name)) { dir =>
|
||||
if (dir.isDirectory) {
|
||||
FileUtils.moveDirectory(dir, getWikiRepositoryDir(form.newOwner, repository.name))
|
||||
}
|
||||
}
|
||||
// Move files directory
|
||||
defining(getRepositoryFilesDir(repository.owner, repository.name)) { dir =>
|
||||
if (dir.isDirectory) {
|
||||
FileUtils.moveDirectory(dir, getRepositoryFilesDir(form.newOwner, repository.name))
|
||||
}
|
||||
}
|
||||
|
||||
// Call hooks
|
||||
PluginRegistry().getRepositoryHooks.foreach(_.transferred(repository.owner, form.newOwner, repository.name))
|
||||
}
|
||||
renameRepository(repository.owner, repository.name, form.newOwner, repository.name)
|
||||
}
|
||||
redirect(s"/${form.newOwner}/${repository.name}")
|
||||
})
|
||||
@@ -425,19 +376,8 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
||||
* Delete the repository.
|
||||
*/
|
||||
post("/:owner/:repository/settings/delete")(ownerOnly { repository =>
|
||||
LockUtil.lock(s"${repository.owner}/${repository.name}") {
|
||||
// Delete the repository and related files
|
||||
deleteRepository(repository.owner, repository.name)
|
||||
|
||||
FileUtils.deleteDirectory(getRepositoryDir(repository.owner, repository.name))
|
||||
FileUtils.deleteDirectory(getWikiRepositoryDir(repository.owner, repository.name))
|
||||
FileUtils.deleteDirectory(getTemporaryDir(repository.owner, repository.name))
|
||||
FileUtils.deleteDirectory(getRepositoryFilesDir(repository.owner, repository.name))
|
||||
|
||||
// Call hooks
|
||||
PluginRegistry().getRepositoryHooks.foreach(_.deleted(repository.owner, repository.name))
|
||||
}
|
||||
|
||||
// Delete the repository and related files
|
||||
deleteRepository(repository.repository)
|
||||
redirect(s"/${repository.owner}")
|
||||
})
|
||||
|
||||
|
||||
@@ -7,14 +7,13 @@ import gitbucket.core.plugin.PluginRegistry
|
||||
import gitbucket.core.repo.html
|
||||
import gitbucket.core.helper
|
||||
import gitbucket.core.service._
|
||||
import gitbucket.core.service.RepositoryCommitFileService.CommitFile
|
||||
import gitbucket.core.util._
|
||||
import gitbucket.core.util.JGitUtil._
|
||||
import gitbucket.core.util.StringUtil._
|
||||
import gitbucket.core.util.SyntaxSugars._
|
||||
import gitbucket.core.util.Implicits._
|
||||
import gitbucket.core.util.Directory._
|
||||
import gitbucket.core.model.{Account, CommitState, CommitStatus, WebHook}
|
||||
import gitbucket.core.service.WebHookService._
|
||||
import gitbucket.core.model.{Account, CommitState, CommitStatus}
|
||||
import gitbucket.core.view
|
||||
import gitbucket.core.view.helpers
|
||||
import org.apache.commons.compress.archivers.{ArchiveEntry, ArchiveOutputStream}
|
||||
@@ -24,15 +23,12 @@ import org.apache.commons.compress.compressors.bzip2.BZip2CompressorOutputStream
|
||||
import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream
|
||||
import org.apache.commons.compress.compressors.xz.XZCompressorOutputStream
|
||||
import org.apache.commons.compress.utils.IOUtils
|
||||
import org.scalatra.forms._
|
||||
import org.apache.commons.io.FileUtils
|
||||
import org.ec4j.core.model.PropertyType
|
||||
import org.scalatra.forms._
|
||||
import org.eclipse.jgit.api.{ArchiveCommand, Git}
|
||||
import org.eclipse.jgit.archive.{TgzFormat, ZipFormat}
|
||||
import org.eclipse.jgit.dircache.{DirCache, DirCacheBuilder}
|
||||
import org.eclipse.jgit.errors.MissingObjectException
|
||||
import org.eclipse.jgit.lib._
|
||||
import org.eclipse.jgit.transport.{ReceiveCommand, ReceivePack}
|
||||
import org.eclipse.jgit.treewalk.TreeWalk
|
||||
import org.eclipse.jgit.treewalk.filter.PathFilter
|
||||
import org.json4s.jackson.Serialization
|
||||
@@ -42,6 +38,7 @@ import org.scalatra.i18n.Messages
|
||||
class RepositoryViewerController
|
||||
extends RepositoryViewerControllerBase
|
||||
with RepositoryService
|
||||
with RepositoryCommitFileService
|
||||
with AccountService
|
||||
with ActivityService
|
||||
with IssuesService
|
||||
@@ -64,6 +61,7 @@ class RepositoryViewerController
|
||||
*/
|
||||
trait RepositoryViewerControllerBase extends ControllerBase {
|
||||
self: RepositoryService
|
||||
with RepositoryCommitFileService
|
||||
with AccountService
|
||||
with ActivityService
|
||||
with IssuesService
|
||||
@@ -319,13 +317,34 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
||||
CommitFile(line.substring(0, i).trim, line.substring(i + 1).trim)
|
||||
}
|
||||
|
||||
val newFiles = files.map { file =>
|
||||
file.copy(name = if (form.path.length == 0) file.name else s"${form.path}/${file.name}")
|
||||
}
|
||||
|
||||
commitFiles(
|
||||
repository = repository,
|
||||
branch = form.branch,
|
||||
path = form.path,
|
||||
files = files,
|
||||
message = form.message.getOrElse("Add files via upload")
|
||||
)
|
||||
message = form.message.getOrElse("Add files via upload"),
|
||||
loginAccount = context.loginAccount.get
|
||||
) {
|
||||
case (git, headTip, builder, inserter) =>
|
||||
JGitUtil.processTree(git, headTip) { (path, tree) =>
|
||||
if (!newFiles.exists(_.name.contains(path))) {
|
||||
builder.add(JGitUtil.createDirCacheEntry(path, tree.getEntryFileMode, tree.getEntryObjectId))
|
||||
}
|
||||
}
|
||||
|
||||
newFiles.foreach { file =>
|
||||
val bytes =
|
||||
FileUtils.readFileToByteArray(new File(getTemporaryDir(session.getId), FileUtil.checkFilename(file.id)))
|
||||
builder.add(
|
||||
JGitUtil.createDirCacheEntry(file.name, FileMode.REGULAR_FILE, inserter.insert(Constants.OBJ_BLOB, bytes))
|
||||
)
|
||||
builder.finish()
|
||||
}
|
||||
}
|
||||
|
||||
if (form.path.length == 0) {
|
||||
redirect(s"/${repository.owner}/${repository.name}/tree/${form.branch}")
|
||||
@@ -394,7 +413,8 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
||||
content = appendNewLine(convertLineSeparator(form.content, form.lineSeparator), form.lineSeparator),
|
||||
charset = form.charset,
|
||||
message = form.message.getOrElse(s"Create ${form.newFileName}"),
|
||||
commit = form.commit
|
||||
commit = form.commit,
|
||||
loginAccount = context.loginAccount.get
|
||||
)
|
||||
|
||||
redirect(
|
||||
@@ -417,7 +437,8 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
||||
} else {
|
||||
form.message.getOrElse(s"Rename ${form.oldFileName.get} to ${form.newFileName}")
|
||||
},
|
||||
commit = form.commit
|
||||
commit = form.commit,
|
||||
loginAccount = context.loginAccount.get
|
||||
)
|
||||
|
||||
redirect(
|
||||
@@ -436,7 +457,8 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
||||
content = "",
|
||||
charset = "",
|
||||
message = form.message.getOrElse(s"Delete ${form.fileName}"),
|
||||
commit = form.commit
|
||||
commit = form.commit,
|
||||
loginAccount = context.loginAccount.get
|
||||
)
|
||||
|
||||
println(form.path)
|
||||
@@ -593,50 +615,17 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
||||
post("/:owner/:repository/commit/:id/comment/new", commentForm)(readableUsersOnly { (form, repository) =>
|
||||
val id = params("id")
|
||||
createCommitComment(
|
||||
repository.owner,
|
||||
repository.name,
|
||||
repository,
|
||||
id,
|
||||
context.loginAccount.get.userName,
|
||||
context.loginAccount.get,
|
||||
form.content,
|
||||
form.fileName,
|
||||
form.oldLineNumber,
|
||||
form.newLineNumber,
|
||||
form.diff,
|
||||
form.issueId
|
||||
)
|
||||
|
||||
for {
|
||||
fileName <- form.fileName
|
||||
diff <- form.diff
|
||||
} {
|
||||
saveCommitCommentDiff(
|
||||
repository.owner,
|
||||
repository.name,
|
||||
id,
|
||||
fileName,
|
||||
form.oldLineNumber,
|
||||
form.newLineNumber,
|
||||
diff
|
||||
)
|
||||
}
|
||||
|
||||
form.issueId match {
|
||||
case Some(issueId) =>
|
||||
recordCommentPullRequestActivity(
|
||||
repository.owner,
|
||||
repository.name,
|
||||
context.loginAccount.get.userName,
|
||||
issueId,
|
||||
form.content
|
||||
)
|
||||
case None =>
|
||||
recordCommentCommitActivity(
|
||||
repository.owner,
|
||||
repository.name,
|
||||
context.loginAccount.get.userName,
|
||||
id,
|
||||
form.content
|
||||
)
|
||||
}
|
||||
redirect(s"/${repository.owner}/${repository.name}/commit/${id}")
|
||||
})
|
||||
|
||||
@@ -661,64 +650,18 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
||||
ajaxPost("/:owner/:repository/commit/:id/comment/_data/new", commentForm)(readableUsersOnly { (form, repository) =>
|
||||
val id = params("id")
|
||||
val commentId = createCommitComment(
|
||||
repository.owner,
|
||||
repository.name,
|
||||
repository,
|
||||
id,
|
||||
context.loginAccount.get.userName,
|
||||
context.loginAccount.get,
|
||||
form.content,
|
||||
form.fileName,
|
||||
form.oldLineNumber,
|
||||
form.newLineNumber,
|
||||
form.diff,
|
||||
form.issueId
|
||||
)
|
||||
|
||||
for {
|
||||
fileName <- form.fileName
|
||||
diff <- form.diff
|
||||
} {
|
||||
saveCommitCommentDiff(
|
||||
repository.owner,
|
||||
repository.name,
|
||||
id,
|
||||
fileName,
|
||||
form.oldLineNumber,
|
||||
form.newLineNumber,
|
||||
diff
|
||||
)
|
||||
}
|
||||
|
||||
val comment = getCommitComment(repository.owner, repository.name, commentId.toString).get
|
||||
form.issueId match {
|
||||
case Some(issueId) =>
|
||||
getPullRequest(repository.owner, repository.name, issueId).foreach {
|
||||
case (issue, pullRequest) =>
|
||||
recordCommentPullRequestActivity(
|
||||
repository.owner,
|
||||
repository.name,
|
||||
context.loginAccount.get.userName,
|
||||
issueId,
|
||||
form.content
|
||||
)
|
||||
PluginRegistry().getPullRequestHooks.foreach(_.addedComment(commentId, form.content, issue, repository))
|
||||
callPullRequestReviewCommentWebHook(
|
||||
"create",
|
||||
comment,
|
||||
repository,
|
||||
issue,
|
||||
pullRequest,
|
||||
context.baseUrl,
|
||||
context.loginAccount.get
|
||||
)
|
||||
}
|
||||
case None =>
|
||||
recordCommentCommitActivity(
|
||||
repository.owner,
|
||||
repository.name,
|
||||
context.loginAccount.get.userName,
|
||||
id,
|
||||
form.content
|
||||
)
|
||||
}
|
||||
helper.html
|
||||
.commitcomment(comment, hasDeveloperRole(repository.owner, repository.name, context.loginAccount), repository)
|
||||
})
|
||||
@@ -930,185 +873,6 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
||||
lazy val isValid: Boolean = fileIds.nonEmpty
|
||||
}
|
||||
|
||||
case class CommitFile(id: String, name: String)
|
||||
|
||||
private def commitFiles(
|
||||
repository: RepositoryService.RepositoryInfo,
|
||||
files: Seq[CommitFile],
|
||||
branch: String,
|
||||
path: String,
|
||||
message: String
|
||||
) = {
|
||||
// prepend path to the filename
|
||||
val newFiles = files.map { file =>
|
||||
file.copy(name = if (path.length == 0) file.name else s"${path}/${file.name}")
|
||||
}
|
||||
|
||||
_commitFile(repository, branch, message) {
|
||||
case (git, headTip, builder, inserter) =>
|
||||
JGitUtil.processTree(git, headTip) { (path, tree) =>
|
||||
if (!newFiles.exists(_.name.contains(path))) {
|
||||
builder.add(JGitUtil.createDirCacheEntry(path, tree.getEntryFileMode, tree.getEntryObjectId))
|
||||
}
|
||||
}
|
||||
|
||||
newFiles.foreach { file =>
|
||||
val bytes =
|
||||
FileUtils.readFileToByteArray(new File(getTemporaryDir(session.getId), FileUtil.checkFilename(file.id)))
|
||||
builder.add(
|
||||
JGitUtil.createDirCacheEntry(file.name, FileMode.REGULAR_FILE, inserter.insert(Constants.OBJ_BLOB, bytes))
|
||||
)
|
||||
builder.finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def commitFile(
|
||||
repository: RepositoryService.RepositoryInfo,
|
||||
branch: String,
|
||||
path: String,
|
||||
newFileName: Option[String],
|
||||
oldFileName: Option[String],
|
||||
content: String,
|
||||
charset: String,
|
||||
message: String,
|
||||
commit: String
|
||||
) = {
|
||||
|
||||
val newPath = newFileName.map { newFileName =>
|
||||
if (path.length == 0) newFileName else s"${path}/${newFileName}"
|
||||
}
|
||||
val oldPath = oldFileName.map { oldFileName =>
|
||||
if (path.length == 0) oldFileName else s"${path}/${oldFileName}"
|
||||
}
|
||||
|
||||
_commitFile(repository, branch, message) {
|
||||
case (git, headTip, builder, inserter) =>
|
||||
if (headTip.getName == commit) {
|
||||
val permission = JGitUtil
|
||||
.processTree(git, headTip) { (path, tree) =>
|
||||
// Add all entries except the editing file
|
||||
if (!newPath.contains(path) && !oldPath.contains(path)) {
|
||||
builder.add(JGitUtil.createDirCacheEntry(path, tree.getEntryFileMode, tree.getEntryObjectId))
|
||||
}
|
||||
// Retrieve permission if file exists to keep it
|
||||
oldPath.collect { case x if x == path => tree.getEntryFileMode.getBits }
|
||||
}
|
||||
.flatten
|
||||
.headOption
|
||||
|
||||
newPath.foreach { newPath =>
|
||||
builder.add(JGitUtil.createDirCacheEntry(newPath, permission.map { bits =>
|
||||
FileMode.fromBits(bits)
|
||||
} getOrElse FileMode.REGULAR_FILE, inserter.insert(Constants.OBJ_BLOB, content.getBytes(charset))))
|
||||
}
|
||||
builder.finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def _commitFile(repository: RepositoryService.RepositoryInfo, branch: String, message: String)(
|
||||
f: (Git, ObjectId, DirCacheBuilder, ObjectInserter) => Unit
|
||||
) = {
|
||||
|
||||
LockUtil.lock(s"${repository.owner}/${repository.name}") {
|
||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
val loginAccount = context.loginAccount.get
|
||||
val builder = DirCache.newInCore.builder()
|
||||
val inserter = git.getRepository.newObjectInserter()
|
||||
val headName = s"refs/heads/${branch}"
|
||||
val headTip = git.getRepository.resolve(headName)
|
||||
|
||||
f(git, headTip, builder, inserter)
|
||||
|
||||
val commitId = JGitUtil.createNewCommit(
|
||||
git,
|
||||
inserter,
|
||||
headTip,
|
||||
builder.getDirCache.writeTree(inserter),
|
||||
headName,
|
||||
loginAccount.fullName,
|
||||
loginAccount.mailAddress,
|
||||
message
|
||||
)
|
||||
|
||||
inserter.flush()
|
||||
inserter.close()
|
||||
|
||||
val receivePack = new ReceivePack(git.getRepository)
|
||||
val receiveCommand = new ReceiveCommand(headTip, commitId, headName)
|
||||
|
||||
// call post commit hook
|
||||
val error = PluginRegistry().getReceiveHooks.flatMap { hook =>
|
||||
hook.preReceive(repository.owner, repository.name, receivePack, receiveCommand, loginAccount.userName)
|
||||
}.headOption
|
||||
|
||||
error match {
|
||||
case Some(error) =>
|
||||
// commit is rejected
|
||||
// TODO Notify commit failure to edited user
|
||||
val refUpdate = git.getRepository.updateRef(headName)
|
||||
refUpdate.setNewObjectId(headTip)
|
||||
refUpdate.setForceUpdate(true)
|
||||
refUpdate.update()
|
||||
|
||||
case None =>
|
||||
// update refs
|
||||
val refUpdate = git.getRepository.updateRef(headName)
|
||||
refUpdate.setNewObjectId(commitId)
|
||||
refUpdate.setForceUpdate(false)
|
||||
refUpdate.setRefLogIdent(new PersonIdent(loginAccount.fullName, loginAccount.mailAddress))
|
||||
refUpdate.update()
|
||||
|
||||
// update pull request
|
||||
updatePullRequests(repository.owner, repository.name, branch)
|
||||
|
||||
// record activity
|
||||
val commitInfo = new CommitInfo(JGitUtil.getRevCommitFromId(git, commitId))
|
||||
recordPushActivity(repository.owner, repository.name, loginAccount.userName, branch, List(commitInfo))
|
||||
|
||||
// create issue comment by commit message
|
||||
createIssueComment(repository.owner, repository.name, commitInfo)
|
||||
|
||||
// close issue by commit message
|
||||
if (branch == repository.repository.defaultBranch) {
|
||||
closeIssuesFromMessage(message, loginAccount.userName, repository.owner, repository.name).foreach {
|
||||
issueId =>
|
||||
getIssue(repository.owner, repository.name, issueId.toString).foreach { issue =>
|
||||
callIssuesWebHook("closed", repository, issue, baseUrl, loginAccount)
|
||||
PluginRegistry().getIssueHooks
|
||||
.foreach(_.closedByCommitComment(issue, repository, message, loginAccount))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// call post commit hook
|
||||
PluginRegistry().getReceiveHooks.foreach { hook =>
|
||||
hook.postReceive(repository.owner, repository.name, receivePack, receiveCommand, loginAccount.userName)
|
||||
}
|
||||
|
||||
//call web hook
|
||||
callPullRequestWebHookByRequestBranch("synchronize", repository, branch, context.baseUrl, loginAccount)
|
||||
val commit = new JGitUtil.CommitInfo(JGitUtil.getRevCommitFromId(git, commitId))
|
||||
callWebHookOf(repository.owner, repository.name, WebHook.Push) {
|
||||
getAccountByUserName(repository.owner).map { ownerAccount =>
|
||||
WebHookPushPayload(
|
||||
git,
|
||||
loginAccount,
|
||||
headName,
|
||||
repository,
|
||||
List(commit),
|
||||
ownerAccount,
|
||||
oldId = headTip,
|
||||
newId = commitId
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val readmeFiles = PluginRegistry().renderableExtensions.map { extension =>
|
||||
s"readme.${extension}"
|
||||
} ++ Seq("readme.txt", "readme")
|
||||
|
||||
@@ -8,7 +8,7 @@ import gitbucket.core.service.RepositoryService.RepositoryInfo
|
||||
import gitbucket.core.service.SystemSettingsService.SystemSettings
|
||||
import gitbucket.core.util.SyntaxSugars._
|
||||
import io.github.gitbucket.solidbase.model.Version
|
||||
import org.apache.sshd.server.command.Command
|
||||
import org.apache.sshd.server.Command
|
||||
import play.twirl.api.Html
|
||||
|
||||
/**
|
||||
|
||||
@@ -24,7 +24,7 @@ import io.github.gitbucket.solidbase.manager.JDBCVersionManager
|
||||
import io.github.gitbucket.solidbase.model.Module
|
||||
import org.apache.commons.io.FileUtils
|
||||
import org.apache.http.client.methods.HttpGet
|
||||
import org.apache.sshd.server.command.Command
|
||||
import org.apache.sshd.server.Command
|
||||
import org.slf4j.LoggerFactory
|
||||
import play.twirl.api.Html
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import gitbucket.core.model.Profile.profile.blockingApi._
|
||||
import gitbucket.core.model.Profile.dateColumnType
|
||||
import gitbucket.core.util.{LDAPUtil, StringUtil}
|
||||
import StringUtil._
|
||||
import gitbucket.core.plugin.PluginRegistry
|
||||
import gitbucket.core.service.SystemSettingsService.SystemSettings
|
||||
|
||||
trait AccountService {
|
||||
@@ -183,6 +184,15 @@ trait AccountService {
|
||||
account
|
||||
}
|
||||
|
||||
def suspendAccount(account: Account)(implicit s: Session): Unit = {
|
||||
// Remove from GROUP_MEMBER and COLLABORATOR
|
||||
removeUserRelatedData(account.userName)
|
||||
updateAccount(account.copy(isRemoved = true))
|
||||
|
||||
// call hooks
|
||||
PluginRegistry().getAccountHooks.foreach(_.deleted(account.userName))
|
||||
}
|
||||
|
||||
def updateAccount(account: Account)(implicit s: Session): Unit =
|
||||
Accounts
|
||||
.filter { a =>
|
||||
@@ -281,6 +291,15 @@ trait AccountService {
|
||||
Collaborators.filter(_.collaboratorName === userName.bind).delete
|
||||
}
|
||||
|
||||
def removeUser(account: Account)(implicit s: Session): Unit = {
|
||||
// Remove from GROUP_MEMBER and COLLABORATOR
|
||||
removeUserRelatedData(account.userName)
|
||||
updateAccount(account.copy(isRemoved = true))
|
||||
|
||||
// call hooks
|
||||
PluginRegistry().getAccountHooks.foreach(_.deleted(account.userName))
|
||||
}
|
||||
|
||||
def getGroupNames(userName: String)(implicit s: Session): List[String] = {
|
||||
List(userName) ++
|
||||
Collaborators.filter(_.collaboratorName === userName.bind).sortBy(_.userName).map(_.userName).list.distinct
|
||||
|
||||
@@ -360,7 +360,7 @@ trait ActivityService {
|
||||
repositoryName,
|
||||
activityUserName,
|
||||
"release",
|
||||
s"[user:${activityUserName}] released ${name} at [repo:${userName}/${repositoryName}]",
|
||||
s"[user:${activityUserName}] released [release:${userName}/${repositoryName}/${name}] at [repo:${userName}/${repositoryName}]",
|
||||
None,
|
||||
currentDate
|
||||
)
|
||||
|
||||
@@ -2,15 +2,20 @@ package gitbucket.core.service
|
||||
|
||||
import java.io.File
|
||||
|
||||
import gitbucket.core.model.CommitComment
|
||||
import gitbucket.core.api.JsonFormat
|
||||
import gitbucket.core.controller.Context
|
||||
import gitbucket.core.model.{Account, CommitComment}
|
||||
import gitbucket.core.model.Profile._
|
||||
import gitbucket.core.model.Profile.profile.blockingApi._
|
||||
import gitbucket.core.model.Profile.dateColumnType
|
||||
import gitbucket.core.plugin.PluginRegistry
|
||||
import gitbucket.core.service.RepositoryService.RepositoryInfo
|
||||
import gitbucket.core.util.Directory._
|
||||
import gitbucket.core.util.{FileUtil, StringUtil}
|
||||
import org.apache.commons.io.FileUtils
|
||||
|
||||
trait CommitsService {
|
||||
self: ActivityService with PullRequestService with WebHookPullRequestReviewCommentService =>
|
||||
|
||||
def getCommitComments(owner: String, repository: String, commitId: String, includePullRequest: Boolean)(
|
||||
implicit s: Session
|
||||
@@ -28,21 +33,21 @@ trait CommitsService {
|
||||
None
|
||||
|
||||
def createCommitComment(
|
||||
owner: String,
|
||||
repository: String,
|
||||
repository: RepositoryInfo,
|
||||
commitId: String,
|
||||
loginUser: String,
|
||||
loginAccount: Account,
|
||||
content: String,
|
||||
fileName: Option[String],
|
||||
oldLine: Option[Int],
|
||||
newLine: Option[Int],
|
||||
diff: Option[String],
|
||||
issueId: Option[Int]
|
||||
)(implicit s: Session): Int =
|
||||
CommitComments returning CommitComments.map(_.commentId) insert CommitComment(
|
||||
userName = owner,
|
||||
repositoryName = repository,
|
||||
)(implicit s: Session, c: JsonFormat.Context, context: Context): Int = {
|
||||
val commentId = CommitComments returning CommitComments.map(_.commentId) insert CommitComment(
|
||||
userName = repository.owner,
|
||||
repositoryName = repository.name,
|
||||
commitId = commitId,
|
||||
commentedUserName = loginUser,
|
||||
commentedUserName = loginAccount.userName,
|
||||
content = content,
|
||||
fileName = fileName,
|
||||
oldLine = oldLine,
|
||||
@@ -55,6 +60,56 @@ trait CommitsService {
|
||||
originalNewLine = newLine
|
||||
)
|
||||
|
||||
for {
|
||||
fileName <- fileName
|
||||
diff <- diff
|
||||
} {
|
||||
saveCommitCommentDiff(
|
||||
repository.owner,
|
||||
repository.name,
|
||||
commitId,
|
||||
fileName,
|
||||
oldLine,
|
||||
newLine,
|
||||
diff
|
||||
)
|
||||
}
|
||||
|
||||
val comment = getCommitComment(repository.owner, repository.name, commentId.toString).get
|
||||
issueId match {
|
||||
case Some(issueId) =>
|
||||
getPullRequest(repository.owner, repository.name, issueId).foreach {
|
||||
case (issue, pullRequest) =>
|
||||
recordCommentPullRequestActivity(
|
||||
repository.owner,
|
||||
repository.name,
|
||||
loginAccount.userName,
|
||||
issueId,
|
||||
content
|
||||
)
|
||||
PluginRegistry().getPullRequestHooks.foreach(_.addedComment(commentId, content, issue, repository))
|
||||
callPullRequestReviewCommentWebHook(
|
||||
"create",
|
||||
comment,
|
||||
repository,
|
||||
issue,
|
||||
pullRequest,
|
||||
loginAccount
|
||||
)
|
||||
}
|
||||
case None =>
|
||||
recordCommentCommitActivity(
|
||||
repository.owner,
|
||||
repository.name,
|
||||
loginAccount.userName,
|
||||
commitId,
|
||||
content
|
||||
)
|
||||
}
|
||||
|
||||
commentId
|
||||
}
|
||||
|
||||
def updateCommitCommentPosition(commentId: Int, commitId: String, oldLine: Option[Int], newLine: Option[Int])(
|
||||
implicit s: Session
|
||||
): Unit =
|
||||
|
||||
@@ -87,9 +87,9 @@ trait HandleCommentService {
|
||||
case "reopen" => "reopened"
|
||||
}
|
||||
if (issue.isPullRequest)
|
||||
callPullRequestWebHook(webHookAction, repository, issue.issueId, context.baseUrl, loginAccount)
|
||||
callPullRequestWebHook(webHookAction, repository, issue.issueId, loginAccount)
|
||||
else
|
||||
callIssuesWebHook(webHookAction, repository, issue, context.baseUrl, loginAccount)
|
||||
callIssuesWebHook(webHookAction, repository, issue, loginAccount)
|
||||
}
|
||||
|
||||
// call hooks
|
||||
|
||||
@@ -57,7 +57,7 @@ trait IssueCreationService {
|
||||
createReferComment(owner, name, issue, title + " " + body.getOrElse(""), loginAccount)
|
||||
|
||||
// call web hooks
|
||||
callIssuesWebHook("opened", repository, issue, context.baseUrl, loginAccount)
|
||||
callIssuesWebHook("opened", repository, issue, loginAccount)
|
||||
|
||||
// call hooks
|
||||
PluginRegistry().getIssueHooks.foreach(_.created(issue, repository))
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
package gitbucket.core.service
|
||||
|
||||
import gitbucket.core.model.Account
|
||||
import gitbucket.core.api.JsonFormat
|
||||
import gitbucket.core.controller.Context
|
||||
import gitbucket.core.model.{Account, PullRequest, WebHook}
|
||||
import gitbucket.core.plugin.PluginRegistry
|
||||
import gitbucket.core.service.RepositoryService.RepositoryInfo
|
||||
import gitbucket.core.util.Directory._
|
||||
import gitbucket.core.util.{JGitUtil, LockUtil}
|
||||
import gitbucket.core.util.SyntaxSugars._
|
||||
import gitbucket.core.model.Profile._
|
||||
import gitbucket.core.model.Profile.profile._
|
||||
import gitbucket.core.model.Profile.profile.blockingApi._
|
||||
import org.eclipse.jgit.merge.{MergeStrategy, Merger, RecursiveMerger}
|
||||
import org.eclipse.jgit.api.Git
|
||||
import org.eclipse.jgit.transport.RefSpec
|
||||
@@ -13,6 +21,13 @@ import org.eclipse.jgit.revwalk.{RevCommit, RevWalk}
|
||||
import scala.collection.JavaConverters._
|
||||
|
||||
trait MergeService {
|
||||
self: AccountService
|
||||
with ActivityService
|
||||
with IssuesService
|
||||
with RepositoryService
|
||||
with PullRequestService
|
||||
with WebHookPullRequestService =>
|
||||
|
||||
import MergeService._
|
||||
|
||||
/**
|
||||
@@ -43,7 +58,13 @@ trait MergeService {
|
||||
}
|
||||
|
||||
/** merge the pull request with a merge commit */
|
||||
def mergePullRequest(git: Git, branch: String, issueId: Int, message: String, committer: PersonIdent): Unit = {
|
||||
def mergePullRequest(
|
||||
git: Git,
|
||||
branch: String,
|
||||
issueId: Int,
|
||||
message: String,
|
||||
committer: PersonIdent
|
||||
): ObjectId = {
|
||||
new MergeCacheInfo(git, branch, issueId).merge(message, committer)
|
||||
}
|
||||
|
||||
@@ -54,12 +75,18 @@ trait MergeService {
|
||||
issueId: Int,
|
||||
commits: Seq[RevCommit],
|
||||
committer: PersonIdent
|
||||
): Unit = {
|
||||
): ObjectId = {
|
||||
new MergeCacheInfo(git, branch, issueId).rebase(committer, commits)
|
||||
}
|
||||
|
||||
/** squash commits in the pull request and append it */
|
||||
def squashPullRequest(git: Git, branch: String, issueId: Int, message: String, committer: PersonIdent): Unit = {
|
||||
def squashPullRequest(
|
||||
git: Git,
|
||||
branch: String,
|
||||
issueId: Int,
|
||||
message: String,
|
||||
committer: PersonIdent
|
||||
): ObjectId = {
|
||||
new MergeCacheInfo(git, branch, issueId).squash(message, committer)
|
||||
}
|
||||
|
||||
@@ -136,27 +163,223 @@ trait MergeService {
|
||||
tryMergeRemote(userName, repositoryName, branch, requestUserName, requestRepositoryName, requestBranch).left.toOption
|
||||
|
||||
def pullRemote(
|
||||
localUserName: String,
|
||||
localRepositoryName: String,
|
||||
localRepository: RepositoryInfo,
|
||||
localBranch: String,
|
||||
remoteUserName: String,
|
||||
remoteRepositoryName: String,
|
||||
remoteRepository: RepositoryInfo,
|
||||
remoteBranch: String,
|
||||
loginAccount: Account,
|
||||
message: String
|
||||
): Option[ObjectId] = {
|
||||
message: String,
|
||||
pullreq: Option[PullRequest]
|
||||
)(implicit s: Session, c: JsonFormat.Context): Option[ObjectId] = {
|
||||
val localUserName = localRepository.owner
|
||||
val localRepositoryName = localRepository.name
|
||||
val remoteUserName = remoteRepository.owner
|
||||
val remoteRepositoryName = remoteRepository.name
|
||||
tryMergeRemote(localUserName, localRepositoryName, localBranch, remoteUserName, remoteRepositoryName, remoteBranch).map {
|
||||
case (newTreeId, oldBaseId, oldHeadId) =>
|
||||
using(Git.open(getRepositoryDir(localUserName, localRepositoryName))) { git =>
|
||||
val existIds = JGitUtil.getAllCommitIds(git).toSet
|
||||
|
||||
val committer = new PersonIdent(loginAccount.fullName, loginAccount.mailAddress)
|
||||
val newCommit =
|
||||
Util.createMergeCommit(git.getRepository, newTreeId, committer, message, Seq(oldBaseId, oldHeadId))
|
||||
Util.updateRefs(git.getRepository, s"refs/heads/${localBranch}", newCommit, false, committer, Some("merge"))
|
||||
|
||||
val commits = git.log
|
||||
.addRange(oldBaseId, newCommit)
|
||||
.call
|
||||
.iterator
|
||||
.asScala
|
||||
.map(c => new JGitUtil.CommitInfo(c))
|
||||
.toList
|
||||
|
||||
commits.foreach { commit =>
|
||||
if (!existIds.contains(commit.id)) {
|
||||
createIssueComment(localUserName, localRepositoryName, commit)
|
||||
}
|
||||
}
|
||||
|
||||
// record activity
|
||||
recordPushActivity(
|
||||
localUserName,
|
||||
localRepositoryName,
|
||||
loginAccount.userName,
|
||||
localBranch,
|
||||
commits
|
||||
)
|
||||
|
||||
// close issue by commit message
|
||||
if (localBranch == localRepository.repository.defaultBranch) {
|
||||
commits.foreach { commit =>
|
||||
closeIssuesFromMessage(commit.fullMessage, loginAccount.userName, localUserName, localRepositoryName)
|
||||
.foreach { issueId =>
|
||||
getIssue(localRepository.owner, localRepository.name, issueId.toString).foreach { issue =>
|
||||
callIssuesWebHook("closed", localRepository, issue, loginAccount)
|
||||
PluginRegistry().getIssueHooks
|
||||
.foreach(
|
||||
_.closedByCommitComment(issue, localRepository, commit.fullMessage, loginAccount)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pullreq.foreach { pullreq =>
|
||||
callWebHookOf(localRepository.owner, localRepository.name, WebHook.Push) {
|
||||
for {
|
||||
ownerAccount <- getAccountByUserName(localRepository.owner)
|
||||
} yield {
|
||||
WebHookService.WebHookPushPayload(
|
||||
git,
|
||||
loginAccount,
|
||||
pullreq.requestBranch,
|
||||
localRepository,
|
||||
commits,
|
||||
ownerAccount,
|
||||
oldId = oldBaseId,
|
||||
newId = newCommit
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
oldBaseId
|
||||
}.toOption
|
||||
}
|
||||
|
||||
def mergePullRequest(
|
||||
repository: RepositoryInfo,
|
||||
issueId: Int,
|
||||
loginAccount: Account,
|
||||
message: String,
|
||||
strategy: String
|
||||
)(implicit s: Session, c: JsonFormat.Context, context: Context): Either[String, ObjectId] = {
|
||||
if (repository.repository.options.mergeOptions.split(",").contains(strategy)) {
|
||||
LockUtil.lock(s"${repository.owner}/${repository.name}") {
|
||||
getPullRequest(repository.owner, repository.name, issueId)
|
||||
.map {
|
||||
case (issue, pullreq) =>
|
||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
// mark issue as merged and close.
|
||||
val commentId =
|
||||
createComment(repository.owner, repository.name, loginAccount.userName, issueId, message, "merge")
|
||||
createComment(repository.owner, repository.name, loginAccount.userName, issueId, "Close", "close")
|
||||
updateClosed(repository.owner, repository.name, issueId, true)
|
||||
|
||||
// record activity
|
||||
recordMergeActivity(repository.owner, repository.name, loginAccount.userName, issueId, message)
|
||||
|
||||
val (commits, _) = getRequestCompareInfo(
|
||||
repository.owner,
|
||||
repository.name,
|
||||
pullreq.commitIdFrom,
|
||||
pullreq.requestUserName,
|
||||
pullreq.requestRepositoryName,
|
||||
pullreq.commitIdTo
|
||||
)
|
||||
|
||||
val revCommits = using(new RevWalk(git.getRepository)) { revWalk =>
|
||||
commits.flatten.map { commit =>
|
||||
revWalk.parseCommit(git.getRepository.resolve(commit.id))
|
||||
}
|
||||
}.reverse
|
||||
|
||||
// merge git repository
|
||||
(strategy match {
|
||||
case "merge-commit" =>
|
||||
Some(
|
||||
mergePullRequest(
|
||||
git,
|
||||
pullreq.branch,
|
||||
issueId,
|
||||
s"Merge pull request #${issueId} from ${pullreq.requestUserName}/${pullreq.requestBranch}\n\n" + message,
|
||||
new PersonIdent(loginAccount.fullName, loginAccount.mailAddress)
|
||||
)
|
||||
)
|
||||
case "rebase" =>
|
||||
Some(
|
||||
rebasePullRequest(
|
||||
git,
|
||||
pullreq.branch,
|
||||
issueId,
|
||||
revCommits,
|
||||
new PersonIdent(loginAccount.fullName, loginAccount.mailAddress)
|
||||
)
|
||||
)
|
||||
case "squash" =>
|
||||
Some(
|
||||
squashPullRequest(
|
||||
git,
|
||||
pullreq.branch,
|
||||
issueId,
|
||||
s"${issue.title} (#${issueId})\n\n" + message,
|
||||
new PersonIdent(loginAccount.fullName, loginAccount.mailAddress)
|
||||
)
|
||||
)
|
||||
case _ =>
|
||||
None
|
||||
}) match {
|
||||
case Some(newCommitId) =>
|
||||
// close issue by content of pull request
|
||||
val defaultBranch = getRepository(repository.owner, repository.name).get.repository.defaultBranch
|
||||
if (pullreq.branch == defaultBranch) {
|
||||
commits.flatten.foreach { commit =>
|
||||
closeIssuesFromMessage(
|
||||
commit.fullMessage,
|
||||
loginAccount.userName,
|
||||
repository.owner,
|
||||
repository.name
|
||||
).foreach { issueId =>
|
||||
getIssue(repository.owner, repository.name, issueId.toString).foreach { issue =>
|
||||
callIssuesWebHook("closed", repository, issue, loginAccount)
|
||||
PluginRegistry().getIssueHooks
|
||||
.foreach(_.closedByCommitComment(issue, repository, commit.fullMessage, loginAccount))
|
||||
}
|
||||
}
|
||||
}
|
||||
val issueContent = issue.title + " " + issue.content.getOrElse("")
|
||||
closeIssuesFromMessage(
|
||||
issueContent,
|
||||
loginAccount.userName,
|
||||
repository.owner,
|
||||
repository.name
|
||||
).foreach { issueId =>
|
||||
getIssue(repository.owner, repository.name, issueId.toString).foreach { issue =>
|
||||
callIssuesWebHook("closed", repository, issue, loginAccount)
|
||||
PluginRegistry().getIssueHooks
|
||||
.foreach(_.closedByCommitComment(issue, repository, issueContent, loginAccount))
|
||||
}
|
||||
}
|
||||
closeIssuesFromMessage(message, loginAccount.userName, repository.owner, repository.name)
|
||||
.foreach { issueId =>
|
||||
getIssue(repository.owner, repository.name, issueId.toString).foreach { issue =>
|
||||
callIssuesWebHook("closed", repository, issue, loginAccount)
|
||||
PluginRegistry().getIssueHooks
|
||||
.foreach(_.closedByCommitComment(issue, repository, issueContent, loginAccount))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updatePullRequests(repository.owner, repository.name, pullreq.branch, loginAccount, "closed")
|
||||
|
||||
// call hooks
|
||||
PluginRegistry().getPullRequestHooks.foreach { h =>
|
||||
h.addedComment(commentId, message, issue, repository)
|
||||
h.merged(issue, repository)
|
||||
}
|
||||
|
||||
Right(newCommitId)
|
||||
case None =>
|
||||
Left("Unknown strategy")
|
||||
}
|
||||
}
|
||||
case _ => Left("Unknown error")
|
||||
}
|
||||
.getOrElse(Left("Pull request not found"))
|
||||
}
|
||||
} else Left("Strategy not allowed")
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
object MergeService {
|
||||
@@ -191,13 +414,15 @@ object MergeService {
|
||||
force: Boolean,
|
||||
committer: PersonIdent,
|
||||
refLogMessage: Option[String] = None
|
||||
): Unit = {
|
||||
): ObjectId = {
|
||||
val refUpdate = repository.updateRef(ref)
|
||||
refUpdate.setNewObjectId(newObjectId)
|
||||
refUpdate.setForceUpdate(force)
|
||||
refUpdate.setRefLogIdent(committer)
|
||||
refLogMessage.foreach(refUpdate.setRefLogMessage(_, true))
|
||||
refUpdate.update()
|
||||
|
||||
newObjectId
|
||||
}
|
||||
}
|
||||
|
||||
@@ -265,7 +490,7 @@ object MergeService {
|
||||
}
|
||||
|
||||
// update branch from cache
|
||||
def merge(message: String, committer: PersonIdent) = {
|
||||
def merge(message: String, committer: PersonIdent): ObjectId = {
|
||||
if (checkConflict().isDefined) {
|
||||
throw new RuntimeException("This pull request can't merge automatically.")
|
||||
}
|
||||
@@ -278,7 +503,7 @@ object MergeService {
|
||||
Util.updateRefs(repository, s"refs/heads/${branch}", mergeCommitId, false, committer, Some("merged"))
|
||||
}
|
||||
|
||||
def rebase(committer: PersonIdent, commits: Seq[RevCommit]): Unit = {
|
||||
def rebase(committer: PersonIdent, commits: Seq[RevCommit]): ObjectId = {
|
||||
if (checkConflict().isDefined) {
|
||||
throw new RuntimeException("This pull request can't merge automatically.")
|
||||
}
|
||||
@@ -310,7 +535,7 @@ object MergeService {
|
||||
Util.updateRefs(repository, s"refs/heads/${branch}", previousId, false, committer, Some("rebased"))
|
||||
}
|
||||
|
||||
def squash(message: String, committer: PersonIdent): Unit = {
|
||||
def squash(message: String, committer: PersonIdent): ObjectId = {
|
||||
if (checkConflict().isDefined) {
|
||||
throw new RuntimeException("This pull request can't merge automatically.")
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import gitbucket.core.model.Profile._
|
||||
import gitbucket.core.model.Profile.profile.blockingApi._
|
||||
import difflib.{Delta, DiffUtils}
|
||||
import gitbucket.core.service.RepositoryService.RepositoryInfo
|
||||
import gitbucket.core.api.JsonFormat
|
||||
import gitbucket.core.util.SyntaxSugars._
|
||||
import gitbucket.core.util.Directory._
|
||||
import gitbucket.core.util.Implicits._
|
||||
@@ -17,7 +18,8 @@ import org.eclipse.jgit.lib.ObjectId
|
||||
|
||||
import scala.collection.JavaConverters._
|
||||
|
||||
trait PullRequestService { self: IssuesService with CommitsService =>
|
||||
trait PullRequestService {
|
||||
self: IssuesService with CommitsService with WebHookService with WebHookPullRequestService with RepositoryService =>
|
||||
import PullRequestService._
|
||||
|
||||
def getPullRequest(owner: String, repository: String, issueId: Int)(
|
||||
@@ -166,7 +168,10 @@ trait PullRequestService { self: IssuesService with CommitsService =>
|
||||
/**
|
||||
* Fetch pull request contents into refs/pull/${issueId}/head and update pull request table.
|
||||
*/
|
||||
def updatePullRequests(owner: String, repository: String, branch: String)(implicit s: Session): Unit =
|
||||
def updatePullRequests(owner: String, repository: String, branch: String, loginAccount: Account, action: String)(
|
||||
implicit s: Session,
|
||||
c: JsonFormat.Context
|
||||
): Unit = {
|
||||
getPullRequestsByRequest(owner, repository, branch, Some(false)).foreach { pullreq =>
|
||||
if (Repositories.filter(_.byRepository(pullreq.userName, pullreq.repositoryName)).exists.run) {
|
||||
// Update the git repository
|
||||
@@ -206,8 +211,17 @@ trait PullRequestService { self: IssuesService with CommitsService =>
|
||||
|
||||
// Update commit id in the PULL_REQUEST table
|
||||
updateCommitId(pullreq.userName, pullreq.repositoryName, pullreq.issueId, commitIdTo, commitIdFrom)
|
||||
|
||||
// call web hook
|
||||
callPullRequestWebHookByRequestBranch(
|
||||
action,
|
||||
getRepository(owner, repository).get,
|
||||
pullreq.requestBranch,
|
||||
loginAccount
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def getPullRequestByRequestCommit(
|
||||
userName: String,
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
package gitbucket.core.service
|
||||
import gitbucket.core.api.JsonFormat
|
||||
import gitbucket.core.model.{Account, WebHook}
|
||||
import gitbucket.core.model.Profile._
|
||||
import gitbucket.core.model.Profile.profile.blockingApi._
|
||||
import gitbucket.core.plugin.PluginRegistry
|
||||
import gitbucket.core.service.WebHookService.WebHookPushPayload
|
||||
import gitbucket.core.util.Directory.getRepositoryDir
|
||||
import gitbucket.core.util.JGitUtil.CommitInfo
|
||||
import gitbucket.core.util.{JGitUtil, LockUtil}
|
||||
import gitbucket.core.util.SyntaxSugars.using
|
||||
import org.eclipse.jgit.api.Git
|
||||
import org.eclipse.jgit.dircache.{DirCache, DirCacheBuilder}
|
||||
import org.eclipse.jgit.lib._
|
||||
import org.eclipse.jgit.transport.{ReceiveCommand, ReceivePack}
|
||||
|
||||
trait RepositoryCommitFileService {
|
||||
self: AccountService with ActivityService with IssuesService with PullRequestService with WebHookPullRequestService =>
|
||||
import RepositoryCommitFileService._
|
||||
|
||||
def commitFiles(
|
||||
repository: RepositoryService.RepositoryInfo,
|
||||
files: Seq[CommitFile],
|
||||
branch: String,
|
||||
path: String,
|
||||
message: String,
|
||||
loginAccount: Account
|
||||
)(
|
||||
f: (Git, ObjectId, DirCacheBuilder, ObjectInserter) => Unit
|
||||
)(implicit s: Session, c: JsonFormat.Context) = {
|
||||
// prepend path to the filename
|
||||
_commitFile(repository, branch, message, loginAccount)(f)
|
||||
}
|
||||
|
||||
def commitFile(
|
||||
repository: RepositoryService.RepositoryInfo,
|
||||
branch: String,
|
||||
path: String,
|
||||
newFileName: Option[String],
|
||||
oldFileName: Option[String],
|
||||
content: String,
|
||||
charset: String,
|
||||
message: String,
|
||||
commit: String,
|
||||
loginAccount: Account
|
||||
)(implicit s: Session, c: JsonFormat.Context) = {
|
||||
|
||||
val newPath = newFileName.map { newFileName =>
|
||||
if (path.length == 0) newFileName else s"${path}/${newFileName}"
|
||||
}
|
||||
val oldPath = oldFileName.map { oldFileName =>
|
||||
if (path.length == 0) oldFileName else s"${path}/${oldFileName}"
|
||||
}
|
||||
|
||||
_commitFile(repository, branch, message, loginAccount) {
|
||||
case (git, headTip, builder, inserter) =>
|
||||
if (headTip.getName == commit) {
|
||||
val permission = JGitUtil
|
||||
.processTree(git, headTip) { (path, tree) =>
|
||||
// Add all entries except the editing file
|
||||
if (!newPath.contains(path) && !oldPath.contains(path)) {
|
||||
builder.add(JGitUtil.createDirCacheEntry(path, tree.getEntryFileMode, tree.getEntryObjectId))
|
||||
}
|
||||
// Retrieve permission if file exists to keep it
|
||||
oldPath.collect { case x if x == path => tree.getEntryFileMode.getBits }
|
||||
}
|
||||
.flatten
|
||||
.headOption
|
||||
|
||||
newPath.foreach { newPath =>
|
||||
builder.add(JGitUtil.createDirCacheEntry(newPath, permission.map { bits =>
|
||||
FileMode.fromBits(bits)
|
||||
} getOrElse FileMode.REGULAR_FILE, inserter.insert(Constants.OBJ_BLOB, content.getBytes(charset))))
|
||||
}
|
||||
builder.finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def _commitFile(
|
||||
repository: RepositoryService.RepositoryInfo,
|
||||
branch: String,
|
||||
message: String,
|
||||
loginAccount: Account
|
||||
)(
|
||||
f: (Git, ObjectId, DirCacheBuilder, ObjectInserter) => Unit
|
||||
)(implicit s: Session, c: JsonFormat.Context) = {
|
||||
|
||||
LockUtil.lock(s"${repository.owner}/${repository.name}") {
|
||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
val builder = DirCache.newInCore.builder()
|
||||
val inserter = git.getRepository.newObjectInserter()
|
||||
val headName = s"refs/heads/${branch}"
|
||||
val headTip = git.getRepository.resolve(headName)
|
||||
|
||||
f(git, headTip, builder, inserter)
|
||||
|
||||
val commitId = JGitUtil.createNewCommit(
|
||||
git,
|
||||
inserter,
|
||||
headTip,
|
||||
builder.getDirCache.writeTree(inserter),
|
||||
headName,
|
||||
loginAccount.fullName,
|
||||
loginAccount.mailAddress,
|
||||
message
|
||||
)
|
||||
|
||||
inserter.flush()
|
||||
inserter.close()
|
||||
|
||||
val receivePack = new ReceivePack(git.getRepository)
|
||||
val receiveCommand = new ReceiveCommand(headTip, commitId, headName)
|
||||
|
||||
// call post commit hook
|
||||
val error = PluginRegistry().getReceiveHooks.flatMap { hook =>
|
||||
hook.preReceive(repository.owner, repository.name, receivePack, receiveCommand, loginAccount.userName)
|
||||
}.headOption
|
||||
|
||||
error match {
|
||||
case Some(error) =>
|
||||
// commit is rejected
|
||||
// TODO Notify commit failure to edited user
|
||||
val refUpdate = git.getRepository.updateRef(headName)
|
||||
refUpdate.setNewObjectId(headTip)
|
||||
refUpdate.setForceUpdate(true)
|
||||
refUpdate.update()
|
||||
|
||||
case None =>
|
||||
// update refs
|
||||
val refUpdate = git.getRepository.updateRef(headName)
|
||||
refUpdate.setNewObjectId(commitId)
|
||||
refUpdate.setForceUpdate(false)
|
||||
refUpdate.setRefLogIdent(new PersonIdent(loginAccount.fullName, loginAccount.mailAddress))
|
||||
refUpdate.update()
|
||||
|
||||
// update pull request
|
||||
updatePullRequests(repository.owner, repository.name, branch, loginAccount, "synchronize")
|
||||
|
||||
// record activity
|
||||
val commitInfo = new CommitInfo(JGitUtil.getRevCommitFromId(git, commitId))
|
||||
recordPushActivity(repository.owner, repository.name, loginAccount.userName, branch, List(commitInfo))
|
||||
|
||||
// create issue comment by commit message
|
||||
createIssueComment(repository.owner, repository.name, commitInfo)
|
||||
|
||||
// close issue by commit message
|
||||
if (branch == repository.repository.defaultBranch) {
|
||||
closeIssuesFromMessage(message, loginAccount.userName, repository.owner, repository.name).foreach {
|
||||
issueId =>
|
||||
getIssue(repository.owner, repository.name, issueId.toString).foreach { issue =>
|
||||
callIssuesWebHook("closed", repository, issue, loginAccount)
|
||||
PluginRegistry().getIssueHooks
|
||||
.foreach(_.closedByCommitComment(issue, repository, message, loginAccount))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// call post commit hook
|
||||
PluginRegistry().getReceiveHooks.foreach { hook =>
|
||||
hook.postReceive(repository.owner, repository.name, receivePack, receiveCommand, loginAccount.userName)
|
||||
}
|
||||
|
||||
val commit = new JGitUtil.CommitInfo(JGitUtil.getRevCommitFromId(git, commitId))
|
||||
callWebHookOf(repository.owner, repository.name, WebHook.Push) {
|
||||
getAccountByUserName(repository.owner).map { ownerAccount =>
|
||||
WebHookPushPayload(
|
||||
git,
|
||||
loginAccount,
|
||||
headName,
|
||||
repository,
|
||||
List(commit),
|
||||
ownerAccount,
|
||||
oldId = headTip,
|
||||
newId = commitId
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
object RepositoryCommitFileService {
|
||||
case class CommitFile(id: String, name: String)
|
||||
}
|
||||
@@ -1,16 +1,25 @@
|
||||
package gitbucket.core.service
|
||||
|
||||
import gitbucket.core.api.JsonFormat
|
||||
import gitbucket.core.controller.Context
|
||||
import gitbucket.core.util._
|
||||
import gitbucket.core.util.SyntaxSugars._
|
||||
import gitbucket.core.model.{Account, Collaborator, Repository, RepositoryOptions, Role, ReleaseTag}
|
||||
import gitbucket.core.model.{CommitComments => _, Session => _, _}
|
||||
import gitbucket.core.model.Profile._
|
||||
import gitbucket.core.model.Profile.profile.blockingApi._
|
||||
import gitbucket.core.model.Profile.dateColumnType
|
||||
import gitbucket.core.util.JGitUtil.FileInfo
|
||||
import gitbucket.core.plugin.PluginRegistry
|
||||
import gitbucket.core.service.WebHookService.WebHookPushPayload
|
||||
import gitbucket.core.util.Directory.{getRepositoryDir, getRepositoryFilesDir, getTemporaryDir, getWikiRepositoryDir}
|
||||
import gitbucket.core.util.JGitUtil.{CommitInfo, FileInfo}
|
||||
import org.apache.commons.io.FileUtils
|
||||
import org.eclipse.jgit.api.Git
|
||||
import org.eclipse.jgit.dircache.{DirCache, DirCacheBuilder}
|
||||
import org.eclipse.jgit.lib.{Repository => _, _}
|
||||
import org.eclipse.jgit.transport.{ReceiveCommand, ReceivePack}
|
||||
|
||||
trait RepositoryService { self: AccountService =>
|
||||
trait RepositoryService {
|
||||
self: AccountService =>
|
||||
import RepositoryService._
|
||||
|
||||
/**
|
||||
@@ -68,181 +77,232 @@ trait RepositoryService { self: AccountService =>
|
||||
(Repositories filter { t =>
|
||||
t.byRepository(oldUserName, oldRepositoryName)
|
||||
} firstOption).foreach { repository =>
|
||||
Repositories insert repository.copy(userName = newUserName, repositoryName = newRepositoryName)
|
||||
LockUtil.lock(s"${repository.userName}/${repository.repositoryName}") {
|
||||
Repositories insert repository.copy(userName = newUserName, repositoryName = newRepositoryName)
|
||||
|
||||
val webHooks = RepositoryWebHooks.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val webHookEvents = RepositoryWebHookEvents.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val milestones = Milestones.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val issueId = IssueId.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val issues = Issues.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val pullRequests = PullRequests.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val labels = Labels.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val priorities = Priorities.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val issueComments = IssueComments.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val issueLabels = IssueLabels.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val commitComments = CommitComments.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val commitStatuses = CommitStatuses.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val collaborators = Collaborators.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val protectedBranches = ProtectedBranches.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val protectedBranchContexts =
|
||||
ProtectedBranchContexts.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val deployKeys = DeployKeys.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val releases = ReleaseTags.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val releaseAssets = ReleaseAssets.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val webHooks = RepositoryWebHooks.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val webHookEvents = RepositoryWebHookEvents.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val milestones = Milestones.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val issueId = IssueId.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val issues = Issues.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val pullRequests = PullRequests.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val labels = Labels.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val priorities = Priorities.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val issueComments = IssueComments.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val issueLabels = IssueLabels.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val commitComments = CommitComments.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val commitStatuses = CommitStatuses.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val collaborators = Collaborators.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val protectedBranches = ProtectedBranches.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val protectedBranchContexts =
|
||||
ProtectedBranchContexts.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val deployKeys = DeployKeys.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val releases = ReleaseTags.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val releaseAssets = ReleaseAssets.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
|
||||
Repositories
|
||||
.filter { t =>
|
||||
(t.originUserName === oldUserName.bind) && (t.originRepositoryName === oldRepositoryName.bind)
|
||||
}
|
||||
.map { t =>
|
||||
t.originUserName -> t.originRepositoryName
|
||||
}
|
||||
.update(newUserName, newRepositoryName)
|
||||
|
||||
Repositories
|
||||
.filter { t =>
|
||||
(t.parentUserName === oldUserName.bind) && (t.parentRepositoryName === oldRepositoryName.bind)
|
||||
}
|
||||
.map { t =>
|
||||
t.parentUserName -> t.parentRepositoryName
|
||||
}
|
||||
.update(newUserName, newRepositoryName)
|
||||
|
||||
// Updates activity fk before deleting repository because activity is sorted by activityId
|
||||
// and it can't be changed by deleting-and-inserting record.
|
||||
Activities.filter(_.byRepository(oldUserName, oldRepositoryName)).list.foreach { activity =>
|
||||
Activities
|
||||
.filter(_.activityId === activity.activityId.bind)
|
||||
.map(x => (x.userName, x.repositoryName))
|
||||
.update(newUserName, newRepositoryName)
|
||||
}
|
||||
|
||||
deleteRepository(oldUserName, oldRepositoryName)
|
||||
|
||||
RepositoryWebHooks.insertAll(
|
||||
webHooks.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)): _*
|
||||
)
|
||||
RepositoryWebHookEvents.insertAll(
|
||||
webHookEvents.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)): _*
|
||||
)
|
||||
Milestones.insertAll(milestones.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)): _*)
|
||||
Priorities.insertAll(priorities.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)): _*)
|
||||
IssueId.insertAll(issueId.map(_.copy(_1 = newUserName, _2 = newRepositoryName)): _*)
|
||||
|
||||
val newMilestones = Milestones.filter(_.byRepository(newUserName, newRepositoryName)).list
|
||||
val newPriorities = Priorities.filter(_.byRepository(newUserName, newRepositoryName)).list
|
||||
Issues.insertAll(issues.map { x =>
|
||||
x.copy(
|
||||
userName = newUserName,
|
||||
repositoryName = newRepositoryName,
|
||||
milestoneId = x.milestoneId.map { id =>
|
||||
newMilestones.find(_.title == milestones.find(_.milestoneId == id).get.title).get.milestoneId
|
||||
},
|
||||
priorityId = x.priorityId.map { id =>
|
||||
newPriorities.find(_.priorityName == priorities.find(_.priorityId == id).get.priorityName).get.priorityId
|
||||
Repositories
|
||||
.filter { t =>
|
||||
(t.originUserName === oldUserName.bind) && (t.originRepositoryName === oldRepositoryName.bind)
|
||||
}
|
||||
.map { t =>
|
||||
t.originUserName -> t.originRepositoryName
|
||||
}
|
||||
.update(newUserName, newRepositoryName)
|
||||
|
||||
Repositories
|
||||
.filter { t =>
|
||||
(t.parentUserName === oldUserName.bind) && (t.parentRepositoryName === oldRepositoryName.bind)
|
||||
}
|
||||
.map { t =>
|
||||
t.parentUserName -> t.parentRepositoryName
|
||||
}
|
||||
.update(newUserName, newRepositoryName)
|
||||
|
||||
// Updates activity fk before deleting repository because activity is sorted by activityId
|
||||
// and it can't be changed by deleting-and-inserting record.
|
||||
Activities.filter(_.byRepository(oldUserName, oldRepositoryName)).list.foreach { activity =>
|
||||
Activities
|
||||
.filter(_.activityId === activity.activityId.bind)
|
||||
.map(x => (x.userName, x.repositoryName))
|
||||
.update(newUserName, newRepositoryName)
|
||||
}
|
||||
|
||||
deleteRepositoryOnModel(oldUserName, oldRepositoryName)
|
||||
|
||||
RepositoryWebHooks.insertAll(
|
||||
webHooks.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)): _*
|
||||
)
|
||||
}: _*)
|
||||
RepositoryWebHookEvents.insertAll(
|
||||
webHookEvents.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)): _*
|
||||
)
|
||||
Milestones.insertAll(milestones.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)): _*)
|
||||
Priorities.insertAll(priorities.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)): _*)
|
||||
IssueId.insertAll(issueId.map(_.copy(_1 = newUserName, _2 = newRepositoryName)): _*)
|
||||
|
||||
PullRequests.insertAll(pullRequests.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)): _*)
|
||||
IssueComments.insertAll(
|
||||
issueComments.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)): _*
|
||||
)
|
||||
Labels.insertAll(labels.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)): _*)
|
||||
CommitComments.insertAll(
|
||||
commitComments.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)): _*
|
||||
)
|
||||
CommitStatuses.insertAll(
|
||||
commitStatuses.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)): _*
|
||||
)
|
||||
ProtectedBranches.insertAll(
|
||||
protectedBranches.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)): _*
|
||||
)
|
||||
ProtectedBranchContexts.insertAll(
|
||||
protectedBranchContexts.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)): _*
|
||||
)
|
||||
DeployKeys.insertAll(deployKeys.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)): _*)
|
||||
ReleaseTags.insertAll(releases.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)): _*)
|
||||
ReleaseAssets.insertAll(
|
||||
releaseAssets.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)): _*
|
||||
)
|
||||
|
||||
// Update source repository of pull requests
|
||||
PullRequests
|
||||
.filter { t =>
|
||||
(t.requestUserName === oldUserName.bind) && (t.requestRepositoryName === oldRepositoryName.bind)
|
||||
}
|
||||
.map { t =>
|
||||
t.requestUserName -> t.requestRepositoryName
|
||||
}
|
||||
.update(newUserName, newRepositoryName)
|
||||
|
||||
// Convert labelId
|
||||
val oldLabelMap = labels.map(x => (x.labelId, x.labelName)).toMap
|
||||
val newLabelMap =
|
||||
Labels.filter(_.byRepository(newUserName, newRepositoryName)).map(x => (x.labelName, x.labelId)).list.toMap
|
||||
IssueLabels.insertAll(
|
||||
issueLabels.map(
|
||||
x =>
|
||||
x.copy(
|
||||
labelId = newLabelMap(oldLabelMap(x.labelId)),
|
||||
userName = newUserName,
|
||||
repositoryName = newRepositoryName
|
||||
val newMilestones = Milestones.filter(_.byRepository(newUserName, newRepositoryName)).list
|
||||
val newPriorities = Priorities.filter(_.byRepository(newUserName, newRepositoryName)).list
|
||||
Issues.insertAll(issues.map { x =>
|
||||
x.copy(
|
||||
userName = newUserName,
|
||||
repositoryName = newRepositoryName,
|
||||
milestoneId = x.milestoneId.map { id =>
|
||||
newMilestones.find(_.title == milestones.find(_.milestoneId == id).get.title).get.milestoneId
|
||||
},
|
||||
priorityId = x.priorityId.map { id =>
|
||||
newPriorities
|
||||
.find(_.priorityName == priorities.find(_.priorityId == id).get.priorityName)
|
||||
.get
|
||||
.priorityId
|
||||
}
|
||||
)
|
||||
): _*
|
||||
)
|
||||
}: _*)
|
||||
|
||||
// TODO Drop transferred owner from collaborators?
|
||||
Collaborators.insertAll(
|
||||
collaborators.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)): _*
|
||||
)
|
||||
PullRequests.insertAll(
|
||||
pullRequests.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)): _*
|
||||
)
|
||||
IssueComments.insertAll(
|
||||
issueComments.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)): _*
|
||||
)
|
||||
Labels.insertAll(labels.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)): _*)
|
||||
CommitComments.insertAll(
|
||||
commitComments.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)): _*
|
||||
)
|
||||
CommitStatuses.insertAll(
|
||||
commitStatuses.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)): _*
|
||||
)
|
||||
ProtectedBranches.insertAll(
|
||||
protectedBranches.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)): _*
|
||||
)
|
||||
ProtectedBranchContexts.insertAll(
|
||||
protectedBranchContexts.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)): _*
|
||||
)
|
||||
DeployKeys.insertAll(deployKeys.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)): _*)
|
||||
ReleaseTags.insertAll(releases.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)): _*)
|
||||
ReleaseAssets.insertAll(
|
||||
releaseAssets.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)): _*
|
||||
)
|
||||
|
||||
// Update activity messages
|
||||
Activities
|
||||
.filter { t =>
|
||||
(t.message like s"%:${oldUserName}/${oldRepositoryName}]%") ||
|
||||
(t.message like s"%:${oldUserName}/${oldRepositoryName}#%") ||
|
||||
(t.message like s"%:${oldUserName}/${oldRepositoryName}@%")
|
||||
// Update source repository of pull requests
|
||||
PullRequests
|
||||
.filter { t =>
|
||||
(t.requestUserName === oldUserName.bind) && (t.requestRepositoryName === oldRepositoryName.bind)
|
||||
}
|
||||
.map { t =>
|
||||
t.requestUserName -> t.requestRepositoryName
|
||||
}
|
||||
.update(newUserName, newRepositoryName)
|
||||
|
||||
// Convert labelId
|
||||
val oldLabelMap = labels.map(x => (x.labelId, x.labelName)).toMap
|
||||
val newLabelMap =
|
||||
Labels.filter(_.byRepository(newUserName, newRepositoryName)).map(x => (x.labelName, x.labelId)).list.toMap
|
||||
IssueLabels.insertAll(
|
||||
issueLabels.map(
|
||||
x =>
|
||||
x.copy(
|
||||
labelId = newLabelMap(oldLabelMap(x.labelId)),
|
||||
userName = newUserName,
|
||||
repositoryName = newRepositoryName
|
||||
)
|
||||
): _*
|
||||
)
|
||||
|
||||
// TODO Drop transferred owner from collaborators?
|
||||
Collaborators.insertAll(
|
||||
collaborators.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)): _*
|
||||
)
|
||||
|
||||
// Update activity messages
|
||||
Activities
|
||||
.filter { t =>
|
||||
(t.message like s"%:${oldUserName}/${oldRepositoryName}]%") ||
|
||||
(t.message like s"%:${oldUserName}/${oldRepositoryName}#%") ||
|
||||
(t.message like s"%:${oldUserName}/${oldRepositoryName}@%")
|
||||
}
|
||||
.map { t =>
|
||||
t.activityId -> t.message
|
||||
}
|
||||
.list
|
||||
.foreach {
|
||||
case (activityId, message) =>
|
||||
Activities
|
||||
.filter(_.activityId === activityId.bind)
|
||||
.map(_.message)
|
||||
.update(
|
||||
message
|
||||
.replace(
|
||||
s"[repo:${oldUserName}/${oldRepositoryName}]",
|
||||
s"[repo:${newUserName}/${newRepositoryName}]"
|
||||
)
|
||||
.replace(
|
||||
s"[branch:${oldUserName}/${oldRepositoryName}#",
|
||||
s"[branch:${newUserName}/${newRepositoryName}#"
|
||||
)
|
||||
.replace(
|
||||
s"[tag:${oldUserName}/${oldRepositoryName}#",
|
||||
s"[tag:${newUserName}/${newRepositoryName}#"
|
||||
)
|
||||
.replace(
|
||||
s"[pullreq:${oldUserName}/${oldRepositoryName}#",
|
||||
s"[pullreq:${newUserName}/${newRepositoryName}#"
|
||||
)
|
||||
.replace(
|
||||
s"[issue:${oldUserName}/${oldRepositoryName}#",
|
||||
s"[issue:${newUserName}/${newRepositoryName}#"
|
||||
)
|
||||
.replace(
|
||||
s"[commit:${oldUserName}/${oldRepositoryName}@",
|
||||
s"[commit:${newUserName}/${newRepositoryName}@"
|
||||
)
|
||||
)
|
||||
}
|
||||
// Move git repository
|
||||
defining(getRepositoryDir(oldUserName, oldRepositoryName)) { dir =>
|
||||
if (dir.isDirectory) {
|
||||
FileUtils.moveDirectory(dir, getRepositoryDir(newUserName, newRepositoryName))
|
||||
}
|
||||
}
|
||||
.map { t =>
|
||||
t.activityId -> t.message
|
||||
// Move wiki repository
|
||||
defining(getWikiRepositoryDir(oldUserName, oldRepositoryName)) { dir =>
|
||||
if (dir.isDirectory) {
|
||||
FileUtils.moveDirectory(dir, getWikiRepositoryDir(newUserName, newRepositoryName))
|
||||
}
|
||||
}
|
||||
.list
|
||||
.foreach {
|
||||
case (activityId, message) =>
|
||||
Activities
|
||||
.filter(_.activityId === activityId.bind)
|
||||
.map(_.message)
|
||||
.update(
|
||||
message
|
||||
.replace(
|
||||
s"[repo:${oldUserName}/${oldRepositoryName}]",
|
||||
s"[repo:${newUserName}/${newRepositoryName}]"
|
||||
)
|
||||
.replace(
|
||||
s"[branch:${oldUserName}/${oldRepositoryName}#",
|
||||
s"[branch:${newUserName}/${newRepositoryName}#"
|
||||
)
|
||||
.replace(s"[tag:${oldUserName}/${oldRepositoryName}#", s"[tag:${newUserName}/${newRepositoryName}#")
|
||||
.replace(
|
||||
s"[pullreq:${oldUserName}/${oldRepositoryName}#",
|
||||
s"[pullreq:${newUserName}/${newRepositoryName}#"
|
||||
)
|
||||
.replace(
|
||||
s"[issue:${oldUserName}/${oldRepositoryName}#",
|
||||
s"[issue:${newUserName}/${newRepositoryName}#"
|
||||
)
|
||||
.replace(
|
||||
s"[commit:${oldUserName}/${oldRepositoryName}@",
|
||||
s"[commit:${newUserName}/${newRepositoryName}@"
|
||||
)
|
||||
)
|
||||
// Move files directory
|
||||
defining(getRepositoryFilesDir(oldUserName, oldRepositoryName)) { dir =>
|
||||
if (dir.isDirectory) {
|
||||
FileUtils.moveDirectory(dir, getRepositoryFilesDir(newUserName, newRepositoryName))
|
||||
}
|
||||
}
|
||||
// Delete parent directory
|
||||
FileUtil.deleteDirectoryIfEmpty(getRepositoryFilesDir(oldUserName, oldRepositoryName))
|
||||
|
||||
// Call hooks
|
||||
if (oldUserName == newUserName) {
|
||||
PluginRegistry().getRepositoryHooks.foreach(_.renamed(oldUserName, oldRepositoryName, newRepositoryName))
|
||||
} else {
|
||||
PluginRegistry().getRepositoryHooks.foreach(_.transferred(oldUserName, newUserName, newRepositoryName))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def deleteRepository(userName: String, repositoryName: String)(implicit s: Session): Unit = {
|
||||
def deleteRepository(repository: Repository)(implicit s: Session): Unit = {
|
||||
LockUtil.lock(s"${repository.userName}/${repository.repositoryName}") {
|
||||
deleteRepositoryOnModel(repository.userName, repository.repositoryName)
|
||||
|
||||
FileUtils.deleteDirectory(getRepositoryDir(repository.userName, repository.repositoryName))
|
||||
FileUtils.deleteDirectory(getWikiRepositoryDir(repository.userName, repository.repositoryName))
|
||||
FileUtils.deleteDirectory(getTemporaryDir(repository.userName, repository.repositoryName))
|
||||
FileUtils.deleteDirectory(getRepositoryFilesDir(repository.userName, repository.repositoryName))
|
||||
|
||||
// Call hooks
|
||||
PluginRegistry().getRepositoryHooks.foreach(_.deleted(repository.userName, repository.repositoryName))
|
||||
}
|
||||
}
|
||||
|
||||
private def deleteRepositoryOnModel(userName: String, repositoryName: String)(implicit s: Session): Unit = {
|
||||
Activities.filter(_.byRepository(userName, repositoryName)).delete
|
||||
Collaborators.filter(_.byRepository(userName, repositoryName)).delete
|
||||
CommitComments.filter(_.byRepository(userName, repositoryName)).delete
|
||||
@@ -718,7 +778,6 @@ trait RepositoryService { self: AccountService =>
|
||||
}
|
||||
|
||||
object RepositoryService {
|
||||
|
||||
case class RepositoryInfo(
|
||||
owner: String,
|
||||
name: String,
|
||||
|
||||
@@ -311,7 +311,6 @@ trait WebHookPullRequestService extends WebHookService {
|
||||
action: String,
|
||||
repository: RepositoryService.RepositoryInfo,
|
||||
issue: Issue,
|
||||
baseUrl: String,
|
||||
sender: Account
|
||||
)(implicit s: Session, context: JsonFormat.Context): Unit = {
|
||||
callWebHookOf(repository.owner, repository.name, WebHook.Issues) {
|
||||
@@ -341,7 +340,6 @@ trait WebHookPullRequestService extends WebHookService {
|
||||
action: String,
|
||||
repository: RepositoryService.RepositoryInfo,
|
||||
issueId: Int,
|
||||
baseUrl: String,
|
||||
sender: Account
|
||||
)(implicit s: Session, c: JsonFormat.Context): Unit = {
|
||||
import WebHookService._
|
||||
@@ -404,7 +402,6 @@ trait WebHookPullRequestService extends WebHookService {
|
||||
action: String,
|
||||
requestRepository: RepositoryService.RepositoryInfo,
|
||||
requestBranch: String,
|
||||
baseUrl: String,
|
||||
sender: Account
|
||||
)(implicit s: Session, c: JsonFormat.Context): Unit = {
|
||||
import WebHookService._
|
||||
@@ -450,7 +447,6 @@ trait WebHookPullRequestReviewCommentService extends WebHookService {
|
||||
repository: RepositoryService.RepositoryInfo,
|
||||
issue: Issue,
|
||||
pullRequest: PullRequest,
|
||||
baseUrl: String,
|
||||
sender: Account
|
||||
)(implicit s: Session, c: JsonFormat.Context): Unit = {
|
||||
import WebHookService._
|
||||
|
||||
@@ -221,6 +221,7 @@ class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl:
|
||||
with PrioritiesService
|
||||
with MilestonesService
|
||||
with WebHookPullRequestService
|
||||
with WebHookPullRequestReviewCommentService
|
||||
with CommitsService {
|
||||
|
||||
private val logger = LoggerFactory.getLogger(classOf[CommitLogHook])
|
||||
@@ -299,7 +300,7 @@ class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl:
|
||||
getAccountByUserName(pusher).foreach { pusherAccount =>
|
||||
closeIssuesFromMessage(commit.fullMessage, pusher, owner, repository).foreach { issueId =>
|
||||
getIssue(owner, repository, issueId.toString).foreach { issue =>
|
||||
callIssuesWebHook("closed", repositoryInfo, issue, baseUrl, pusherAccount)
|
||||
callIssuesWebHook("closed", repositoryInfo, issue, pusherAccount)
|
||||
PluginRegistry().getIssueHooks
|
||||
.foreach(_.closedByCommitComment(issue, repositoryInfo, commit.fullMessage, pusherAccount))
|
||||
}
|
||||
@@ -319,7 +320,7 @@ class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl:
|
||||
}.isDefined) {
|
||||
markMergeAndClosePullRequest(pusher, owner, repository, pull)
|
||||
getAccountByUserName(pusher).foreach { pusherAccount =>
|
||||
callPullRequestWebHook("closed", repositoryInfo, pull.issueId, baseUrl, pusherAccount)
|
||||
callPullRequestWebHook("closed", repositoryInfo, pull.issueId, pusherAccount)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -346,15 +347,8 @@ class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl:
|
||||
command.getType match {
|
||||
case ReceiveCommand.Type.CREATE | ReceiveCommand.Type.UPDATE |
|
||||
ReceiveCommand.Type.UPDATE_NONFASTFORWARD =>
|
||||
updatePullRequests(owner, repository, branchName)
|
||||
getAccountByUserName(pusher).foreach { pusherAccount =>
|
||||
callPullRequestWebHookByRequestBranch(
|
||||
"synchronize",
|
||||
repositoryInfo,
|
||||
branchName,
|
||||
baseUrl,
|
||||
pusherAccount
|
||||
)
|
||||
updatePullRequests(owner, repository, branchName, pusherAccount, "synchronize")
|
||||
}
|
||||
case _ =>
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import gitbucket.core.service.{AccountService, DeployKeyService, RepositoryServi
|
||||
import gitbucket.core.servlet.{CommitLogHook, Database}
|
||||
import gitbucket.core.util.{SyntaxSugars, Directory}
|
||||
import org.apache.sshd.server.{Environment, ExitCallback, SessionAware}
|
||||
import org.apache.sshd.server.command.{Command, CommandFactory}
|
||||
import org.apache.sshd.server.{Command, CommandFactory}
|
||||
import org.apache.sshd.server.session.ServerSession
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.io.{File, InputStream, OutputStream}
|
||||
@@ -16,7 +16,7 @@ import org.eclipse.jgit.api.Git
|
||||
import Directory._
|
||||
import gitbucket.core.ssh.PublicKeyAuthenticator.AuthType
|
||||
import org.eclipse.jgit.transport.{ReceivePack, UploadPack}
|
||||
import org.apache.sshd.server.shell.UnknownCommand
|
||||
import org.apache.sshd.server.scp.UnknownCommand
|
||||
import org.eclipse.jgit.errors.RepositoryNotFoundException
|
||||
|
||||
object GitCommand {
|
||||
|
||||
@@ -3,7 +3,7 @@ package gitbucket.core.ssh
|
||||
import gitbucket.core.service.SystemSettingsService.SshAddress
|
||||
import org.apache.sshd.common.Factory
|
||||
import org.apache.sshd.server.{Environment, ExitCallback}
|
||||
import org.apache.sshd.server.command.Command
|
||||
import org.apache.sshd.server.Command
|
||||
import java.io.{OutputStream, InputStream}
|
||||
import org.eclipse.jgit.lib.Constants
|
||||
|
||||
|
||||
@@ -232,6 +232,12 @@ object helpers extends AvatarImageProvider with LinkConverter with RequestCache
|
||||
s"""<a href="${context.path}/${m.group(1)}/${m.group(2)}/commit/${m.group(3)}">${m.group(1)}/${m
|
||||
.group(2)}@${m.group(3).substring(0, 7)}</a>"""
|
||||
)
|
||||
.replaceAll(
|
||||
"\\[release:([^\\s]+?)/([^\\s]+?)/([^\\s]+?)\\]",
|
||||
(m: Match) =>
|
||||
s"""<a href="${context.path}/${m.group(1)}/${m.group(2)}/releases/${encodeRefName(m.group(3))}">${m
|
||||
.group(3)}</a>"""
|
||||
)
|
||||
)
|
||||
|
||||
/**
|
||||
|
||||
@@ -40,10 +40,6 @@
|
||||
<label class="col-md-2">Version</label>
|
||||
<span class="col-md-10">@plugin.pluginVersion</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<label class="col-md-2">Name</label>
|
||||
<span class="col-md-10">@plugin.pluginName</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<label class="col-md-2">Description</label>
|
||||
<span class="col-md-10 muted">@plugin.description</span>
|
||||
|
||||
@@ -12,7 +12,9 @@ import org.scalatest.FunSpec
|
||||
import java.io.File
|
||||
|
||||
class MergeServiceSpec extends FunSpec {
|
||||
val service = new MergeService {}
|
||||
val service = new MergeService with AccountService with ActivityService with IssuesService with LabelsService
|
||||
with MilestonesService with RepositoryService with PrioritiesService with PullRequestService with CommitsService
|
||||
with WebHookPullRequestService with WebHookPullRequestReviewCommentService {}
|
||||
val branch = "master"
|
||||
val issueId = 10
|
||||
def initRepository(owner: String, name: String): File = {
|
||||
|
||||
@@ -9,11 +9,15 @@ class PullRequestServiceSpec
|
||||
with PullRequestService
|
||||
with IssuesService
|
||||
with AccountService
|
||||
with ActivityService
|
||||
with RepositoryService
|
||||
with CommitsService
|
||||
with LabelsService
|
||||
with MilestonesService
|
||||
with PrioritiesService {
|
||||
with PrioritiesService
|
||||
with WebHookService
|
||||
with WebHookPullRequestService
|
||||
with WebHookPullRequestReviewCommentService {
|
||||
|
||||
def swap(r: (Issue, PullRequest)) = (r._2 -> r._1)
|
||||
|
||||
|
||||
@@ -43,8 +43,10 @@ trait ServiceSpecBase {
|
||||
|
||||
def user(name: String)(implicit s: Session): Account = AccountService.getAccountByUserName(name).get
|
||||
|
||||
lazy val dummyService = new RepositoryService with AccountService with IssuesService with PullRequestService
|
||||
with CommitsService with CommitStatusService with LabelsService with MilestonesService with PrioritiesService() {}
|
||||
lazy val dummyService = new RepositoryService with AccountService with ActivityService with IssuesService
|
||||
with PullRequestService with CommitsService with CommitStatusService with LabelsService with MilestonesService
|
||||
with PrioritiesService with WebHookService with WebHookPullRequestService
|
||||
with WebHookPullRequestReviewCommentService {}
|
||||
|
||||
def generateNewUserWithDBRepository(userName: String, repositoryName: String)(implicit s: Session): Account = {
|
||||
val ac = AccountService.getAccountByUserName(userName).getOrElse(generateNewAccount(userName))
|
||||
|
||||
@@ -5,8 +5,9 @@ import org.scalatest.FunSuite
|
||||
import gitbucket.core.model.WebHookContentType
|
||||
|
||||
class WebHookServiceSpec extends FunSuite with ServiceSpecBase {
|
||||
lazy val service = new WebHookPullRequestService with AccountService with RepositoryService with PullRequestService
|
||||
with IssuesService with CommitsService with LabelsService with MilestonesService with PrioritiesService
|
||||
lazy val service = new WebHookPullRequestService with AccountService with ActivityService with RepositoryService
|
||||
with PullRequestService with IssuesService with CommitsService with LabelsService with MilestonesService
|
||||
with PrioritiesService with WebHookPullRequestReviewCommentService
|
||||
|
||||
test("WebHookPullRequestService.getPullRequestsByRequestForWebhook") {
|
||||
withTestDB { implicit session =>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package gitbucket.core.ssh
|
||||
|
||||
import org.apache.sshd.server.shell.UnknownCommand
|
||||
import org.apache.sshd.server.scp.UnknownCommand
|
||||
import org.scalatest.FunSpec
|
||||
|
||||
class GitCommandFactorySpec extends FunSpec {
|
||||
|
||||
Reference in New Issue
Block a user