mirror of
https://github.com/gitbucket/gitbucket.git
synced 2026-02-28 01:10:58 +01:00
create repository setting branches page and move default branch setting to there.
This commit is contained in:
@@ -26,15 +26,21 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
||||
with OwnerAuthenticator with UsersAuthenticator =>
|
||||
|
||||
// for repository options
|
||||
case class OptionsForm(repositoryName: String, description: Option[String], defaultBranch: String, isPrivate: Boolean)
|
||||
case class OptionsForm(repositoryName: String, description: Option[String], isPrivate: Boolean)
|
||||
|
||||
val optionsForm = mapping(
|
||||
"repositoryName" -> trim(label("Repository Name", text(required, maxlength(40), identifier, renameRepositoryName))),
|
||||
"description" -> trim(label("Description" , optional(text()))),
|
||||
"defaultBranch" -> trim(label("Default Branch" , text(required, maxlength(100)))),
|
||||
"isPrivate" -> trim(label("Repository Type", boolean()))
|
||||
)(OptionsForm.apply)
|
||||
|
||||
// for default branch
|
||||
case class DefaultBranchForm(defaultBranch: String)
|
||||
|
||||
val defaultBranchForm = mapping(
|
||||
"defaultBranch" -> trim(label("Default Branch" , text(required, maxlength(100))))
|
||||
)(DefaultBranchForm.apply)
|
||||
|
||||
// for collaborator addition
|
||||
case class CollaboratorForm(userName: String)
|
||||
|
||||
@@ -75,12 +81,10 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
||||
* Save the repository options.
|
||||
*/
|
||||
post("/:owner/:repository/settings/options", optionsForm)(ownerOnly { (form, repository) =>
|
||||
val defaultBranch = if(repository.branchList.isEmpty) "master" else form.defaultBranch
|
||||
saveRepositoryOptions(
|
||||
repository.owner,
|
||||
repository.name,
|
||||
form.description,
|
||||
defaultBranch,
|
||||
repository.repository.parentUserName.map { _ =>
|
||||
repository.repository.isPrivate
|
||||
} getOrElse form.isPrivate
|
||||
@@ -98,14 +102,28 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
||||
FileUtils.moveDirectory(dir, getWikiRepositoryDir(repository.owner, form.repositoryName))
|
||||
}
|
||||
}
|
||||
// Change repository HEAD
|
||||
using(Git.open(getRepositoryDir(repository.owner, form.repositoryName))) { git =>
|
||||
git.getRepository.updateRef(Constants.HEAD, true).link(Constants.R_HEADS + defaultBranch)
|
||||
}
|
||||
flash += "info" -> "Repository settings has been updated."
|
||||
redirect(s"/${repository.owner}/${form.repositoryName}/settings/options")
|
||||
})
|
||||
|
||||
|
||||
get("/:owner/:repository/settings/branches")(ownerOnly { repository =>
|
||||
html.branches(repository, flash.get("info"))
|
||||
});
|
||||
|
||||
post("/:owner/:repository/settings/update_default_branch", defaultBranchForm)(ownerOnly { (form, repository) =>
|
||||
if(repository.branchList.find(_ == form.defaultBranch).isEmpty){
|
||||
redirect(s"/${repository.owner}/${repository.name}/settings/options")
|
||||
}else{
|
||||
saveRepositoryDefaultBranch(repository.owner, repository.name, form.defaultBranch)
|
||||
// Change repository HEAD
|
||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
git.getRepository.updateRef(Constants.HEAD, true).link(Constants.R_HEADS + form.defaultBranch)
|
||||
}
|
||||
flash += "info" -> "Repository default branch has been updated."
|
||||
redirect(s"/${repository.owner}/${repository.name}/settings/branches")
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* Display the Collaborators page.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
package gitbucket.core.service
|
||||
|
||||
import gitbucket.core.model.{Collaborator, Repository, Account, CommitState}
|
||||
import gitbucket.core.model.Profile._
|
||||
import gitbucket.core.util.JGitUtil
|
||||
import profile.simple._
|
||||
|
||||
import org.eclipse.jgit.transport.ReceiveCommand
|
||||
import org.eclipse.jgit.transport.ReceivePack
|
||||
import org.eclipse.jgit.lib.ObjectId
|
||||
|
||||
|
||||
trait ProtectedBrancheService {
|
||||
import ProtectedBrancheService._
|
||||
def getProtectedBranchInfo(owner: String, repository: String, branch: String)(implicit session: Session): Option[ProtectedBranchInfo] = {
|
||||
// TODO: mock
|
||||
if(owner == "root" && repository == "test58" && branch == "hoge2"){
|
||||
Some(new ProtectedBranchInfo(owner, repository, Seq.empty, false))
|
||||
}else{
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
def getBranchProtectedReason(owner: String, repository: String, receivePack: ReceivePack, command: ReceiveCommand, pusher: String)(implicit session: Session): Option[String] = {
|
||||
val branch = command.getRefName.stripPrefix("refs/heads/")
|
||||
if(branch != command.getRefName){
|
||||
getProtectedBranchInfo(owner, repository, branch).flatMap(_.getStopReason(receivePack, command, pusher))
|
||||
}else{
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
object ProtectedBrancheService {
|
||||
class ProtectedBranchInfo(
|
||||
owner: String,
|
||||
repository: String,
|
||||
/**
|
||||
* Require status checks to pass before merging
|
||||
* Choose which status checks must pass before branches can be merged into test.
|
||||
* When enabled, commits must first be pushed to another branch,
|
||||
* then merged or pushed directly to test after status checks have passed.
|
||||
*/
|
||||
requireStatusChecksToPass: Seq[String],
|
||||
/**
|
||||
* Include administrators
|
||||
* Enforce required status checks for repository administrators.
|
||||
*/
|
||||
includeAdministrators: Boolean) extends AccountService with CommitStatusService {
|
||||
|
||||
def isAdministrator(pusher: String)(implicit session: Session): Boolean = pusher == owner || getGroupMembers(owner).filter(gm => gm.userName == pusher && gm.isManager).nonEmpty
|
||||
|
||||
/**
|
||||
* Can't be force pushed
|
||||
* Can't be deleted
|
||||
* Can't have changes merged into them until required status checks pass
|
||||
*/
|
||||
def getStopReason(receivePack: ReceivePack, command: ReceiveCommand, pusher: String)(implicit session: Session): Option[String] = {
|
||||
command.getType() match {
|
||||
case ReceiveCommand.Type.UPDATE|ReceiveCommand.Type.UPDATE_NONFASTFORWARD if receivePack.isAllowNonFastForwards =>
|
||||
Some("Cannot force-push to a protected branch")
|
||||
case ReceiveCommand.Type.UPDATE|ReceiveCommand.Type.UPDATE_NONFASTFORWARD if needStatusCheck(pusher) =>
|
||||
unSuccessedContexts(command.getNewId.name) match {
|
||||
case s if s.size == 1 => Some(s"""Required status check "${s.toSeq(0)}" is expected""")
|
||||
case s if s.size >= 1 => Some(s"${s.size} of ${requireStatusChecksToPass.size} required status checks are expected")
|
||||
case _ => None
|
||||
}
|
||||
case ReceiveCommand.Type.DELETE =>
|
||||
Some("Cannot delete a protected branch")
|
||||
case _ => None
|
||||
}
|
||||
}
|
||||
def unSuccessedContexts(sha1: String)(implicit session: Session): Set[String] = if(requireStatusChecksToPass.isEmpty){
|
||||
Set.empty
|
||||
} else {
|
||||
requireStatusChecksToPass.toSet -- getCommitStatues(owner, repository, sha1).filter(_.state == CommitState.SUCCESS).map(_.context).toSet
|
||||
}
|
||||
def needStatusCheck(pusher: String)(implicit session: Session): Boolean =
|
||||
if(requireStatusChecksToPass.isEmpty){
|
||||
false
|
||||
}else if(includeAdministrators){
|
||||
true
|
||||
}else{
|
||||
!isAdministrator(pusher)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -309,11 +309,17 @@ trait RepositoryService { self: AccountService =>
|
||||
/**
|
||||
* Save repository options.
|
||||
*/
|
||||
def saveRepositoryOptions(userName: String, repositoryName: String,
|
||||
description: Option[String], defaultBranch: String, isPrivate: Boolean)(implicit s: Session): Unit =
|
||||
def saveRepositoryOptions(userName: String, repositoryName: String,
|
||||
description: Option[String], isPrivate: Boolean)(implicit s: Session): Unit =
|
||||
Repositories.filter(_.byRepository(userName, repositoryName))
|
||||
.map { r => (r.description.?, r.defaultBranch, r.isPrivate, r.updatedDate) }
|
||||
.update (description, defaultBranch, isPrivate, currentDate)
|
||||
.map { r => (r.description.?, r.isPrivate, r.updatedDate) }
|
||||
.update (description, isPrivate, currentDate)
|
||||
|
||||
def saveRepositoryDefaultBranch(userName: String, repositoryName: String,
|
||||
defaultBranch: String)(implicit s: Session): Unit =
|
||||
Repositories.filter(_.byRepository(userName, repositoryName))
|
||||
.map { r => r.defaultBranch }
|
||||
.update (defaultBranch)
|
||||
|
||||
/**
|
||||
* Add collaborator to the repository.
|
||||
|
||||
@@ -111,13 +111,18 @@ import scala.collection.JavaConverters._
|
||||
class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl: String)(implicit session: Session)
|
||||
extends PostReceiveHook with PreReceiveHook
|
||||
with RepositoryService with AccountService with IssuesService with ActivityService with PullRequestService with WebHookService
|
||||
with WebHookPullRequestService {
|
||||
with WebHookPullRequestService with ProtectedBrancheService {
|
||||
|
||||
private val logger = LoggerFactory.getLogger(classOf[CommitLogHook])
|
||||
private var existIds: Seq[String] = Nil
|
||||
|
||||
def onPreReceive(receivePack: ReceivePack, commands: java.util.Collection[ReceiveCommand]): Unit = {
|
||||
try {
|
||||
commands.asScala.foreach { command =>
|
||||
getBranchProtectedReason(owner, repository, receivePack, command, pusher).map{ reason =>
|
||||
command.setResult(ReceiveCommand.Result.REJECTED_OTHER_REASON, reason)
|
||||
}
|
||||
}
|
||||
using(Git.open(Directory.getRepositoryDir(owner, repository))) { git =>
|
||||
existIds = JGitUtil.getAllCommitIds(git)
|
||||
}
|
||||
@@ -134,6 +139,7 @@ class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl:
|
||||
using(Git.open(Directory.getRepositoryDir(owner, repository))) { git =>
|
||||
val pushedIds = scala.collection.mutable.Set[String]()
|
||||
commands.asScala.foreach { command =>
|
||||
println(s"onPostReceive commandType: ${command.getType}, refName: ${command.getRefName}")
|
||||
logger.debug(s"commandType: ${command.getType}, refName: ${command.getRefName}")
|
||||
implicit val apiContext = api.JsonFormat.Context(baseUrl)
|
||||
val refName = command.getRefName.split("/")
|
||||
|
||||
37
src/main/twirl/gitbucket/core/settings/branches.scala.html
Normal file
37
src/main/twirl/gitbucket/core/settings/branches.scala.html
Normal file
@@ -0,0 +1,37 @@
|
||||
@(repository: gitbucket.core.service.RepositoryService.RepositoryInfo,
|
||||
info: Option[Any])(implicit context: gitbucket.core.controller.Context)
|
||||
@import context._
|
||||
@import gitbucket.core.view.helpers._
|
||||
@import gitbucket.core.model.WebHook._
|
||||
@html.main("Branches", Some(repository)){
|
||||
@html.menu("settings", repository){
|
||||
@menu("branches", repository){
|
||||
@if(repository.branchList.isEmpty){
|
||||
<div class="well">
|
||||
<center>
|
||||
<p><i class="octicon octicon-git-branch" style="font-size:300%"></i></p>
|
||||
<p>You don’t have any branches</p>
|
||||
<p>Before you can edit branch settings, you need to add a branch.</p>
|
||||
</center>
|
||||
</div>
|
||||
}else{
|
||||
@helper.html.information(info)
|
||||
<div class="box">
|
||||
<div class="box-header">Default branch</div>
|
||||
<div class="box-content">
|
||||
<p>The default branch is considered the “base” branch in your repository, against which all pull requests and code commits are automatically made, unless you specify a different branch.</p>
|
||||
<form id="form" method="post" action="@url(repository)/settings/update_default_branch" validate="true" class="form-inline">
|
||||
<span class="error" id="error-defaultBranch"></span>
|
||||
<select name="defaultBranch" id="defaultBranch">
|
||||
@repository.branchList.map { branch =>
|
||||
<option @if(branch==repository.repository.defaultBranch){ selected}>@branch</option>
|
||||
}
|
||||
</select>
|
||||
<input type="submit" class="btn" value="Update" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,11 @@
|
||||
<li@if(active=="collaborators"){ class="active"}>
|
||||
<a href="@url(repository)/settings/collaborators">Collaborators</a>
|
||||
</li>
|
||||
@if(!repository.branchList.isEmpty){
|
||||
<li@if(active=="branches"){ class="active"}>
|
||||
<a href="@url(repository)/settings/branches">Branches</a>
|
||||
</li>
|
||||
}
|
||||
<li@if(active=="hooks"){ class="active"}>
|
||||
<a href="@url(repository)/settings/hooks">Service Hooks</a>
|
||||
</li>
|
||||
|
||||
@@ -18,22 +18,6 @@
|
||||
<label for="description" class="strong">Description:</label>
|
||||
<input type="text" name="description" id="description" class="form-control" value="@repository.repository.description"/>
|
||||
</fieldset>
|
||||
<fieldset class="margin form-group">
|
||||
<label for="defaultBranch" class="strong">Default Branch:</label>
|
||||
<select name="defaultBranch" id="defaultBranch"@if(repository.branchList.isEmpty){ disabled} class="form-control">
|
||||
@if(repository.branchList.isEmpty){
|
||||
<option value="none" selected>No Branch</option>
|
||||
} else {
|
||||
@repository.branchList.map { branch =>
|
||||
<option@if(branch==repository.repository.defaultBranch){ selected}>@branch</option>
|
||||
}
|
||||
}
|
||||
</select>
|
||||
@if(repository.branchList.isEmpty){
|
||||
<input type="hidden" name="defaultBranch" value="none"/>
|
||||
}
|
||||
<span class="error" id="error-defaultBranch"></span>
|
||||
</fieldset>
|
||||
<fieldset class="margin">
|
||||
<label class="radio">
|
||||
<input type="radio" name="isPrivate" value="false"
|
||||
|
||||
Reference in New Issue
Block a user