Merge branch 'master' into 'bootstrap4'

# Conflicts:
#   app/views/groups/group_members/index.html.haml
This commit is contained in:
Clement Ho 2018-05-11 13:51:58 +00:00
commit 15cdc8bfc8
44 changed files with 500 additions and 157 deletions

View File

@ -6,7 +6,7 @@ end
gem_versions = {}
gem_versions['activerecord_sane_schema_dumper'] = rails5? ? '1.0' : '0.2'
gem_versions['default_value_for'] = rails5? ? '~> 3.0.5' : '~> 3.0.0'
gem_versions['rails'] = rails5? ? '5.0.6' : '4.2.10'
gem_versions['rails'] = rails5? ? '5.0.7' : '4.2.10'
gem_versions['rails-i18n'] = rails5? ? '~> 5.1' : '~> 4.0.9'
# --- The end of special code for migrating to Rails 5.0 ---

View File

@ -4,43 +4,43 @@ GEM
RedCloth (4.3.2)
abstract_type (0.0.7)
ace-rails-ap (4.1.4)
actioncable (5.0.6)
actionpack (= 5.0.6)
actioncable (5.0.7)
actionpack (= 5.0.7)
nio4r (>= 1.2, < 3.0)
websocket-driver (~> 0.6.1)
actionmailer (5.0.6)
actionpack (= 5.0.6)
actionview (= 5.0.6)
activejob (= 5.0.6)
actionmailer (5.0.7)
actionpack (= 5.0.7)
actionview (= 5.0.7)
activejob (= 5.0.7)
mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 2.0)
actionpack (5.0.6)
actionview (= 5.0.6)
activesupport (= 5.0.6)
actionpack (5.0.7)
actionview (= 5.0.7)
activesupport (= 5.0.7)
rack (~> 2.0)
rack-test (~> 0.6.3)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.0.2)
actionview (5.0.6)
activesupport (= 5.0.6)
actionview (5.0.7)
activesupport (= 5.0.7)
builder (~> 3.1)
erubis (~> 2.7.0)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.0.3)
activejob (5.0.6)
activesupport (= 5.0.6)
activejob (5.0.7)
activesupport (= 5.0.7)
globalid (>= 0.3.6)
activemodel (5.0.6)
activesupport (= 5.0.6)
activerecord (5.0.6)
activemodel (= 5.0.6)
activesupport (= 5.0.6)
activemodel (5.0.7)
activesupport (= 5.0.7)
activerecord (5.0.7)
activemodel (= 5.0.7)
activesupport (= 5.0.7)
arel (~> 7.0)
activerecord_sane_schema_dumper (1.0)
rails (>= 5, < 6)
activesupport (5.0.6)
activesupport (5.0.7)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (~> 0.7)
i18n (>= 0.7, < 2)
minitest (~> 5.1)
tzinfo (~> 1.1)
acts-as-taggable-on (5.0.0)
@ -62,13 +62,13 @@ GEM
asciidoctor (1.5.6.1)
asciidoctor-plantuml (0.0.8)
asciidoctor (~> 1.5)
asset_sync (2.2.0)
asset_sync (2.4.0)
activemodel (>= 4.1.0)
fog-core
mime-types (>= 2.99)
unf
ast (2.4.0)
atomic (1.1.100)
atomic (1.1.99)
attr_encrypted (3.1.0)
encryptor (~> 3.0.0)
attr_required (1.0.1)
@ -144,12 +144,10 @@ GEM
connection_pool (2.2.1)
crack (0.4.3)
safe_yaml (~> 1.0.0)
crass (1.0.3)
crass (1.0.4)
creole (0.5.0)
css_parser (1.6.0)
addressable
d3_rails (3.5.17)
railties (>= 3.1.0)
daemons (1.2.6)
database_cleaner (1.5.3)
debug_inspector (0.0.3)
@ -292,7 +290,7 @@ GEM
po_to_json (>= 1.0.0)
rails (>= 3.2.0)
gherkin-ruby (0.3.2)
gitaly-proto (0.97.0)
gitaly-proto (0.99.0)
google-protobuf (~> 3.1)
grpc (~> 1.10)
github-linguist (5.3.3)
@ -335,9 +333,8 @@ GEM
activesupport (>= 4.2.0)
gollum-grit_adapter (1.0.1)
gitlab-grit (~> 2.7, >= 2.7.1)
gon (6.1.0)
gon (6.2.0)
actionpack (>= 3.0)
json
multi_json
request_store (>= 1.0)
google-api-client (0.19.8)
@ -367,8 +364,8 @@ GEM
rack (>= 1.3.0)
rack-accept
virtus (>= 1.0.0)
grape-entity (0.6.1)
activesupport (>= 5.0.0)
grape-entity (0.7.1)
activesupport (>= 4.0)
multi_json (>= 1.3.2)
grape-route-helpers (2.1.0)
activesupport
@ -420,7 +417,7 @@ GEM
json (~> 1.8)
multi_xml (>= 0.5.2)
httpclient (2.8.3)
i18n (0.9.5)
i18n (1.0.1)
concurrent-ruby (~> 1.0)
ice_nine (0.11.2)
influxdb (0.5.3)
@ -515,7 +512,7 @@ GEM
net-ldap (0.16.1)
net-ssh (4.2.0)
netrc (0.11.0)
nio4r (2.2.0)
nio4r (2.3.1)
nokogiri (1.8.2)
mini_portile2 (~> 2.3.0)
numerizer (0.1.1)
@ -545,9 +542,9 @@ GEM
omniauth (~> 1.2)
omniauth-facebook (4.0.0)
omniauth-oauth2 (~> 1.2)
omniauth-github (1.1.2)
omniauth (~> 1.0)
omniauth-oauth2 (~> 1.1)
omniauth-github (1.3.0)
omniauth (~> 1.5)
omniauth-oauth2 (>= 1.4.0, < 2.0)
omniauth-gitlab (1.0.3)
omniauth (~> 1.0)
omniauth-oauth2 (~> 1.0)
@ -633,7 +630,7 @@ GEM
parser
unparser
procto (0.0.3)
prometheus-client-mmap (0.9.1)
prometheus-client-mmap (0.9.2)
pry (0.11.3)
coderay (~> 1.1.0)
method_source (~> 0.9.0)
@ -644,7 +641,7 @@ GEM
pry (>= 0.10.4)
public_suffix (3.0.2)
pyu-ruby-sasl (0.0.3.3)
rack (2.0.4)
rack (2.0.5)
rack-accept (0.4.5)
rack (>= 0.4)
rack-attack (4.4.1)
@ -662,17 +659,17 @@ GEM
rack
rack-test (0.6.3)
rack (>= 1.0)
rails (5.0.6)
actioncable (= 5.0.6)
actionmailer (= 5.0.6)
actionpack (= 5.0.6)
actionview (= 5.0.6)
activejob (= 5.0.6)
activemodel (= 5.0.6)
activerecord (= 5.0.6)
activesupport (= 5.0.6)
rails (5.0.7)
actioncable (= 5.0.7)
actionmailer (= 5.0.7)
actionpack (= 5.0.7)
actionview (= 5.0.7)
activejob (= 5.0.7)
activemodel (= 5.0.7)
activerecord (= 5.0.7)
activesupport (= 5.0.7)
bundler (>= 1.3.0)
railties (= 5.0.6)
railties (= 5.0.7)
sprockets-rails (>= 2.0.0)
rails-controller-testing (1.0.2)
actionpack (~> 5.x, >= 5.0.1)
@ -683,21 +680,21 @@ GEM
rails-dom-testing (2.0.3)
activesupport (>= 4.2.0)
nokogiri (>= 1.6)
rails-html-sanitizer (1.0.3)
loofah (~> 2.0)
rails-html-sanitizer (1.0.4)
loofah (~> 2.2, >= 2.2.2)
rails-i18n (5.1.1)
i18n (>= 0.7, < 2)
railties (>= 5.0, < 6)
railties (5.0.6)
actionpack (= 5.0.6)
activesupport (= 5.0.6)
railties (5.0.7)
actionpack (= 5.0.7)
activesupport (= 5.0.7)
method_source
rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0)
rainbow (2.2.2)
rake
raindrops (0.19.0)
rake (12.3.0)
rake (12.3.1)
rb-fsevent (0.10.3)
rb-inotify (0.9.10)
ffi (>= 0.5.0, < 2)
@ -1001,7 +998,7 @@ DEPENDENCIES
asana (~> 0.6.0)
asciidoctor (~> 1.5.6)
asciidoctor-plantuml (= 0.0.8)
asset_sync (~> 2.2.0)
asset_sync (~> 2.4)
attr_encrypted (~> 3.1.0)
awesome_print (~> 1.2.0)
babosa (~> 1.0.2)
@ -1027,12 +1024,11 @@ DEPENDENCIES
concurrent-ruby (~> 1.0.5)
connection_pool (~> 2.0)
creole (~> 0.5.0)
d3_rails (~> 3.5.0)
database_cleaner (~> 1.5.0)
deckar01-task_list (= 2.0.0)
default_value_for (~> 3.0.5)
device_detector
devise (~> 4.2)
devise (~> 4.4)
devise-two-factor (~> 3.0.0)
diffy (~> 3.1.0)
doorkeeper (~> 4.3)
@ -1063,7 +1059,7 @@ DEPENDENCIES
gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.3)
gitaly-proto (~> 0.97.0)
gitaly-proto (~> 0.99.0)
github-linguist (~> 5.3.3)
gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-gollum-lib (~> 4.2)
@ -1071,12 +1067,12 @@ DEPENDENCIES
gitlab-markup (~> 1.6.2)
gitlab-styles (~> 2.3)
gitlab_omniauth-ldap (~> 2.0.4)
gon (~> 6.1.0)
gon (~> 6.2)
google-api-client (~> 0.19.8)
google-protobuf (= 3.5.1)
gpgme
grape (~> 1.0)
grape-entity (~> 0.6.0)
grape-entity (~> 0.7.1)
grape-route-helpers (~> 2.1.0)
grape_logging (~> 1.7)
grpc (~> 1.11.0)
@ -1117,7 +1113,7 @@ DEPENDENCIES
omniauth-azure-oauth2 (~> 0.0.9)
omniauth-cas3 (~> 1.1.4)
omniauth-facebook (~> 4.0.0)
omniauth-github (~> 1.1.1)
omniauth-github (~> 1.3)
omniauth-gitlab (~> 1.0.2)
omniauth-google-oauth2 (~> 0.5.3)
omniauth-kerberos (~> 0.3.0)
@ -1136,14 +1132,14 @@ DEPENDENCIES
peek-sidekiq (~> 1.0.3)
pg (~> 0.18.2)
premailer-rails (~> 1.9.7)
prometheus-client-mmap (~> 0.9.1)
prometheus-client-mmap (~> 0.9.2)
pry-byebug (~> 3.4.1)
pry-rails (~> 0.3.4)
rack-attack (~> 4.4.1)
rack-cors (~> 1.0.0)
rack-oauth2 (~> 1.2.1)
rack-proxy (~> 0.6.0)
rails (= 5.0.6)
rails (= 5.0.7)
rails-controller-testing
rails-deprecated_sanitizer (~> 1.0.3)
rails-i18n (~> 5.1)

