GitLab steht aufgrund wichtiger Wartungsarbeiten am Montag, den 8. März, zwischen 17:00 und 19:00 Uhr nicht zur Verfügung.

Commit 7ac065d0 authored by Paweł Chojnacki's avatar Paweł Chojnacki

Merge branch 'master' into 'bump-prometheus-version-to-fix-text-representation-problem'

# Conflicts:
#   Gemfile.lock
parents b5f3903b d453bb86
......@@ -285,6 +285,7 @@ group :metrics do
# Prometheus
gem 'prometheus-client-mmap', '~>0.7.0.beta5'
gem 'raindrops', '~> 0.18'
end
group :development do
......
......@@ -658,7 +658,7 @@ GEM
thor (>= 0.18.1, < 2.0)
rainbow (2.2.2)
rake
raindrops (0.17.0)
raindrops (0.18.0)
rake (10.5.0)
rblineprof (0.3.6)
debugger-ruby_core_source (~> 1.3)
......@@ -1062,6 +1062,7 @@ DEPENDENCIES
rails-deprecated_sanitizer (~> 1.0.3)
rails-i18n (~> 4.0.9)
rainbow (~> 2.2)
raindrops (~> 0.18)
rblineprof (~> 0.3.6)
rdoc (~> 4.2)
recaptcha (~> 3.0)
......
......@@ -206,8 +206,6 @@ function UsersSelect(currentUser, els) {
return $dropdown.glDropdown({
showMenuAbove: showMenuAbove,
data: function(term, callback) {
var isAuthorFilter;
isAuthorFilter = $('.js-author-search');
return _this.users(term, options, function(users) {
// GitLabDropdownFilter returns this.instance
// GitLabDropdownRemote returns this.options.instance
......
......@@ -60,13 +60,13 @@ def by_active(users)
end
def by_external_identity(users)
return users unless current_user.admin? && params[:extern_uid] && params[:provider]
return users unless current_user&.admin? && params[:extern_uid] && params[:provider]
users.joins(:identities).merge(Identity.with_extern_uid(params[:provider], params[:extern_uid]))
end
def by_external(users)
return users = users.where.not(external: true) unless current_user.admin?
return users = users.where.not(external: true) unless current_user&.admin?
return users unless params[:external]
users.external
......
......@@ -16,8 +16,8 @@ def form_errors(model)
end
end
def issue_dropdown_options(issuable, has_multiple_assignees = true)
options = {
def issue_assignees_dropdown_options
{
toggle_class: 'js-user-search js-assignee-search js-multiselect js-save-user-data',
title: 'Select assignee',
filter: true,
......@@ -27,8 +27,8 @@ def issue_dropdown_options(issuable, has_multiple_assignees = true)
first_user: current_user&.username,
null_user: true,
current_user: true,
project_id: issuable.project.try(:id),
field_name: "#{issuable.class.model_name.param_key}[assignee_ids][]",
project_id: @project.id,
field_name: 'issue[assignee_ids][]',
default_label: 'Unassigned',
'max-select': 1,
'dropdown-header': 'Assignee',
......@@ -38,13 +38,5 @@ def issue_dropdown_options(issuable, has_multiple_assignees = true)
current_user_info: current_user.to_json(only: [:id, :name])
}
}
if has_multiple_assignees
options[:title] = 'Select assignee(s)'
options[:data][:'dropdown-header'] = 'Assignee(s)'
options[:data].delete(:'max-select')
end
options
end
end
......@@ -58,6 +58,11 @@ def group_issues(group)
IssuesFinder.new(current_user, group_id: group.id).execute
end
def remove_group_message(group)
_("You are going to remove %{group_name}.\nRemoved groups CANNOT be restored!\nAre you ABSOLUTELY sure?") %
{ group_name: group.name }
end
private
def group_title_link(group, hidable: false)
......
......@@ -126,6 +126,18 @@ def search_filter_path(options = {})
search_path(options)
end
def search_filter_input_options(type)
{
id: "filtered-search-#{type}",
placeholder: 'Search or filter results...',
data: {
'project-id' => @project.id,
'username-params' => @users.to_json(only: [:id, :username]),
'base-endpoint' => namespace_project_path(@project.namespace, @project)
}
}
end
# Sanitize a HTML field for search display. Most tags are stripped out and the
# maximum length is set to 200 characters.
def search_md_sanitize(object, field)
......
......@@ -102,6 +102,14 @@ def award_emojis_loaded?
def locking_enabled?
title_changed? || description_changed?
end
def allows_multiple_assignees?
false
end
def has_multiple_assignees?
assignees.count > 1
end
end
module ClassMethods
......
......@@ -197,11 +197,19 @@ def card_attributes
}
end
# This method is needed for compatibility with issues to not mess view and other code
# These method are needed for compatibility with issues to not mess view and other code
def assignees
Array(assignee)
end
def assignee_ids
Array(assignee_id)
end
def assignee_ids=(ids)
write_attribute(:assignee_id, ids.last)
end
def assignee_or_author?(user)
author_id == user.id || assignee_id == user.id
end
......
require_dependency 'declarative_policy'
class BasePolicy < DeclarativePolicy::Base
include Gitlab::CurrentSettings
desc "User is an instance admin"
with_options scope: :user, score: 0
condition(:admin) { @user&.admin? }
......@@ -10,4 +12,9 @@ class BasePolicy < DeclarativePolicy::Base
with_options scope: :user, score: 0
condition(:can_create_group) { @user&.can_create_group }
desc "The application is restricted from public visibility"
condition(:restricted_public_level, scope: :global) do
current_application_settings.restricted_visibility_levels.include?(Gitlab::VisibilityLevel::PUBLIC)
end
end
......@@ -11,10 +11,16 @@ class GlobalPolicy < BasePolicy
with_options scope: :user, score: 0
condition(:access_locked) { @user.access_locked? }
rule { anonymous }.prevent_all
rule { anonymous }.policy do
prevent :log_in
prevent :access_api
prevent :access_git
prevent :receive_notifications
prevent :use_quick_actions
prevent :create_group
end
rule { default }.policy do
enable :read_users_list
enable :log_in
enable :access_api
enable :access_git
......@@ -37,4 +43,8 @@ class GlobalPolicy < BasePolicy
rule { access_locked }.policy do
prevent :log_in
end
rule { ~restricted_public_level }.policy do
enable :read_users_list
end
end
class UserPolicy < BasePolicy
include Gitlab::CurrentSettings
desc "The application is restricted from public visibility"
condition(:restricted_public_level, scope: :global) do
current_application_settings.restricted_visibility_levels.include?(Gitlab::VisibilityLevel::PUBLIC)
end
desc "The current user is the user in question"
condition(:user_is_self, score: 0) { @subject == @user }
......
......@@ -92,9 +92,12 @@ def extractor
desc 'Assign'
explanation do |users|
"Assigns #{users.first.to_reference}." if users.any?
users = issuable.allows_multiple_assignees? ? users : users.take(1)
"Assigns #{users.map(&:to_reference).to_sentence}."
end
params do
issuable.allows_multiple_assignees? ? '@user1 @user2' : '@user'
end
params '@user'
condition do
current_user.can?(:"admin_#{issuable.to_ability_name}", project)
end
......@@ -104,28 +107,69 @@ def extractor
command :assign do |users|
next if users.empty?
if issuable.is_a?(Issue)
@updates[:assignee_ids] = [users.last.id]
@updates[:assignee_ids] =
if issuable.allows_multiple_assignees?
issuable.assignees.pluck(:id) + users.map(&:id)
else
[users.last.id]
end
end
desc do
if issuable.allows_multiple_assignees?
'Remove all or specific assignee(s)'
else
@updates[:assignee_id] = users.last.id
'Remove assignee'
end
end
desc 'Remove assignee'
explanation do
"Removes assignee #{issuable.assignees.first.to_reference}."
"Removes #{'assignee'.pluralize(issuable.assignees.size)} #{issuable.assignees.map(&:to_reference).to_sentence}."
end
params do
issuable.allows_multiple_assignees? ? '@user1 @user2' : ''
end
condition do
issuable.persisted? &&
issuable.assignees.any? &&
current_user.can?(:"admin_#{issuable.to_ability_name}", project)
end
command :unassign do
if issuable.is_a?(Issue)
@updates[:assignee_ids] = []
else
@updates[:assignee_id] = nil
end
parse_params do |unassign_param|
# When multiple users are assigned, all will be unassigned if multiple assignees are no longer allowed
extract_users(unassign_param) if issuable.allows_multiple_assignees?
end
command :unassign do |users = nil|
@updates[:assignee_ids] =
if users&.any?
issuable.assignees.pluck(:id) - users.map(&:id)
else
[]
end
end
desc do
"Change assignee#{'(s)' if issuable.allows_multiple_assignees?}"
end
explanation do |users|
users = issuable.allows_multiple_assignees? ? users : users.take(1)
"Change #{'assignee'.pluralize(users.size)} to #{users.map(&:to_reference).to_sentence}."
end
params do
issuable.allows_multiple_assignees? ? '@user1 @user2' : '@user'
end
condition do
issuable.persisted? &&
current_user.can?(:"admin_#{issuable.to_ability_name}", project)
end
parse_params do |assignee_param|
extract_users(assignee_param)
end
command :reassign do |users|
@updates[:assignee_ids] =
if issuable.allows_multiple_assignees?
users.map(&:id)
else
[users.last.id]
end
end
desc 'Set milestone'
......
......@@ -45,10 +45,13 @@
.panel.panel-danger
.panel-heading Remove group
.panel-body
%p
Removing group will cause all child projects and resources to be removed.
%br
%strong Removed group can not be restored!
= form_tag(@group, method: :delete) do
%p
Removing group will cause all child projects and resources to be removed.
%br
%strong Removed group can not be restored!
.form-actions
= link_to 'Remove group', @group, data: {confirm: 'Removed group can not be restored! Are you sure?'}, method: :delete, class: "btn btn-remove"
.form-actions
= button_to 'Remove group', '#', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => remove_group_message(@group) }
= render 'shared/confirm_modal', phrase: @group.path
......@@ -19,10 +19,11 @@
":data-name" => "assignee.name",
":data-username" => "assignee.username" }
.dropdown
%button.dropdown-menu-toggle.js-user-search.js-author-search.js-multiselect.js-save-user-data.js-issue-board-sidebar{ type: "button", ref: "assigneeDropdown", data: { toggle: "dropdown", field_name: "issue[assignee_ids][]", first_user: (current_user.username if current_user), current_user: "true", project_id: @project.id, null_user: "true", multi_select: "true", 'max-select' => 1, dropdown: { header: 'Assignee' } },
- dropdown_options = issue_assignees_dropdown_options
%button.dropdown-menu-toggle.js-user-search.js-author-search.js-multiselect.js-save-user-data.js-issue-board-sidebar{ type: 'button', ref: 'assigneeDropdown', data: { toggle: 'dropdown', field_name: 'issue[assignee_ids][]', first_user: current_user&.username, current_user: 'true', project_id: @project.id, null_user: 'true', multi_select: 'true', 'dropdown-header': dropdown_options[:data][:'dropdown-header'], 'max-select': dropdown_options[:data][:'max-select'] },
":data-issuable-id" => "issue.id",
":data-issue-update" => "'#{namespace_project_issues_path(@project.namespace, @project)}/' + issue.id + '.json'" }
Select assignee
= dropdown_options[:title]
= icon("chevron-down")
.dropdown-menu.dropdown-select.dropdown-menu-user.dropdown-menu-selectable.dropdown-menu-author
= dropdown_title("Assign to")
......
- @no_container = true
- page_title "Charts", "Pipelines"
- page_title _("Charts"), _("Pipelines")
- content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('common_d3')
= page_specific_javascript_bundle_tag('graphs')
......@@ -8,7 +8,7 @@
%div{ class: container_class }
.sub-header-block
.oneline
A collection of graphs for Continuous Integration
= _("A collection of graphs regarding Continuous Integration")
#charts.ci-charts
.row
......
%h4 Overall stats
%h4= s_("PipelineCharts|Overall statistics")
%ul
%li
Total:
%strong= pluralize @counts[:total], 'pipeline'
= s_("PipelineCharts|Total:")
%strong= n_("1 pipeline", "%d pipelines", @counts[:total]) % @counts[:total]
%li
Successful:
%strong= pluralize @counts[:success], 'pipeline'
= s_("PipelineCharts|Successful:")
%strong= n_("1 pipeline", "%d pipelines", @counts[:success]) % @counts[:success]
%li
Failed:
%strong= pluralize @counts[:failed], 'pipeline'
= s_("PipelineCharts|Failed:")
%strong= n_("1 pipeline", "%d pipelines", @counts[:failed]) % @counts[:failed]
%li
Success ratio:
= s_("PipelineCharts|Success ratio:")
%strong
#{success_ratio(@counts)}%
%div
%p.light
Commit duration in minutes for last 30 commits
= _("Commit duration in minutes for last 30 commits")
%canvas#build_timesChart{ height: 200 }
......
%h4 Pipelines charts
%h4= _("Pipelines charts")
%p
&nbsp;
%span.cgreen
= icon("circle")
success
= s_("Pipeline|success")
&nbsp;
%span.cgray
= icon("circle")
all
= s_("Pipeline|all")
.prepend-top-default
%p.light
Jobs for last week
= _("Jobs for last week")
(#{date_from_to(Date.today - 7.days, Date.today)})
%canvas#weekChart{ height: 200 }
.prepend-top-default
%p.light
Jobs for last month
= _("Jobs for last month")
(#{date_from_to(Date.today - 30.days, Date.today)})
%canvas#monthChart{ height: 200 }
.prepend-top-default
%p.light
Jobs for last year
= _("Jobs for last year")
%canvas#yearChart.padded{ height: 250 }
- [:week, :month, :year].each do |scope|
......
......@@ -23,7 +23,7 @@
.scroll-container
%ul.tokens-container.list-unstyled
%li.input-token
%input.form-control.filtered-search{ id: "filtered-search-#{type.to_s}", placeholder: 'Search or filter results...', data: { 'project-id' => @project.id, 'username-params' => @users.to_json(only: [:id, :username]), 'base-endpoint' => namespace_project_path(@project.namespace, @project) } }
%input.form-control.filtered-search{ search_filter_input_options(type) }
= icon('filter')
#js-dropdown-hint.filtered-search-input-dropdown-menu.dropdown-menu.hint-dropdown
%ul{ data: { dropdown: true } }
......
......@@ -37,19 +37,20 @@
- issuable.assignees.each do |assignee|
= hidden_field_tag "#{issuable.to_ability_name}[assignee_ids][]", assignee.id, id: nil, data: { avatar_url: assignee.avatar_url, name: assignee.name, username: assignee.username }
- options = { toggle_class: 'js-user-search js-author-search', title: 'Assign to', filter: true, dropdown_class: 'dropdown-menu-user dropdown-menu-selectable dropdown-menu-author', placeholder: 'Search users', data: { first_user: (current_user.username if current_user), current_user: true, project_id: (@project.id if @project), author_id: issuable.author_id, field_name: "#{issuable.to_ability_name}[assignee_ids][]", issue_update: issuable_json_path(issuable), ability_name: issuable.to_ability_name, null_user: true } }
- options = { toggle_class: 'js-user-search js-author-search', title: 'Assign to', filter: true, dropdown_class: 'dropdown-menu-user dropdown-menu-selectable dropdown-menu-author', placeholder: 'Search users', data: { first_user: current_user&.username, current_user: true, project_id: @project&.id, author_id: issuable.author_id, field_name: "#{issuable.to_ability_name}[assignee_ids][]", issue_update: issuable_json_path(issuable), ability_name: issuable.to_ability_name, null_user: true } }
- title = 'Select assignee'
- if issuable.is_a?(Issue)
- unless issuable.assignees.any?
= hidden_field_tag "#{issuable.to_ability_name}[assignee_ids][]", 0, id: nil
- dropdown_options = issue_assignees_dropdown_options
- title = dropdown_options[:title]
- options[:toggle_class] += ' js-multiselect js-save-user-data'
- data = { field_name: "#{issuable.to_ability_name}[assignee_ids][]" }
- data[:multi_select] = true
- data['dropdown-title'] = title
- data['dropdown-header'] = 'Assignee'
- data['max-select'] = 1
- data['dropdown-header'] = dropdown_options[:data][:'dropdown-header']
- data['max-select'] = dropdown_options[:data][:'max-select']
- options[:data].merge!(data)
= dropdown_tag(title, options: options)
......@@ -7,5 +7,5 @@
- if issuable.assignees.length === 0
= hidden_field_tag "#{issuable.to_ability_name}[assignee_ids][]", 0, id: nil, data: { meta: '' }
= dropdown_tag(users_dropdown_label(issuable.assignees), options: issue_dropdown_options(issuable,false))
= dropdown_tag(users_dropdown_label(issuable.assignees), options: issue_assignees_dropdown_options)
= link_to 'Assign to me', '#', class: "assign-to-me-link #{'hide' if issuable.assignees.include?(current_user)}"
---
title: "Remove group modal like remove project modal (requires typing + confirmation)"
merge_request: 12569
author: Diego Souza
---
title: Allow unauthenticated access to the /api/v4/users API
merge_request: 12445
author:
......@@ -543,6 +543,10 @@ production: &base
# enabled: true
# host: localhost
# port: 3808
prometheus:
# Time between sampling of unicorn socket metrics, in seconds
# unicorn_sampler_interval: 10
#
# 5. Extra customization
......
......@@ -494,6 +494,12 @@ def cron_random_weekly_time
Settings.webpack.dev_server['host'] ||= 'localhost'
Settings.webpack.dev_server['port'] ||= 3808
#
# Prometheus metrics settings
#
Settings['prometheus'] ||= Settingslogic.new({})
Settings.prometheus['unicorn_sampler_interval'] ||= 10
#
# Testing settings
#
......
......@@ -119,6 +119,13 @@ def instrument_classes(instrumentation)
end
# rubocop:enable Metrics/AbcSize
Gitlab::Metrics::UnicornSampler.initialize_instance(Settings.prometheus.unicorn_sampler_interval).start
Gitlab::Application.configure do |config|
# 0 should be Sentry to catch errors in this middleware
config.middleware.insert(1, Gitlab::Metrics::ConnectionRackMiddleware)
end
if Gitlab::Metrics.enabled?
require 'pathname'
require 'influxdb'
......@@ -175,7 +182,7 @@ def instrument_classes(instrumentation)
GC::Profiler.enable
Gitlab::Metrics::Sampler.new.start
Gitlab::Metrics::InfluxSampler.initialize_instance.start
module TrackNewRedisConnections
def connect(*args)
......
# Using Docker Images
# Using Docker images
GitLab CI in conjunction with [GitLab Runner](../runners/README.md) can use
[Docker Engine](https://www.docker.com/) to test and build any application.
......@@ -17,14 +17,16 @@ can also run on your workstation. The added benefit is that you can test all
the commands that we will explore later from your shell, rather than having to
test them on a dedicated CI server.
## Register docker runner
## Register Docker Runner
To use GitLab Runner with docker you need to register a new runner to use the
`docker` executor:
To use GitLab Runner with Docker you need to [register a new Runner][register]
to use the `docker` executor.
A one-line example can be seen below:
```bash
gitlab-ci-multi-runner register \
--url "https://gitlab.com/" \
sudo gitlab-runner register \
--url "https://gitlab.example.com/" \
--registration-token "PROJECT_REGISTRATION_TOKEN" \
--description "docker-ruby-2.1" \
--executor "docker" \
......@@ -33,26 +35,26 @@ gitlab-ci-multi-runner register \
--docker-mysql latest
```
The registered runner will use the `ruby:2.1` docker image and will run two
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 an image
The `image` keyword is the name of the docker image the docker executor
will run to perform the CI tasks.
The `image` keyword is the name of the Docker image the Docker executor
will run to perform the CI tasks.
By default the executor will only pull images from [Docker Hub][hub],
By default, the executor will only pull images from [Docker Hub][hub],
but this can be configured in the `gitlab-runner/config.toml` by setting
the [docker pull policy][] to allow using local images.
the [Docker pull policy][] to allow using local images.
For more information about images and Docker Hub please read
the [Docker Fundamentals][] documentation.
## What is a service
The `services` keyword defines just another docker image that is run during
your job and is linked to the docker image that the `image` keyword defines.
The `services` keyword defines just another Docker image that is run during
your job and is linked to the Docker image that the `image` keyword defines.
This allows you to access the service image during build time.
The service image can run any application, but the most common use case is to
......@@ -60,6 +62,11 @@ run a database container, eg. `mysql`. It's easier and faster to use an
existing image and run it as an additional container than install `mysql` every
time the project is built.
You are not limited to have only database services. You can add as many
services you need to `.gitlab-ci.yml` or manually modify `config.toml`.
Any image found at [Docker Hub][hub] or your private Container Registry can be
used as a service.
You can see some widely used services examples in the relevant documentation of