Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorRobert Speicher <rspeicher@gmail.com>2016-07-08 01:40:29 +0300
committerRobert Speicher <rspeicher@gmail.com>2016-07-08 01:40:29 +0300
commitdad406da23f7a4d94f9f8df1a4b8743dc0e9dc96 (patch)
treec3f1fea5d3786e9ad1c3c316754ba1e68f9abc8a /app
parentbf2a86b73cce332ff8f4392ffc8df501193f32ec (diff)
parentbf89e06a459556fc55c0f5582a552ede8f6675c8 (diff)
Merge remote-tracking branch 'origin/master'
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/gl_dropdown.js.coffee4
-rw-r--r--app/assets/javascripts/protected_branch_select.js.coffee40
-rw-r--r--app/assets/javascripts/protected_branches.js.coffee3
-rw-r--r--app/controllers/projects/protected_branches_controller.rb26
-rw-r--r--app/models/project.rb21
-rw-r--r--app/models/protected_branch.rb47
-rw-r--r--app/views/projects/protected_branches/_branches_list.html.haml33
-rw-r--r--app/views/projects/protected_branches/_dropdown.html.haml17
-rw-r--r--app/views/projects/protected_branches/_matching_branch.html.haml9
-rw-r--r--app/views/projects/protected_branches/_protected_branch.html.haml21
-rw-r--r--app/views/projects/protected_branches/index.html.haml24
-rw-r--r--app/views/projects/protected_branches/show.html.haml25
12 files changed, 212 insertions, 58 deletions
diff --git a/app/assets/javascripts/gl_dropdown.js.coffee b/app/assets/javascripts/gl_dropdown.js.coffee
index ed9dfcc917e..1c65e833d47 100644
--- a/app/assets/javascripts/gl_dropdown.js.coffee
+++ b/app/assets/javascripts/gl_dropdown.js.coffee
@@ -56,6 +56,7 @@ class GitLabDropdownFilter
return BLUR_KEYCODES.indexOf(keyCode) >= 0
filter: (search_text) ->
+ @options.onFilter(search_text) if @options.onFilter
data = @options.data()
if data? and not @options.filterByText
@@ -195,6 +196,7 @@ class GitLabDropdown
@filter = new GitLabDropdownFilter @filterInput,
filterInputBlur: @filterInputBlur
filterByText: @options.filterByText
+ onFilter: @options.onFilter
remote: @options.filterRemote
query: @options.data
keys: searchFields
@@ -530,7 +532,7 @@ class GitLabDropdown
if $el.length
e.preventDefault()
e.stopImmediatePropagation()
- $(selector, @dropdown)[0].click()
+ $el.first().trigger('click')
addArrowKeyEvent: ->
ARROW_KEY_CODES = [38, 40]
diff --git a/app/assets/javascripts/protected_branch_select.js.coffee b/app/assets/javascripts/protected_branch_select.js.coffee
new file mode 100644
index 00000000000..6d45770ace9
--- /dev/null
+++ b/app/assets/javascripts/protected_branch_select.js.coffee
@@ -0,0 +1,40 @@
+class @ProtectedBranchSelect
+ constructor: (currentProject) ->
+ $('.dropdown-footer').hide();
+ @dropdown = $('.js-protected-branch-select').glDropdown(
+ data: @getProtectedBranches
+ filterable: true
+ remote: false
+ search:
+ fields: ['title']
+ selectable: true
+ toggleLabel: (selected) -> if (selected and 'id' of selected) then selected.title else 'Protected Branch'
+ fieldName: 'protected_branch[name]'
+ text: (protected_branch) -> _.escape(protected_branch.title)
+ id: (protected_branch) -> _.escape(protected_branch.id)
+ onFilter: @toggleCreateNewButton
+ clicked: () -> $('.protect-branch-btn').attr('disabled', false)
+ )
+
+ $('.create-new-protected-branch').on 'click', (event) =>
+ # Refresh the dropdown's data, which ends up calling `getProtectedBranches`
+ @dropdown.data('glDropdown').remote.execute()
+ @dropdown.data('glDropdown').selectRowAtIndex(event, 0)
+
+ getProtectedBranches: (term, callback) =>
+ if @selectedBranch
+ callback(gon.open_branches.concat(@selectedBranch))
+ else
+ callback(gon.open_branches)
+
+ toggleCreateNewButton: (branchName) =>
+ @selectedBranch = { title: branchName, id: branchName, text: branchName }
+
+ if branchName is ''
+ $('.protected-branch-select-footer-list').addClass('hidden')
+ $('.dropdown-footer').hide();
+ else
+ $('.create-new-protected-branch').text("Create Protected Branch: #{branchName}")
+ $('.protected-branch-select-footer-list').removeClass('hidden')
+ $('.dropdown-footer').show();
+
diff --git a/app/assets/javascripts/protected_branches.js.coffee b/app/assets/javascripts/protected_branches.js.coffee
index 5753c9d4e72..79c2306e4d2 100644
--- a/app/assets/javascripts/protected_branches.js.coffee
+++ b/app/assets/javascripts/protected_branches.js.coffee
@@ -11,7 +11,8 @@ $ ->
dataType: "json"
data:
id: id
- developers_can_push: checked
+ protected_branch:
+ developers_can_push: checked
success: ->
row = $(e.target)
diff --git a/app/controllers/projects/protected_branches_controller.rb b/app/controllers/projects/protected_branches_controller.rb
index efa7bf14d0f..80dad758afa 100644
--- a/app/controllers/projects/protected_branches_controller.rb
+++ b/app/controllers/projects/protected_branches_controller.rb
@@ -2,12 +2,14 @@ class Projects::ProtectedBranchesController < Projects::ApplicationController
# Authorize
before_action :require_non_empty_project
before_action :authorize_admin_project!
+ before_action :load_protected_branch, only: [:show, :update, :destroy]
layout "project_settings"
def index
- @branches = @project.protected_branches.to_a
+ @protected_branches = @project.protected_branches.order(:name).page(params[:page])
@protected_branch = @project.protected_branches.new
+ gon.push({ open_branches: @project.open_branches.map { |br| { text: br.name, id: br.name, title: br.name } } })
end
def create
@@ -16,26 +18,24 @@ class Projects::ProtectedBranchesController < Projects::ApplicationController
@project)
end
- def update
- protected_branch = @project.protected_branches.find(params[:id])
-
- if protected_branch &&
- protected_branch.update_attributes(
- developers_can_push: params[:developers_can_push]
- )
+ def show
+ @matching_branches = @protected_branch.matching(@project.repository.branches)
+ end
+ def update
+ if @protected_branch && @protected_branch.update_attributes(protected_branch_params)
respond_to do |format|
- format.json { render json: protected_branch, status: :ok }
+ format.json { render json: @protected_branch, status: :ok }
end
else
respond_to do |format|
- format.json { render json: protected_branch.errors, status: :unprocessable_entity }
+ format.json { render json: @protected_branch.errors, status: :unprocessable_entity }
end
end
end
def destroy
- @project.protected_branches.find(params[:id]).destroy
+ @protected_branch.destroy
respond_to do |format|
format.html { redirect_to namespace_project_protected_branches_path }
@@ -45,6 +45,10 @@ class Projects::ProtectedBranchesController < Projects::ApplicationController
private
+ def load_protected_branch
+ @protected_branch = @project.protected_branches.find(params[:id])
+ end
+
def protected_branch_params
params.require(:protected_branch).permit(:name, :developers_can_push)
end
diff --git a/app/models/project.rb b/app/models/project.rb
index 029026a4e56..5005ee7cd32 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -802,18 +802,12 @@ class Project < ActiveRecord::Base
@repo_exists = false
end
+ # Branches that are not _exactly_ matched by a protected branch.
def open_branches
- # We're using a Set here as checking values in a large Set is faster than
- # checking values in a large Array.
- protected_set = Set.new(protected_branch_names)
-
- repository.branches.reject do |branch|
- protected_set.include?(branch.name)
- end
- end
-
- def protected_branch_names
- @protected_branch_names ||= protected_branches.pluck(:name)
+ exact_protected_branch_names = protected_branches.reject(&:wildcard?).map(&:name)
+ branch_names = repository.branches.map(&:name)
+ non_open_branch_names = Set.new(exact_protected_branch_names).intersection(Set.new(branch_names))
+ repository.branches.reject { |branch| non_open_branch_names.include? branch.name }
end
def root_ref?(branch)
@@ -830,11 +824,12 @@ class Project < ActiveRecord::Base
# Check if current branch name is marked as protected in the system
def protected_branch?(branch_name)
- protected_branch_names.include?(branch_name)
+ @protected_branches ||= self.protected_branches.to_a
+ ProtectedBranch.matching(branch_name, protected_branches: @protected_branches).present?
end
def developers_can_push_to_protected_branch?(branch_name)
- protected_branches.any? { |pb| pb.name == branch_name && pb.developers_can_push }
+ protected_branches.matching(branch_name).any?(&:developers_can_push)
end
def forked?
diff --git a/app/models/protected_branch.rb b/app/models/protected_branch.rb
index 33cf046fa75..b7011d7afdf 100644
--- a/app/models/protected_branch.rb
+++ b/app/models/protected_branch.rb
@@ -8,4 +8,51 @@ class ProtectedBranch < ActiveRecord::Base
def commit
project.commit(self.name)
end
+
+ # Returns all protected branches that match the given branch name.
+ # This realizes all records from the scope built up so far, and does
+ # _not_ return a relation.
+ #
+ # This method optionally takes in a list of `protected_branches` to search
+ # through, to avoid calling out to the database.
+ def self.matching(branch_name, protected_branches: nil)
+ (protected_branches || all).select { |protected_branch| protected_branch.matches?(branch_name) }
+ end
+
+ # Returns all branches (among the given list of branches [`Gitlab::Git::Branch`])
+ # that match the current protected branch.
+ def matching(branches)
+ branches.select { |branch| self.matches?(branch.name) }
+ end
+
+ # Checks if the protected branch matches the given branch name.
+ def matches?(branch_name)
+ return false if self.name.blank?
+
+ exact_match?(branch_name) || wildcard_match?(branch_name)
+ end
+
+ # Checks if this protected branch contains a wildcard
+ def wildcard?
+ self.name && self.name.include?('*')
+ end
+
+ protected
+
+ def exact_match?(branch_name)
+ self.name == branch_name
+ end
+
+ def wildcard_match?(branch_name)
+ wildcard_regex === branch_name
+ end
+
+ def wildcard_regex
+ @wildcard_regex ||= begin
+ name = self.name.gsub('*', 'STAR_DONT_ESCAPE')
+ quoted_name = Regexp.quote(name)
+ regex_string = quoted_name.gsub('STAR_DONT_ESCAPE', '.*?')
+ /\A#{regex_string}\z/
+ end
+ end
end
diff --git a/app/views/projects/protected_branches/_branches_list.html.haml b/app/views/projects/protected_branches/_branches_list.html.haml
index 565905cbe7b..97cb1a9052b 100644
--- a/app/views/projects/protected_branches/_branches_list.html.haml
+++ b/app/views/projects/protected_branches/_branches_list.html.haml
@@ -1,6 +1,6 @@
%h5.prepend-top-0
- Already Protected (#{@branches.size})
-- if @branches.empty?
+ Already Protected (#{@protected_branches.size})
+- if @protected_branches.empty?
%p.settings-message.text-center
No branches are protected, protect a branch with the form above.
- else
@@ -9,33 +9,18 @@
%table.table.protected-branches-list
%colgroup
%col{ width: "30%" }
- %col{ width: "30%" }
+ %col{ width: "25%" }
%col{ width: "25%" }
- if can_admin_project
%col
%thead
%tr
- %th Branch
- %th Last commit
- %th Developers can push
+ %th Protected Branch
+ %th Commit
+ %th Developers Can Push
- if can_admin_project
%th
%tbody
- - @branches.each do |branch|
- - @url = namespace_project_protected_branch_path(@project.namespace, @project, branch)
- %tr
- %td
- = link_to(branch.name, namespace_project_commits_path(@project.namespace, @project, branch.name))
- - if @project.root_ref?(branch.name)
- %span.label.label-info.prepend-left-5 default
- %td
- - if commit = branch.commit
- = link_to(commit.short_id, namespace_project_commit_path(@project.namespace, @project, commit.id), class: 'commit_short_id')
- #{time_ago_with_tooltip(commit.committed_date)}
- - else
- (branch was removed from repository)
- %td
- = check_box_tag("developers_can_push", branch.id, branch.developers_can_push, data: { url: @url })
- - if can_admin_project
- %td
- = link_to 'Unprotect', [@project.namespace.becomes(Namespace), @project, branch], data: { confirm: 'Branch will be writable for developers. Are you sure?' }, method: :delete, class: "btn btn-warning btn-sm"
+ = render partial: @protected_branches, locals: { can_admin_project: can_admin_project }
+
+ = paginate @protected_branches, theme: 'gitlab'
diff --git a/app/views/projects/protected_branches/_dropdown.html.haml b/app/views/projects/protected_branches/_dropdown.html.haml
new file mode 100644
index 00000000000..b803d932e67
--- /dev/null
+++ b/app/views/projects/protected_branches/_dropdown.html.haml
@@ -0,0 +1,17 @@
+= f.hidden_field(:name)
+
+= dropdown_tag("Protected Branch",
+ options: { title: "Pick protected branch", toggle_class: 'js-protected-branch-select js-filter-submit',
+ filter: true, dropdown_class: "dropdown-menu-selectable", placeholder: "Search protected branches",
+ footer_content: true,
+ data: { show_no: true, show_any: true, show_upcoming: true,
+ selected: params[:protected_branch_name],
+ project_id: @project.try(:id) } }) do
+
+ %ul.dropdown-footer-list.hidden.protected-branch-select-footer-list
+ %li
+ = link_to '#', title: "New Protected Branch", class: "create-new-protected-branch" do
+ Create new
+
+:javascript
+ new ProtectedBranchSelect();
diff --git a/app/views/projects/protected_branches/_matching_branch.html.haml b/app/views/projects/protected_branches/_matching_branch.html.haml
new file mode 100644
index 00000000000..8a5332ca5bb
--- /dev/null
+++ b/app/views/projects/protected_branches/_matching_branch.html.haml
@@ -0,0 +1,9 @@
+%tr
+ %td
+ = link_to matching_branch.name, namespace_project_tree_path(@project.namespace, @project, matching_branch.name)
+ - if @project.root_ref?(matching_branch.name)
+ %span.label.label-info.prepend-left-5 default
+ %td
+ - commit = @project.commit(matching_branch.name)
+ = link_to(commit.short_id, namespace_project_commit_path(@project.namespace, @project, commit.id), class: 'commit_short_id')
+ = time_ago_with_tooltip(commit.committed_date)
diff --git a/app/views/projects/protected_branches/_protected_branch.html.haml b/app/views/projects/protected_branches/_protected_branch.html.haml
new file mode 100644
index 00000000000..474aec3a97c
--- /dev/null
+++ b/app/views/projects/protected_branches/_protected_branch.html.haml
@@ -0,0 +1,21 @@
+- url = namespace_project_protected_branch_path(@project.namespace, @project, protected_branch)
+%tr
+ %td
+ = protected_branch.name
+ - if @project.root_ref?(protected_branch.name)
+ %span.label.label-info.prepend-left-5 default
+ %td
+ - if protected_branch.wildcard?
+ - matching_branches = protected_branch.matching(repository.branches)
+ = link_to pluralize(matching_branches.count, "matching branch"), namespace_project_protected_branch_path(@project.namespace, @project, protected_branch)
+ - else
+ - if commit = protected_branch.commit
+ = link_to(commit.short_id, namespace_project_commit_path(@project.namespace, @project, commit.id), class: 'commit_short_id')
+ = time_ago_with_tooltip(commit.committed_date)
+ - else
+ (branch was removed from repository)
+ %td
+ = check_box_tag("developers_can_push", protected_branch.id, protected_branch.developers_can_push, data: { url: url })
+ - if can_admin_project
+ %td
+ = link_to 'Unprotect', [@project.namespace.becomes(Namespace), @project, protected_branch], data: { confirm: 'Branch will be writable for developers. Are you sure?' }, method: :delete, class: "btn btn-warning btn-sm pull-right"
diff --git a/app/views/projects/protected_branches/index.html.haml b/app/views/projects/protected_branches/index.html.haml
index c7d317dbaee..5669713d9a1 100644
--- a/app/views/projects/protected_branches/index.html.haml
+++ b/app/views/projects/protected_branches/index.html.haml
@@ -4,30 +4,38 @@
.col-lg-3
%h4.prepend-top-0
= page_title
- %p Keep stable branches secure and force developers to use Merge Requests
- .col-lg-9
- %h5.prepend-top-0
- Protect a branch
- .account-well.append-bottom-default
- %p.light-header.append-bottom-0 Protected branches are designed to
+ %p Keep stable branches secure and force developers to use merge requests.
+ %p.prepend-top-20
+ Protected branches are designed to:
%ul
%li prevent pushes from everybody except #{link_to "masters", help_page_path("permissions", "permissions"), class: "vlink"}
%li prevent anyone from force pushing to the branch
%li prevent anyone from deleting the branch
%p.append-bottom-0 Read more about #{link_to "project permissions", help_page_path("permissions", "permissions"), class: "underlined-link"}
+ .col-lg-9
+ %h5.prepend-top-0
+ Protect a branch
- if can? current_user, :admin_project, @project
= form_for [@project.namespace.becomes(Namespace), @project, @protected_branch] do |f|
= form_errors(@protected_branch)
.form-group
= f.label :name, "Branch", class: "label-light"
- = f.select(:name, @project.open_branches.map { |br| [br.name, br.name] } , {include_blank: true}, {class: "select2", data: {placeholder: "Select branch"}})
+ = render partial: "dropdown", locals: { f: f }
+ %p.help-block
+ = link_to "Wildcards", help_page_path(category: 'workflow', file: 'protected_branches', format: 'md', anchor: "wildcard-protected-branches")
+ such as
+ %code *-stable
+ or
+ %code production/*
+ are supported.
+
.form-group
= f.check_box :developers_can_push, class: "pull-left"
.prepend-left-20
= f.label :developers_can_push, "Developers can push", class: "label-light append-bottom-0"
%p.light.append-bottom-0
Allow developers to push to this branch
- = f.submit "Protect", class: "btn-create btn"
+ = f.submit "Protect", class: "btn-create btn protect-branch-btn", disabled: true
%hr
= render "branches_list"
diff --git a/app/views/projects/protected_branches/show.html.haml b/app/views/projects/protected_branches/show.html.haml
new file mode 100644
index 00000000000..4d8169815b3
--- /dev/null
+++ b/app/views/projects/protected_branches/show.html.haml
@@ -0,0 +1,25 @@
+- page_title @protected_branch.name, "Protected Branches"
+
+.row.prepend-top-default.append-bottom-default
+ .col-lg-3
+ %h4.prepend-top-0
+ = @protected_branch.name
+
+ .col-lg-9
+ %h5 Matching Branches
+ - if @matching_branches.present?
+ .table-responsive
+ %table.table.protected-branches-list
+ %colgroup
+ %col{ width: "30%" }
+ %col{ width: "30%" }
+ %thead
+ %tr
+ %th Branch
+ %th Last commit
+ %tbody
+ - @matching_branches.each do |matching_branch|
+ = render partial: "matching_branch", object: matching_branch
+ - else
+ %p.settings-message.text-center
+ Couldn't find any matching branches.