Commit 52be9e20 authored by Kamil Trzcinski's avatar Kamil Trzcinski

Merge remote-tracking branch 'origin/master' into ci-commit-as-pipeline

# Conflicts:
#	app/views/projects/commits/_commit.html.haml
parents af7214d0 7998725e
...@@ -691,7 +691,7 @@ Style/ZeroLengthPredicate: ...@@ -691,7 +691,7 @@ Style/ZeroLengthPredicate:
# branches, and conditions. # branches, and conditions.
Metrics/AbcSize: Metrics/AbcSize:
Enabled: true Enabled: true
Max: 70 Max: 60
# Avoid excessive block nesting. # Avoid excessive block nesting.
Metrics/BlockNesting: Metrics/BlockNesting:
......
class @Compare
constructor: (@opts) ->
@source_loading = $ ".js-source-loading"
@target_loading = $ ".js-target-loading"
$('.js-compare-dropdown').each (i, dropdown) =>
$dropdown = $(dropdown)
$dropdown.glDropdown(
selectable: true
fieldName: $dropdown.data 'field-name'
filterable: true
id: (obj, $el) ->
$el.data 'id'
toggleLabel: (obj, $el) ->
$el.text().trim()
clicked: (e, el) =>
if $dropdown.is '.js-target-branch'
@getTargetHtml()
else if $dropdown.is '.js-source-branch'
@getSourceHtml()
else if $dropdown.is '.js-target-project'
@getTargetProject()
)
@initialState()
initialState: ->
@getSourceHtml()
@getTargetHtml()
getTargetProject: ->
$.ajax(
url: @opts.targetProjectUrl
data:
target_project_id: $("input[name='merge_request[target_project_id]']").val()
beforeSend: ->
$('.mr_target_commit').empty()
success: (html) ->
$('.js-target-branch-dropdown .dropdown-content').html html
)
getSourceHtml: ->
@sendAjax(@opts.sourceBranchUrl, @source_loading, '.mr_source_commit',
ref: $("input[name='merge_request[source_branch]']").val()
)
getTargetHtml: ->
@sendAjax(@opts.targetBranchUrl, @target_loading, '.mr_target_commit',
target_project_id: $("input[name='merge_request[target_project_id]']").val()
ref: $("input[name='merge_request[target_branch]']").val()
)
sendAjax: (url, loading, target, data) ->
$target = $(target)
$.ajax(
url: url
data: data
beforeSend: ->
loading.show()
$target.empty()
success: (html) ->
loading.hide()
$target.html html
$('.js-timeago', $target).timeago()
)
...@@ -57,14 +57,30 @@ class GitLabDropdownFilter ...@@ -57,14 +57,30 @@ class GitLabDropdownFilter
filter: (search_text) -> filter: (search_text) ->
data = @options.data() data = @options.data()
if data?
results = data results = data
if search_text isnt "" if search_text isnt ''
results = fuzzaldrinPlus.filter(data, search_text, results = fuzzaldrinPlus.filter(data, search_text,
key: @options.keys key: @options.keys
) )
@options.callback results @options.callback results
else
elements = @options.elements()
if search_text
elements.each ->
$el = $(@)
matches = fuzzaldrinPlus.match($el.text().trim(), search_text)
if matches.length
$el.show()
else
$el.hide()
else
elements.show()
class GitLabDropdownRemote class GitLabDropdownRemote
constructor: (@dataEndpoint, @options) -> constructor: (@dataEndpoint, @options) ->
...@@ -123,7 +139,7 @@ class GitLabDropdown ...@@ -123,7 +139,7 @@ class GitLabDropdown
if _.isString(@filterInput) if _.isString(@filterInput)
@filterInput = @getElement(@filterInput) @filterInput = @getElement(@filterInput)
search_fields = if @options.search then @options.search.fields else []; searchFields = if @options.search then @options.search.fields else [];
if @options.data if @options.data
# If data is an array # If data is an array
...@@ -147,7 +163,14 @@ class GitLabDropdown ...@@ -147,7 +163,14 @@ class GitLabDropdown
filterInputBlur: @filterInputBlur filterInputBlur: @filterInputBlur
remote: @options.filterRemote remote: @options.filterRemote
query: @options.data query: @options.data
keys: @options.search.fields keys: searchFields
elements: =>
selector = '.dropdown-content li:not(.divider)'
if @dropdown.find('.dropdown-toggle-page').length
selector = ".dropdown-page-one #{selector}"
return $(selector)
data: => data: =>
return @fullData return @fullData
callback: (data) => callback: (data) =>
...@@ -376,7 +399,7 @@ class GitLabDropdown ...@@ -376,7 +399,7 @@ class GitLabDropdown
# Toggle the dropdown label # Toggle the dropdown label
if @options.toggleLabel if @options.toggleLabel
$(@el).find(".dropdown-toggle-text").text @options.toggleLabel(selectedObject) $(@el).find(".dropdown-toggle-text").text @options.toggleLabel(selectedObject, el)
if value? if value?
if !field.length and fieldName if !field.length and fieldName
# Create hidden input for form # Create hidden input for form
......
...@@ -85,15 +85,21 @@ class @MilestoneSelect ...@@ -85,15 +85,21 @@ class @MilestoneSelect
# display:block overrides the hide-collapse rule # display:block overrides the hide-collapse rule
$value.removeAttr('style') $value.removeAttr('style')
clicked: (selected) -> clicked: (selected) ->
page = $('body').data 'page'
isIssueIndex = page is 'projects:issues:index'
isMRIndex = page is page is 'projects:merge_requests:index'
if $dropdown.hasClass 'js-filter-bulk-update' if $dropdown.hasClass 'js-filter-bulk-update'
return return
if $dropdown.hasClass('js-filter-submit') if $dropdown.hasClass('js-filter-submit') and (isIssueIndex or isMRIndex)
if selected.name? if selected.name?
selectedMilestone = selected.name selectedMilestone = selected.name
else else
selectedMilestone = '' selectedMilestone = ''
Issues.filterResults $dropdown.closest('form') Issues.filterResults $dropdown.closest('form')
else if $dropdown.hasClass('js-filter-submit')
$dropdown.closest('form').submit()
else else
selected = $selectbox selected = $selectbox
.find('input[type="hidden"]') .find('input[type="hidden"]')
......
...@@ -248,7 +248,7 @@ ...@@ -248,7 +248,7 @@
.dropdown-title { .dropdown-title {
position: relative; position: relative;
padding: 0 0 15px; padding: 0 25px 15px;
margin: 0 10px 10px; margin: 0 10px 10px;
font-weight: 600; font-weight: 600;
line-height: 1; line-height: 1;
...@@ -275,7 +275,7 @@ ...@@ -275,7 +275,7 @@
} }
.dropdown-menu-close { .dropdown-menu-close {
right: 7px; right: 5px;
width: 20px; width: 20px;
height: 20px; height: 20px;
top: -1px; top: -1px;
......
...@@ -47,6 +47,7 @@ li.commit { ...@@ -47,6 +47,7 @@ li.commit {
.commit_short_id { .commit_short_id {
min-width: 65px; min-width: 65px;
color: $gl-dark-link-color;
font-family: $monospace_font; font-family: $monospace_font;
} }
...@@ -88,6 +89,10 @@ li.commit { ...@@ -88,6 +89,10 @@ li.commit {
padding: 0; padding: 0;
margin: 0; margin: 0;
} }
a {
color: $gl-dark-link-color;
}
} }
.commit-row-info { .commit-row-info {
......
...@@ -59,6 +59,9 @@ ...@@ -59,6 +59,9 @@
position: relative; position: relative;
overflow-y: auto; overflow-y: auto;
padding: 15px; padding: 15px;
.form-actions {
margin: -$gl-padding+1;
}
} }
body.modal-open { body.modal-open {
......
...@@ -123,6 +123,8 @@ ...@@ -123,6 +123,8 @@
.mr_source_commit, .mr_source_commit,
.mr_target_commit { .mr_target_commit {
margin-bottom: 0;
.commit { .commit {
margin: 0; margin: 0;
padding: 2px 0; padding: 2px 0;
...@@ -174,10 +176,6 @@ ...@@ -174,10 +176,6 @@
display: none; display: none;
} }
.merge-request-form .select2-container {
width: 250px !important;
}
#modal_merge_info .modal-dialog { #modal_merge_info .modal-dialog {
width: 600px; width: 600px;
...@@ -200,3 +198,76 @@ ...@@ -200,3 +198,76 @@
overflow-x: scroll; overflow-x: scroll;
} }
} }
.panel-new-merge-request {
.panel-heading {
padding: 5px 10px;
font-weight: 600;
line-height: 25px;
}
.panel-body {
padding: 10px 5px;
}
.panel-footer {
padding: 5px 10px;
}
.commit {
.commit-row-title {
margin-bottom: 4px;
}
.avatar {
width: 20px;
height: 20px;
margin-right: 5px;
}
.commit-row-info {
line-height: 20px;
}
}
.btn-clipboard {
margin-right: 5px;
padding: 0;
background: transparent;
}
.ci-status-link {
margin-right: 5px;
}
}
.merge-request-select {
padding-left: 5px;
padding-right: 5px;
margin-bottom: 10px;
&:last-child {
margin-bottom: 0;
}
@media (min-width: $screen-sm-min) {
float: left;
width: 50%;
margin-bottom: 0;
}
.dropdown-menu-toggle {
width: 100%;
}
.dropdown-menu {
left: 5px;
right: 5px;
width: auto;
}
}
.issuable-form-select-holder {
display: inline-block;
width: 250px;
}
...@@ -208,20 +208,20 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -208,20 +208,20 @@ class Projects::MergeRequestsController < Projects::ApplicationController
#This is always source #This is always source
@source_project = @merge_request.nil? ? @project : @merge_request.source_project @source_project = @merge_request.nil? ? @project : @merge_request.source_project
@commit = @repository.commit(params[:ref]) if params[:ref].present? @commit = @repository.commit(params[:ref]) if params[:ref].present?
render layout: false
end end
def branch_to def branch_to
@target_project = selected_target_project @target_project = selected_target_project
@commit = @target_project.commit(params[:ref]) if params[:ref].present? @commit = @target_project.commit(params[:ref]) if params[:ref].present?
render layout: false
end end
def update_branches def update_branches
@target_project = selected_target_project @target_project = selected_target_project
@target_branches = @target_project.repository.branch_names @target_branches = @target_project.repository.branch_names
respond_to do |format| render layout: false
format.js
end
end end
def ci_status def ci_status
......
...@@ -28,7 +28,7 @@ module CommitsHelper ...@@ -28,7 +28,7 @@ module CommitsHelper
def commit_to_html(commit, project, inline = true) def commit_to_html(commit, project, inline = true)
template = inline ? "inline_commit" : "commit" template = inline ? "inline_commit" : "commit"
escape_javascript(render "projects/commits/#{template}", commit: commit, project: project) unless commit.nil? render "projects/commits/#{template}", commit: commit, project: project unless commit.nil?
end end
# Breadcrumb links for a Project and, if applicable, a tree path # Breadcrumb links for a Project and, if applicable, a tree path
...@@ -117,7 +117,7 @@ module CommitsHelper ...@@ -117,7 +117,7 @@ module CommitsHelper
end end
end end
link_to( link_to(
"Browse Files »", "Browse Files",
namespace_project_tree_path(project.namespace, project, commit), namespace_project_tree_path(project.namespace, project, commit),
class: "pull-right" class: "pull-right"
) )
......
module FormHelper
def form_errors(model)
return unless model.errors.any?
pluralized = 'error'.pluralize(model.errors.count)
headline = "The form contains the following #{pluralized}:"
content_tag(:div, class: 'alert alert-danger', id: 'error_explanation') do
content_tag(:h4, headline) <<
content_tag(:ul) do
model.errors.full_messages.
map { |msg| content_tag(:li, msg) }.
join.
html_safe
end
end
end
end
...@@ -3,11 +3,9 @@ ...@@ -3,11 +3,9 @@
%p Please use this form to report users who create spam issues, comments or behave inappropriately. %p Please use this form to report users who create spam issues, comments or behave inappropriately.
%hr %hr
= form_for @abuse_report, html: { class: 'form-horizontal js-quick-submit js-requires-input'} do |f| = form_for @abuse_report, html: { class: 'form-horizontal js-quick-submit js-requires-input'} do |f|
= form_errors(@abuse_report)
= f.hidden_field :user_id = f.hidden_field :user_id
- if @abuse_report.errors.any?
.alert.alert-danger
- @abuse_report.errors.full_messages.each do |msg|
%p= msg
.form-group .form-group
= f.label :user_id, class: 'control-label' = f.label :user_id, class: 'control-label'
.col-sm-10 .col-sm-10
......
= form_for @appearance, url: admin_appearances_path, html: { class: 'form-horizontal'} do |f| = form_for @appearance, url: admin_appearances_path, html: { class: 'form-horizontal'} do |f|
- if @appearance.errors.any? = form_errors(@appearance)
.alert.alert-danger
- @appearance.errors.full_messages.each do |msg|
%p= msg
%fieldset.sign-in %fieldset.sign-in
%legend %legend
......
= form_for @application_setting, url: admin_application_settings_path, html: { class: 'form-horizontal fieldset-form' } do |f| = form_for @application_setting, url: admin_application_settings_path, html: { class: 'form-horizontal fieldset-form' } do |f|
- if @application_setting.errors.any? = form_errors(@application_setting)
#error_explanation
.alert.alert-danger
- @application_setting.errors.full_messages.each do |msg|
%p= msg
%fieldset %fieldset
%legend Visibility and Access Controls %legend Visibility and Access Controls
......
= form_for [:admin, @application], url: @url, html: {class: 'form-horizontal', role: 'form'} do |f| = form_for [:admin, @application], url: @url, html: {class: 'form-horizontal', role: 'form'} do |f|
- if application.errors.any? = form_errors(application)
.alert.alert-danger
%button{ type: "button", class: "close", "data-dismiss" => "alert"} &times;
- application.errors.full_messages.each do |msg|
%p= msg
= content_tag :div, class: 'form-group' do = content_tag :div, class: 'form-group' do
= f.label :name, class: 'col-sm-2 control-label' = f.label :name, class: 'col-sm-2 control-label'
.col-sm-10 .col-sm-10
......
...@@ -4,10 +4,8 @@ ...@@ -4,10 +4,8 @@
= render_broadcast_message(@broadcast_message.message.presence || "Your message here") = render_broadcast_message(@broadcast_message.message.presence || "Your message here")
= form_for [:admin, @broadcast_message], html: { class: 'broadcast-message-form form-horizontal js-quick-submit js-requires-input'} do |f| = form_for [:admin, @broadcast_message], html: { class: 'broadcast-message-form form-horizontal js-quick-submit js-requires-input'} do |f|
-if @broadcast_message.errors.any? = form_errors(@broadcast_message)
.alert.alert-danger
- @broadcast_message.errors.full_messages.each do |msg|
%p= msg
.form-group .form-group
= f.label :message, class: 'control-label' = f.label :message, class: 'control-label'
.col-sm-10 .col-sm-10
......
...@@ -4,11 +4,7 @@ ...@@ -4,11 +4,7 @@
%div %div
= form_for [:admin, @deploy_key], html: { class: 'deploy-key-form form-horizontal' } do |f| = form_for [:admin, @deploy_key], html: { class: 'deploy-key-form form-horizontal' } do |f|
-if @deploy_key.errors.any? = form_errors(@deploy_key)
.alert.alert-danger
%ul
- @deploy_key.errors.full_messages.each do |msg|
%li= msg
.form-group .form-group
= f.label :title, class: "control-label" = f.label :title, class: "control-label"
......
= form_for [:admin, @group], html: { class: "form-horizontal" } do |f| = form_for [:admin, @group], html: { class: "form-horizontal" } do |f|
- if @group.errors.any? = form_errors(@group)
.alert.alert-danger
%span= @group.errors.full_messages.first
= render 'shared/group_form', f: f = render 'shared/group_form', f: f
.form-group.group-description-holder .form-group.group-description-holder
......
...@@ -10,10 +10,8 @@ ...@@ -10,10 +10,8 @@
= form_for @hook, as: :hook, url: admin_hooks_path, html: { class: 'form-horizontal' } do |f| = form_for @hook, as: :hook, url: admin_hooks_path, html: { class: 'form-horizontal' } do |f|
-if @hook.errors.any? = form_errors(@hook)
.alert.alert-danger
- @hook.errors.full_messages.each do |msg|
%p= msg
.form-group .form-group
= f.label :url, "URL:", class: 'control-label' = f.label :url, "URL:", class: 'control-label'
.col-sm-10 .col-sm-10
......
= form_for [:admin, @user, @identity], html: { class: 'form-horizontal fieldset-form' } do |f| = form_for [:admin, @user, @identity], html: { class: 'form-horizontal fieldset-form' } do |f|
- if @identity.errors.any? = form_errors(@identity)
#error_explanation
.alert.alert-danger
- @identity.errors.full_messages.each do |msg|
%p= msg
.form-group .form-group
= f.label :provider, class: 'control-label' = f.label :provider, class: 'control-label'
......
= form_for [:admin, @label], html: { class: 'form-horizontal label-form js-requires-input' } do |f| = form_for [:admin, @label], html: { class: 'form-horizontal label-form js-requires-input' } do |f|
-if @label.errors.any? = form_errors(@label)
.row
.col-sm-offset-2.col-sm-10
.alert.alert-danger
- @label.errors.full_messages.each do |msg|
%span= msg
%br
.form-group .form-group
= f.label :title, class: 'control-label' = f.label :title, class: 'control-label'
......
.user_new .user_new
= form_for [:admin, @user], html: { class: 'form-horizontal fieldset-form' } do |f| = form_for [:admin, @user], html: { class: 'form-horizontal fieldset-form' } do |f|
-if @user.errors.any? = form_errors(@user)
#error_explanation
.alert.alert-danger
- @user.errors.full_messages.each do |msg|
%p= msg
%fieldset %fieldset
%legend Account %legend Account
......
= form_for application, url: doorkeeper_submit_path(application), html: {role: 'form'} do |f| = form_for application, url: doorkeeper_submit_path(application), html: {role: 'form'} do |f|
- if application.errors.any? = form_errors(application)
.alert.alert-danger
%ul
- application.errors.full_messages.each do |msg|
%li= msg
.form-group .form-group
= f.label :name, class: 'label-light' = f.label :name, class: 'label-light'
......
...@@ -5,9 +5,7 @@ ...@@ -5,9 +5,7 @@
Group settings Group settings
.panel-body .panel-body
= form_for @group, html: { multipart: true, class: "form-horizontal" }, authenticity_token: true do |f| = form_for @group, html: { multipart: true, class: "form-horizontal" }, authenticity_token: true do |f|
- if @group.errors.any? = form_errors(@group)
.alert.alert-danger
%span= @group.errors.full_messages.first
= render 'shared/group_form', f: f = render 'shared/group_form', f: f
.form-group .form-group
......
...@@ -6,10 +6,7 @@ ...@@ -6,10 +6,7 @@
%hr %hr
= form_for @group, html: { class: 'group-form form-horizontal' } do |f| = form_for @group, html: { class: 'group-form form-horizontal' } do |f|
- if @group.errors.any? = form_errors(@group)
.alert.alert-danger
%span= @group.errors.full_messages.first
= render 'shared/group_form', f: f, autofocus: true = render 'shared/group_form', f: f, autofocus: true
.form-group.group-description-holder .form-group.group-description-holder
......
%div %div
= form_for [:profile, @key], html: { class: 'js-requires-input' } do |f| = form_for [:profile, @key], html: { class: 'js-requires-input' } do |f|
- if @key.errors.any? = form_errors(@key)
.alert.alert-danger
%ul
- @key.errors.full_messages.each do |msg|
%li= msg
.form-group .form-group
= f.label :key, class: 'label-light' = f.label :key, class: 'label-light'
......
...@@ -2,11 +2,7 @@ ...@@ -2,11 +2,7 @@
- header_title page_title, profile_notifications_path - header_title page_title, profile_notifications_path
= form_for @user, url: profile_notifications_path, method: :put, html: { class: 'update-notifications prepend-top-default' } do |f| = form_for @user, url: profile_notifications_path, method: :put, html: { class: 'update-notifications prepend-top-default' } do |f|
-if @user.errors.any? = form_errors(@user)
%div.alert.alert-danger
%ul
- @user.errors.full_messages.each do |msg|
%li= msg
= hidden_field_tag :notification_type, 'global' = hidden_field_tag :notification_type, 'global'
.row .row
......
...@@ -13,11 +13,8 @@ ...@@ -13,11 +13,8 @@
- unless @user.password_automatically_set? - unless @user.password_automatically_set?
or recover your current one or recover your current one
= form_for @user, url: profile_password_path, method: :put, html: {class: "update-password"} do |f| = form_for @user, url: profile_password_path, method: :put, html: {class: "update-password"} do |f|
-if @user.errors.any? = form_errors(@user)
.alert.alert-danger
%ul
- @user.errors.full_messages.each do |msg|
%li= msg
- unless @user.password_automatically_set? - unless @user.password_automatically_set?
.form-group .form-group
= f.label :current_password, class: 'label-light' = f.label :current_password, class: 'label-light'
......
...@@ -7,11 +7,8 @@ ...@@ -7,11 +7,8 @@
Please set a new password before proceeding. Please set a new password before proceeding.
%br %br
After a successful password update you will be redirected to login screen. After a successful password update you will be redirected to login screen.
-if @user.errors.any?
.alert.alert-danger = form_errors(@user)
%ul
- @user.errors.full_messages.each do |msg|
%li= msg
- unless @user.password_automatically_set? - unless @user.password_automatically_set?
.form-group .form-group
......
= form_for @user, url: profile_path, method: :put, html: { multipart: true, class: "edit-user prepend-top-default" }, authenticity_token: true do |f| = form_for @user, url: profile_path, method: :put, html: { multipart: true, class: "edit-user prepend-top-default" }, authenticity_token: true do |f|
-if @user.errors.any? = form_errors(@user)
%div.alert.alert-danger
%ul
- @user.errors.full_messages.each do |msg|
%li= msg
.row .row
.col-lg-3.profile-settings-sidebar .col-lg-3.profile-settings-sidebar
%h4.prepend-top-0 %h4.prepend-top-0
......
- if @project.errors.any? = form_errors(@project)
.alert.alert-danger
%button{ type: "button", class: "close", "data-dismiss" => "alert"} &times;
= @project.errors.full_messages.first
...@@ -18,24 +18,17 @@ ...@@ -18,24 +18,17 @@
.pull-right .pull-right
- if commit.status - if commit.status
= render_ci_status(commit) = render_ci_status(commit)
&nbsp;
= clipboard_button(clipboard_text: commit.id) = clipboard_button(clipboard_text: commit.id)
= link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit_short_id" = link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit_short_id"
.notes_count
- if note_count > 0
%span.light
%i.fa.fa-comments
= note_count
- if commit.description? - if commit.description?
.commit-row-description.js-toggle-content .commit-row-description.js-toggle-content
%pre %pre
= preserve(markdown(escape_once(commit.description), pipeline: :single_line)) = preserve(markdown(escape_once(commit.description), pipeline: :single_line))
.commit-row-info .commit-row-info
by
= commit_author_link(commit, avatar: true, size: 24) = commit_author_link(commit, avatar: true, size: 24)
authored
.committed_ago .committed_ago
#{time_ago_with_tooltip(commit.committed_date, skip_js: true)} &nbsp; #{time_ago_with_tooltip(commit.committed_date, skip_js: true)} &nbsp;
= link_to_browse_code(project, commit) = link_to_browse_code(project, commit)
%div %div
= form_for [@project.namespace.becomes(Namespace), @project, @key], url: namespace_project_deploy_keys_path, html: { class: 'deploy-key-form form-horizontal js-requires-input' } do |f| = form_for [@project.namespace.becomes(Namespace), @project, @key], url: namespace_project_deploy_keys_path, html: { class: 'deploy-key-form form-horizontal js-requires-input' } do |f|
-if @key.errors.any? = form_errors(@key)
.alert.alert-danger
%ul
- @key.errors.full_messages.each do |msg|
%li= msg
.form-group .form-group
= f.label :title, class: "control-label" = f.label :title, class: "control-label"
......
...@@ -9,10 +9,8 @@ ...@@ -9,10 +9,8 @@
%hr.clearfix %hr.clearfix
= form_for [@project.namespace.becomes(Namespace), @project, @hook], as: :hook, url: namespace_project_hooks_path(@project.namespace, @project), html: { class: 'form-horizontal' } do |f| = form_for [@project.namespace.becomes(Namespace), @project, @hook], as: :hook, url: namespace_project_hooks_path(@project.namespace, @project), html: { class: 'form-horizontal' } do |f|
-if @hook.errors.any? = form_errors(@hook)
.alert.alert-danger
- @hook.errors.full_messages.each do |msg|
%p= msg
.form-group .form-group
= f.label :url, "URL", class: 'control-label' = f.label :url, "URL", class: 'control-label'
.col-sm-10 .col-sm-10
......
= form_for [@project.namespace.becomes(Namespace), @project, @label], html: { class: 'form-horizontal label-form js-quick-submit js-requires-input' } do |f| = form_for [@project.namespace.becomes(Namespace), @project, @label], html: { class: 'form-horizontal label-form js-quick-submit js-requires-input' } do |f|
-if @label.errors.any? = form_errors(@label)
.row
.col-sm-offset-2.col-sm-10
.alert.alert-danger
- @label.errors.full_messages.each do |msg|
%span= msg
%br
.form-group .form-group
= f.label :title, class: 'control-label' = f.label :title, class: 'control-label'
......
...@@ -5,33 +5,74 @@ ...@@ -5,33 +5,74 @@
.hide.alert.alert-danger.mr-compare-errors .hide.alert.alert-danger.mr-compare-errors
.merge-request-branches.row .merge-request-branches.row
.col-md-6 .col-md-6
.panel.panel-default .panel.panel-default.panel-new-merge-request
.panel-heading .panel-heading
%strong Source branch Source branch
.panel-body .panel-body.clearfix
= f.select(:source_project_id, [[@merge_request.source_project_path,@merge_request.source_project.id]] , {}, { class: 'source_project select2 span3', disabled: @merge_request.persisted?, required: true }) .merge-request-select.dropdown
&nbsp; = f.hidden_field :source_project_id
= f.select(:source_branch, @merge_request.source_branches, { include_blank: true }, { class: 'source_branch select2 span2', required: true, data: { placeholder: "Select source branch" } }) = dropdown_toggle @merge_request.source_project_path, { toggle: "dropdown", field_name: "#{f.object_name}[source_project_id]", disabled: @merge_request.persisted? }, { toggle_class: "js-compare-dropdown js-source-project" }
.dropdown-menu.dropdown-menu-selectable.dropdown-source-project
= dropdown_title("Select source project")
= dropdown_filter("Search projects")
= dropdown_content do
- is_active = f.object.source_project_id == @merge_request.source_project.id
%ul
%li
%a{ href: "#", class: "#{("is-active" if is_active)}", data: { id: @merge_request.source_project.id } }
= @merge_request.source_project_path
.merge-request-select.dropdown
= f.hidden_field :source_branch
= dropdown_toggle "Select source branch", { toggle: "dropdown", field_name: "#{f.object_name}[source_branch]" }, { toggle_class: "js-compare-dropdown js-source-branch" }
.dropdown-menu.dropdown-menu-selectable.dropdown-source-branch
= dropdown_title("Select source branch")
= dropdown_filter("Search branches")
= dropdown_content do
%ul
- @merge_request.source_branches.each do |branch|
%li
%a{ href: "#", class: "#{("is-active" if f.object.source_branch == branch)}", data: { id: branch } }
= branch
.panel-footer .panel-footer
.mr_source_commit = icon('spinner spin', class: 'js-source-loading')
%ul.list-unstyled.mr_source_commit
.col-md-6 .col-md-6
.panel.panel-default .panel.panel-default.panel-new-merge-request
.panel-heading .panel-heading
%strong Target branch Target branch
.panel-body .panel-body.clearfix
- projects = @project.forked_from_project.nil? ? [@project] : [@project, @project.forked_from_project] - projects = @project.forked_from_project.nil? ? [@project] : [@project, @project.forked_from_project]
= f.select(:target_project_id, options_from_collection_for_select(projects, 'id', 'path_with_namespace', f.object.target_project_id), {}, { class: 'target_project select2 span3', disabled: @merge_request.persisted?, required: true }) .merge-request-select.dropdown
&nbsp; = f.hidden_field :target_project_id
= f.select(:target_branch, @merge_request.target_branches, { include_blank: true }, { class: 'target_branch select2 span2', required: true, data: { placeholder: "Select target branch" } }) = dropdown_toggle f.object.target_project.path_with_namespace, { toggle: "dropdown", field_name: "#{f.object_name}[target_project_id]", disabled: @merge_request.persisted? }, { toggle_class: "js-compare-dropdown js-target-project" }
.dropdown-menu.dropdown-menu-selectable.dropdown-target-project
= dropdown_title("Select target project")
= dropdown_filter("Search projects")
= dropdown_content do
%ul
- projects.each do |project|
%li
%a{ href: "#", class: "#{("is-active" if f.object.target_project_id == project.id)}", data: { id: project.id } }
= project.path_with_namespace
.merge-request-select.dropdown
= f.hidden_field :target_branch
= dropdown_toggle f.object.target_branch, { toggle: "dropdown", field_name: "#{f.object_name}[target_branch]" }, { toggle_class: "js-compare-dropdown js-target-branch" }
.dropdown-menu.dropdown-menu-selectable.dropdown-target-branch.js-target-branch-dropdown
= dropdown_title("Select target branch")
= dropdown_filter("Search branches")
= dropdown_content do
%ul
- @merge_request.target_branches.each do |branch|
%li
%a{ href: "#", class: "#{("is-active" if f.object.target_branch == branch)}", data: { id: branch } }
= branch
.panel-footer .panel-footer
.mr_target_commit = icon('spinner spin', class: "js-target-loading")
%ul.list-unstyled.mr_target_commit
- if @merge_request.errors.any? - if @merge_request.errors.any?
.alert.alert-danger = form_errors(@merge_request)
- @merge_request.errors.full_messages.each do |msg|
%div= msg
- elsif @merge_request.source_branch.present? && @merge_request.target_branch.present? - elsif @merge_request.source_branch.present? && @merge_request.target_branch.present?
.light-well.append-bottom-default .light-well.append-bottom-default
.center .center
...@@ -45,40 +86,11 @@ ...@@ -45,40 +86,11 @@
and and
%span.label-branch #{@merge_request.target_branch} %span.label-branch #{@merge_request.target_branch}
are the same. are the same.
.form-actions
= f.submit 'Compare branches and continue', class: "btn btn-new mr-compare-btn" = f.submit 'Compare branches and continue', class: "btn btn-new mr-compare-btn"
:javascript :javascript
var source_branch = $("#merge_request_source_branch") new Compare({
, target_branch = $("#merge_request_target_branch") targetProjectUrl: "#{update_branches_namespace_project_merge_requests_path(@source_project.namespace, @source_project)}",
, target_project = $("#merge_request_target_project_id"); sourceBranchUrl: "#{branch_from_namespace_project_merge_requests_path(@source_project.namespace, @source_project)}",
targetBranchUrl: "#{branch_to_namespace_project_merge_requests_path(@source_project.namespace, @source_project)}"
$.get("#{branch_from_namespace_project_merge_requests_path(@source_project.namespace, @source_project)}", {ref: source_branch.val() });
$.get("#{branch_to_namespace_project_merge_requests_path(@source_project.namespace, @source_project)}", {target_project_id: target_project.val(),ref: target_branch.val() });
target_project.on("change", function() {
$.get("#{update_branches_namespace_project_merge_requests_path(@source_project.namespace, @source_project)}", {target_project_id: $(this).val() });
});
source_branch.on("change", function() {
$.get("#{branch_from_namespace_project_merge_requests_path(@source_project.namespace, @source_project)}", {ref: $(this).val() });
$(".mr-compare-errors").fadeOut();
$(".mr-compare-btn").enable();
});
target_branch.on("change", function() {
$.get("#{branch_to_namespace_project_merge_requests_path(@source_project.namespace, @source_project)}", {target_project_id: target_project.val(),ref: $(this).val() });
$(".mr-compare-errors").fadeOut();
$(".mr-compare-btn").enable();
});
:javascript
$(".merge-request-form").on('submit', function () {
if ($("#merge_request_source_branch").val() === "" || $('#merge_request_target_branch').val() === "") {
$(".mr-compare-errors").html("You must select source and target branch to proceed");
$(".mr-compare-errors").fadeIn();
event.preventDefault();
return;
}
}); });
= commit_to_html(@commit, @source_project, false)
:plain
$(".mr_source_commit").html("#{commit_to_html(@commit, @source_project, false)}");
$('.js-timeago').timeago()
= commit_to_html(@commit, @target_project, false)
:plain
$(".mr_target_commit").html("#{commit_to_html(@commit, @target_project, false)}");
$('.js-timeago').timeago()
%ul
- @target_branches.each do |branch|
%li
%a{ href: "#", class: "#{("is-active" if "a" == branch)}", data: { id: branch } }
= branch
:plain
$(".target_branch").html("#{escape_javascript(options_for_select(@target_branches))}");
$('select.target_branch').select2({
width: 'resolve',
dropdownAutoWidth: true
});
$(".mr_target_commit").html("");
= form_for [@project.namespace.becomes(Namespace), @project, @milestone], html: {class: 'form-horizontal milestone-form gfm-form js-quick-submit js-requires-input'} do |f| = form_for [@project.namespace.becomes(Namespace), @project, @milestone], html: {class: 'form-horizontal milestone-form gfm-form js-quick-submit js-requires-input'} do |f|
-if @milestone.errors.any? = form_errors(@milestone)
.alert.alert-danger
%ul
- @milestone.errors.full_messages.each do |msg|
%li= msg
.row .row
.col-md-6 .col-md-6
.form-group .form-group
......
...@@ -13,11 +13,7 @@ ...@@ -13,11 +13,7 @@
- if can? current_user, :admin_project, @project - if can? current_user, :admin_project, @project
= form_for [@project.namespace.becomes(Namespace), @project, @protected_branch], html: { class: 'form-horizontal' } do |f| = form_for [@project.namespace.becomes(Namespace), @project, @protected_branch], html: { class: 'form-horizontal' } do |f|
-if @protected_branch.errors.any? = form_errors(@protected_branch)
.alert.alert-danger
%ul
- @protected_branch.errors.full_messages.each do |msg|
%li= msg
.form-group .form-group
= f.label :name, "Branch", class: 'control-label' = f.label :name, "Branch", class: 'control-label'
......
...@@ -13,13 +13,7 @@ ...@@ -13,13 +13,7 @@
= nested_form_for @project, url: url_for(controller: 'projects/variables', action: 'update'), html: { class: 'form-horizontal' } do |f| = nested_form_for @project, url: url_for(controller: 'projects/variables', action: 'update'), html: { class: 'form-horizontal' } do |f|
- if @project.errors.any? = form_errors(@project)
#error_explanation
%p.lead= "#{pluralize(@project.errors.count, "error")} prohibited this project from being saved:"
.alert.alert-error
%ul
- @project.errors.full_messages.each do |msg|
%li= msg
= f.fields_for :variables do |variable_form| = f.fields_for :variables do |variable_form|
.form-group .form-group
......
= form_for [@project.namespace.becomes(Namespace), @project, @page], method: @page.persisted? ? :put : :post, html: { class: 'form-horizontal wiki-form gfm-form prepend-top-default js-quick-submit' } do |f| = form_for [@project.namespace.becomes(Namespace), @project, @page], method: @page.persisted? ? :put : :post, html: { class: 'form-horizontal wiki-form gfm-form prepend-top-default js-quick-submit' } do |f|
-if @page.errors.any? = form_errors(@page)
#error_explanation
.alert.alert-danger
- @page.errors.full_messages.each do |msg|
%p= msg
= f.hidden_field :title, value: @page.title = f.hidden_field :title, value: @page.title
.form-group .form-group
......
- if @service.errors.any? = form_errors(@service)
#error_explanation
.alert.alert-danger
%ul
- @service.errors.full_messages.each do |msg|
%li= msg
- if @service.help.present? - if @service.help.present?
.well .well
......
- if issuable.errors.any? = form_errors(issuable)
.row
.col-sm-offset-2.col-sm-10
.alert.alert-danger
- issuable.errors.full_messages.each do |msg|
%span= msg
%br
.form-group .form-group
= f.label :title, class: 'control-label' = f.label :title, class: 'control-label'
.col-sm-10 .col-sm-10
...@@ -53,6 +48,7 @@ ...@@ -53,6 +48,7 @@
.issue-assignee .issue-assignee
= f.label :assignee_id, "Assignee", class: 'control-label' = f.label :assignee_id, "Assignee", class: 'control-label'
.col-sm-10 .col-sm-10
.issuable-form-select-holder
= users_select_tag("#{issuable.class.model_name.param_key}[assignee_id]", = users_select_tag("#{issuable.class.model_name.param_key}[assignee_id]",
placeholder: 'Select assignee', class: 'custom-form-control', null_user: true, placeholder: 'Select assignee', class: 'custom-form-control', null_user: true,
selected: issuable.assignee_id, project: @target_project || @project, selected: issuable.assignee_id, project: @target_project || @project,
...@@ -64,6 +60,7 @@ ...@@ -64,6 +60,7 @@
= f.label :milestone_id, "Milestone", class: 'control-label' = f.label :milestone_id, "Milestone", class: 'control-label'
.col-sm-10 .col-sm-10
- if milestone_options(issuable).present? - if milestone_options(issuable).present?
.issuable-form-select-holder
= f.select(:milestone_id, milestone_options(issuable), = f.select(:milestone_id, milestone_options(issuable),
{ include_blank: true }, { class: 'select2', data: { placeholder: 'Select milestone' } }) { include_blank: true }, { class: 'select2', data: { placeholder: 'Select milestone' } })
- else - else
......
.snippet-form-holder .snippet-form-holder
= form_for @snippet, url: url, html: { class: "form-horizontal snippet-form js-requires-input" } do |f| = form_for @snippet, url: url, html: { class: "form-horizontal snippet-form js-requires-input" } do |f|
- if @snippet.errors.any? = form_errors(@snippet)
.alert.alert-danger
%ul
- @snippet.errors.full_messages.each do |msg|
%li= msg
.form-group .form-group
= f.label :title, class: 'control-label' = f.label :title, class: 'control-label'
......
...@@ -75,6 +75,29 @@ if Gitlab::Metrics.enabled? ...@@ -75,6 +75,29 @@ if Gitlab::Metrics.enabled?
config.instrument_methods(const) config.instrument_methods(const)
config.instrument_instance_methods(const) config.instrument_instance_methods(const)
end end
# Instruments all Banzai filters
Dir[Rails.root.join('lib', 'banzai', 'filter', '*.rb')].each do |file|
klass = File.basename(file, File.extname(file)).camelize
const = Banzai::Filter.const_get(klass)
config.instrument_methods(const)
config.instrument_instance_methods(const)
end
config.instrument_methods(Banzai::ReferenceExtractor)
config.instrument_instance_methods(Banzai::ReferenceExtractor)
config.instrument_methods(Banzai::Renderer)
config.instrument_methods(Banzai::Querying)
[Issuable, Mentionable, Participable].each do |klass|
config.instrument_instance_methods(klass)
config.instrument_instance_methods(klass::ClassMethods)
end
config.instrument_methods(Gitlab::ReferenceExtractor)
config.instrument_instance_methods(Gitlab::ReferenceExtractor)
end end
GC::Profiler.enable GC::Profiler.enable
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
- [Architecture](architecture.md) of GitLab - [Architecture](architecture.md) of GitLab
- [CI setup](ci_setup.md) for testing GitLab - [CI setup](ci_setup.md) for testing GitLab
- [Code review guidelines](code_review.md) for reviewing code and having code
reviewed.
- [Gotchas](gotchas.md) to avoid - [Gotchas](gotchas.md) to avoid
- [How to dump production data to staging](db_dump.md) - [How to dump production data to staging](db_dump.md)
- [Instrumentation](instrumentation.md) - [Instrumentation](instrumentation.md)
......
# Code Review Guidelines
This guide contains advice and best practices for performing code review, and
having your code reviewed.
All merge requests for GitLab CE and EE, whether written by a GitLab team member
or a volunteer contributor, must go through a code review process to ensure the
code is effective, understandable, and maintainable.
Any developer can, and is encouraged to, perform code review on merge requests
of colleagues and contributors. However, the final decision to accept a merge
request is up to one of our merge request "endbosses", denoted on the
[team page](https://about.gitlab.com/team).
## Everyone
- Accept that many programming decisions are opinions. Discuss tradeoffs, which
you prefer, and reach a resolution quickly.
- Ask questions; don't make demands. ("What do you think about naming this
`:user_id`?")
- Ask for clarification. ("I didn't understand. Can you clarify?")
- Avoid selective ownership of code. ("mine", "not mine", "yours")
- Avoid using terms that could be seen as referring to personal traits. ("dumb",
"stupid"). Assume everyone is attractive, intelligent, and well-meaning.
- Be explicit. Remember people don't always understand your intentions online.
- Be humble. ("I'm not sure - let's look it up.")
- Don't use hyperbole. ("always", "never", "endlessly", "nothing")
- Be careful about the use of sarcasm. Everything we do is public; what seems
like good-natured ribbing to you and a long-time colleague might come off as
mean and unwelcoming to a person new to the project.
- Consider one-on-one chats or video calls if there are too many "I didn't
understand" or "Alternative solution:" comments. Post a follow-up comment
summarizing one-on-one discussion.
## Having your code reviewed
- The first reviewer of your code is _you_. Before you perform that first push
of your shiny new branch, read through the entire diff. Does it make sense?
Did you include something unrelated to the overall purpose of the changes? Did
you forget to remove any debugging code?
- Be grateful for the reviewer's suggestions. ("Good call. I'll make that
change.")
- Don't take it personally. The review is of the code, not of you.
- Explain why the code exists. ("It's like that because of these reasons. Would
it be more clear if I rename this class/file/method/variable?")
- Extract unrelated changes and refactorings into future merge requests/issues.
- Seek to understand the reviewer's perspective.
- Try to respond to every comment.
- Push commits based on earlier rounds of feedback as isolated commits to the
branch. Do not squash until the branch is ready to merge. Reviewers should be
able to read individual updates based on their earlier feedback.
## Reviewing code
Understand why the change is necessary (fixes a bug, improves the user
experience, refactors the existing code). Then:
- Communicate which ideas you feel strongly about and those you don't.
- Identify ways to simplify the code while still solving the problem.
- Offer alternative implementations, but assume the author already considered
them. ("What do you think about using a custom validator here?")
- Seek to understand the author's perspective.
- If you don't understand a piece of code, _say so_. There's a good chance
someone else would be confused by it as well.
- After a round of line notes, it can be helpful to post a summary note such as
"LGTM :thumbsup:", or "Just a couple things to address."
- Avoid accepting a merge request before the build succeeds ("Merge when build
succeeds" is fine).
## Credits
Largely based on the [thoughtbot code review guide].
[thoughtbot code review guide]: https://github.com/thoughtbot/guides/tree/master/code-review
---
[Return to Development documentation](README.md)
...@@ -2,36 +2,35 @@ ...@@ -2,36 +2,35 @@
GitLab Performance Monitoring allows instrumenting of custom blocks of Ruby GitLab Performance Monitoring allows instrumenting of custom blocks of Ruby
code. This can be used to measure the time spent in a specific part of a larger code. This can be used to measure the time spent in a specific part of a larger
chunk of code. The resulting data is written to a separate series. chunk of code. The resulting data is stored as a field in the transaction that
executed the block.
To start measuring a block of Ruby code you should use To start measuring a block of Ruby code you should use `Gitlab::Metrics.measure`
`Gitlab::Metrics.measure` and give it a name for the series to store the data and give it a name:
in:
```ruby ```ruby
Gitlab::Metrics.measure(:user_logins) do Gitlab::Metrics.measure(:foo) do
... ...
end end
``` ```
The first argument of this method is the series name and should be plural. This 3 values are measured for a block:
name will be prefixed with `rails_` or `sidekiq_` depending on whether the code
was run in the Rails application or one of the Sidekiq workers. In the
above example the final series names would be as follows:
- rails_user_logins 1. The real time elapsed, stored in NAME_real_time.
- sidekiq_user_logins 2. The CPU time elapsed, stored in NAME_cpu_time.
3. The call count, stored in NAME_call_count.
Series names should be plural as this keeps the naming style in line with the Both the real and CPU timings are measured in milliseconds.
other series names.
By default metrics measured using a block contain a single value, "duration", Multiple calls to the same block will result in the final values being the sum
which contains the number of milliseconds it took to execute the block. Custom of all individual values. Take this code for example:
values can be added by passing a Hash as the 2nd argument. Custom tags can be
added by passing a Hash as the 3rd argument. A simple example is as follows:
```ruby ```ruby
Gitlab::Metrics.measure(:example_series, { number: 10 }, { class: self.class.to_s }) do 3.times do
... Gitlab::Metrics.measure(:sleep) do
sleep 1
end
end end
``` ```
Here the final value of `sleep_real_time` will be `3`, _not_ `1`.
...@@ -352,7 +352,7 @@ GitLab Shell is an SSH access and repository management software developed speci ...@@ -352,7 +352,7 @@ GitLab Shell is an SSH access and repository management software developed speci
cd /home/git cd /home/git
sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-workhorse.git sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-workhorse.git
cd gitlab-workhorse cd gitlab-workhorse
sudo -u git -H git checkout v0.7.2 sudo -u git -H git checkout v0.7.1
sudo -u git -H make sudo -u git -H make
### Initialize Database and Activate Advanced Features ### Initialize Database and Activate Advanced Features
......
...@@ -58,7 +58,7 @@ GitLab 8.1. ...@@ -58,7 +58,7 @@ GitLab 8.1.
```bash ```bash
cd /home/git/gitlab-workhorse cd /home/git/gitlab-workhorse
sudo -u git -H git fetch --all sudo -u git -H git fetch --all
sudo -u git -H git checkout v0.7.2 sudo -u git -H git checkout v0.7.1
sudo -u git -H make sudo -u git -H make
``` ```
......
...@@ -4,6 +4,7 @@ Feature: Project Forked Merge Requests ...@@ -4,6 +4,7 @@ Feature: Project Forked Merge Requests
And I am a member of project "Shop" And I am a member of project "Shop"
And I have a project forked off of "Shop" called "Forked Shop" And I have a project forked off of "Shop" called "Forked Shop"
@javascript
Scenario: I submit new unassigned merge request to a forked project Scenario: I submit new unassigned merge request to a forked project
Given I visit project "Forked Shop" merge requests page Given I visit project "Forked Shop" merge requests page
And I click link "New Merge Request" And I click link "New Merge Request"
......
...@@ -70,6 +70,7 @@ Feature: Project Merge Requests ...@@ -70,6 +70,7 @@ Feature: Project Merge Requests
When I click link "Reopen" When I click link "Reopen"
Then I should see reopened merge request "Bug NS-04" Then I should see reopened merge request "Bug NS-04"
@javascript
Scenario: I submit new unassigned merge request Scenario: I submit new unassigned merge request
Given I click link "New Merge Request" Given I click link "New Merge Request"
And I submit new merge request "Wiki Feature" And I submit new merge request "Wiki Feature"
......
...@@ -34,10 +34,14 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps ...@@ -34,10 +34,14 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps
end end
step 'I fill out a "Merge Request On Forked Project" merge request' do step 'I fill out a "Merge Request On Forked Project" merge request' do
select @forked_project.path_with_namespace, from: "merge_request_source_project_id" first('.js-source-project').click
select @project.path_with_namespace, from: "merge_request_target_project_id" first('.dropdown-source-project a', text: @forked_project.path_with_namespace)
select "fix", from: "merge_request_source_branch"
select "master", from: "merge_request_target_branch" first('.js-target-project').click
first('.dropdown-target-project a', text: @project.path_with_namespace)
first('.js-source-branch').click
first('.dropdown-source-branch .dropdown-content a', text: 'fix').click
click_button "Compare branches and continue" click_button "Compare branches and continue"
...@@ -115,10 +119,10 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps ...@@ -115,10 +119,10 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps
end end
step 'I fill out an invalid "Merge Request On Forked Project" merge request' do step 'I fill out an invalid "Merge Request On Forked Project" merge request' do
expect(find(:select, "merge_request_source_project_id", {}).value).to eq @forked_project.id.to_s expect(find_by_id("merge_request_source_project_id", visible: false).value).to eq @forked_project.id.to_s
expect(find(:select, "merge_request_target_project_id", {}).value).to eq @project.id.to_s expect(find_by_id("merge_request_target_project_id", visible: false).value).to eq @project.id.to_s
expect(find(:select, "merge_request_source_branch", {}).value).to eq "" expect(find_by_id("merge_request_source_branch", visible: false).value).to eq nil
expect(find(:select, "merge_request_target_branch", {}).value).to eq "master" expect(find_by_id("merge_request_target_branch", visible: false).value).to eq "master"
click_button "Compare branches" click_button "Compare branches"
end end
...@@ -127,7 +131,7 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps ...@@ -127,7 +131,7 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps
end end
step 'the target repository should be the original repository' do step 'the target repository should be the original repository' do
expect(page).to have_select("merge_request_target_project_id", selected: @project.path_with_namespace) expect(find_by_id("merge_request_target_project_id").value).to eq "#{@project.id}"
end end
step 'I click "Assign to" dropdown"' do step 'I click "Assign to" dropdown"' do
......
...@@ -93,8 +93,12 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps ...@@ -93,8 +93,12 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
end end
step 'I submit new merge request "Wiki Feature"' do step 'I submit new merge request "Wiki Feature"' do
select "fix", from: "merge_request_source_branch" find('.js-source-branch').click
select "feature", from: "merge_request_target_branch" find('.dropdown-source-branch .dropdown-content a', text: 'fix').click
find('.js-target-branch').click
first('.dropdown-target-branch .dropdown-content a', text: 'feature').click
click_button "Compare branches" click_button "Compare branches"
fill_in "merge_request_title", with: "Wiki Feature" fill_in "merge_request_title", with: "Wiki Feature"
click_button "Submit merge request" click_button "Submit merge request"
......
...@@ -213,13 +213,12 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps ...@@ -213,13 +213,12 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
end end
step 'I see Browse file link' do step 'I see Browse file link' do
expect(page).to have_link 'Browse File »' expect(page).to have_link 'Browse File'
expect(page).not_to have_link 'Browse Files »' expect(page).not_to have_link 'Browse Files'
end end
step 'I see Browse code link' do step 'I see Browse code link' do
expect(page).to have_link 'Browse Files »' expect(page).to have_link 'Browse Files'
expect(page).not_to have_link 'Browse File »'
expect(page).not_to have_link 'Browse Directory »' expect(page).not_to have_link 'Browse Directory »'
end end
......
...@@ -19,9 +19,11 @@ module Banzai ...@@ -19,9 +19,11 @@ module Banzai
cache_key = full_cache_key(cache_key, context[:pipeline]) cache_key = full_cache_key(cache_key, context[:pipeline])
if cache_key if cache_key
Gitlab::Metrics.measure(:banzai_cached_render) do
Rails.cache.fetch(cache_key) do Rails.cache.fetch(cache_key) do
cacheless_render(text, context) cacheless_render(text, context)
end end
end
else else
cacheless_render(text, context) cacheless_render(text, context)
end end
...@@ -64,6 +66,7 @@ module Banzai ...@@ -64,6 +66,7 @@ module Banzai
private private
def self.cacheless_render(text, context = {}) def self.cacheless_render(text, context = {})
Gitlab::Metrics.measure(:banzai_cacheless_render) do
result = render_result(text, context) result = render_result(text, context)
output = result[:output] output = result[:output]
...@@ -73,6 +76,7 @@ module Banzai ...@@ -73,6 +76,7 @@ module Banzai
output.to_s output.to_s
end end
end end
end
def self.full_cache_key(cache_key, pipeline_name) def self.full_cache_key(cache_key, pipeline_name)
return unless cache_key return unless cache_key
......
...@@ -74,24 +74,32 @@ module Gitlab ...@@ -74,24 +74,32 @@ module Gitlab
# #
# Example: # Example:
# #
# Gitlab::Metrics.measure(:find_by_username_timings) do # Gitlab::Metrics.measure(:find_by_username_duration) do
# User.find_by_username(some_username) # User.find_by_username(some_username)
# end # end
# #
# series - The name of the series to store the data in. # name - The name of the field to store the execution time in.
# values - A Hash containing extra values to add to the metric.
# tags - A Hash containing extra tags to add to the metric.
# #
# Returns the value yielded by the supplied block. # Returns the value yielded by the supplied block.
def self.measure(series, values = {}, tags = {}) def self.measure(name)
return yield unless Transaction.current trans = current_transaction
return yield unless trans
real_start = Time.now.to_f
cpu_start = System.cpu_time
start = Time.now.to_f
retval = yield retval = yield
duration = (Time.now.to_f - start) * 1000.0
values = values.merge(duration: duration)
Transaction.current.add_metric(series, values, tags) cpu_stop = System.cpu_time
real_stop = Time.now.to_f
real_time = (real_stop - real_start) * 1000.0
cpu_time = cpu_stop - cpu_start
trans.increment("#{name}_real_time", real_time)
trans.increment("#{name}_cpu_time", cpu_time)
trans.increment("#{name}_call_count", 1)
retval retval
end end
...@@ -107,5 +115,11 @@ module Gitlab ...@@ -107,5 +115,11 @@ module Gitlab
new(udp: { host: host, port: port }) new(udp: { host: host, port: port })
end end
end end
private
def self.current_transaction
Transaction.current
end
end end
end end
...@@ -30,6 +30,17 @@ module Gitlab ...@@ -30,6 +30,17 @@ module Gitlab
0 0
end end
end end
# THREAD_CPUTIME is not supported on OS X
if Process.const_defined?(:CLOCK_THREAD_CPUTIME_ID)
def self.cpu_time
Process.clock_gettime(Process::CLOCK_THREAD_CPUTIME_ID, :millisecond)
end
else
def self.cpu_time
Process.clock_gettime(Process::CLOCK_PROCESS_CPUTIME_ID, :millisecond)
end
end
end end
end end
end end
require 'spec_helper'
describe "Dashboard Issues filtering", feature: true, js: true do
let(:user) { create(:user) }
let(:project) { create(:project) }
let(:milestone) { create(:milestone, project: project) }
context 'filtering by milestone' do
before do
project.team << [user, :master]
login_as(user)
create(:issue, project: project, author: user, assignee: user)
create(:issue, project: project, author: user, assignee: user, milestone: milestone)
visit_issues
end
it 'should show all issues with no milestone' do
show_milestone_dropdown
click_link 'No Milestone'
expect(page).to have_selector('.issue', count: 1)
end
it 'should show all issues with any milestone' do
show_milestone_dropdown
click_link 'Any Milestone'
expect(page).to have_selector('.issue', count: 2)
end
it 'should show all issues with the selected milestone' do
show_milestone_dropdown
page.within '.dropdown-content' do
click_link milestone.title
end
expect(page).to have_selector('.issue', count: 1)
end
end
def show_milestone_dropdown
click_button 'Milestone'
expect(page).to have_selector('.dropdown-content', visible: true)
end
def visit_issues
visit issues_dashboard_path
end
end
require 'spec_helper' require 'spec_helper'
feature 'Create New Merge Request', feature: true, js: false do feature 'Create New Merge Request', feature: true, js: true do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:project) { create(:project, :public) } let(:project) { create(:project, :public) }
...@@ -13,9 +13,12 @@ feature 'Create New Merge Request', feature: true, js: false do ...@@ -13,9 +13,12 @@ feature 'Create New Merge Request', feature: true, js: false do
it 'generates a diff for an orphaned branch' do it 'generates a diff for an orphaned branch' do
click_link 'New Merge Request' click_link 'New Merge Request'
select "orphaned-branch", from: "merge_request_source_branch"
select "master", from: "merge_request_target_branch" first('.js-source-branch').click
first('.dropdown-source-branch .dropdown-content a', text: 'orphaned-branch').click
click_button "Compare branches" click_button "Compare branches"
click_link "Changes"
expect(page).to have_content "README.md" expect(page).to have_content "README.md"
expect(page).to have_content "wm.png" expect(page).to have_content "wm.png"
...@@ -23,6 +26,8 @@ feature 'Create New Merge Request', feature: true, js: false do ...@@ -23,6 +26,8 @@ feature 'Create New Merge Request', feature: true, js: false do
fill_in "merge_request_title", with: "Orphaned MR test" fill_in "merge_request_title", with: "Orphaned MR test"
click_button "Submit merge request" click_button "Submit merge request"
click_link "Check out branch"
expect(page).to have_content 'git checkout -b orphaned-branch origin/orphaned-branch' expect(page).to have_content 'git checkout -b orphaned-branch origin/orphaned-branch'
end end
end end
require 'rails_helper'
describe FormHelper do
describe 'form_errors' do
it 'returns nil when model has no errors' do
model = double(errors: [])
expect(helper.form_errors(model)).to be_nil
end
it 'renders an alert div' do
model = double(errors: errors_stub('Error 1'))
expect(helper.form_errors(model)).
to include('<div class="alert alert-danger" id="error_explanation">')
end
it 'contains a summary message' do
single_error = double(errors: errors_stub('A'))
multi_errors = double(errors: errors_stub('A', 'B', 'C'))
expect(helper.form_errors(single_error)).
to include('<h4>The form contains the following error:')
expect(helper.form_errors(multi_errors)).
to include('<h4>The form contains the following errors:')
end
it 'renders each message' do
model = double(errors: errors_stub('Error 1', 'Error 2', 'Error 3'))
errors = helper.form_errors(model)
aggregate_failures do
expect(errors).to include('<li>Error 1</li>')
expect(errors).to include('<li>Error 2</li>')
expect(errors).to include('<li>Error 3</li>')
end
end
def errors_stub(*messages)
ActiveModel::Errors.new(double).tap do |errors|
messages.each { |msg| errors.add(:base, msg) }
end
end
end
end
...@@ -26,4 +26,10 @@ describe Gitlab::Metrics::System do ...@@ -26,4 +26,10 @@ describe Gitlab::Metrics::System do
end end
end end
end end
describe '.cpu_time' do
it 'returns a Fixnum' do
expect(described_class.cpu_time).to be_an_instance_of(Fixnum)
end
end
end end
...@@ -74,24 +74,21 @@ describe Gitlab::Metrics do ...@@ -74,24 +74,21 @@ describe Gitlab::Metrics do
let(:transaction) { Gitlab::Metrics::Transaction.new } let(:transaction) { Gitlab::Metrics::Transaction.new }
before do before do
allow(Gitlab::Metrics::Transaction).to receive(:current). allow(Gitlab::Metrics).to receive(:current_transaction).
and_return(transaction) and_return(transaction)
end end
it 'adds a metric to the current transaction' do it 'adds a metric to the current transaction' do
expect(transaction).to receive(:add_metric). expect(transaction).to receive(:increment).
with(:foo, { duration: a_kind_of(Numeric) }, { tag: 'value' }) with('foo_real_time', a_kind_of(Numeric))
Gitlab::Metrics.measure(:foo, {}, tag: 'value') { 10 } expect(transaction).to receive(:increment).
end with('foo_cpu_time', a_kind_of(Numeric))
it 'supports adding of custom values' do
values = { duration: a_kind_of(Numeric), number: 10 }
expect(transaction).to receive(:add_metric). expect(transaction).to receive(:increment).
with(:foo, values, { tag: 'value' }) with('foo_call_count', 1)
Gitlab::Metrics.measure(:foo, { number: 10 }, tag: 'value') { 10 } Gitlab::Metrics.measure(:foo) { 10 }
end end
it 'returns the return value of the block' do it 'returns the return value of the block' do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment