Commit 8605afe7 authored by Valery Sizov's avatar Valery Sizov

Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce into ce_upstream

parents 9b6a84a3 65256719
......@@ -3,14 +3,26 @@ Please view this file on the master branch, on stable branches it's out of date.
## 8.14.0 (2016-11-22)
- Adds user project membership expired event to clarify why user was removed (Callum Dryden)
- Trim leading and trailing whitespace on project_path (Linus Thiel)
- Prevent award emoji via notes for issues/MRs authored by user (barthc)
- Adds an optional path parameter to the Commits API to filter commits by path (Luis HGO)
- Fix HipChat notifications rendering (airatshigapov, eisnerd)
- Add hover to trash icon in notes !7008 (blackst0ne)
- Simpler arguments passed to named_route on toggle_award_url helper method
- Fix: Backup restore doesn't clear cache
- Use MergeRequestsClosingIssues cache data on Issue#closed_by_merge_requests method
- Fix documents and comments on Build API `scope`
- Refactor email, use setter method instead AR callbacks for email attribute (Semyon Pupkov)
## 8.13.0 (2016-10-22)
## 8.13.1 (unreleased)
- Fix error in generating labels
- Fix reply-by-email not working due to queue name mismatch
- Expire and build repository cache after project import
- Fix 404 for group pages when GitLab setup uses relative url
- Simpler arguments passed to named_route on toggle_award_url helper method
- Better handle when no users were selected for adding to group or project. (Linus Thiel)
## 8.13.0 (2016-10-22)
- Removes extra line for empty issue description. (!7045)
- Fix save button on project pipeline settings page. (!6955)
- All Sidekiq workers now use their own queue
- Avoid race condition when asynchronously removing expired artifacts. (!6881)
......@@ -31,6 +43,7 @@ Please view this file on the master branch, on stable branches it's out of date.
- Update duration at the end of pipeline
- ExpireBuildArtifactsWorker query builds table without ordering enqueuing one job per build to cleanup
- Add group level labels. (!6425)
- Fix Cycle analytics not showing correct data when filtering by date. !6906
- Add an example for testing a phoenix application with Gitlab CI in the docs (Manthan Mallikarjun)
- Cancelled pipelines could be retried. !6927
- Updating verbiage on git basics to be more intuitive
......@@ -38,6 +51,7 @@ Please view this file on the master branch, on stable branches it's out of date.
- Clarify documentation for Runners API (Gennady Trafimenkov)
- The instrumentation for Banzai::Renderer has been restored
- Change user & group landing page routing from /u/:username to /:username
- Fixed issue boards user link when in subdirectory
- Added documentation for .gitattributes files
- Move Pipeline Metrics to separate worker
- AbstractReferenceFilter caches project_refs on RequestStore when active
......@@ -382,6 +396,7 @@ Please view this file on the master branch, on stable branches it's out of date.
- Fix inconsistent checkbox alignment (ClemMakesApps)
- Use the default branch for displaying the project icon instead of master !5792 (Hannes Rosenögger)
- Adds response mime type to transaction metric action when it's not HTML
- Fix branch protection API !6215
- Fix hover leading space bug in pipeline graph !5980
- Avoid conflict with admin labels when importing GitHub labels
- User can edit closed MR with deleted fork (Katarzyna Kobierska Ula Budziszewska) !5496
......
......@@ -84,14 +84,15 @@
};
// Disable button if text field is empty
window.disableButtonIfEmptyField = function(field_selector, button_selector) {
window.disableButtonIfEmptyField = function(field_selector, button_selector, event_name) {
event_name = event_name || 'input';
var closest_submit, field;
field = $(field_selector);
closest_submit = field.closest('form').find(button_selector);
if (rstrip(field.val()) === "") {
closest_submit.disable();
}
return field.on('input', function() {
return field.on(event_name, function() {
if (rstrip($(this).val()) === "") {
return closest_submit.disable();
} else {
......
......@@ -36,7 +36,11 @@
method: 'GET',
dataType: 'json',
contentType: 'application/json',
data: { start_date: options.startDate }
data: {
cycle_analytics: {
start_date: options.startDate
}
}
}).done((data) => {
this.decorateData(data);
this.initDropdown();
......
......@@ -10,6 +10,7 @@
$('.project_member, .group_member').off('ajax:success').on('ajax:success', this.removeRow);
$('.js-member-update-control').off('change').on('change', this.formSubmit);
$('.js-edit-member-form').off('ajax:success').on('ajax:success', this.formSuccess);
disableButtonIfEmptyField('#user_ids', 'input[name=commit]', 'change');
}
removeRow(e) {
......
......@@ -388,28 +388,25 @@
// So we dont affix the tabs on these
if (Breakpoints.get().getBreakpointSize() === 'xs' || !$tabs.length) return;
var tabsWidth = $tabs.outerWidth(),
$diffTabs = $('#diff-notes-app'),
offsetTop = $tabs.offset().top - ($('.navbar-fixed-top').height() + $('.layout-nav').height());
var $diffTabs = $('#diff-notes-app'),
$fixedNav = $('.navbar-fixed-top'),
$layoutNav = $('.layout-nav');
$tabs.off('affix.bs.affix affix-top.bs.affix')
.affix({
offset: {
top: offsetTop
top: function () {
var tabsTop = $diffTabs.offset().top - $tabs.height();
tabsTop = tabsTop - ($fixedNav.height() + $layoutNav.height());
return tabsTop;
}
}
}).on('affix.bs.affix', function () {
$tabs.css({
left: $tabs.offset().left,
width: tabsWidth
});
$diffTabs.css({
marginTop: $tabs.height()
});
}).on('affix-top.bs.affix', function () {
$tabs.css({
left: '',
width: ''
});
$diffTabs.css({
marginTop: ''
});
......
......@@ -376,3 +376,5 @@ table {
margin-right: -$gl-padding;
border-top: 1px solid $border-color;
}
.hide-bottom-border { border-bottom: none !important; }
......@@ -185,6 +185,10 @@ header.header-sidebar-pinned {
@media (min-width: $screen-sm-min) {
padding-right: $sidebar_collapsed_width;
.merge-request-tabs-holder.affix {
right: $sidebar_collapsed_width;
}
}
.sidebar-collapsed-icon {
......@@ -207,6 +211,10 @@ header.header-sidebar-pinned {
@media (min-width: $screen-md-min) {
padding-right: $gutter_width;
.merge-request-tabs-holder.affix {
right: $gutter_width;
}
}
&.with-overlay {
......
......@@ -143,6 +143,7 @@
&:not(.active) {
background-color: $gray-light;
border-left: 1px solid $border-color;
}
a {
......@@ -170,6 +171,31 @@
}
}
// Ldap configurations may need more tabs & the tab labels are user generated (arbitrarily long).
// These styles prevent this from breaking the layout, and only applied when providers are configured.
.new-session-tabs.custom-provider-tabs {
flex-wrap: wrap;
li {
min-width: 85px;
flex-basis: auto;
// This styles tab elements that have wrapped to a second line. We cannot easily predict when this will happen.
// We are making somewhat of an assumption about the configuration here: that users do not have more than
// 3 LDAP servers configured (in addition to standard login) and they are not using especially long names for any
// of them. If either condition is false, this will work as expected. If both are true, there may be a missing border
// above one of the bottom row elements. If you know a better way, please implement it!
&:nth-child(n+5) {
border-top: 1px solid $border-color;
}
}
a {
font-size: 16px;
}
}
.form-control {
&:active, &:focus {
......@@ -203,6 +229,7 @@
.login-page {
.col-sm-5.pull-right {
float: none !important;
margin-bottom: 45px;
}
}
}
......@@ -244,7 +271,11 @@
}
.navless-container {
padding: 65px; // height of footer + bottom padding of email confirmation link
padding: 65px 15px; // height of footer + bottom padding of email confirmation link
@media (max-width: $screen-xs-max) {
padding: 0 15px 65px;
}
}
}
......@@ -263,3 +294,4 @@
bottom: 0;
}
}
......@@ -183,11 +183,11 @@
.ci-coverage {
float: right;
}
.stop-env-container {
color: $gl-text-color;
float: right;
a {
color: $gl-text-color;
}
......@@ -438,11 +438,18 @@
}
}
.merge-request-tabs {
.merge-request-tabs-holder {
background-color: #fff;
&.affix {
top: 100px;
left: 0;
z-index: 9;
transition: right .15s;
}
&:not(.affix) .container-fluid {
padding-left: 0;
padding-right: 0;
}
}
......@@ -13,9 +13,18 @@
.new_project,
.edit-project {
fieldset {
&.features .control-label {
font-weight: normal;
&.features {
.label-light {
margin-bottom: 0;
}
.help-block {
margin-top: 0;
}
}
.form-group {
......@@ -40,6 +49,7 @@
}
.input-group > div {
&:last-child {
padding-right: 0;
}
......@@ -47,6 +57,7 @@
@media (max-width: $screen-xs-max) {
.input-group > div {
margin-bottom: 14px;
&:last-child {
......@@ -60,6 +71,7 @@
}
.input-group-addon {
&.static-namespace {
height: 35px;
border-radius: 3px;
......
......@@ -21,6 +21,10 @@ class Groups::GroupMembersController < Groups::ApplicationController
end
def create
if params[:user_ids].blank?
return redirect_to(group_group_members_path(@group), alert: 'No users specified.')
end
@group.add_users(
params[:user_ids].split(','),
params[:access_level],
......
......@@ -2,8 +2,8 @@ class Import::GitlabProjectsController < Import::BaseController
before_action :verify_gitlab_project_import_enabled
def new
@namespace_id = project_params[:namespace_id]
@namespace_name = Namespace.find(project_params[:namespace_id]).name
@namespace = Namespace.find(project_params[:namespace_id])
return render_404 unless current_user.can?(:create_projects, @namespace)
@path = project_params[:path]
end
......
......@@ -25,6 +25,10 @@ class Projects::ProjectMembersController < Projects::ApplicationController
end
def create
if params[:user_ids].blank?
return redirect_to(namespace_project_project_members_path(@project.namespace, @project), alert: 'No users or groups specified.')
end
@project.team.add_users(
params[:user_ids].split(','),
params[:access_level],
......@@ -38,7 +42,7 @@ class Projects::ProjectMembersController < Projects::ApplicationController
log_audit_event(member, action: :create)
end
redirect_to namespace_project_project_members_path(@project.namespace, @project)
redirect_to namespace_project_project_members_path(@project.namespace, @project), notice: 'Users were successfully added.'
end
def update
......
......@@ -18,6 +18,9 @@ module ProtectedBranchAccess
else
:role
end
scope :master, -> { where(access_level: Gitlab::Access::MASTER) }
scope :developer, -> { where(access_level: Gitlab::Access::DEVELOPER) }
end
def humanize
......
......@@ -7,10 +7,8 @@ class Email < ActiveRecord::Base
validates :email, presence: true, uniqueness: true, email: true
validate :unique_email, if: ->(email) { email.email_changed? }
before_validation :cleanup_email
def cleanup_email
self.email = self.email.downcase.strip
def email=(value)
write_attribute(:email, value.downcase.strip)
end
def unique_email
......
......@@ -478,6 +478,17 @@ class Repository
@exists = nil
end
# expire cache that doesn't depend on repository data (when expiring)
def expire_content_cache
expire_tags_cache
expire_tag_count_cache
expire_branches_cache
expire_branch_count_cache
expire_root_ref_cache
expire_emptiness_caches
expire_exists_cache
end
# Runs code after a repository has been created.
def after_create
expire_exists_cache
......@@ -493,14 +504,7 @@ class Repository
expire_cache if exists?
# expire cache that don't depend on repository data (when expiring)
expire_tags_cache
expire_tag_count_cache
expire_branches_cache
expire_branch_count_cache
expire_root_ref_cache
expire_emptiness_caches
expire_exists_cache
expire_content_cache
repository_event(:remove_repository)
end
......@@ -532,14 +536,13 @@ class Repository
end
def before_import
expire_emptiness_caches
expire_exists_cache
expire_content_cache
end
# Runs code after a repository has been forked/imported.
def after_import
expire_emptiness_caches
expire_exists_cache
expire_content_cache
build_cache
end
# Runs code after a new commit has been pushed.
......
......@@ -4,7 +4,7 @@ module MergeRequests
@assignable_issues ||= begin
if current_user == merge_request.author
closes_issues.select do |issue|
!issue.assignee_id? && can?(current_user, :admin_issue, issue)
!issue.is_a?(ExternalIssue) && !issue.assignee_id? && can?(current_user, :admin_issue, issue)
end
else
[]
......
......@@ -7,8 +7,10 @@ module Notes
if note.award_emoji?
noteable = note.noteable
todo_service.new_award_emoji(noteable, current_user)
return noteable.create_award_emoji(note.award_emoji_name, current_user)
if noteable.user_can_award?(current_user, note.award_emoji_name)
todo_service.new_award_emoji(noteable, current_user)
return noteable.create_award_emoji(note.award_emoji_name, current_user)
end
end
# We execute commands (extracted from `params[:note]`) on the noteable
......
# The protected branches API still uses the `developers_can_push` and `developers_can_merge`
# flags for backward compatibility, and so performs translation between that format and the
# internal data model (separate access levels). The translation code is non-trivial, and so
# lives in this service.
module ProtectedBranches
class ApiCreateService < BaseService
def execute
push_access_level =
if params.delete(:developers_can_push)
Gitlab::Access::DEVELOPER
else
Gitlab::Access::MASTER
end
merge_access_level =
if params.delete(:developers_can_merge)
Gitlab::Access::DEVELOPER
else
Gitlab::Access::MASTER
end
@params.merge!(push_access_levels_attributes: [{ access_level: push_access_level }],
merge_access_levels_attributes: [{ access_level: merge_access_level }])
service = ProtectedBranches::CreateService.new(@project, @current_user, @params)
service.execute
end
end
end
# The protected branches API still uses the `developers_can_push` and `developers_can_merge`
# flags for backward compatibility, and so performs translation between that format and the
# internal data model (separate access levels). The translation code is non-trivial, and so
# lives in this service.
module ProtectedBranches
class ApiUpdateService < BaseService
def execute(protected_branch)
@developers_can_push = params.delete(:developers_can_push)
@developers_can_merge = params.delete(:developers_can_merge)
@protected_branch = protected_branch
protected_branch.transaction do
delete_redundant_access_levels
case @developers_can_push
when true
params.merge!(push_access_levels_attributes: [{ access_level: Gitlab::Access::DEVELOPER }])
when false
params.merge!(push_access_levels_attributes: [{ access_level: Gitlab::Access::MASTER }])
end
case @developers_can_merge
when true
params.merge!(merge_access_levels_attributes: [{ access_level: Gitlab::Access::DEVELOPER }])
when false
params.merge!(merge_access_levels_attributes: [{ access_level: Gitlab::Access::MASTER }])
end
service = ProtectedBranches::UpdateService.new(@project, @current_user, @params)
service.execute(protected_branch)
end
end
private
def delete_redundant_access_levels
unless @developers_can_merge.nil?
@protected_branch.merge_access_levels.destroy_all
end
unless @developers_can_push.nil?
@protected_branch.push_access_levels.destroy_all
end
end
end
end
......@@ -75,4 +75,4 @@
- @runners.each do |runner|
= render "admin/runners/runner", runner: runner
= paginate @runners
= paginate @runners, theme: "gitlab"
......@@ -67,7 +67,7 @@
= form_for [:admin, project.namespace.becomes(Namespace), project, project.runner_projects.new] do |f|
= f.hidden_field :runner_id, value: @runner.id
= f.submit 'Enable', class: 'btn btn-xs'
= paginate @projects
= paginate @projects, theme: "gitlab"
.col-md-6
%h4 Recent builds served by this Runner
......
......@@ -10,7 +10,7 @@
= form_for(resource, as: resource_name, url: session_path(resource_name), method: :post, html: { class: 'edit_user show-gl-field-errors' }) do |f|
- resource_params = params[resource_name].presence || params
= f.hidden_field :remember_me, value: resource_params.fetch(:remember_me, 0)
.form-group
%div
= f.label 'Two-Factor Authentication code', name: :otp_attempt
= f.text_field :otp_attempt, class: 'form-control', required: true, autofocus: true, autocomplete: 'off', title: 'This field is required.'
%p.help-block.hint Enter the code from the two-factor app on your mobile device. If you've lost your device, you may enter one of your recovery codes.
......
%ul.new-session-tabs.nav-links.nav-tabs
%ul.new-session-tabs.nav-links.nav-tabs{ class: ('custom-provider-tabs' if form_based_providers.any?) }
- if crowd_enabled?
%li.active
= link_to "Crowd", "#crowd", 'data-toggle' => 'tab'
......
......@@ -9,12 +9,12 @@
%p
Project will be imported as
%strong
#{@namespace_name}/#{@path}
#{@namespace.name}/#{@path}
%p
To move or copy an entire GitLab project from another GitLab installation to this one, navigate to the original project's settings page, generate an export file, and upload it here.
.form-group
= hidden_field_tag :namespace_id, @namespace_id
= hidden_field_tag :namespace_id, @namespace.id
= hidden_field_tag :path, @path
= label_tag :file, class: 'control-label' do
%span GitLab project export
......
......@@ -26,7 +26,7 @@
":title" => "label.description",
data: { container: 'body' } }
{{ label.title }}
%a.has-tooltip{ ":href" => "'/' + issue.assignee.username",
%a.has-tooltip{ ":href" => "'#{root_path}' + issue.assignee.username",
":title" => "'Assigned to ' + issue.assignee.name",
"v-if" => "issue.assignee",
data: { container: 'body' } }
......
......@@ -10,7 +10,7 @@
= button_tag type: 'button', class: "form-control compare-dropdown-toggle js-compare-dropdown", required: true, data: { refs_url: refs_namespace_project_path(@project.namespace, @project), toggle: "dropdown", target: ".js-compare-from-dropdown", selected: params[:from], field_name: :from } do
.dropdown-toggle-text= params[:from] || 'Select branch/tag'
= render "ref_dropdown"
.compare-ellipsis ...
.compare-ellipsis.inline ...
.form-group.dropdown.compare-form-group.to.js-compare-to-dropdown
.input-group.inline-input-group
%span.input-group-addon to
......
......@@ -54,70 +54,70 @@
%h5.prepend-top-0
Feature Visibility
= f.fields_for :project_feature do |feature_fields|
.form_group.prepend-top-20
.row
.col-md-9
= feature_fields.label :repository_access_level, "Repository", class: 'label-light'
%span.help-block Push files to be stored in this project
.col-md-3.js-repo-access-level
= project_feature_access_select(:repository_access_level)
= f.fields_for :project_feature do |feature_fields|
.form_group.prepend-top-20
.row
.col-md-9
= feature_fields.label :repository_access_level, "Repository", class: 'label-light'
%span.help-block Push files to be stored in this project
.col-md-3.js-repo-access-level
= project_feature_access_select(:repository_access_level)
.col-sm-12
.row
.col-md-9.project-feature-nested
= feature_fields.label :merge_requests_access_level, "Merge requests", class: 'label-light'
%span.help-block Submit changes to be merged upstream
.col-md-3
= project_feature_access_select(:merge_requests_access_level)
.col-sm-12
.row
.col-md-9.project-feature-nested
= feature_fields.label :merge_requests_access_level, "Merge requests", class: 'label-light'
%span.help-block Submit changes to be merged upstream
.col-md-3
= project_feature_access_select(:merge_requests_access_level)
.row
.col-md-9.project-feature-nested
= feature_fields.label :builds_access_level, "Builds", class: 'label-light'
%span.help-block Submit, test and deploy your changes before merge
.col-md-3
= project_feature_access_select(:builds_access_level)
.row
.col-md-9.project-feature-nested
= feature_fields.label :builds_access_level, "Builds", class: 'label-light'
%span.help-block Submit, test and deploy your changes before merge
.col-md-3
= project_feature_access_select(:builds_access_level)
.row
.col-md-9
= feature_fields.label :snippets_access_level, "Snippets", class: 'label-light'
%span.help-block Share code pastes with others out of Git repository
.col-md-3
= project_feature_access_select(:snippets_access_level)
.row
.col-md-9
= feature_fields.label :snippets_access_level, "Snippets", class: 'label-light'
%span.help-block Share code pastes with others out of Git repository
.col-md-3
= project_feature_access_select(:snippets_access_level)
.row
.col-md-9
= feature_fields.label :issues_access_level, "Issues", class: 'label-light'
%span.help-block Lightweight issue tracking system for this project
.col-md-3
= project_feature_access_select(:issues_access_level)
.row
.col-md-9
= feature_fields.label :issues_access_level, "Issues", class: 'label-light'
%span.help-block Lightweight issue tracking system for this project
.col-md-3
= project_feature_access_select(:issues_access_level)
.row
.col-md-9
= feature_fields.label :wiki_access_level, "Wiki", class: 'label-light'
%span.help-block Pages for project documentation
.col-md-3
= project_feature_access_select(:wiki_access_level)
- if Gitlab.config.lfs.enabled && current_user.admin?
.checkbox
= f.label :lfs_enabled do
= f.check_box :lfs_enabled
%strong LFS
%br
%span.descr
Git Large File Storage
= link_to icon('question-circle'), help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs')
.row
.col-md-9
= feature_fields.label :wiki_access_level, "Wiki", class: 'label-light'
%span.help-block Pages for project documentation
.col-md-3
= project_feature_access_select(:wiki_access_level)
- if Gitlab.config.lfs.enabled && current_user.admin?
.form-group
.checkbox
= f.label :container_registry_enabled do
= f.check_box :container_registry_enabled
%strong Container Registry
%br
%span.descr Enable Container Registry for this repository
= link_to icon('question-circle'), help_page_path('user/project/container_registry'), target: '_blank'
.checkbox
= f.label :lfs_enabled do
= f.check_box :lfs_enabled
%strong LFS
%br
%span.descr
Git Large File Storage
= link_to icon('question-circle'), help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs')
- if Gitlab.config.registry.enabled
.form-group
.checkbox
= f.label :container_registry_enabled do
= f.check_box :container_registry_enabled
%strong Container Registry
%br
%span.descr Enable Container Registry for this project
= link_to icon('question-circle'), help_page_path('user/project/container_registry'), target: '_blank'
%hr
= render 'issues_settings', f: f
......
......@@ -53,7 +53,7 @@
.issue-details.issuable-details
.detail-page-description.content-block
.detail-page-description.content-block{ class: ('hide-bottom-border' unless @issue.description.present? ) }
%h2.title
= markdown_field(@issue, :title)
- if @issue.description.present?
......
......@@ -47,39 +47,41 @@
= link_to "command line", "#modal_merge_info", class: "how_to_merge_link vlink", title: "How To Merge", "data-toggle" => "modal"
- if @commits_count.nonzero?
%ul.merge-request-tabs.nav-links.no-top.no-bottom{ class: ("js-tabs-affix" unless ENV['RAILS_ENV'] == 'test') }
%li.notes-tab
= link_to namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#notes', action: 'notes', toggle: 'tab' } do
Discussion
%span.badge= @merge_request.mr_and_commit_notes.user.count
- if @merge_request.source_project
%li.commits-tab
= link_to commits_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#commits', action: 'commits', toggle: 'tab' } do
Commits
%span.badge= @commits_count
- if @pipeline
%li.pipelines-tab
= link_to pipelines_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: '#pipelines', action: 'pipelines', toggle: 'tab' } do
Pipelines
%span.badge= @pipelines.size
%li.builds-tab
= link_to builds_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: '#builds', action: 'builds', toggle: 'tab' } do
Builds
%span.badge= @statuses.size
%li.diffs-tab
= link_to diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#diffs', action: 'diffs', toggle: 'tab' } do
Changes
%span.badge= @merge_request.diff_size
%li#resolve-count-app.line-resolve-all-container.pull-right.prepend-top-10.hidden-xs{ "v-cloak" => true }
%resolve-count{ "inline-template" => true, ":logged-out" => "#{current_user.nil?}" }
.line-resolve-all{ "v-show" => "discussionCount > 0",
":class" => "{ 'has-next-btn': !loggedOut && resolvedDiscussionCount !== discussionCount }" }
%span.line-resolve-btn.is-disabled{ type: "button",
":class" => "{ 'is-active': resolvedDiscussionCount === discussionCount }" }
= render "shared/icons/icon_status_success.svg"
%span.line-resolve-text
{{ resolvedDiscussionCount }}/{{ discussionCount }} {{ discussionCount | pluralize 'discussion' }} resolved
= render "discussions/jump_to_next"
.merge-request-tabs-holder{ class: ("js-tabs-affix" unless ENV['RAILS_ENV'] == 'test') }
%div{ class: container_class }
%ul.merge-request-tabs.nav-links.no-top.no-bottom
%li.notes-tab
= link_to namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#notes', action: 'notes', toggle: 'tab' } do
Discussion
%span.badge= @merge_request.mr_and_commit_notes.user.count
- if @merge_request.source_project
%li.commits-tab
= link_to commits_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#commits', action: 'commits', toggle: 'tab' } do
Commits
%span.badge= @commits_count
- if @pipeline
%li.pipelines-tab
= link_to pipelines_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: '#pipelines', action: 'pipelines', toggle: 'tab' } do
Pipelines
%span.badge= @pipelines.size
%li.builds-tab
= link_to builds_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: '#builds', action: 'builds', toggle: 'tab' } do
Builds
%span.badge= @statuses.size
%li.diffs-tab
= link_to diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#diffs', action: 'diffs', toggle: 'tab' } do
Changes
%span.badge= @merge_request.diff_size
%li#resolve-count-app.line-resolve-all-container.pull-right.prepend-top-10.hidden-xs{ "v-cloak" => true }
%resolve-count{ "inline-template" => true, ":logged-out" => "#{current_user.nil?}" }
.line-resolve-all{ "v-show" => "discussionCount > 0",
":class" => "{ 'has-next-btn': !loggedOut && resolvedDiscussionCount !== discussionCount }" }
%span.line-resolve-btn.is-disabled{ type: "button",
":class" => "{ 'is-active': resolvedDiscussionCount === discussionCount }" }
= render "shared/icons/icon_status_success.svg"
%span.line-resolve-text
{{ resolvedDiscussionCount }}/{{ discussionCount }} {{ discussionCount | pluralize 'discussion' }} resolved
= render "discussions/jump_to_next"
.tab-content#diff-notes-app
#notes.notes.tab-pane.voting_notes
......
......@@ -31,7 +31,7 @@
= link_to namespace_project_milestone_path(@project.namespace, @project, @milestone), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-grouped btn-danger" do
Delete
.detail-page-description.milestone-detail
.detail-page-description.milestone-detail{ class: ('hide-bottom-border' unless @milestone.description.present? ) }
%h2.title
= markdown_field(@milestone, :title)
%div
......
......@@ -57,7 +57,7 @@
= link_to '#', title: 'Edit comment', class: 'note-action-button js-note-edit' do
= icon('pencil', class: 'link-highlight')
= link_to namespace_project_note_path(note.project.namespace, note.project, note), title: 'Remove comment', method: :delete, data: { confirm: 'Are you sure you want to remove this comment?' }, remote: true, class: 'note-action-button hidden-xs js-note-delete danger' do
= icon('trash-o')
= icon('trash-o', class: 'danger-highlight')
.note-body{class: note_editable ? 'js-task-list-container' : ''}
.note-text.md
= preserve do
......
......@@ -26,4 +26,4 @@
%h4.underlined-title Available specific runners
%ul.bordered-list.available-specific-runners
= render partial: 'runner', collection: @assignable_runners, as: :runner
= paginate @assignable_runners
= paginate @assignable_runners, theme: "gitlab"
......@@ -25,7 +25,7 @@
:delivery_options:
:redis_url: <%= config[:redis_url].to_json %>
:namespace: <%= Gitlab::Redis::SIDEKIQ_NAMESPACE %>
:queue: incoming_email
:queue: email_receiver
:worker: EmailReceiverWorker
:arbitration_method: redis
......
......@@ -27,6 +27,7 @@
- [gitlab_shell, 2]
- [email_receiver, 2]
- [emails_on_push, 2]
- [mailers, 2]
- [repository_fork, 1]
- [repository_import, 1]
- [project_service, 1]
......
require 'json'
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class MigrateMailroomQueueFromDefault < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = true
DOWNTIME_REASON = <<-EOF
Moving Sidekiq jobs from queues requires Sidekiq to be stopped. Not stopping
Sidekiq will result in the loss of jobs that are scheduled after this
migration completes.
EOF
disable_ddl_transaction!
# Jobs for which the queue names have been changed (e.g. multiple workers
# using the same non-default queue).
#
# The keys are the old queue names, the values the jobs to move and their new
# queue names.
RENAMED_QUEUES = {
incoming_email: {
'EmailReceiverWorker' => :email_receiver
}
}
def up
Sidekiq.redis do |redis|
RENAMED_QUEUES.each do |queue, jobs|
migrate_from_queue(redis, queue, jobs)
end
end
end
def down
Sidekiq.redis do |redis|
RENAMED_QUEUES.each do |dest_queue, jobs|
jobs.each do |worker, from_queue|
migrate_from_queue(redis, from_queue, worker => dest_queue)
end
end
end
end
def migrate_from_queue(redis, queue, job_mapping)
while job = redis.lpop("queue:#{queue}")
payload = JSON.load(job)
new_queue = job_mapping[payload['class']]
# If we have no target queue to migrate to we're probably dealing with
# some ancient job for which the worker no longer exists. In that case
# there's no sane option we can take, other than just dropping the job.
next unless new_queue
payload['queue'] = new_queue
redis.lpush("queue:#{new_queue}", JSON.dump(payload))
end
end
end
......@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20161021185735) do
ActiveRecord::Schema.define(version: 20161024042317) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
......
......@@ -11,10 +11,10 @@ GET /projects/:id/builds
| Attribute | Type | Required | Description |
|-----------|---------|----------|---------------------|
| `id` | integer | yes | The ID of a project |
| `scope` | string **or** array of strings | no | The scope of builds to show, one or array of: `pending`, `running`, `failed`, `success`, `canceled`; showing all builds if none provided |
| `scope` | string **or** array of strings | no | The scope of builds to show, one or array of: `created`, `pending`, `running`, `failed`, `success`, `canceled`, `skipped`; showing all builds if none provided |
```
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/builds"
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" 'https://gitlab.example.com/api/v3/projects/1/builds?scope%5B0%5D=pending&scope%5B1%5D=running'
```
Example of response
......@@ -132,10 +132,10 @@ GET /projects/:id/repository/commits/:sha/builds
|-----------|---------|----------|---------------------|
| `id` | integer | yes | The ID of a project |
| `sha` | string | yes | The SHA id of a commit |
| `scope` | string **or** array of strings | no | The scope of builds to show, one or array of: `pending`, `running`, `failed`, `success`, `canceled`; showing all builds if none provided |
| `scope` | string **or** array of strings | no | The scope of builds to show, one or array of: `created`, `pending`, `running`, `failed`, `success`, `canceled`, `skipped`; showing all builds if none provided |
```
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/repository/commits/0ff3ae198f8601a285adcf5c0fff204ee6fba5fd/builds"
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" 'https://gitlab.example.com/api/v3/projects/1/repository/commits/0ff3ae198f8601a285adcf5c0fff204ee6fba5fd/builds?scope%5B0%5D=pending&scope%5B1%5D=running'
```
Example of response
......
......@@ -645,7 +645,7 @@ Parameters:
| `id` | integer | yes | The ID of the user |
```bash
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/user/:id/events
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/users/:id/events
```
Example response:
......
......@@ -188,7 +188,7 @@ In order to do that, follow the steps:
image = "docker:latest"
privileged = false
disable_cache = false
volumes = ["/var/run/docker.sock", "/cache"]
volumes = ["/var/run/docker.sock:/var/run/docker.sock", "/cache"]
[runners.cache]
Insecure = false
```
......
......@@ -37,7 +37,7 @@ The registered runner will use the `ruby:2.1` docker image and will run two
services, `postgres:latest` and `mysql:latest`, both of which will be
accessible during the build process.
## What is image
## What is an image
The `image` keyword is the name of the docker image that is present in the
local Docker Engine (list all images with `docker images`) or any image that
......@@ -47,7 +47,7 @@ Hub please read the [Docker Fundamentals][] documentation.
In short, with `image` we refer to the docker image, which will be used to
create a container on which your build will run.
## What is service
## What is a service
The `services` keyword defines just another docker image that is run during
your build and is linked to the docker image that the `image` keyword defines.
......@@ -61,7 +61,7 @@ time the project is built.
You can see some widely used services examples in the relevant documentation of
[CI services examples](../services/README.md).
### How is service linked to the build
### How services are linked to the build
To better understand how the container linking works, read
[Linking containers together][linking-containers].
......
......@@ -146,13 +146,17 @@ variables:
```
These variables can be later used in all executed commands and scripts.
The YAML-defined variables are also set to all created service containers,
thus allowing to fine tune them.
thus allowing to fine tune them. Variables can be also defined on a
[job level](#job-variables).
Variables can be also defined on [job level](#job-variables).
Except for the user defined variables, there are also the ones set up by the
Runner itself. One example would be `CI_BUILD_REF_NAME` which has the value of
the branch or tag name for which project is built. Apart from the variables
you can set in `.gitlab-ci.yml`, there are also the so called secret variables
which can be set in GitLab's UI.
[Learn more about variables.](../variables/README.md)
[Learn more about variables.][variables]
### cache
......@@ -541,20 +545,29 @@ An example usage of manual actions is deployment to production.
> Introduced in GitLab 8.9.
`environment` is used to define that a job deploys to a specific [environment].
This allows easy tracking of all deployments to your environments straight from
GitLab.
> You can read more about environments and find more examples in the
[documentation about environments][environment].
`environment` is used to define that a job deploys to a specific environment.
If `environment` is specified and no environment under that name exists, a new
one will be created automatically.
The `environment` name must contain only letters, digits, '-', '_', '/', '$', '{', '}' and spaces. Common
names are `qa`, `staging`, and `production`, but you can use whatever name works
with your workflow.
The `environment` name can contain:
---
- letters
- digits
- spaces
- `-`
- `_`
- `/`
- `$`
- `{`
- `}`
**Example configurations**
Common names are `qa`, `staging`, and `production`, but you can use whatever
name works with your workflow.
In its simplest form, the `environment` keyword can be defined like:
```
deploy to production:
......@@ -563,39 +576,134 @@ deploy to production:
environment: production
```
The `deploy to production` job will be marked as doing deployment to
`production` environment.
In the above example, the `deploy to production` job will be marked as doing a
deployment to the `production` environment.
#### environment:name
> Introduced in GitLab 8.11.
>**Note:**
Before GitLab 8.11, the name of an environment could be defined as a string like
`environment: production`. The recommended way now is to define it under the
`name` keyword.
Instead of defining the name of the environment right after the `environment`
keyword, it is also possible to define it as a separate value. For that, use
the `name` keyword under `environment`:
```
deploy to production:
stage: deploy
script: git push production HEAD:master
environment:
name: production
```
#### environment:url
> Introduced in GitLab 8.11.
>**Note:**
Before GitLab 8.11, the URL could be added only in GitLab's UI. The
recommended way now is to define it in `.gitlab-ci.yml`.
This is an optional value that when set, it exposes buttons in various places
in GitLab which when clicked take you to the defined URL.
In the example below, if the job finishes successfully, it will create buttons
in the merge requests and in the environments/deployments pages which will point
to `https://prod.example.com`.
```
deploy to production:
stage: deploy
script: git push production HEAD:master
environment:
name: production
url: https://prod.example.com
```
#### environment:on_stop
> [Introduced][ce-6669] in GitLab 8.13.
Closing (stoping) environments can be achieved with the `on_stop` keyword defined under
`environment`. It declares a different job that runs in order to close
the environment.
Read the `environment:action` section for an example.
#### environment:action
> [Introduced][ce-6669] in GitLab 8.13.
The `action` keyword is to be used in conjunction with `on_stop` and is defined
in the job that is called to close the environment.
Take for instance:
```yaml
review_app:
stage: deploy
script: make deploy-app
environment:
name: review
on_stop: stop_review_app
stop_review_app:
stage: deploy
script: make delete-app
when: manual
environment:
name: review
action: stop
```
In the above example we set up the `review_app` job to deploy to the `review`
environment, and we also defined a new `stop_review_app` job under `on_stop`.
Once the `review_app` job is successfully finished, it will trigger the
`stop_review_app` job based on what is defined under `when`. In this case we
set it up to `manual` so it will need a [manual action](#manual-actions) via
GitLab's web interface in order to run.
The `stop_review_app` job is **required** to have the following keywords defined:
- `when` - [reference](#when)
- `environment:name`
- `environment:action`
#### dynamic environments
> [Introduced][ce-6323] in GitLab 8.12 and GitLab Runner 1.6.
`environment` can also represent a configuration hash with `name` and `url`.
These parameters can use any of the defined CI [variables](#variables)
These parameters can use any of the defined [CI variables](#variables)
(including predefined, secure variables and `.gitlab-ci.yml` variables).
The common use case is to create dynamic environments for branches and use them
as review apps.
---
**Example configurations**
For example:
```
deploy as review app:
stage: deploy
script: ...
script: make deploy
environment:
name: review-apps/$CI_BUILD_REF_NAME
url: https://$CI_BUILD_REF_NAME.review.example.com/
```
The `deploy as review app` job will be marked as deployment to dynamically
create the `review-apps/branch-name` environment.
create the `review-apps/$CI_BUILD_REF_NAME` environment, which `$CI_BUILD_REF_NAME`
is an [environment variable][variables] set by the Runner. If for example the
`deploy as review app` job was run in a branch named `pow`, this environment
should be accessible under `https://pow.review.example.com/`.
This environment should be accessible under `https://branch-name.review.example.com/`.
This of course implies that the underlying server which hosts the application
is properly configured.
You can see a simple example at https://gitlab.com/gitlab-examples/review-apps-nginx/.
The common use case is to create dynamic environments for branches and use them
as Review Apps. You can see a simple example using Review Apps at
https://gitlab.com/gitlab-examples/review-apps-nginx/.
### artifacts
......@@ -1105,3 +1213,5 @@ CI with various languages.
[examples]: ../examples/README.md
[ce-6323]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/6323
[environment]: ../environments.md
[ce-6669]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/6669
[variables]: ../variables/README.md
......@@ -37,7 +37,7 @@ graphs/dashboards.
GitLab provides built-in tools to aid the process of improving performance:
* [Sherlock](profiling.md#sherlock)
* [GitLab Performance Monitoring](../monitoring/performance/monitoring.md)
* [GitLab Performance Monitoring](../administration/monitoring/performance/monitoring.md)
* [Request Profiling](../administration/monitoring/performance/request_profiling.md)
GitLab employees can use GitLab.com's performance monitoring systems located at
......
This document was moved to [administration/monitoring/performance/gitlab_configuration](../administration/monitoring/performance/gitlab_configuration.md).
This document was moved to [administration/monitoring/performance/gitlab_configuration](../../administration/monitoring/performance/gitlab_configuration.md).
This document was moved to [administration/monitoring/performance/influxdb_configuration](../administration/monitoring/performance/influxdb_configuration.md).
This document was moved to [administration/monitoring/performance/influxdb_configuration](../../administration/monitoring/performance/influxdb_configuration.md).
This document was moved to [administration/monitoring/performance/influxdb_schema](../administration/monitoring/performance/influxdb_schema.md).
This document was moved to [administration/monitoring/performance/influxdb_schema](../../administration/monitoring/performance/influxdb_schema.md).
This document was moved to [administration/monitoring/performance/introduction](../administration/monitoring/performance/introduction.md).
This document was moved to [administration/monitoring/performance/introduction](../../administration/monitoring/performance/introduction.md).
......@@ -30,6 +30,10 @@ Use this if you've installed GitLab from source:
```
sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
```
If you are running GitLab within a Docker container, you can run the backup from the host:
```
docker -t exec <container name> gitlab-rake gitlab:backup:create
```
You can specify that portions of the application data be skipped using the
environment variable `SKIP`. You can skip:
......
......@@ -72,7 +72,7 @@ sudo -u git -H git checkout 8-12-stable-ee
```bash
cd /home/git/gitlab-shell
sudo -u git -H git fetch --all --tags
sudo -u git -H git checkout v3.6.0
sudo -u git -H git checkout v3.6.1
```
### 6. Update gitlab-workhorse
......
......@@ -501,6 +501,10 @@ There are two ways to create links, inline-style and reference-style.
[I'm a reference-style link][Arbitrary case-insensitive reference text]
[I'm a relative reference to a repository file](LICENSE)
[I am an absolute reference within the repository](/doc/user/markdown.md)
[I link to the Milestones page](/../milestones)
[You can use numbers for reference-style link definitions][1]
......@@ -518,6 +522,10 @@ There are two ways to create links, inline-style and reference-style.
[I'm a relative reference to a repository file](LICENSE)[^1]
[I am an absolute reference within the repository](/doc/user/markdown.md)
[I link to the Milestones page](/../milestones)
[You can use numbers for reference-style link definitions][1]
Or leave it empty and use the [link text itself][]
......
......@@ -54,43 +54,25 @@ module API
not_found!('Branch') unless @branch
protected_branch = user_project.protected_branches.find_by(name: @branch.name)
developers_can_merge = to_boolean(params[:developers_can_merge])
developers_can_push = to_boolean(params[:developers_can_push])
protected_branch_params = {
name: @branch.name
name: @branch.name,
developers_can_push: to_boolean(params[:developers_can_push]),
developers_can_merge: to_boolean(params[:developers_can_merge])
}
# If `developers_can_merge` is switched off, _all_ `DEVELOPER`
# merge_access_levels need to be deleted.
if developers_can_merge == false
protected_branch.merge_access_levels.where(access_level: Gitlab::Access::DEVELOPER).destroy_all
end
service_args = [user_project, current_user, protected_branch_params]
# If `developers_can_push` is switched off, _all_ `DEVELOPER`
# push_access_levels need to be deleted.
if developers_can_push == false
protected_branch.push_access_levels.where(access_level: Gitlab::Access::DEVELOPER).destroy_all
end
protected_branch = if protected_branch
ProtectedBranches::ApiUpdateService.new(*service_args).execute(protected_branch)
else
ProtectedBranches::ApiCreateService.new(*service_args).execute
end
protected_branch_params.merge!(
merge_access_levels_attributes: [{
access_level: developers_can_merge ? Gitlab::Access::DEVELOPER : Gitlab::Access::MASTER
}],
push_access_levels_attributes: [{
access_level: developers_can_push ? Gitlab::Access::DEVELOPER : Gitlab::Access::MASTER
}]
)
if protected_branch
service = ProtectedBranches::UpdateService.new(user_project, current_user, protected_branch_params)
service.execute(protected_branch)
if protected_branch.valid?
present @branch, with: Entities::RepoBranch, project: user_project
else
service = ProtectedBranches::CreateService.new(user_project, current_user, protected_branch_params)
service.execute
render_api_error!(protected_branch.errors.full_messages, 422)
end
present @branch, with: Entities::RepoBranch, project: user_project
end
# Unprotect a single branch
......@@ -123,7 +105,7 @@ module API
post ":id/repository/branches" do
authorize_push_project
result = CreateBranchService.new(user_project, current_user).
execute(params[:branch_name], params[:ref])
execute(params[:branch_name], params[:ref])
if result[:status] == :success
present result[:branch],
......@@ -142,10 +124,10 @@ module API
# Example Request:
# DELETE /projects/:id/repository/branches/:branch
delete ":id/repository/branches/:branch",
requirements: { branch: /.+/ } do
requirements: { branch: /.+/ } do
authorize_push_project
result = DeleteBranchService.new(user_project, current_user).
execute(params[:branch])
execute(params[:branch])
if result[:status] == :success
{
......
......@@ -8,7 +8,7 @@ module API
#
# Parameters:
# id (required) - The ID of a project
# scope (optional) - The scope of builds to show (one or array of: pending, running, failed, success, canceled;
# scope (optional) - The scope of builds to show (one or array of: created, pending, running, failed, success, canceled, skipped;
# if none provided showing all builds)
# Example Request:
# GET /projects/:id/builds
......@@ -25,7 +25,7 @@ module API
# Parameters:
# id (required) - The ID of a project
# sha (required) - The SHA id of a commit
# scope (optional) - The scope of builds to show (one or array of: pending, running, failed, success, canceled;
# scope (optional) - The scope of builds to show (one or array of: created, pending, running, failed, success, canceled, skipped;
# if none provided showing all builds)
# Example Request:
# GET /projects/:id/repository/commits/:sha/builds
......
......@@ -19,6 +19,7 @@ module API
optional :until, type: String, desc: 'Only commits before or in this date will be returned'
optional :page, type: Integer, default: 0, desc: 'The page for pagination'
optional :per_page, type: Integer, default: 20, desc: 'The number of results per page'
optional :path, type: String, desc: 'The file path'
end
get ":id/repository/commits" do
# TODO remove the next line for 9.0, use DateTime type in the params block
......@@ -28,6 +29,7 @@ module API
offset = params[:page] * params[:per_page]
commits = user_project.repository.commits(ref,
path: params[:path],
limit: params[:per_page],
offset: offset,
after: params[:since],
......
class NamespaceUrlConstrainer
def matches?(request)
id = request.path.sub(/\A\/+/, '').split('/').first.sub(/.atom\z/, '')
id = request.path
id = id.sub(/\A#{relative_url_root}/, '') if relative_url_root
id = id.sub(/\A\/+/, '').split('/').first
id = id.sub(/.atom\z/, '') if id
if id =~ Gitlab::Regex.namespace_regex
find_resource(id)
......@@ -10,4 +13,12 @@ class NamespaceUrlConstrainer
def find_resource(id)
Namespace.find_by_path(id)
end
private
def relative_url_root
if defined?(Gitlab::Application.config.relative_url_root)
Gitlab::Application.config.relative_url_root
end
end
end
......@@ -19,7 +19,7 @@ module Gitlab
]
labels.each do |params|
::Labels::FindOrCreateService.new(project.owner, project).execute(params)
::Labels::FindOrCreateService.new(project.owner, project, params).execute
end
end
end
......
......@@ -29,5 +29,5 @@ namespace :cache do
task all: [:db, :redis]
end
task clear: 'cache:clear:all'
task clear: 'cache:clear:redis'
end
......@@ -13,6 +13,49 @@ describe Groups::GroupMembersController do
end
end
describe 'POST create' do
let(:group_user) { create(:user) }
before { sign_in(user) }
context 'when user does not have enough rights' do
before { group.add_developer(user) }
it 'returns 403' do
post :create, group_id: group,
user_ids: group_user.id,
access_level: Gitlab::Access::GUEST
expect(response).to have_http_status(403)
expect(group.users).not_to include group_user
end
end
context 'when user has enough rights' do
before { group.add_owner(user) }
it 'adds user to members' do
post :create, group_id: group,
user_ids: group_user.id,
access_level: Gitlab::Access::GUEST
expect(response).to set_flash.to 'Users were successfully added.'
expect(response).to redirect_to(group_group_members_path(group))
expect(group.users).to include group_user
end
it 'adds no user to members' do
post :create, group_id: group,
user_ids: '',
access_level: Gitlab::Access::GUEST
expect(response).to set_flash.to 'No users specified.'
expect(response).to redirect_to(group_group_members_path(group))
expect(group.users).not_to include group_user
end
end
end
describe 'DELETE destroy' do
let(:member) { create(:group_member, :developer, group: group) }
......
......@@ -70,4 +70,19 @@ describe Projects::LabelsController do
get :index, namespace_id: project.namespace.to_param, project_id: project.to_param
end
end
describe 'POST #generate' do
let(:admin) { create(:admin) }
let(:project) { create(:empty_project) }
before do
sign_in(admin)
end
it 'creates labels' do
post :generate, namespace_id: project.namespace.to_param, project_id: project.to_param
expect(response).to have_http_status(302)
end
end
end
......@@ -13,6 +13,54 @@ describe Projects::ProjectMembersController do
end
end
describe 'POST create' do
context 'when users are added' do
let(:project_user) { create(:user) }
before { sign_in(user) }
context 'when user does not have enough rights' do
before { project.team << [user, :developer] }
it 'returns 404' do
post :create, namespace_id: project.namespace,
project_id: project,
user_ids: project_user.id,
access_level: Gitlab::Access::GUEST
expect(response).to have_http_status(404)
expect(project.users).not_to include project_user
end
end
context 'when user has enough rights' do
before { project.team << [user, :master] }
it 'adds user to members' do
post :create, namespace_id: project.namespace,
project_id: project,
user_ids: project_user.id,
access_level: Gitlab::Access::GUEST
expect(response).to set_flash.to 'Users were successfully added.'
expect(response).to redirect_to(namespace_project_project_members_path(project.namespace, project))
expect(project.users).to include project_user
end
it 'adds no user to members' do
post :create, namespace_id: project.namespace,
project_id: project,
user_ids: '',
access_level: Gitlab::Access::GUEST
expect(response).to set_flash.to 'No users or groups specified.'
expect(response).to redirect_to(namespace_project_project_members_path(project.namespace, project))
expect(project.users).not_to include project_user
end
end
end
end
describe 'DELETE destroy' do
let(:member) { create(:project_member, :developer, project: project) }
......
......@@ -17,6 +17,16 @@ describe NamespaceUrlConstrainer, lib: true do
it { expect(subject.matches?(request '/g/gitlab')).to be_falsey }
it { expect(subject.matches?(request '/.gitlab')).to be_falsey }
end
context 'relative url' do
before do
allow(Gitlab::Application.config).to receive(:relative_url_root) { '/gitlab' }
end
it { expect(subject.matches?(request '/gitlab/gitlab')).to be_truthy }
it { expect(subject.matches?(request '/gitlab/gitlab-ce')).to be_falsey }
it { expect(subject.matches?(request '/gitlab/')).to be_falsey }
end
end
def request(path)
......
require 'spec_helper'
require 'email_spec'
require 'mailers/shared/notify'
describe Notify do
include EmailSpec::Matchers
......
require 'spec_helper'
require 'email_spec'
require 'mailers/shared/notify'
describe Notify, "merge request notifications" do
include EmailSpec::Matchers
......
require 'spec_helper'
require 'email_spec'
require 'mailers/shared/notify'
describe Notify do
include EmailSpec::Matchers
......
require 'spec_helper'
require 'email_spec'
require 'mailers/shared/notify'
describe Notify do
include EmailSpec::Helpers
......
......@@ -6,4 +6,9 @@ describe Email, models: true do
subject { build(:email) }
end
end
it 'normalize email value' do
expect(described_class.new(email: ' inFO@exAMPLe.com ').email)
.to eq 'info@example.com'
end
end
......@@ -1172,28 +1172,17 @@ describe Repository, models: true do
end
describe '#before_import' do
it 'flushes the emptiness cachess' do
expect(repository).to receive(:expire_emptiness_caches)
repository.before_import
end
it 'flushes the exists cache' do
expect(repository).to receive(:expire_exists_cache)
it 'flushes the repository caches' do
expect(repository).to receive(:expire_content_cache)
repository.before_import
end
end
describe '#after_import' do
it 'flushes the emptiness cachess' do
expect(repository).to receive(:expire_emptiness_caches)
repository.after_import
end
it 'flushes the exists cache' do
expect(repository).to receive(:expire_exists_cache)
it 'flushes and builds the cache' do
expect(repository).to receive(:expire_content_cache)
expect(repository).to receive(:build_cache)
repository.after_import
end
......
This diff is collapsed.
......@@ -72,6 +72,17 @@ describe API::API, api: true do
expect(json_response['message']).to include "\"since\" must be a timestamp in ISO 8601 format"
end
end
context "path optional parameter" do
it "returns project commits matching provided path parameter" do
path = 'files/ruby/popen.rb'
get api("/projects/#{project.id}/repository/commits?path=#{path}", user)
expect(json_response.size).to eq(3)
expect(json_response.first["id"]).to eq("570e7b2abdd848b95f2f578043fc23bd6f6fd24d")
end
end
end
describe "Create a commit with multiple files and actions" do
......
......@@ -221,12 +221,23 @@ describe API::API, api: true do
end
end
context 'when the user is posting an award emoji' do
context 'when the user is posting an award emoji on an issue created by someone else' do
let(:issue2) { create(:issue, project: project) }
it 'returns an award emoji' do
post api("/projects/#{project.id}/issues/#{issue2.id}/notes", user), body: ':+1:'
expect(response).to have_http_status(201)
expect(json_response['awardable_id']).to eq issue2.id
end
end
context 'when the user is posting an award emoji on his/her own issue' do
it 'creates a new issue note' do
post api("/projects/#{project.id}/issues/#{issue.id}/notes", user), body: ':+1:'
expect(response).to have_http_status(201)
expect(json_response['awardable_id']).to eq issue.id
expect(json_response['body']).to eq(':+1:')
end
end
end
......
......@@ -858,7 +858,7 @@ describe API::API, api: true do
end
end
describe 'PUT /user/:id/block' do
describe 'PUT /users/:id/block' do
before { admin }
it 'blocks existing user' do
put api("/users/#{user.id}/block", admin)
......@@ -885,7 +885,7 @@ describe API::API, api: true do
end
end
describe 'PUT /user/:id/unblock' do
describe 'PUT /users/:id/unblock' do
let(:blocked_user) { create(:user, state: 'blocked') }
before { admin }
......@@ -926,7 +926,7 @@ describe API::API, api: true do
end
end
describe 'GET /user/:id/events' do
describe 'GET /users/:id/events' do
let(:user) { create(:user) }
let(:project) { create(:empty_project) }
let(:note) { create(:note_on_issue, note: 'What an awesome day!', project: project) }
......
......@@ -46,4 +46,16 @@ describe MergeRequests::AssignIssuesService, services: true do
it 'assigns these to the merge request owner' do
expect { service.execute }.to change { issue.reload.assignee }.to(user)
end
it 'ignores external issues' do
external_issue = ExternalIssue.new('JIRA-123', project)
service = described_class.new(
project,
user,
merge_request: merge_request,
closes_issues: [external_issue]
)
expect(service.assignable_issues.count).to eq 0
end
end
......@@ -17,9 +17,9 @@ module Select2Helper
selector = options.fetch(:from)
if options[:multiple]
execute_script("$('#{selector}').select2('val', ['#{value}'], true);")
execute_script("$('#{selector}').select2('val', ['#{value}']).trigger('change');")
else
execute_script("$('#{selector}').select2('val', '#{value}', true);")
execute_script("$('#{selector}').select2('val', '#{value}').trigger('change');")
end
end
end
......@@ -9,6 +9,7 @@ describe 'gitlab:app namespace rake task' do
Rake.application.rake_require 'tasks/gitlab/backup'
Rake.application.rake_require 'tasks/gitlab/shell'
Rake.application.rake_require 'tasks/gitlab/db'
Rake.application.rake_require 'tasks/cache'
# empty task as env is already loaded
Rake::Task.define_task :environment
......
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