View File

@ -140,7 +140,7 @@ export default {
this.file.staged && this.file.key.indexOf('unstaged-') === 0 ? head : null,
);
if (this.viewer === viewerTypes.mr) {
if (this.viewer === viewerTypes.mr && this.file.mrChange) {
this.editor.attachMergeRequestModel(this.model);
} else {
this.editor.attachModel(this.model);

View File

@ -44,6 +44,7 @@ export const dataStructure = () => ({
size: 0,
parentPath: null,
lastOpenedAt: 0,
mrChange: null,
});
export const decorateData = entity => {

View File

@ -111,7 +111,13 @@
</td>
<td>
{{ timeFormated(item.createdAt) }}
<span
v-tooltip
:title="tooltipTitle(item.createdAt)"
data-placement="bottom"
>
{{ timeFormated(item.createdAt) }}
</span>
</td>
<td class="content">

View File

@ -15,7 +15,7 @@ export default class UserCallout {
init() {
if (!this.isCalloutDismissed || this.isCalloutDismissed === 'false') {
$('.js-close-callout').on('click', e => this.dismissCallout(e));
this.userCalloutBody.find('.js-close-callout').on('click', e => this.dismissCallout(e));
}
}
@ -23,12 +23,15 @@ export default class UserCallout {
const $currentTarget = $(e.currentTarget);
if (this.options.setCalloutPerProject) {
Cookies.set(this.cookieName, 'true', { expires: 365, path: this.userCalloutBody.data('projectPath') });
Cookies.set(this.cookieName, 'true', {
expires: 365,
path: this.userCalloutBody.data('projectPath'),
});
} else {
Cookies.set(this.cookieName, 'true', { expires: 365 });
}
if ($currentTarget.hasClass('close')) {
if ($currentTarget.hasClass('close') || $currentTarget.hasClass('js-close')) {
this.userCalloutBody.remove();
}
}

View File

@ -109,12 +109,12 @@ export default {
rel="noopener noreferrer nofollow"
class="deploy-link js-deploy-url"
>
{{ deployment.external_url_formatted }}
<i
class="fa fa-external-link"
aria-hidden="true"
>
</i>
{{ deployment.external_url_formatted }}
</a>
</template>
<span

View File

@ -17,6 +17,16 @@
*/
@mixin markdown-table {
width: auto;
display: inline-block;
overflow-x: auto;
border-left: 0;
border-right: 0;
border-bottom: 0;
@supports(width: fit-content) {
display: block;
width: fit-content;
}
}
/*

View File

@ -17,6 +17,7 @@
display: flex;
align-items: center;
justify-content: space-between;
line-height: $line-height-base;
.title {
display: flex;
@ -33,10 +34,14 @@
.navbar-collapse {
padding-right: 0;
.navbar-nav {
margin: 0;
}
}
.nav li a {
color: $theme-gray-700;
.nav li {
float: none;
}
}

View File

@ -180,11 +180,6 @@ ul.wiki-pages-list.content-list {
}
}
.wiki-holder {
overflow-x: auto;
overflow-y: hidden;
}
.wiki {
table {
@include markdown-table;

View File

@ -11,13 +11,20 @@ class Groups::GroupMembersController < Groups::ApplicationController
:override
def index
can_manage_members = can?(current_user, :admin_group_member, @group)
@sort = params[:sort].presence || sort_value_name
@project = @group.projects.find(params[:project_id]) if params[:project_id]
@members = GroupMembersFinder.new(@group).execute
@members = @members.non_invite unless can?(current_user, :admin_group, @group)
@members = @members.non_invite unless can_manage_members
@members = @members.search(params[:search]) if params[:search].present?
@members = @members.sort_by_attribute(@sort)
if can_manage_members && params[:two_factor].present?
@members = @members.filter_by_2fa(params[:two_factor])
end
@members = @members.page(params[:page]).per(50)
@members = present_members(@members.includes(:user))

View File

@ -108,7 +108,13 @@ module Ci
end
def assign_to(project, current_user = nil)
self.is_shared = false if shared?
if shared?
self.is_shared = false if shared?
self.runner_type = :project_type
elsif group_type?
raise ArgumentError, 'Transitioning a group runner to a project runner is not supported'
end
self.save
project.runner_projects.create(runner_id: self.id)
end

View File

@ -4,7 +4,9 @@ module Routable
extend ActiveSupport::Concern
included do
has_one :route, as: :source, autosave: true, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
# Remove `inverse_of: source` when upgraded to rails 5.2
# See https://github.com/rails/rails/pull/28808
has_one :route, as: :source, autosave: true, dependent: :destroy, inverse_of: :source # rubocop:disable Cop/ActiveRecordDependent
has_many :redirect_routes, as: :source, autosave: true, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
validates :route, presence: true

View File

@ -22,7 +22,8 @@ module ShaAttribute
column = columns.find { |c| c.name == name.to_s }
unless column
raise ArgumentError.new("sha_attribute #{name.inspect} is invalid since the column doesn't exist")
warn "WARNING: sha_attribute #{name.inspect} is invalid since the column doesn't exist - you may need to run database migrations"
return
end
unless column.type == :binary

View File

@ -96,6 +96,17 @@ class Member < ActiveRecord::Base
joins(:user).merge(User.search(query))
end
def filter_by_2fa(value)
case value
when 'enabled'
left_join_users.merge(User.with_two_factor_indistinct)
when 'disabled'
left_join_users.merge(User.without_two_factor)
else
all
end
end
def sort_by_attribute(method)
case method.to_s
when 'access_level_asc' then reorder(access_level: :asc)

View File

@ -237,14 +237,18 @@ class User < ActiveRecord::Base
scope :order_recent_sign_in, -> { reorder(Gitlab::Database.nulls_last_order('current_sign_in_at', 'DESC')) }
scope :order_oldest_sign_in, -> { reorder(Gitlab::Database.nulls_last_order('current_sign_in_at', 'ASC')) }
def self.with_two_factor
def self.with_two_factor_indistinct
joins("LEFT OUTER JOIN u2f_registrations AS u2f ON u2f.user_id = users.id")
.where("u2f.id IS NOT NULL OR otp_required_for_login = ?", true).distinct(arel_table[:id])
.where("u2f.id IS NOT NULL OR users.otp_required_for_login = ?", true)
end
def self.with_two_factor
with_two_factor_indistinct.distinct(arel_table[:id])
end
def self.without_two_factor
joins("LEFT OUTER JOIN u2f_registrations AS u2f ON u2f.user_id = users.id")
.where("u2f.id IS NULL AND otp_required_for_login = ?", false)
.where("u2f.id IS NULL AND users.otp_required_for_login = ?", false)
end
#

View File

@ -1,10 +1,11 @@
- page_title "Members"
- can_manage_members = can?(current_user, :admin_group_member, @group)
.project-members-page.prepend-top-default
%h4
Members
%hr
- if can?(current_user, :admin_group_member, @group)
- if can_manage_members
.project-members-new.append-bottom-default
%p.clearfix
Add new member to
@ -13,20 +14,23 @@
= render 'shared/members/requests', membership_source: @group, requesters: @requesters
.append-bottom-default.clearfix
.clearfix
%h5.member.existing-title
Existing members
= form_tag group_group_members_path(@group), method: :get, class: 'form-inline member-search-form' do
.form-group
= search_field_tag :search, params[:search], { placeholder: 'Find existing members by name', class: 'form-control', spellcheck: false }
%button.member-search-btn{ type: "submit", "aria-label" => "Submit search" }
= icon("search")
= render 'shared/members/sort_dropdown'
.card
.card-header
Members with access to
%strong= @group.name
%span.badge.badge-pill= @members.total_count
.card-header.flex-project-members-panel
%span.flex-project-title
Members with access to
%strong= @group.name
%span.badge= @members.total_count
= form_tag group_group_members_path(@group), method: :get, class: 'form-inline member-search-form flex-project-members-form' do
.form-group
= search_field_tag :search, params[:search], { placeholder: 'Find existing members by name', class: 'form-control', spellcheck: false }
%button.member-search-btn{ type: "submit", "aria-label" => "Submit search" }
= icon("search")
- if can_manage_members
= render 'shared/members/filter_2fa_dropdown'
= render 'shared/members/sort_dropdown'
%ul.content-list.members-list
= render partial: 'shared/members/member', collection: @members, as: :member
= paginate @members, theme: 'gitlab'

View File

@ -8,7 +8,7 @@
.top-area
= render 'shared/issuable/nav', type: :issues
.nav-controls
= link_to params.merge(rss_url_options), class: 'btn' do
= link_to safe_params.merge(rss_url_options), class: 'btn' do
= icon('rss')
%span.icon-label
Subscribe

View File

@ -20,10 +20,10 @@
= brand_header_logo
- logo_text = brand_header_logo_type
- if logo_text.present?
%span.logo-text.hidden-xs.prepend-left-8
%span.logo-text.prepend-left-8
= logo_text
- if header_link?(:user_dropdown)
.navbar-collapse.collapse
.navbar-collapse
%ul.nav.navbar-nav
%li.header-user.dropdown
= link_to current_user, class: user_dropdown_class, data: { toggle: "dropdown" } do

View File

@ -1,6 +1,6 @@
- if @wiki_home.present?
%div{ class: container_class }
.wiki-holder.prepend-top-default.append-bottom-default
.prepend-top-default.append-bottom-default
.wiki
= render_wiki_content(@wiki_home)
- else

View File

@ -24,7 +24,7 @@
- history_link = link_to s_("WikiHistoricalPage|history"), project_wiki_history_path(@project, @page)
= (s_("WikiHistoricalPage|You can view the %{most_recent_link} or browse the %{history_link}.") % { most_recent_link: most_recent_link, history_link: history_link }).html_safe
.wiki-holder.prepend-top-default.append-bottom-default
.prepend-top-default.append-bottom-default
.wiki
= render_wiki_content(@page)

View File

@ -0,0 +1,11 @@
- filter = params[:two_factor] || 'everyone'
- filter_options = { 'everyone' => 'Everyone', 'enabled' => 'Enabled', 'disabled' => 'Disabled' }
.dropdown.inline.member-filter-2fa-dropdown
= dropdown_toggle('2FA: ' + filter_options[filter], { toggle: 'dropdown' })
%ul.dropdown-menu.dropdown-menu-align-right.dropdown-menu-selectable
%li.dropdown-header
Filter by two-factor authentication
- filter_options.each do |value, title|
%li
= link_to filter_group_project_member_path(two_factor: value), class: ("is-active" if filter == value) do
= title

View File

@ -20,6 +20,10 @@
%label.badge.badge-danger
%strong Blocked
- if user.two_factor_enabled?
%label.label.label-info
2FA
- if source.instance_of?(Group) && source != @group
&middot;
= link_to source.full_name, source, class: "member-group-link"

View File

@ -0,0 +1,5 @@
---
title: Moves MR widget external link icon to the right
merge_request: 18828
author: Jacopo Beschi @jacopo-beschi
type: changed

View File

@ -0,0 +1,5 @@
---
title: 46210 Display logo and user dropdown on mobile for terms page and fix styling
merge_request:
author:
type: fixed

View File

@ -0,0 +1,5 @@
---
title: Expand documentation for Runners API
merge_request: 16484
author:
type: other

View File

@ -0,0 +1,5 @@
---
title: Add 2FA filter to the group members page
merge_request: 18483
author:
type: changed

View File

@ -0,0 +1,5 @@
---
title: 'Add missing tooltip to creation date on container registry overview'
merge_request: 18767
author: Lars Greiss
type: fixed

View File

@ -0,0 +1,5 @@
---
title: Finding a wiki page is done by Gitaly by default
merge_request:
author:
type: other

View File

@ -26,17 +26,6 @@ def validate_storages_config
Gitlab.config.repositories.storages.each do |name, repository_storage|
storage_validation_error("\"#{name}\" is not a valid storage name") unless storage_name_valid?(name)
if repository_storage.is_a?(String)
raise "#{name} is not a valid storage, because it has no `path` key. " \
"It may be configured as:\n\n#{name}:\n path: #{repository_storage}\n\n" \
"For source installations, update your config/gitlab.yml Refer to gitlab.yml.example for an updated example.\n\n" \
"If you're using the Gitlab Development Kit, you can update your configuration running `gdk reconfigure`.\n"
end
if !repository_storage.is_a?(Gitlab::GitalyClient::StorageSettings) || repository_storage.legacy_disk_path.nil?
storage_validation_error("#{name} is not a valid storage, because it has no `path` key. Refer to gitlab.yml.example for an updated example")
end
%w(failure_count_threshold failure_reset_time storage_timeout).each do |setting|
# Falling back to the defaults is fine!
next if repository_storage[setting].nil?

View File

@ -411,3 +411,86 @@ DELETE /projects/:id/runners/:runner_id
```
curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/9/runners/9"
```
## Register a new Runner
Register a new Runner for the instance.
```
POST /runners
```
| Attribute | Type | Required | Description |
|-------------|---------|----------|---------------------|
| `token` | string | yes | Registration token ([Read how to obtain a token](../ci/runners/README.md)) |
| `description`| string | no | Runner's description|
| `info` | hash | no | Runner's metadata |
| `active` | boolean| no | Whether the Runner is active |
| `locked` | boolean| no | Whether the Runner should be locked for current project |
| `run_untagged` | boolean | no | Whether the Runner should handle untagged jobs |
| `tag_list` | Array[String] | no | List of Runner's tags |
| `maximum_timeout` | integer | no | Maximum timeout set when this Runner will handle the job |
```
curl --request POST "https://gitlab.example.com/api/v4/runners" --form "token=ipzXrMhuyyJPifUt6ANz" --form "description=test-1-20150125-test" --form "tag_list=ruby,mysql,tag1,tag2"
```
Response:
| Status | Description |
|-----------|---------------------------------|
| 201 | Runner was created |
Example response:
```json
{
"id": "12345",
"token": "6337ff461c94fd3fa32ba3b1ff4125"
}
```
## Delete a registered Runner
Deletes a registed Runner.
```
DELETE /runners
```
| Attribute | Type | Required | Description |
|-------------|---------|----------|---------------------|
| `token` | string | yes | Runner's authentication token |
```
curl --request DELETE "https://gitlab.example.com/api/v4/runners" --form "token=ebb6fc00521627750c8bb750f2490e"
```
Response:
| Status | Description |
|-----------|---------------------------------|
| 204 | Runner was deleted |
## Verify authentication for a registered Runner
Validates authentication credentials for a registered Runner.
```
POST /runners/verify
```
| Attribute | Type | Required | Description |
|-------------|---------|----------|---------------------|
| `token` | string | yes | Runner's authentication token |
```
curl --request POST "https://gitlab.example.com/api/v4/runners/verify" --form "token=ebb6fc00521627750c8bb750f2490e"
```
Response:
| Status | Description |
|-----------|---------------------------------|
| 200 | Credentials are valid |
| 403 | Credentials are invalid |

View File

@ -129,8 +129,8 @@ You may see a temporary error message `SchedulerPredicates failed due to Persist
Add the GitLab Helm repository and initialize Helm:
```bash
helm repo add gitlab https://charts.gitlab.io
helm init
helm repo add gitlab https://charts.gitlab.io
```
Once you have reviewed the [configuration settings](#configuring-and-installing-gitlab) you can install the chart. We recommending saving your configuration options in a `values.yaml` file for easier upgrades in the future.

View File

@ -67,7 +67,8 @@ module Gitlab
end
def page(title:, version: nil, dir: nil)
@repository.gitaly_migrate(:wiki_find_page) do |is_enabled|
@repository.gitaly_migrate(:wiki_find_page,
status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
if is_enabled
gitaly_find_page(title: title, version: version, dir: dir)
else

View File

@ -5,6 +5,14 @@ module Gitlab
# directly.
class StorageSettings
DirectPathAccessError = Class.new(StandardError)
InvalidConfigurationError = Class.new(StandardError)
INVALID_STORAGE_MESSAGE = <<~MSG.freeze
Storage is invalid because it has no `path` key.
For source installations, update your config/gitlab.yml Refer to gitlab.yml.example for an updated example.
If you're using the Gitlab Development Kit, you can update your configuration running `gdk reconfigure`.
MSG
# This class will give easily recognizable NoMethodErrors
Deprecated = Class.new
@ -12,7 +20,8 @@ module Gitlab
attr_reader :legacy_disk_path
def initialize(storage)
raise "expected a Hash, got a #{storage.class.name}" unless storage.is_a?(Hash)
raise InvalidConfigurationError, "expected a Hash, got a #{storage.class.name}" unless storage.is_a?(Hash)
raise InvalidConfigurationError, INVALID_STORAGE_MESSAGE unless storage.has_key?('path')
# Support a nil 'path' field because some of the circuit breaker tests use it.
@legacy_disk_path = File.expand_path(storage['path'], Rails.root) if storage['path']

View File

@ -5,7 +5,7 @@ module Gitlab
def initialize(*collections, per_page: nil)
raise ArgumentError.new('Only 2 collections are supported') if collections.size != 2
@per_page = per_page || Kaminari.config.default_per_page
@per_page = (per_page || Kaminari.config.default_per_page).to_i
@first_collection, @second_collection = collections
end

View File

@ -0,0 +1,54 @@
require 'spec_helper'
feature 'Groups > Members > Filter members' do
let(:user) { create(:user) }
let(:user_with_2fa) { create(:user, :two_factor_via_otp) }
let(:group) { create(:group) }
background do
group.add_owner(user)
group.add_master(user_with_2fa)
sign_in(user)
end
scenario 'shows all members' do
visit_members_list
expect(first_member).to include(user.name)
expect(second_member).to include(user_with_2fa.name)
expect(page).to have_css('.member-filter-2fa-dropdown .dropdown-toggle-text', text: '2FA: Everyone')
end
scenario 'shows only 2FA members' do
visit_members_list(two_factor: 'enabled')
expect(first_member).to include(user_with_2fa.name)
expect(members_list.size).to eq(1)
expect(page).to have_css('.member-filter-2fa-dropdown .dropdown-toggle-text', text: '2FA: Enabled')
end
scenario 'shows only non 2FA members' do
visit_members_list(two_factor: 'disabled')
expect(first_member).to include(user.name)
expect(members_list.size).to eq(1)
expect(page).to have_css('.member-filter-2fa-dropdown .dropdown-toggle-text', text: '2FA: Disabled')
end
def visit_members_list(options = {})
visit group_group_members_path(group.to_param, options)
end
def members_list
page.all('ul.content-list > li')
end
def first_member
members_list.first.text
end
def second_member
members_list.last.text
end
end

View File

@ -42,26 +42,6 @@ describe '6_validations' do
expect { validate_storages_config }.to raise_error('"name with spaces" is not a valid storage name. Please fix this in your gitlab.yml before starting GitLab.')
end
end
context 'with incomplete settings' do
before do
mock_storages('foo' => {})
end
it 'throws an error suggesting the user to update its settings' do
expect { validate_storages_config }.to raise_error('foo is not a valid storage, because it has no `path` key. Refer to gitlab.yml.example for an updated example. Please fix this in your gitlab.yml before starting GitLab.')
end
end
context 'with deprecated settings structure' do
before do
mock_storages('foo' => 'tmp/tests/paths/a/b/c')
end
it 'throws an error suggesting the user to update its settings' do
expect { validate_storages_config }.to raise_error("foo is not a valid storage, because it has no `path` key. It may be configured as:\n\nfoo:\n path: tmp/tests/paths/a/b/c\n\nFor source installations, update your config/gitlab.yml Refer to gitlab.yml.example for an updated example.\n\nIf you're using the Gitlab Development Kit, you can update your configuration running `gdk reconfigure`.\n")
end
end
end
describe 'validate_storages_paths' do

View File

@ -24,7 +24,7 @@ describe('RepoEditor', () => {
f.active = true;
f.tempFile = true;
vm.$store.state.openFiles.push(f);
vm.$store.state.entries[f.path] = f;
Vue.set(vm.$store.state.entries, f.path, f);
vm.monaco = true;
vm.$mount();
@ -215,6 +215,30 @@ describe('RepoEditor', () => {
expect(vm.editor.attachModel).toHaveBeenCalledWith(vm.model);
});
it('attaches model to merge request editor', () => {
vm.$store.state.viewer = 'mrdiff';
vm.file.mrChange = true;
spyOn(vm.editor, 'attachMergeRequestModel');
Editor.editorInstance.modelManager.dispose();
vm.setupEditor();
expect(vm.editor.attachMergeRequestModel).toHaveBeenCalledWith(vm.model);
});
it('does not attach model to merge request editor when not a MR change', () => {
vm.$store.state.viewer = 'mrdiff';
vm.file.mrChange = false;
spyOn(vm.editor, 'attachMergeRequestModel');
Editor.editorInstance.modelManager.dispose();
vm.setupEditor();
expect(vm.editor.attachMergeRequestModel).not.toHaveBeenCalledWith(vm.model);
});
it('adds callback methods', () => {
spyOn(vm.editor, 'onPositionChange').and.callThrough();

View File

@ -0,0 +1,29 @@
require 'spec_helper'
describe Gitlab::GitalyClient::StorageSettings do
describe "#initialize" do
context 'when the storage contains no path' do
it 'raises an error' do
expect do
described_class.new("foo" => {})
end.to raise_error(described_class::InvalidConfigurationError)
end
end
context "when the argument isn't a hash" do
it 'raises an error' do
expect do
described_class.new("test")
end.to raise_error("expected a Hash, got a String")
end
end
context 'when the storage is valid' do
it 'raises no error' do
expect do
described_class.new("path" => Rails.root)
end.not_to raise_error
end
end
end
end

View File

@ -200,15 +200,29 @@ describe Ci::Runner do
describe '#assign_to' do
let!(:project) { FactoryBot.create(:project) }
let!(:shared_runner) { FactoryBot.create(:ci_runner, :shared) }
before do
shared_runner.assign_to(project)
subject { runner.assign_to(project) }
context 'with shared_runner' do
let!(:runner) { FactoryBot.create(:ci_runner, :shared) }
it 'transitions shared runner to project runner and assigns project' do
subject
expect(runner).to be_specific
expect(runner).to be_project_type
expect(runner.projects).to eq([project])
expect(runner.only_for?(project)).to be_truthy
end
end
it { expect(shared_runner).to be_specific }
it { expect(shared_runner.projects).to eq([project]) }
it { expect(shared_runner.only_for?(project)).to be_truthy }
context 'with group runner' do
let!(:runner) { FactoryBot.create(:ci_runner, runner_type: :group_type) }
it 'raises an error' do
expect { subject }
.to raise_error(ArgumentError, 'Transitioning a group runner to a project runner is not supported')
end
end
end
describe '.online' do

View File

@ -55,13 +55,9 @@ describe Clusters::Applications::Runner do
context 'without a runner' do
let(:project) { create(:project) }
let(:cluster) { create(:cluster) }
let(:cluster) { create(:cluster, projects: [project]) }
let(:gitlab_runner) { create(:clusters_applications_runner, cluster: cluster) }
before do
cluster.projects << project
end
it 'creates a runner' do
expect do
subject

View File

@ -239,17 +239,19 @@ describe Clusters::Platforms::Kubernetes, :use_clean_rails_memory_store_caching
it { is_expected.to be_nil }
end
context 'when kubernetes responds with valid pods' do
context 'when kubernetes responds with valid pods and deployments' do
before do
stub_kubeclient_pods
stub_kubeclient_deployments
end
it { is_expected.to eq(pods: [kube_pod]) }
it { is_expected.to include(pods: [kube_pod]) }
end
context 'when kubernetes responds with 500s' do
before do
stub_kubeclient_pods(status: 500)
stub_kubeclient_deployments(status: 500)
end
it { expect { subject }.to raise_error(Kubeclient::HttpError) }
@ -258,9 +260,10 @@ describe Clusters::Platforms::Kubernetes, :use_clean_rails_memory_store_caching
context 'when kubernetes responds with 404s' do
before do
stub_kubeclient_pods(status: 404)
stub_kubeclient_deployments(status: 404)
end
it { is_expected.to eq(pods: []) }
it { is_expected.to include(pods: []) }
end
end
end

View File

@ -36,24 +36,26 @@ describe ShaAttribute do
end
context 'when the table does not exist' do
it 'allows the attribute to be added' do
it 'allows the attribute to be added and issues a warning' do
allow(model).to receive(:table_exists?).and_return(false)
expect(model).not_to receive(:columns)
expect(model).to receive(:attribute)
expect(model).to receive(:warn)
model.sha_attribute(:name)
end
end
context 'when the column does not exist' do
it 'raises ArgumentError' do
it 'allows the attribute to be added and issues a warning' do
allow(model).to receive(:table_exists?).and_return(true)
expect(model).to receive(:columns)
expect(model).not_to receive(:attribute)
expect(model).to receive(:attribute)
expect(model).to receive(:warn)
expect { model.sha_attribute(:no_name) }.to raise_error(ArgumentError)
model.sha_attribute(:no_name)
end
end

View File

@ -9,8 +9,13 @@ module KubernetesHelpers
kube_response(kube_pods_body)
end
def kube_deployments_response
kube_response(kube_deployments_body)
end
def stub_kubeclient_discover(api_url)
WebMock.stub_request(:get, api_url + '/api/v1').to_return(kube_response(kube_v1_discovery_body))
WebMock.stub_request(:get, api_url + '/apis/extensions/v1beta1').to_return(kube_response(kube_v1beta1_discovery_body))
end
def stub_kubeclient_pods(response = nil)
@ -20,6 +25,13 @@ module KubernetesHelpers
WebMock.stub_request(:get, pods_url).to_return(response || kube_pods_response)
end
def stub_kubeclient_deployments(response = nil)
stub_kubeclient_discover(service.api_url)
deployments_url = service.api_url + "/apis/extensions/v1beta1/namespaces/#{service.actual_namespace}/deployments"
WebMock.stub_request(:get, deployments_url).to_return(response || kube_deployments_response)
end
def stub_kubeclient_get_secrets(api_url, **options)
WebMock.stub_request(:get, api_url + '/api/v1/secrets')
.to_return(kube_response(kube_v1_secrets_body(options)))
@ -53,6 +65,18 @@ module KubernetesHelpers
"kind" => "APIResourceList",
"resources" => [
{ "name" => "pods", "namespaced" => true, "kind" => "Pod" },
{ "name" => "deployments", "namespaced" => true, "kind" => "Deployment" },
{ "name" => "secrets", "namespaced" => true, "kind" => "Secret" }
]
}
end
def kube_v1beta1_discovery_body
{
"kind" => "APIResourceList",
"resources" => [
{ "name" => "pods", "namespaced" => true, "kind" => "Pod" },
{ "name" => "deployments", "namespaced" => true, "kind" => "Deployment" },
{ "name" => "secrets", "namespaced" => true, "kind" => "Secret" }
]
}
@ -65,14 +89,25 @@ module KubernetesHelpers
}
end
def kube_deployments_body
{
"kind" => "DeploymentList",
"items" => [kube_deployment]
}
end
# This is a partial response, it will have many more elements in reality but
# these are the ones we care about at the moment
def kube_pod(name: "kube-pod", app: "valid-pod-label")
def kube_pod(name: "kube-pod", app: "valid-pod-label", status: "Running", track: nil)
{
"metadata" => {
"name" => name,
"generate_name" => "generated-name-with-suffix",
"creationTimestamp" => "2016-11-25T19:55:19Z",
"labels" => { "app" => app }
"labels" => {
"app" => app,
"track" => track
}
},
"spec" => {
"containers" => [
@ -80,7 +115,27 @@ module KubernetesHelpers
{ "name" => "container-1" }
]
},
"status" => { "phase" => "Running" }
"status" => { "phase" => status }
}
end
def kube_deployment(name: "kube-deployment", app: "valid-deployment-label", track: nil)
{
"metadata" => {
"name" => name,
"generation" => 4,
"labels" => {
"app" => app,
"track" => track
}.compact
},
"spec" => { "replicas" => 3 },
"status" => {
"observedGeneration" => 4,
"replicas" => 3,
"updatedReplicas" => 3,
"availableReplicas" => 3
}
}
end
@ -101,4 +156,12 @@ module KubernetesHelpers
terminal
end
end
def kube_deployment_rollout_status
::Gitlab::Kubernetes::RolloutStatus.from_deployments(kube_deployment)
end
def empty_deployment_rollout_status
::Gitlab::Kubernetes::RolloutStatus.from_deployments()
end
end