create repository setting branches page and move default branch setting to there.

This commit is contained in:
nazoking
2015-11-29 22:57:45 +09:00
parent 100b34085c
commit e187d026cc
7 changed files with 172 additions and 30 deletions

View File

@@ -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.
*/

View File

@@ -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)
}
}
}

View File

@@ -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.

View File

@@ -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("/")

View 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 dont 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>
}
}
}
}

View File

@@ -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>

View File

@@ -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"