Asynchronize repository creation

This commit is contained in:
Naoki Takezoe
2017-12-04 16:12:43 +09:00
parent 13800a7023
commit efd5a64749
5 changed files with 142 additions and 65 deletions

View File

@@ -2,7 +2,7 @@ package gitbucket.core.controller
import gitbucket.core.account.html
import gitbucket.core.helper
import gitbucket.core.model.{AccountWebHook, GroupMember, RepositoryWebHook, RepositoryWebHookEvent, Role, WebHook, WebHookContentType}
import gitbucket.core.model.{AccountWebHook, GroupMember, RepositoryWebHook, Role, WebHook, WebHookContentType}
import gitbucket.core.plugin.PluginRegistry
import gitbucket.core.service._
import gitbucket.core.service.WebHookService._
@@ -17,6 +17,8 @@ import org.scalatra.i18n.Messages
import org.scalatra.BadRequest
import org.scalatra.forms._
import scala.concurrent.ExecutionContext.Implicits.global
class AccountController extends AccountControllerBase
with AccountService with RepositoryService with ActivityService with WikiService with LabelsService with SshKeyService
with OneselfAuthenticator with UsersAuthenticator with GroupManagerAuthenticator with ReadableUsersAuthenticator
@@ -462,7 +464,6 @@ trait AccountControllerBase extends AccountManagementControllerBase {
get("/:groupName/_editgroup")(managersOnly {
defining(params("groupName")){ groupName =>
// TODO Don't use Option.get
getAccountByUserName(groupName, true).map { account =>
html.editgroup(account, getGroupMembers(groupName), flash.get("info"))
} getOrElse NotFound()
@@ -530,10 +531,11 @@ trait AccountControllerBase extends AccountManagementControllerBase {
LockUtil.lock(s"${form.owner}/${form.name}"){
if(getRepository(form.owner, form.name).isEmpty){
// Create the repository
createRepository(context.loginAccount.get, form.owner, form.name, form.description, form.isPrivate, form.initOption, form.sourceUrl)
// Call hooks
PluginRegistry().getRepositoryHooks.foreach(_.created(form.owner, form.name))
val f = createRepository(context.loginAccount.get, form.owner, form.name, form.description, form.isPrivate, form.initOption, form.sourceUrl).map { _ =>
// Call hooks
PluginRegistry().getRepositoryHooks.foreach(_.created(form.owner, form.name))
}
//Await.result(f, Duration.Inf)
}
}

View File

@@ -16,6 +16,8 @@ import org.eclipse.jgit.revwalk.RevWalk
import org.scalatra.{Created, NoContent, UnprocessableEntity}
import scala.collection.JavaConverters._
import scala.concurrent.Await
import scala.concurrent.duration.Duration
class ApiController extends ApiControllerBase
with RepositoryService
@@ -249,7 +251,8 @@ trait ApiControllerBase extends ControllerBase {
} yield {
LockUtil.lock(s"${owner}/${data.name}") {
if(getRepository(owner, data.name).isEmpty){
createRepository(context.loginAccount.get, owner, data.name, data.description, data.`private`, data.auto_init)
val f = createRepository(context.loginAccount.get, owner, data.name, data.description, data.`private`, data.auto_init)
Await.result(f, Duration.Inf)
val repository = getRepository(owner, data.name).get
JsonFormat(ApiRepository(repository, ApiUser(getAccountByUserName(owner).get)))
} else {
@@ -273,7 +276,8 @@ trait ApiControllerBase extends ControllerBase {
} yield {
LockUtil.lock(s"${groupName}/${data.name}") {
if(getRepository(groupName, data.name).isEmpty){
createRepository(context.loginAccount.get, groupName, data.name, data.description, data.`private`, data.auto_init)
val f = createRepository(context.loginAccount.get, groupName, data.name, data.description, data.`private`, data.auto_init)
Await.result(f, Duration.Inf)
val repository = getRepository(groupName, data.name).get
JsonFormat(ApiRepository(repository, ApiUser(getAccountByUserName(groupName).get)))
} else {

View File

@@ -148,14 +148,27 @@ trait RepositoryViewerControllerBase extends ControllerBase {
* Displays the file list of the repository root and the default branch.
*/
get("/:owner/:repository") {
params.get("go-get") match {
case Some("1") => defining(request.paths){ paths =>
getRepository(paths(0), paths(1)).map(gitbucket.core.html.goget(_))getOrElse NotFound()
val owner = params("owner")
val repository = params("repository")
if (RepositoryCreationService.isCreating(owner, repository)) {
gitbucket.core.repo.html.creating(owner, repository)
} else {
params.get("go-get") match {
case Some("1") => defining(request.paths) { paths =>
getRepository(owner, repository).map(gitbucket.core.html.goget(_)) getOrElse NotFound()
}
case _ => referrersOnly(fileList(_))
}
case _ => referrersOnly(fileList(_))
}
}
ajaxGet("/:owner/:repository/creating") {
val owner = params("owner")
val repository = params("repository")
RepositoryCreationService.isCreating(owner, repository)
}
/**
* Displays the file list of the specified path and branch.
*/

View File

@@ -1,91 +1,123 @@
package gitbucket.core.service
import java.nio.file.Files
import java.util.concurrent.ConcurrentHashMap
import gitbucket.core.model.Profile.profile.blockingApi._
import gitbucket.core.util.SyntaxSugars._
import gitbucket.core.util.Directory._
import gitbucket.core.util.JGitUtil
import gitbucket.core.model.Account
import gitbucket.core.servlet.Database
import org.apache.commons.io.FileUtils
import org.eclipse.jgit.api.Git
import org.eclipse.jgit.dircache.DirCache
import org.eclipse.jgit.lib.{Constants, FileMode}
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
object RepositoryCreationService {
private val Creating = new ConcurrentHashMap[String, Boolean]()
def isCreating(owner: String, repository: String): Boolean = {
Creating.containsKey(s"${owner}/${repository}")
}
def startCreation(owner: String, repository: String): Unit = {
Creating.put(s"${owner}/${repository}", true)
}
def endCreation(owner: String, repository: String): Unit = {
Creating.remove(s"${owner}/${repository}")
}
}
trait RepositoryCreationService {
self: AccountService with RepositoryService with LabelsService with WikiService with ActivityService with PrioritiesService =>
def createRepository(loginAccount: Account, owner: String, name: String, description: Option[String],
isPrivate: Boolean, createReadme: Boolean)(implicit s: Session): Unit = {
isPrivate: Boolean, createReadme: Boolean): Future[Unit] = {
createRepository(loginAccount, owner, name, description, isPrivate, if (createReadme) "README" else "EMPTY", None)
}
def createRepository(loginAccount: Account, owner: String, name: String, description: Option[String],
isPrivate: Boolean, initOption: String, sourceUrl: Option[String])(implicit s: Session): Unit = {
val ownerAccount = getAccountByUserName(owner).get
val loginUserName = loginAccount.userName
isPrivate: Boolean, initOption: String, sourceUrl: Option[String]): Future[Unit] = Future {
RepositoryCreationService.startCreation(owner, name)
try {
Database() withTransaction { implicit session =>
val ownerAccount = getAccountByUserName(owner).get
val loginUserName = loginAccount.userName
// Insert to the database at first
insertRepository(name, owner, description, isPrivate)
// Insert to the database at first
insertRepository(name, owner, description, isPrivate)
// // Add collaborators for group repository
// if(ownerAccount.isGroupAccount){
// getGroupMembers(owner).foreach { member =>
// addCollaborator(owner, name, member.userName)
// }
// }
// // Add collaborators for group repository
// if(ownerAccount.isGroupAccount){
// getGroupMembers(owner).foreach { member =>
// addCollaborator(owner, name, member.userName)
// }
// }
// Insert default labels
insertDefaultLabels(owner, name)
// Insert default labels
insertDefaultLabels(owner, name)
// Insert default priorities
insertDefaultPriorities(owner, name)
// Insert default priorities
insertDefaultPriorities(owner, name)
// Create the actual repository
val gitdir = getRepositoryDir(owner, name)
JGitUtil.initRepository(gitdir)
// Create the actual repository
val gitdir = getRepositoryDir(owner, name)
JGitUtil.initRepository(gitdir)
if (initOption == "README") {
using(Git.open(gitdir)){ git =>
val builder = DirCache.newInCore.builder()
val inserter = git.getRepository.newObjectInserter()
val headId = git.getRepository.resolve(Constants.HEAD + "^{commit}")
val content = if(description.nonEmpty){
name + "\n" +
"===============\n" +
"\n" +
description.get
} else {
name + "\n" +
"===============\n"
if (initOption == "README") {
using(Git.open(gitdir)) { git =>
val builder = DirCache.newInCore.builder()
val inserter = git.getRepository.newObjectInserter()
val headId = git.getRepository.resolve(Constants.HEAD + "^{commit}")
val content = if (description.nonEmpty) {
name + "\n" +
"===============\n" +
"\n" +
description.get
} else {
name + "\n" +
"===============\n"
}
builder.add(JGitUtil.createDirCacheEntry("README.md", FileMode.REGULAR_FILE,
inserter.insert(Constants.OBJ_BLOB, content.getBytes("UTF-8"))))
builder.finish()
JGitUtil.createNewCommit(git, inserter, headId, builder.getDirCache.writeTree(inserter),
Constants.HEAD, loginAccount.fullName, loginAccount.mailAddress, "Initial commit")
}
}
builder.add(JGitUtil.createDirCacheEntry("README.md", FileMode.REGULAR_FILE,
inserter.insert(Constants.OBJ_BLOB, content.getBytes("UTF-8"))))
builder.finish()
if (initOption == "COPY") {
sourceUrl.foreach { url =>
// TODO How to feedback error in this block?
val dir = Files.createTempDirectory(s"gitbucket-${owner}-${name}").toFile
JGitUtil.createNewCommit(git, inserter, headId, builder.getDirCache.writeTree(inserter),
Constants.HEAD, loginAccount.fullName, loginAccount.mailAddress, "Initial commit")
}
}
Git.cloneRepository().setBare(true).setURI(url).setDirectory(dir).setCloneAllBranches(true).call()
using(Git.open(dir)) { git =>
git.push().setRemote(gitdir.toURI.toString).setPushAll().setPushTags().call()
}
if (initOption == "COPY") {
sourceUrl.foreach { url =>
val dir = Files.createTempDirectory(s"gitbucket-${owner}-${name}").toFile
println("Cloning to " + dir.getAbsolutePath)
Git.cloneRepository().setBare(true).setURI(url).setDirectory(dir).setCloneAllBranches(true).call()
using(Git.open(dir)) { git =>
git.push().setRemote(gitdir.toURI.toString).setPushAll().setPushTags().call()
FileUtils.deleteQuietly(dir)
}
}
FileUtils.deleteQuietly(dir)
// Create Wiki repository
createWikiRepository(loginAccount, owner, name)
// Record activity
recordCreateRepositoryActivity(owner, name, loginUserName)
}
} finally {
RepositoryCreationService.endCreation(owner, name)
}
// Create Wiki repository
createWikiRepository(loginAccount, owner, name)
// Record activity
recordCreateRepositoryActivity(owner, name, loginUserName)
}
def insertDefaultLabels(userName: String, repositoryName: String)(implicit s: Session): Unit = {

View File

@@ -0,0 +1,26 @@
@(owner: String, repository: String)(implicit context: gitbucket.core.controller.Context)
@gitbucket.core.html.main("Creating...") {
<div class="content-wrapper main-center">
<div class="content body">
<div class="text-center">
<h2>Creating repository...</h2>
<img src="@context.path/assets/common/images/indicator-bar.gif"/>
</div>
</div>
</div>
}
<script>
$(function () {
checkCreating();
});
function checkCreating() {
$.get('@context.path/@owner/@repository/creating', function (data) {
if (data == 'true') {
setTimeout(checkCreating, 2000);
} else {
location.href = '@context.path/@owner/@repository';
}
});
}
</script>