
gitbucket.core.service.RepositoryCreationService.scala Maven / Gradle / Ivy
package gitbucket.core.service
import java.nio.file.Files
import java.util.concurrent.ConcurrentHashMap
import gitbucket.core.model.Profile.profile.blockingApi.*
import gitbucket.core.model.activity.{CreateRepositoryInfo, ForkInfo}
import gitbucket.core.util.Directory.*
import gitbucket.core.util.{FileUtil, JGitUtil, LockUtil}
import gitbucket.core.model.{Account, Role}
import gitbucket.core.plugin.PluginRegistry
import gitbucket.core.service.RepositoryService.RepositoryInfo
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
import scala.jdk.CollectionConverters._
import scala.util.Using
object RepositoryCreationService {
private val Creating = new ConcurrentHashMap[String, Option[String]]()
def isCreating(owner: String, repository: String): Boolean = {
Option(Creating.get(s"${owner}/${repository}")).exists(_.isEmpty)
}
def startCreation(owner: String, repository: String): Unit = {
Creating.put(s"${owner}/${repository}", None)
}
def endCreation(owner: String, repository: String, error: Option[String]): Unit = {
error match {
case None => Creating.remove(s"${owner}/${repository}")
case Some(error) => Creating.put(s"${owner}/${repository}", Some(error))
}
}
def getCreationError(owner: String, repository: String): Option[String] = {
Option(Creating.remove(s"${owner}/${repository}")).flatten
}
}
trait RepositoryCreationService {
self: AccountService
with RepositoryService
with LabelsService
with WikiService
with ActivityService
with PrioritiesService =>
def canCreateRepository(repositoryOwner: String, loginAccount: Account)(implicit session: Session): Boolean = {
repositoryOwner == loginAccount.userName || getGroupsByUserName(loginAccount.userName)
.contains(repositoryOwner) || loginAccount.isAdmin
}
def createRepository(
loginAccount: Account,
owner: String,
name: String,
description: Option[String],
isPrivate: Boolean,
createReadme: Boolean,
defaultBranch: String
): Future[Unit] = {
createRepository(
loginAccount,
owner,
name,
description,
isPrivate,
if (createReadme) "README" else "EMPTY",
None,
defaultBranch
)
}
def createRepository(
loginAccount: Account,
owner: String,
name: String,
description: Option[String],
isPrivate: Boolean,
initOption: String,
sourceUrl: Option[String],
defaultBranch: String
): Future[Unit] = Future {
RepositoryCreationService.startCreation(owner, name)
try {
Database() withTransaction { implicit session =>
// val ownerAccount = getAccountByUserName(owner).get
val loginUserName = loginAccount.userName
val copyRepositoryDir = if (initOption == "COPY") {
sourceUrl.flatMap { url =>
val dir = Files.createTempDirectory(s"gitbucket-${owner}-${name}").toFile
Git.cloneRepository().setBare(true).setURI(url).setDirectory(dir).setCloneAllBranches(true).call()
Some(dir)
}
} else None
// Insert to the database at first
insertRepository(name, owner, description, isPrivate, defaultBranch)
// // 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 priorities
insertDefaultPriorities(owner, name)
// Create the actual repository
val gitdir = getRepositoryDir(owner, name)
JGitUtil.initRepository(gitdir, defaultBranch)
if (initOption == "README" || initOption == "EMPTY_COMMIT") {
Using.resource(Git.open(gitdir)) { git =>
val builder = DirCache.newInCore.builder()
val inserter = git.getRepository.newObjectInserter()
val headId = git.getRepository.resolve(Constants.HEAD + "^{commit}")
if (initOption == "README") {
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"
)
}
}
copyRepositoryDir.foreach { dir =>
try {
Using.resource(Git.open(dir)) { git =>
git.push().setRemote(gitdir.toURI.toString).setPushAll().setPushTags().call()
// Adjust the default branch
val branches = git.branchList().call().asScala.map(_.getName.stripPrefix("refs/heads/"))
if (!branches.contains(defaultBranch)) {
val defaultBranch = Seq("master", "main").find(branches.contains).getOrElse(branches.head)
saveRepositoryDefaultBranch(owner, name, defaultBranch)
// Change repository HEAD
Using.resource(Git.open(getRepositoryDir(owner, name))) { git =>
git.getRepository.updateRef(Constants.HEAD, true).link(Constants.R_HEADS + defaultBranch)
}
}
}
} finally {
FileUtils.deleteQuietly(dir)
}
}
// Create Wiki repository
createWikiRepository(loginAccount, owner, name, defaultBranch)
// Record activity
recordActivity(CreateRepositoryInfo(owner, name, loginUserName))
// Call hooks
PluginRegistry().getRepositoryHooks.foreach(_.created(owner, name))
}
RepositoryCreationService.endCreation(owner, name, None)
} catch {
case ex: Exception => RepositoryCreationService.endCreation(owner, name, Some(ex.toString))
}
}
def forkRepository(accountName: String, repository: RepositoryInfo, loginUserName: String): Future[Unit] = Future {
RepositoryCreationService.startCreation(accountName, repository.name)
try {
LockUtil.lock(s"${accountName}/${repository.name}") {
Database() withTransaction { implicit session =>
val originUserName = repository.repository.originUserName.getOrElse(repository.owner)
val originRepositoryName = repository.repository.originRepositoryName.getOrElse(repository.name)
val originDefaultBranchName = repository.repository.defaultBranch
insertRepository(
repositoryName = repository.name,
userName = accountName,
description = repository.repository.description,
isPrivate = repository.repository.isPrivate,
defaultBranch = originDefaultBranchName,
originRepositoryName = Some(originRepositoryName),
originUserName = Some(originUserName),
parentRepositoryName = Some(repository.name),
parentUserName = Some(repository.owner)
)
// Set default collaborators for the private fork
if (repository.repository.isPrivate) {
// Copy collaborators from the source repository
getCollaborators(repository.owner, repository.name).foreach { case (collaborator, _) =>
addCollaborator(accountName, repository.name, collaborator.collaboratorName, collaborator.role)
}
// Register an owner of the source repository as a collaborator
addCollaborator(accountName, repository.name, repository.owner, Role.ADMIN.name)
}
// Insert default labels
insertDefaultLabels(accountName, repository.name)
// Insert default priorities
insertDefaultPriorities(accountName, repository.name)
// clone repository actually
JGitUtil.cloneRepository(
getRepositoryDir(repository.owner, repository.name),
FileUtil.deleteIfExists(getRepositoryDir(accountName, repository.name))
)
// Create Wiki repository
JGitUtil.cloneRepository(
getWikiRepositoryDir(repository.owner, repository.name),
FileUtil.deleteIfExists(getWikiRepositoryDir(accountName, repository.name))
)
// Copy LFS files
val lfsDir = getLfsDir(repository.owner, repository.name)
if (lfsDir.exists) {
FileUtils.copyDirectory(lfsDir, FileUtil.deleteIfExists(getLfsDir(accountName, repository.name)))
}
// Record activity
val forkInfo = ForkInfo(repository.owner, repository.name, loginUserName, accountName)
recordActivity(forkInfo)
// Call hooks
PluginRegistry().getRepositoryHooks.foreach(_.forked(repository.owner, accountName, repository.name))
RepositoryCreationService.endCreation(accountName, repository.name, None)
}
}
} catch {
case ex: Exception => RepositoryCreationService.endCreation(accountName, repository.name, Some(ex.toString))
}
}
def insertDefaultLabels(userName: String, repositoryName: String)(implicit s: Session): Unit = {
createLabel(userName, repositoryName, "bug", "fc2929")
createLabel(userName, repositoryName, "duplicate", "cccccc")
createLabel(userName, repositoryName, "enhancement", "84b6eb")
createLabel(userName, repositoryName, "invalid", "e6e6e6")
createLabel(userName, repositoryName, "question", "cc317c")
createLabel(userName, repositoryName, "wontfix", "ffffff")
}
def insertDefaultPriorities(userName: String, repositoryName: String)(implicit s: Session): Unit = {
createPriority(
userName,
repositoryName,
"highest",
Some("All defects at this priority must be fixed before any public product is delivered."),
"fc2929"
)
createPriority(
userName,
repositoryName,
"very high",
Some("Issues must be addressed before a final product is delivered."),
"fc5629"
)
createPriority(
userName,
repositoryName,
"high",
Some(
"Issues should be addressed before a final product is delivered. If the issue cannot be resolved before delivery, it should be prioritized for the next release."
),
"fc9629"
)
createPriority(
userName,
repositoryName,
"important",
Some("Issues can be shipped with a final product, but should be reviewed before the next release."),
"fccd29"
)
createPriority(userName, repositoryName, "default", Some("Default."), "acacac")
setDefaultPriority(userName, repositoryName, getPriority(userName, repositoryName, "default").map(_.priorityId))
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy