Commit 1ca6581d authored by Martin Hanzel's avatar Martin Hanzel

Merge branch 'master' into mh/editor-indents-ee

parents fac8f949 e10c552d
......@@ -262,25 +262,6 @@ Naming/HeredocDelimiterNaming:
Naming/RescuedExceptionsVariableName:
Enabled: false
# Offense count: 6
# Cop supports --auto-correct.
Performance/InefficientHashSearch:
Exclude:
- 'app/controllers/concerns/sessionless_authentication.rb'
- 'app/models/note.rb'
- 'app/models/user_preference.rb'
- 'ee/app/models/ee/project.rb'
- 'lib/gitlab/import_export/members_mapper.rb'
- 'qa/spec/spec_helper.rb'
# Offense count: 3
# Cop supports --auto-correct.
Performance/ReverseEach:
Exclude:
- 'app/models/commit.rb'
- 'db/migrate/20190222051615_add_indexes_for_merge_request_diffs_query.rb'
- 'lib/gitlab/profiler.rb'
# Offense count: 7081
# Configuration parameters: Prefixes.
# Prefixes: when, with, without
......
import $ from 'jquery';
import { pluralize } from './lib/utils/text_utility';
import { n__ } from '~/locale';
import { localTimeAgo } from './lib/utils/datetime_utility';
import Pager from './pager';
import axios from './lib/utils/axios_utils';
......@@ -90,9 +90,10 @@ export default class CommitsList {
.first()
.find('li.commit').length,
);
$commitsHeadersLast
.find('span.commits-count')
.text(`${commitsCount} ${pluralize('commit', commitsCount)}`);
.text(n__('%d commit', '%d commits', commitsCount));
}
localTimeAgo($processedData.find('.js-timeago'));
......
......@@ -8,22 +8,26 @@ export default class CycleAnalyticsService {
}
fetchCycleAnalyticsData(options = { startDate: 30 }) {
const { startDate, projectIds } = options;
return this.axios
.get('', {
params: {
'cycle_analytics[start_date]': options.startDate,
'cycle_analytics[start_date]': startDate,
'cycle_analytics[project_ids]': projectIds,
},
})
.then(x => x.data);
}
fetchStageData(options) {
const { stage, startDate } = options;
const { stage, startDate, projectIds } = options;
return this.axios
.get(`events/${stage.name}.json`, {
params: {
'cycle_analytics[start_date]': startDate,
'cycle_analytics[project_ids]': projectIds,
},
})
.then(x => x.data);
......
<script>
import { n__ } from '~/locale';
import Icon from '~/vue_shared/components/icon.vue';
import { pluralize, truncate } from '~/lib/utils/text_utility';
import { truncate } from '~/lib/utils/text_utility';
import UserAvatarImage from '~/vue_shared/components/user_avatar/user_avatar_image.vue';
import { GlTooltipDirective } from '@gitlab/ui';
import { COUNT_OF_AVATARS_IN_GUTTER, LENGTH_OF_AVATAR_TOOLTIP } from '../constants';
......@@ -42,7 +43,7 @@ export default {
return '';
}
return pluralize(`${this.moreCount} more comment`, this.moreCount);
return n__('%d more comment', '%d more comments', this.moreCount);
},
},
methods: {
......
......@@ -2,8 +2,7 @@ import $ from 'jquery';
import _ from 'underscore';
import timeago from 'timeago.js';
import dateFormat from 'dateformat';
import { pluralize } from './text_utility';
import { languageCode, s__, __ } from '../../locale';
import { languageCode, s__, __, n__ } from '../../locale';
window.timeago = timeago;
......@@ -231,14 +230,10 @@ export const timeIntervalInWords = intervalInSeconds => {
const secondsInteger = parseInt(intervalInSeconds, 10);
const minutes = Math.floor(secondsInteger / 60);
const seconds = secondsInteger - minutes * 60;
let text = '';
if (minutes >= 1) {
text = `${minutes} ${pluralize('minute', minutes)} ${seconds} ${pluralize('second', seconds)}`;
} else {
text = `${seconds} ${pluralize('second', seconds)}`;
}
return text;
const secondsText = n__('%d second', '%d seconds', seconds);
return minutes >= 1
? [n__('%d minute', '%d minutes', minutes), secondsText].join(' ')
: secondsText;
};
export const dateInWords = (date, abbreviated = false, hideYear = false) => {
......
......@@ -28,14 +28,6 @@ export const highCountTrim = count => (count > 99 ? '99+' : count);
export const humanize = string =>
string.charAt(0).toUpperCase() + string.replace(/_/g, ' ').slice(1);
/**
* Adds an 's' to the end of the string when count is bigger than 0
* @param {String} str
* @param {Number} count
* @returns {String}
*/
export const pluralize = (str, count) => str + (count > 1 || count === 0 ? 's' : '');
/**
* Replaces underscores with dashes
* @param {*} str
......
.tooltip-inner {
font-size: $tooltip-font-size;
font-size: $gl-font-size-small;
border-radius: $border-radius-default;
line-height: 16px;
line-height: $gl-line-height;
font-weight: $gl-font-weight-normal;
padding: 8px;
}
......@@ -48,3 +48,7 @@ $spacers: (
9: ($spacer * 8)
);
$pagination-color: $gl-text-color;
$tooltip-padding-y: 0.5rem;
$tooltip-padding-x: 0.75rem;
$tooltip-arrow-height: 0.5rem;
$tooltip-arrow-width: 1rem;
......@@ -106,6 +106,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:lets_encrypt_notification_email,
:lets_encrypt_terms_of_service_accepted,
:domain_blacklist_file,
:raw_blob_request_limit,
disabled_oauth_sign_in_sources: [],
import_sources: [],
repository_storages: [],
......
......@@ -13,7 +13,7 @@ module SessionlessAuthentication
end
def sessionless_user?
current_user && !session.keys.include?('warden.user.user.key')
current_user && !session.key?('warden.user.user.key')
end
def sessionless_sign_in(user)
......
......@@ -10,7 +10,7 @@ class Import::GithubController < Import::BaseController
rescue_from Octokit::Unauthorized, with: :provider_unauthorized
def new
if github_import_configured? && logged_in_with_provider?
if !ci_cd_only? && github_import_configured? && logged_in_with_provider?
go_to_provider_for_permissions
elsif session[access_token_key]
redirect_to status_import_url
......@@ -169,11 +169,15 @@ class Import::GithubController < Import::BaseController
# rubocop: enable CodeReuse/ActiveRecord
def provider_auth
if session[access_token_key].blank?
if !ci_cd_only? && session[access_token_key].blank?
go_to_provider_for_permissions
end
end
def ci_cd_only?
%w[1 true].include?(params[:ci_cd_only])
end
def client_options
{}
end
......
......@@ -8,10 +8,30 @@ class Projects::RawController < Projects::ApplicationController
before_action :require_non_empty_project
before_action :assign_ref_vars
before_action :authorize_download_code!
before_action :show_rate_limit, only: [:show]
def show
@blob = @repository.blob_at(@commit.id, @path)
send_blob(@repository, @blob, inline: (params[:inline] != 'false'))
end
private
def show_rate_limit
limiter = ::Gitlab::ActionRateLimiter.new(action: :show_raw_controller)
return unless limiter.throttled?([@project, @commit, @path], raw_blob_request_limit)
limiter.log_request(request, :raw_blob_request_limit, current_user)
flash[:alert] = _('You cannot access the raw file. Please wait a minute.')
redirect_to project_blob_path(@project, File.join(@ref, @path))
end
def raw_blob_request_limit
Gitlab::CurrentSettings
.current_application_settings
.raw_blob_request_limit
end
end
......@@ -177,6 +177,7 @@ module ApplicationSettingsHelper
:domain_blacklist_enabled,
:domain_blacklist_raw,
:domain_whitelist_raw,
:outbound_local_requests_whitelist_raw,
:dsa_key_restriction,
:ecdsa_key_restriction,
:ed25519_key_restriction,
......
......@@ -52,7 +52,7 @@ module AvatarsHelper
user: commit_or_event.author,
user_name: commit_or_event.author_name,
user_email: commit_or_event.author_email,
css_class: 'd-none d-sm-inline'
css_class: 'd-none d-sm-inline-block'
}))
end
......
......@@ -4,11 +4,10 @@ module LabelsHelper
extend self
include ActionView::Helpers::TagHelper
def show_label_issuables_link?(label, issuables_type, current_user: nil, project: nil)
def show_label_issuables_link?(label, issuables_type, current_user: nil)
return true unless label.project_label?
return true unless project
project.feature_available?(issuables_type, current_user)
label.project.feature_available?(issuables_type, current_user)
end
# Link to a Label
......
......@@ -41,6 +41,11 @@ class ApplicationSetting < ApplicationRecord
validates :uuid, presence: true
validates :outbound_local_requests_whitelist,
length: { maximum: 1_000, message: N_('is too long (maximum is 1000 entries)') }
validates :outbound_local_requests_whitelist, qualified_domain_array: true, allow_blank: true
validates :session_expire_delay,
presence: true,
numericality: { only_integer: true, greater_than_or_equal_to: 0 }
......
......@@ -2,6 +2,7 @@
module ApplicationSettingImplementation
extend ActiveSupport::Concern
include Gitlab::Utils::StrongMemoize
DOMAIN_LIST_SEPARATOR = %r{\s*[,;]\s* # comma or semicolon, optionally surrounded by whitespace
| # or
......@@ -96,7 +97,9 @@ module ApplicationSettingImplementation
diff_max_patch_bytes: Gitlab::Git::Diff::DEFAULT_MAX_PATCH_BYTES,
commit_email_hostname: default_commit_email_hostname,
protected_ci_variables: false,
local_markdown_version: 0
local_markdown_version: 0,
outbound_local_requests_whitelist: [],
raw_blob_request_limit: 300
}
end
......@@ -131,31 +134,52 @@ module ApplicationSettingImplementation
end
def domain_whitelist_raw
self.domain_whitelist&.join("\n")
array_to_string(self.domain_whitelist)
end
def domain_blacklist_raw
self.domain_blacklist&.join("\n")
array_to_string(self.domain_blacklist)
end
def domain_whitelist_raw=(values)
self.domain_whitelist = []
self.domain_whitelist = values.split(DOMAIN_LIST_SEPARATOR)
self.domain_whitelist.reject! { |d| d.empty? }
self.domain_whitelist
self.domain_whitelist = domain_strings_to_array(values)
end
def domain_blacklist_raw=(values)
self.domain_blacklist = []
self.domain_blacklist = values.split(DOMAIN_LIST_SEPARATOR)
self.domain_blacklist.reject! { |d| d.empty? }
self.domain_blacklist
self.domain_blacklist = domain_strings_to_array(values)
end
def domain_blacklist_file=(file)
self.domain_blacklist_raw = file.read
end
def outbound_local_requests_whitelist_raw
array_to_string(self.outbound_local_requests_whitelist)
end
def outbound_local_requests_whitelist_raw=(values)
self.outbound_local_requests_whitelist = domain_strings_to_array(values)
end
def outbound_local_requests_whitelist_arrays
strong_memoize(:outbound_local_requests_whitelist_arrays) do
ip_whitelist = []
domain_whitelist = []
self.outbound_local_requests_whitelist.each do |str|
ip_obj = Gitlab::Utils.string_to_ip_object(str)
if ip_obj
ip_whitelist << ip_obj
else
domain_whitelist << str
end
end
[ip_whitelist, domain_whitelist]
end
end
def repository_storages
Array(read_attribute(:repository_storages))
end
......@@ -255,6 +279,17 @@ module ApplicationSettingImplementation
private
def array_to_string(arr)
arr&.join("\n")
end
def domain_strings_to_array(values)
values
.split(DOMAIN_LIST_SEPARATOR)
.reject(&:empty?)
.uniq
end
def ensure_uuid!
return if uuid?
......
......@@ -346,7 +346,7 @@ class Commit
if commits_in_merge_request.present?
message_body << ""
commits_in_merge_request.reverse.each do |commit_in_merge|
commits_in_merge_request.reverse_each do |commit_in_merge|
message_body << "#{commit_in_merge.short_id} #{commit_in_merge.title}"
end
end
......
# frozen_string_literal: true
# This module makes it possible to handle items as a list, where the order of items can be easily altered
# Requirements:
#
# - Only works for ActiveRecord models
# - relative_position integer field must present on the model
# - This module uses GROUP BY: the model should have a parent relation, example: project -> issues, project is the parent relation (issues table has a parent_id column)
#
# Setup like this in the body of your class:
#
# include RelativePositioning
#
# # base query used for the position calculation
# def self.relative_positioning_query_base(issue)
# where(deleted: false)
# end
#
# # column that should be used in GROUP BY
# def self.relative_positioning_parent_column
# :project_id
# end
#
module RelativePositioning
extend ActiveSupport::Concern
......@@ -93,7 +114,7 @@ module RelativePositioning
return move_after(before) unless after
return move_before(after) unless before
# If there is no place to insert an issue we need to create one by moving the before issue closer
# If there is no place to insert an item we need to create one by moving the before item closer
# to its predecessor. This process will recursively move all the predecessors until we have a place
if (after.relative_position - before.relative_position) < 2
before.move_before
......@@ -108,11 +129,11 @@ module RelativePositioning
pos_after = before.next_relative_position
if before.shift_after?
issue_to_move = self.class.in_parents(parent_ids).find_by!(relative_position: pos_after)
issue_to_move.move_after
@positionable_neighbours = [issue_to_move] # rubocop:disable Gitlab/ModuleWithInstanceVariables
item_to_move = self.class.relative_positioning_query_base(self).find_by!(relative_position: pos_after)
item_to_move.move_after
@positionable_neighbours = [item_to_move] # rubocop:disable Gitlab/ModuleWithInstanceVariables
pos_after = issue_to_move.relative_position
pos_after = item_to_move.relative_position
end
self.relative_position = self.class.position_between(pos_before, pos_after)
......@@ -123,11 +144,11 @@ module RelativePositioning
pos_before = after.prev_relative_position
if after.shift_before?
issue_to_move = self.class.in_parents(parent_ids).find_by!(relative_position: pos_before)
issue_to_move.move_before
@positionable_neighbours = [issue_to_move] # rubocop:disable Gitlab/ModuleWithInstanceVariables
item_to_move = self.class.relative_positioning_query_base(self).find_by!(relative_position: pos_before)
item_to_move.move_before
@positionable_neighbours = [item_to_move] # rubocop:disable Gitlab/ModuleWithInstanceVariables
pos_before = issue_to_move.relative_position
pos_before = item_to_move.relative_position
end
self.relative_position = self.class.position_between(pos_before, pos_after)
......@@ -141,13 +162,13 @@ module RelativePositioning
self.relative_position = self.class.position_between(min_relative_position || START_POSITION, MIN_POSITION)
end
# Indicates if there is an issue that should be shifted to free the place
# Indicates if there is an item that should be shifted to free the place
def shift_after?
next_pos = next_relative_position
next_pos && (next_pos - relative_position) == 1
end
# Indicates if there is an issue that should be shifted to free the place
# Indicates if there is an item that should be shifted to free the place
def shift_before?
prev_pos = prev_relative_position
prev_pos && (relative_position - prev_pos) == 1
......@@ -159,7 +180,7 @@ module RelativePositioning
def save_positionable_neighbours
return unless @positionable_neighbours
status = @positionable_neighbours.all? { |issue| issue.save(touch: false) }
status = @positionable_neighbours.all? { |item| item.save(touch: false) }
@positionable_neighbours = nil
status
......@@ -170,16 +191,15 @@ module RelativePositioning
# When calculating across projects, this is much more efficient than
# MAX(relative_position) without the GROUP BY, due to index usage:
# https://gitlab.com/gitlab-org/gitlab-ce/issues/54276#note_119340977
relation = self.class
.in_parents(parent_ids)
relation = self.class.relative_positioning_query_base(self)
.order(Gitlab::Database.nulls_last_order('position', 'DESC'))
.group(self.class.relative_positioning_parent_column)
.limit(1)
.group(self.class.parent_column)
relation = yield relation if block_given?
relation
.pluck(self.class.parent_column, Arel.sql("#{calculation}(relative_position) AS position"))
.pluck(self.class.relative_positioning_parent_column, Arel.sql("#{calculation}(relative_position) AS position"))
.first&.
last
end
......
......@@ -86,4 +86,9 @@ class ContainerRepository < ApplicationRecord
def self.build_root_repository(project)
self.new(project: project, name: '')
end
def self.find_by_path!(path)
self.find_by!(project: path.repository_project,
name: path.repository_name)
end
end
......@@ -91,11 +91,11 @@ class Issue < ApplicationRecord
end
end
class << self
alias_method :in_parents, :in_projects
def self.relative_positioning_query_base(issue)
in_projects(issue.parent_ids)
end
def self.parent_column
def self.relative_positioning_parent_column
:project_id
end
......
......@@ -33,7 +33,7 @@ class Label < ApplicationRecord
default_scope { order(title: :asc) }
scope :templates, -> { where(template: true) }
scope :templates, -> { where(template: true, type: [Label.name, nil]) }
scope :with_title, ->(title) { where(title: title) }
scope :with_lists_and_board, -> { joins(lists: :board).merge(List.movable) }
scope :on_project_boards, ->(project_id) { with_lists_and_board.where(boards: { project_id: project_id }) }
......
......@@ -292,7 +292,7 @@ class Note < ApplicationRecord
end
def special_role=(role)
raise "Role is undefined, #{role} not found in #{SpecialRole.values}" unless SpecialRole.values.include?(role)
raise "Role is undefined, #{role} not found in #{SpecialRole.values}" unless SpecialRole.value?(role)
@special_role = role
end
......
......@@ -1862,16 +1862,24 @@ class Project < ApplicationRecord
end
def append_or_update_attribute(name, value)
old_values = public_send(name.to_s) # rubocop:disable GitlabSecurity/PublicSend
if Project.reflect_on_association(name).try(:macro) == :has_many
# if this is 1-to-N relation, update the parent object
value.each do |item|
item.update!(
Project.reflect_on_association(name).foreign_key => id)
end
# force to drop relation cache
public_send(name).reset # rubocop:disable GitlabSecurity/PublicSend
if Project.reflect_on_association(name).try(:macro) == :has_many && old_values.any?
update_attribute(name, old_values + value)
# succeeded
true
else
# if this is another relation or attribute, update just object
update_attribute(name, value)
end
rescue ActiveRecord::RecordNotSaved => e
handle_update_attribute_error(e, value)
rescue ActiveRecord::RecordInvalid => e
raise e, "Failed to set #{name}: #{e.message}"
end
# Tries to set repository as read_only, checking for existing Git transfers in progress beforehand
......@@ -2260,18 +2268,6 @@ class Project < ApplicationRecord
ContainerRepository.build_root_repository(self).has_tags?
end
def handle_update_attribute_error(ex, value)
if ex.message.start_with?('Failed to replace')
if value.respond_to?(:each)
invalid = value.detect(&:invalid?)
raise ex, ([ex.message] + invalid.errors.full_messages).join(' ') if invalid
end
end
raise ex
end
def fetch_branch_allows_collaboration(user, branch_name = nil)
return false unless user
......
# frozen_string_literal: true
class ProjectAutoDevops < ApplicationRecord
include IgnorableColumn
ignore_column :domain
belongs_to :project, inverse_of: :auto_devops
enum deploy_strategy: {
......
......@@ -26,7 +26,7 @@ class UserPreference < ApplicationRecord
def set_notes_filter(filter_id, issuable)
# No need to update the column if the value is already set.
if filter_id && NOTES_FILTERS.values.include?(filter_id)
if filter_id && NOTES_FILTERS.value?(filter_id)
field = notes_filter_field_for(issuable)
self[field] = filter_id
......
......@@ -8,6 +8,13 @@
= f.label :allow_local_requests_from_hooks_and_services, class: 'form-check-label' do
Allow requests to the local network from hooks and services
.form-group
= f.label :outbound_local_requests_whitelist_raw, class: 'label-bold' do
= _('Whitelist to allow requests to the local network from hooks and services')
= f.text_area :outbound_local_requests_whitelist_raw, placeholder: "example.com, 192.168.1.1", class: 'form-control', rows: 8
%span.form-text.text-muted
= _('Requests to these domain(s)/address(es) on the local network will be allowed when local requests from hooks and services are disabled. IP ranges such as 1:0:0:0:0:0:0:0/124 or 127.0.0.0/28 are supported. Domain wildcards are not supported currently. Use comma, semicolon, or newline to separate multiple entries. The whitelist can hold a maximum of 4000 entries. Domains should use IDNA encoding. Ex: domain.com, 192.168.1.1, 127.0.0.0/28.')
.form-group
.form-check
= f.check_box :dns_rebinding_protection_enabled, class: 'form-check-input'
......
......@@ -15,4 +15,10 @@
AuthorizedKeysCommand. Click on the help icon for more details.
= link_to icon('question-circle'), help_page_path('administration/operations/fast_ssh_key_lookup')
.form-group
= f.label :raw_blob_request_limit, _('Raw blob request rate limit per minute'), class: 'label-bold'
= f.number_field :raw_blob_request_limit, class: 'form-control'
.form-text.text-muted
= _('Highest number of requests per minute for each raw path, default to 300. To disable throttling set to 0.')
= f.submit 'Save changes', class: "btn btn-success"
......@@ -5,7 +5,7 @@
.branch
= merge_path_description(@merge_request, 'to')
.author
Author #{@merge_request.author_name}
Author: #{@merge_request.author_name}
.assignee
= assignees_label(@merge_request)
.approvers
......
- local_assigns.fetch(:view)
%span.bold
%span{ title: _('Invoke Time'), data: { defer_to: "#{view.defer_key}-gc_time" } }...
\/
%span{ title: _('Invoke Count'), data: { defer_to: "#{view.defer_key}-invokes" } }...
gc
- local_assigns.fetch(:view)
%span.bold
%span{ data: { defer_to: "#{view.defer_key}-duration" } }...
\/
%span{ data: { defer_to: "#{view.defer_key}-calls" } }...
redis
- local_assigns.fetch(:view)
%span.bold
%span{ data: { defer_to: "#{view.defer_key}-duration" } }...
\/
%span{ data: { defer_to: "#{view.defer_key}-calls" } }...
sidekiq
......@@ -4,7 +4,7 @@
- page_title @tag.name, s_('TagsPage|Tags')
%div{ class: container_class }
.top-area.multi-line
.top-area.multi-line.flex-wrap
.nav-text
.title
%span.item-title.ref-name
......
- force_priority = local_assigns.fetch(:force_priority, false)
- subject_or_group_defined = defined?(@project) || defined?(@group)
- show_label_issues_link = subject_or_group_defined && show_label_issuables_link?(label, :issues, project: @project)
- show_label_merge_requests_link = subject_or_group_defined && show_label_issuables_link?(label, :merge_requests, project: @project)
- show_label_issues_link = subject_or_group_defined && show_label_issuables_link?(label, :issues)
- show_label_merge_requests_link = subject_or_group_defined && show_label_issuables_link?(label, :merge_requests)
.label-name
= render_label(label, tooltip: false)
......
---
title: Add Rate Request Limiter to RawController#show endpoint
merge_request: 30635
author:
type: added
---
title: Fix admin labels page when there are invalid records
merge_request: 30885
author:
type: fixed
---
title: Fixed distorted avatars when resource not reachable
merge_request: 30904
author: Marc Schwede
type: other
---
title: Fix tag page layout
merge_request:
author:
type: fixed
---
title: Use tablesample approximate counting by default.
merge_request: 31048
author:
type: performance
---
title: Add Outbound requests whitelist for local networks
merge_request: 30350
author: Istvan Szalai
type: added
---
title: Optimise import performance
merge_request: 31045
author:
type: performance
......@@ -400,6 +400,15 @@ production: &base
# path: shared/registry
# issuer: gitlab-issuer
# Add notification settings if you plan to use Geo Replication for the registry
# notifications:
# - name: geo_event
# url: https://example.com/api/v4/container_registry_event/events
# timeout: 2s
# threshold: 5
# backoff: 1s
# headers:
# Authorization: secret_phrase
## Error Reporting and Logging with Sentry
sentry:
......
......@@ -259,6 +259,7 @@ Settings.registry['key'] ||= nil
Settings.registry['issuer'] ||= nil
Settings.registry['host_port'] ||= [Settings.registry['host'], Settings.registry['port']].compact.join(':')
Settings.registry['path'] = Settings.absolute(Settings.registry['path'] || File.join(Settings.shared['path'], 'registry'))
Settings.registry['notifications'] ||= []
#
# Error Reporting and Logging with Sentry
......
# frozen_string_literal: true
class AddOutboundRequestsWhitelistToApplicationSettings < ActiveRecord::Migration[5.1]
DOWNTIME = false
def change
add_column :application_settings, :outbound_local_requests_whitelist, :string, array: true, limit: 255
end
end
......@@ -35,7 +35,7 @@ class AddIndexesForMergeRequestDiffsQuery < ActiveRecord::Migration[5.0]
end
def down
INDEX_SPECS.reverse.each do |spec|
INDEX_SPECS.reverse_each do |spec|
remove_concurrent_index(*spec)
end
end
......
# frozen_string_literal: true
class AddGeoContainerRepositoryUpdatedEventsTable < ActiveRecord::Migration[5.1]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def change
create_table :geo_container_repository_updated_events, force: :cascade do |t|
t.integer :container_repository_id, null: false
t.index :container_repository_id, name: :idx_geo_con_rep_updated_events_on_container_repository_id, using: :btree
end
add_column :geo_event_log, :container_repository_updated_event_id, :bigint
end
end
# frozen_string_literal: true
class AddIndexToGeoEventLog < ActiveRecord::Migration[5.1]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_concurrent_index :geo_event_log, :container_repository_updated_event_id
end
def down
remove_concurrent_index(:geo_event_log, :container_repository_updated_event_id)
end
end
# frozen_string_literal: true
class AddForeignKeysForContainerRepository < ActiveRecord::Migration[5.1]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_concurrent_foreign_key(:geo_container_repository_updated_events, :container_repositories, column: :container_repository_id, on_delete: :cascade)
add_concurrent_foreign_key(:geo_event_log, :geo_container_repository_updated_events, column: :container_repository_updated_event_id, on_delete: :cascade)
end
def down
if foreign_key_exists?(:geo_container_repository_updated_events, :container_repositories)
remove_foreign_key(:geo_container_repository_updated_events, :container_repositories)
end
if foreign_key_exists?(:geo_event_log, :geo_container_repository_updated_events)
remove_foreign_key(:geo_event_log, :geo_container_repository_updated_events)
end
end
end
# frozen_string_literal: true
class AddRawBlobRequestLimitToApplicationSettings < ActiveRecord::Migration[5.2]
DOWNTIME = false
def change
add_column :application_settings, :raw_blob_request_limit, :integer, default: 300, null: false
end
end
......@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2019_07_15_114644) do
ActiveRecord::Schema.define(version: 2019_07_15_142138) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_trgm"
......@@ -228,6 +228,8 @@ ActiveRecord::Schema.define(version: 2019_07_15_114644) do
t.boolean "lock_memberships_to_ldap", default: false, null: false
t.boolean "time_tracking_limit_to_hours", default: false, null: false
t.string "grafana_url", default: "/-/grafana", null: false
t.string "outbound_local_requests_whitelist", limit: 255, array: true
t.integer "raw_blob_request_limit", default: 300, null: false
t.index ["custom_project_templates_group_id"], name: "index_application_settings_on_custom_project_templates_group_id"
t.index ["file_template_project_id"], name: "index_application_settings_on_file_template_project_id"
t.index ["usage_stats_set_by_user_id"], name: "index_application_settings_on_usage_stats_set_by_user_id"
......@@ -1275,6 +1277,11 @@ ActiveRecord::Schema.define(version: 2019_07_15_114644) do
t.string "key", null: false
end
create_table "geo_container_repository_updated_events", force: :cascade do |t|
t.integer "container_repository_id", null: false
t.index ["container_repository_id"], name: "idx_geo_con_rep_updated_events_on_container_repository_id"
end
create_table "geo_event_log", force: :cascade do |t|
t.datetime "created_at", null: false
t.bigint "repository_updated_event_id"
......@@ -1289,7 +1296,9 @@ ActiveRecord::Schema.define(version: 2019_07_15_114644) do
t.bigint "job_artifact_deleted_event_id"
t.bigint "reset_checksum_event_id"
t.bigint "cache_invalidation_event_id"
t.bigint "container_repository_updated_event_id"
t.index ["cache_invalidation_event_id"], name: "index_geo_event_log_on_cache_invalidation_event_id", where: "(cache_invalidation_event_id IS NOT NULL)"
t.index ["container_repository_updated_event_id"], name: "index_geo_event_log_on_container_repository_updated_event_id"
t.index ["hashed_storage_attachments_event_id"], name: "index_geo_event_log_on_hashed_storage_attachments_event_id", where: "(hashed_storage_attachments_event_id IS NOT NULL)"
t.index ["hashed_storage_migrated_event_id"], name: "index_geo_event_log_on_hashed_storage_migrated_event_id", where: "(hashed_storage_migrated_event_id IS NOT NULL)"
t.index ["job_artifact_deleted_event_id"], name: "index_geo_event_log_on_job_artifact_deleted_event_id", where: "(job_artifact_deleted_event_id IS NOT NULL)"
......@@ -3700,7 +3709,9 @@ ActiveRecord::Schema.define(version: 2019_07_15_114644) do
add_foreign_key "fork_network_members", "projects", on_delete: :cascade
add_foreign_key "fork_networks", "projects", column: "root_project_id", name: "fk_e7b436b2b5", on_delete: :nullify
add_foreign_key "forked_project_links", "projects", column: "forked_to_project_id", name: "fk_434510edb0", on_delete: :cascade
add_foreign_key "geo_container_repository_updated_events", "container_repositories", name: "fk_212c89c706", on_delete: :cascade
add_foreign_key "geo_event_log", "geo_cache_invalidation_events", column: "cache_invalidation_event_id", name: "fk_42c3b54bed", on_delete: :cascade
add_foreign_key "geo_event_log", "geo_container_repository_updated_events", column: "container_repository_updated_event_id", name: "fk_6ada82d42a", on_delete: :cascade
add_foreign_key "geo_event_log", "geo_hashed_storage_migrated_events", column: "hashed_storage_migrated_event_id", name: "fk_27548c6db3", on_delete: :cascade
add_foreign_key "geo_event_log", "geo_job_artifact_deleted_events", column: "job_artifact_deleted_event_id", name: "fk_176d3fbb5d", on_delete: :cascade
add_foreign_key "geo_event_log", "geo_lfs_object_deleted_events", column: "lfs_object_deleted_event_id", name: "fk_d5af95fcd9", on_delete: :cascade
......
......@@ -17,7 +17,7 @@ You are encouraged to first read through all the steps before executing them
in your testing/production environment.
NOTE: **Note:**
**Do not** set up any custom authentication for the **secondary** nodes. This will be handled by the **primary** node.
**Do not** set up any custom authentication for the **secondary** nodes. This will be handled by the **primary** node.
Any change that requires access to the **Admin Area** needs to be done in the
**primary** node because the **secondary** node is a read-only replica.
......@@ -242,7 +242,7 @@ node's Geo Nodes dashboard in your browser.
![Geo dashboard](img/geo_node_dashboard.png)
If your installation isn't working properly, check the
[troubleshooting document].
[troubleshooting document](troubleshooting.md).
The two most obvious issues that can become apparent in the dashboard are:
......
......@@ -251,7 +251,7 @@ Omnibus is the following:
1. Check the steps about defining `postgresql['sql_user_password']`, `gitlab_rails['db_password']`.
1. Make sure `postgresql['max_replication_slots']` matches the number of **secondary** Geo nodes locations.
1. Install GitLab on the **secondary** server.
1. Re-run the [database replication process][database-replication].
1. Re-run the [database replication process](database.md#step-3-initiate-the-replication-process).
## Special update notes for 9.0.x
......
......@@ -209,7 +209,7 @@ ps auwx | grep unicorn | awk '{ print " -p " $2}' | xargs strace -tt -T -f -s 10
The output in `/tmp/unicorn.txt` may help diagnose the root cause.
# More information
## More information
- [Debugging Stuck Ruby Processes](https://blog.newrelic.com/2013/04/29/debugging-stuck-ruby-processes-what-to-do-before-you-kill-9/)
- [Cheatsheet of using gdb and ruby processes](gdb-stuck-ruby.txt)
......@@ -29,7 +29,7 @@ The following API resources are available in the project context:
| [Commits](commits.md) | `/projects/:id/repository/commits`, `/projects/:id/statuses` |
| [Container Registry](container_registry.md) | `/projects/:id/registry/repositories` |
| [Custom attributes](custom_attributes.md) | `/projects/:id/custom_attributes` (also available for groups and users) |
| [Dependencies](dependencies.md) **[ULTIMATE]** | `/projects/:id/dependencies`
| [Dependencies](dependencies.md) **(ULTIMATE)** | `/projects/:id/dependencies`
| [Deploy keys](deploy_keys.md) | `/projects/:id/deploy_keys` (also available standalone) |
| [Deployments](deployments.md) | `/projects/:id/deployments` |
| [Discussions](discussions.md) (threaded comments) | `/projects/:id/issues/.../discussions`, `/projects/:id/snippets/.../discussions`, `/projects/:id/merge_requests/.../discussions`, `/projects/:id/commits/.../discussions` (also available for groups) |
......
......@@ -10,7 +10,7 @@ If epics feature is not available a `403` status code will be returned.
The [epic issues API](epic_issues.md) allows you to interact with issues associated with an epic.
# Milestone dates integration
## Milestone dates integration
> [Introduced][ee-6448] in GitLab 11.3.
......
......@@ -39,6 +39,7 @@ Example response:
"session_expire_delay" : 10080,
"home_page_url" : null,
"default_snippet_visibility" : "private",
"outbound_local_requests_whitelist": [],
"domain_whitelist" : [],
"domain_blacklist_enabled" : false,
"domain_blacklist" : [],
......@@ -113,6 +114,7 @@ Example response:
"default_project_visibility": "internal",
"default_snippet_visibility": "private",
"default_group_visibility": "private",
"outbound_local_requests_whitelist": [],
"domain_whitelist": [],
"domain_blacklist_enabled" : false,
"domain_blacklist" : [],
......@@ -193,6 +195,7 @@ are listed in the descriptions of the relevant settings.
| `domain_blacklist` | array of strings | required by: `domain_blacklist_enabled` | Users with e-mail addresses that match these domain(s) will NOT be able to sign-up. Wildcards allowed. Use separate lines for multiple entries. Ex: `domain.com`, `*.domain.com`. |
| `domain_blacklist_enabled` | boolean | no | (**If enabled, requires:** `domain_blacklist`) Allows blocking sign-ups from emails from specific domains. |
| `domain_whitelist` | array of strings | no | Force people to use only corporate emails for sign-up. Default is `null`, meaning there is no restriction. |
| `outbound_local_requests_whitelist` | array of strings | no | Define a list of trusted domains or ip addresses to which local requests are allowed when local requests for hooks and services are disabled.
| `dsa_key_restriction` | integer | no | The minimum allowed bit length of an uploaded DSA key. Default is `0` (no restriction). `-1` disables DSA keys. |
| `ecdsa_key_restriction` | integer | no | The minimum allowed curve size (in bits) of an uploaded ECDSA key. Default is `0` (no restriction). `-1` disables ECDSA keys. |
| `ed25519_key_restriction` | integer | no | The minimum allowed curve size (in bits) of an uploaded ED25519 key. Default is `0` (no restriction). `-1` disables ED25519 keys. |
......
......@@ -345,7 +345,7 @@ GitLab provides API endpoints to:
- [Triggering pipelines through the API](triggers/README.md).
- [Pipeline triggers API](../api/pipeline_triggers.md).
### Start multiple manual actions in a stage
### Start multiple manual actions in a stage
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/27188) in GitLab 11.11.
......
......@@ -69,7 +69,7 @@ curl http://localhost:3000/-/chaos/leakmem?memory_mb=1024&duration_s=10&token=se
This endpoint attempts to fully utilise a single core, at 100%, for the given period.
Depending on your rack server setup, your request may timeout after a predermined period (normally 60 seconds).
Depending on your rack server setup, your request may timeout after a predetermined period (normally 60 seconds).
If you're using Unicorn, this is done by killing the worker process.
```
......@@ -80,7 +80,7 @@ GET /-/chaos/cpu_spin?duration_s=50&async=true
| Attribute | Type | Required | Description |
| ------------ | ------- | -------- | --------------------------------------------------------------------- |
| `duration_s` | integer | no | Duration, in seconds, that the core will be utilised. Defaults to 30s |
| `duration_s` | integer | no | Duration, in seconds, that the core will be utilized. Defaults to 30s |
| `async` | boolean | no | Set to true to consume CPU in a Sidekiq background worker process |
```bash
......@@ -93,7 +93,7 @@ curl http://localhost:3000/-/chaos/cpu_spin?duration_s=60&token=secret
This endpoint attempts to fully utilise a single core, and interleave it with DB request, for the given period.
This endpoint can be used to model yielding execution to another threads when running concurrently.
Depending on your rack server setup, your request may timeout after a predermined period (normally 60 seconds).
Depending on your rack server setup, your request may timeout after a predetermined period (normally 60 seconds).
If you're using Unicorn, this is done by killing the worker process.
```
......@@ -105,7 +105,7 @@ GET /-/chaos/db_spin?duration_s=50&async=true
| Attribute | Type | Required | Description |
| ------------ | ------- | -------- | --------------------------------------------------------------------------- |
| `interval_s` | float | no | Interval, in seconds, for every DB request. Defaults to 1s |
| `duration_s` | integer | no | Duration, in seconds, that the core will be utilised. Defaults to 30s |
| `duration_s` | integer | no | Duration, in seconds, that the core will be utilized. Defaults to 30s |
| `async` | boolean | no | Set to true to perform the operation in a Sidekiq background worker process |
```bash
......
### Community members & roles
# Community members & roles
GitLab community members and their privileges/responsibilities.
......
# Axios
We use [axios][axios] to communicate with the server in Vue applications and most new code.
In order to guarantee all defaults are set you *should not use `axios` directly*, you should import `axios` from `axios_utils`.
## CSRF token
All our request require a CSRF token.
To guarantee this token is set, we are importing [axios][axios], setting the token, and exporting `axios` .
This exported module should be used instead of directly using `axios` to ensure the token is set.
## Usage
```javascript
import axios from './lib/utils/axios_utils';
......
......@@ -212,6 +212,7 @@ selectors are intended for use only with JavaScript to allow for removal or
renaming without breaking styling.
### IDs
Don't use ID selectors in CSS.
```scss
......
......@@ -173,6 +173,45 @@ func Test(t *testing.T) {
}
```
### Table-Driven Tests
Using [Table-Driven Tests](https://github.com/golang/go/wiki/TableDrivenTests)
is generally good practice when you have multiple entries of
inputs/outputs for the same function. Below are some guidelines one can
follow when writing table-driven test. These guidelines are mostly
extracted from Go standard library source code. Keep in mind it's OK not
to follow these guidelines when it makes sense.
#### Defining test cases
Each table entry is a complete test case with inputs and expected
results, and sometimes with additional information such as a test name
to make the test output easily readable.
- [Define a slice of anonymous struct](https://github.com/golang/go/blob/50bd1c4d4eb4fac8ddeb5f063c099daccfb71b26/src/encoding/csv/reader_test.go#L16)
inside of the test.
- [Define a slice of anonymous struct](https://github.com/golang/go/blob/55d31e16c12c38d36811bdee65ac1f7772148250/src/cmd/go/internal/module/module_test.go#L9-L66)
outside of the test.
- [Named structs](https://github.com/golang/go/blob/2e0cd2aef5924e48e1ceb74e3d52e76c56dd34cc/src/cmd/go/internal/modfetch/coderepo_test.go#L54-L69)
for code reuse.
- [Using `map[string]struct{}`](https://github.com/golang/go/blob/6d5caf38e37bf9aeba3291f1f0b0081f934b1187/src/cmd/trace/annotations_test.go#L180-L235).
#### Contents of the test case
- Ideally, each test case should have a field with a unique identifier
to use for naming subtests. In the Go standard library, this is commonly the
`name string` field.
- Use `want`/`expect`/`actual` when you are specifcing something in the
test case that will be used for assertion.
#### Variable names
- Each table-driven test map/slice of struct can be named `tests`.
- When looping through `tests` the anonymous struct can be referred
to as `tt` or `tc`.
- The description of the test can be referred to as
`name`/`testName`/`tn`.
### Benchmarks
Programs handling a lot of IO or complex operations should always include
......
## Modules with instance variables could be considered harmful
# Modules with instance variables could be considered harmful
### Background
## Background
Rails somehow encourages people using modules and instance variables
everywhere. For example, using instance variables in the controllers,
......@@ -9,7 +9,7 @@ helpers, and views. They're also encouraging the use of
saving everything in a giant, single object, and people could access
everything in that one giant object.
### The problems
## The problems
Of course this is convenient to develop, because we just have everything
within reach. However this has a number of downsides when that chosen object
......@@ -24,7 +24,7 @@ manipulated from 3 different modules. It's hard to track when those variables
start giving us troubles. We don't know which module would suddenly change
one of the variables. Everything could touch anything.
### Similar concerns
## Similar concerns
People are saying multiple inheritance is bad. Mixing multiple modules with
multiple instance variables scattering everywhere suffer from the same issue.
......@@ -40,7 +40,7 @@ Note that `included` doesn't solve the whole issue. They define the
dependencies, but they still allow each modules to talk implicitly via the
instance variables in the final giant object, and that's where the problem is.
### Solutions
## Solutions
We should split the giant object into multiple objects, and they communicate
with each other with the API, i.e. public methods. In short, composition over
......@@ -53,7 +53,7 @@ With clearly defined API, this would make things less coupled and much easier
to debug and track, and much more extensible for other objects to use, because
they communicate in a clear way, rather than implicit dependencies.
### Acceptable use
## Acceptable use
However, it's not always bad to use instance variables in a module,
as long as it's contained in the same module; that is, no other modules or
......@@ -74,7 +74,7 @@ Unfortunately it's not easy to code more complex rules into the cop, so
we rely on people's best judgement. If we could find another good pattern
we could easily add to the cop, we should do it.
### How to rewrite and avoid disabling this cop
## How to rewrite and avoid disabling this cop
Even if we could just disable the cop, we should avoid doing so. Some code
could be easily rewritten in simple form. Consider this acceptable method:
......@@ -181,7 +181,7 @@ rather than whatever includes the module, and those modules which were also
included, making it much easier to track down any issues,
and reducing the chance of having name conflicts.
### How to disable this cop
## How to disable this cop
Put the disabling comment right after your code in the same line:
......@@ -210,14 +210,14 @@ end
Note that you need to enable it at some point, otherwise everything below
won't be checked.
### Things we might need to ignore right now
## Things we might need to ignore right now
Because of the way Rails helpers and mailers work, we might not be able to
avoid the use of instance variables there. For those cases, we could ignore
them at the moment. At least we're not going to share those modules with
other random objects, so they're still somewhat isolated.
### Instance variables in views
## Instance variables in views
They're bad because we can't easily tell who's using the instance variables
(from controller's point of view) and where we set them up (from partials'
......
......@@ -15,6 +15,18 @@ Exceptions are made for some tools that we require in the
`gitlab:assets:compile` CI job such as `webpack-bundle-analyzer` to analyze our
production assets post-compile.
To add or upgrade a dependency, run:
```sh
yarn add <your dependency here>
```
This may introduce duplicate dependencies. To de-duplicate `yarn.lock`, run:
```sh
node_modules/.bin/yarn-deduplicate --list --strategy fewer yarn.lock && yarn install
```
---
> TODO: Add Dependencies
# Accessiblity
Using semantic HTML plays a key role when it comes to accessibility.
## Accessible Rich Internet Applications - ARIA
WAI-ARIA, the Accessible Rich Internet Applications specification, defines a way to make Web content and Web applications more accessible to people with disabilities.
> Note: It is [recommended][using-aria] to use semantic elements as the primary method to achieve accessibility rather than adding aria attributes. Adding aria attributes should be seen as a secondary method for creating accessible elements.
### Role
The `role` attribute describes the role the element plays in the context of the document.
Check the list of WAI-ARIA roles [here][roles]
## Icons
When using icons or images that aren't absolutely needed to understand the context, we should use `aria-hidden="true"`.
On the other hand, if an icon is crucial to understand the context we should do one of the following:
......@@ -20,6 +24,7 @@ On the other hand, if an icon is crucial to understand the context we should do
1. Use `aria-labelledby` to point to an element that contains the explanation for that icon
## Form inputs
In forms we should use the `for` attribute in the label statement:
```
......
......@@ -8,7 +8,7 @@
## [Vue style guide](vue.md)
# Tooling
## Tooling
## [Prettier](prettier.md)
......
......@@ -14,13 +14,13 @@ on maintainability, the ability to easily debug problems, or even performance.
An example would be to use `ProjectsFinder` in `IssuesFinder` to limit issues to
those belonging to a set of projects. While initially this may seem like a good
idea, both classes provide a very high level interface with very little control.
This means that `IssuesFinder` may not be able to produce a better optimised
This means that `IssuesFinder` may not be able to produce a better optimized
database query, as a large portion of the query is controlled by the internals
of `ProjectsFinder`.
To work around this problem, you would use the same code used by
`ProjectsFinder`, instead of using `ProjectsFinder` itself directly. This allows
you to compose your behaviour better, giving you more control over the behaviour
you to compose your behavior better, giving you more control over the behavior
of the code.
To illustrate, consider the following code from `IssuableFinder#projects`:
......@@ -52,7 +52,7 @@ functionality is added to this (high level) interface. Instead of _only_
affecting the cases where this is necessary, it may also start affecting
`IssuableFinder` in a negative way. For example, the query produced by
`GroupProjectsFinder` may include unnecessary conditions. Since we're using a
finder here, we can't easily opt-out of that behaviour. We could add options to
finder here, we can't easily opt-out of that behavior. We could add options to
do so, but then we'd need as many options as we have features. Every option adds
two code paths, which means that for four features we have to cover 8 different
code paths.
......@@ -213,6 +213,5 @@ The API provided by Active Record itself, such as the `where` method, `save`,
Everything in `app/workers`.
The scheduling of Sidekiq jobs using `SomeWorker.perform_async`, `perform_in`,
etc. Directly invoking a worker using `SomeWorker.new.perform` should be avoided
at all times in application code, though this is fine to use in tests.
Use `SomeWorker.perform_async` or `SomeWorker.perform_in` to schedule Sidekiq
jobs. Never directly invoke a worker using `SomeWorker.new.perform`.
......@@ -25,7 +25,7 @@ else
end
```
# Read-only database
## Read-only database
The database can be used in read-only mode. In this case we have to
make sure all GET requests don't attempt any write operations to the
......
---
redirect_to: '../project_services/slack.md'
redirect_to: '../user/project/integrations/slack.md'
---
This document was moved to [project_services/slack.md](../project_services/slack.md).
This document was moved to [project_services/slack.md](../user/project/integrations/slack.md).
This diff is collapsed.
# Abuse reports
Report abuse from users to GitLab administrators.
You can report abuse from other GitLab users to GitLab administrators.
A GitLab administrator [can then choose](admin_area/abuse_reports.md) to:
- Remove the user, which deletes them from the instance.
- Block the user, which denies them access to the instance.
- Or remove the report, which retains the users access to the instance.
You can report a user through their:
......@@ -12,7 +18,8 @@ You can report a user through their:
To report abuse from a user's profile page:
1. Click on the exclamation point report abuse button at the top right of the user's profile.
1. Click on the exclamation point report abuse button at the top right of the
user's profile.
1. Complete an abuse report.
1. Click the **Send report** button.
......@@ -26,15 +33,18 @@ To report abuse from a user's comment:
1. Click the **Send report** button.
NOTE: **Note:**
A URL to the reported user's comment will be
pre-filled in the abuse report's **Message** field.
A URL to the reported user's comment will be pre-filled in the abuse report's
**Message** field.
## Reporting abuse through a user's issue or merge request
The **Report abuse** button is displayed at the top right of the issue or merge request:
- When **Report abuse** is selected from the menu that appears when the **Close issue** or **Close merge request** button is clicked, for users that have permission to close the issue or merge request.
- When viewing the issue or merge request, for users that don't have permission to close the issue or merge request.
- When **Report abuse** is selected from the menu that appears when the
**Close issue** or **Close merge request** button is clicked, for users that
have permission to close the issue or merge request.
- When viewing the issue or merge request, for users that don't have permission
to close the issue or merge request.
With the **Report abuse** button displayed, to submit an abuse report:
......
......@@ -2,30 +2,60 @@
View and resolve abuse reports from GitLab users.
Admins can view abuse reports in the admin area and are able to
resolve the reports by removing the reported user, blocking the reported user, or removing the report.
GitLab administrators can view and [resolve](#resolving-abuse-reports) abuse
reports in the Admin Area.
## Reporting abuse
To find out more about reporting abuse, see [abuse reports user documentation](../abuse_reports.md).
To find out more about reporting abuse, see [abuse reports user
documentation](../abuse_reports.md).
## Resolving abuse reports
To access abuse reports, go to **Admin area > Abuse Reports**.
To access abuse reports, go to **Admin Area > Abuse Reports**.
There are 3 ways to resolve an abuse report, with a button for each method:
- Remove user & report: [Deletes the reported user](../profile/account/delete_account.md) from the instance and removes the abuse report from the list.
- Block user: Blocks the reported user from the instance and does not remove the abuse report from the list.
- Remove report: Removes the abuse report from the list and does not restrict the access for the reported user.
- Remove user & report. This will:
- [Delete the reported user](../profile/account/delete_account.md) from the
instance.
- Remove the abuse report from the list.
- [Block user](#blocking-users).
- Remove report. This will:
- Remove the abuse report from the list.
- Remove access restrictions for the reported user.
The following is an example of the **Abuse Reports** page:
![abuse-reports-page-image](img/abuse_reports_page.png)
## Blocked users
### Blocking users
A blocked user cannot log in or access any repositories, but all of their data
remains.
Blocking a user:
- Leaves them in the abuse report list.
- Changes the **Block user** button to a disabled **Already blocked** button.
Blocking a user will not remove the abuse report from the list.
The user will be notified with the
[following message](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/app/workers/email_receiver_worker.rb#L38):
Instead, the block button will be disabled and explain that the user is "Already blocked".
You are still able to remove the user and/or report if necessary.
```text
Your account has been blocked. If you believe this is in error, contact a staff member.
```
After blocking, you can still either:
- Remove the user and report if necessary.
- Remove the report.
The following is an example of a blocked user listed on the **Abuse Reports**
page:
![abuse-report-blocked-user-image](img/abuse_report_blocked_user.png)
NOTE: **Note:**
Users can be [blocked](../../api/users.md#block-user) and
[unblocked](../../api/users.md#unblock-user) using the GitLab API.
......@@ -52,8 +52,8 @@ You can view the exact JSON payload in the administration panel. To view the pay
1. Expand **Settings** in the left sidebar and click on **Metrics and profiling**.
1. Expand **Usage statistics** and click on the **Preview payload** button.
You can see how [the usage ping data maps to different stages of the product](https://gitlab.com/gitlab-data/analytics/blob/master/transform/snowflake-dbt/data/ping_metrics_to_stage_mapping_data.csv).
You can see how [the usage ping data maps to different stages of the product](https://gitlab.com/gitlab-data/analytics/blob/master/transform/snowflake-dbt/data/ping_metrics_to_stage_mapping_data.csv).
### Deactivate the usage ping
The usage ping is opt-out. If you want to deactivate this feature, go to
......
......@@ -331,7 +331,7 @@ project's dependencies with their versions. This list can be generated only for
[languages and package managers](#supported-languages-and-package-managers)
supported by Gemnasium.
To see the generated dependency list, navigate to your project's **Project > Dependency List**.
To see the generated dependency list, navigate to your project's **Security & Compliance > Dependency List**.
## Versioning and release process
......
......@@ -49,7 +49,7 @@ The group Security Dashboard gives an overview of the vulnerabilities of all the
projects in a group and its subgroups.
First, navigate to the Security Dashboard found under your group's
**Overview > Security Dashboard**.
**Security** tab.
Once you're on the dashboard, at the top you should see a series of filters for:
......@@ -58,7 +58,7 @@ Once you're on the dashboard, at the top you should see a series of filters for:
- Report type
- Project
![dashboard with action buttons and metrics](img/dashboard.png)
![dashboard with action buttons and metrics](img/group_security_dashboard.png)
Selecting one or more filters will filter the results in this page.
The first section is an overview of all the vulnerabilities, grouped by severity.
......
......@@ -45,7 +45,7 @@ The following identity providers are supported:
Feature.enable(:group_scim, group)
```
### GitLab configuration
## GitLab configuration
Once [Single sign-on](index.md) has been configured, we can:
......@@ -55,41 +55,48 @@ Once [Single sign-on](index.md) has been configured, we can:
![SCIM token configuration](img/scim_token.png)
## SCIM IdP configuration
## Identity Provider configuration
### Configuration on Azure
### Azure
In the [Single sign-on](index.md) configuration for the group, make sure
that the **Name identifier value** (NameID) points to a unique identifier, such
as the `user.objectid`. This will match the `extern_uid` used on GitLab.
First, double check the [Single sign-on](index.md) configuration for your group and ensure that **Name identifier value** (NameID) points to `user.objectid` or another unique identifier. This will match the `extern_uid` used on GitLab.
The GitLab app in Azure needs to be configured following
[Azure's SCIM setup](https://docs.microsoft.com/en-us/azure/active-directory/manage-apps/use-scim-to-provision-users-and-groups#getting-started).
![Name identifier value mapping](img/scim_name_identifier_mapping.png)
Note the following:
#### Set up admin credentials
Next, configure your GitLab application in Azure by following the
[Provisioning users and groups to applications that support SCIM](https://docs.microsoft.com/en-us/azure/active-directory/manage-apps/use-scim-to-provision-users-and-groups#provisioning-users-and-groups-to-applications-that-support-scim)
section in Azure's SCIM setup documentation.
During this configuration, note the following:
- The `Tenant URL` and `secret token` are the ones retrieved in the
[previous step](#gitlab-configuration).
- Should there be any problems with the availability of GitLab or similar
errors, the notification email set will get those.
- It is recommended to set a notification email and check the **Send an email notification when a failure occurs** checkbox.
- For mappings, we will only leave `Synchronize Azure Active Directory Users to AppName` enabled.
You can then test the connection clicking on `Test Connection`.
You can then test the connection by clicking on **Test Connection**. If the connection is successful, be sure to save your configuration before moving on.
### Synchronize Azure Active Directory users
#### Configure attribute mapping
1. Click on `Synchronize Azure Active Directory Users to AppName`, to configure
the attribute mapping.
1. Select the unique identifier (in the example `objectId`) as the `id` and `externalId`,
and enable the `Create`, `Update`, and `Delete` actions.
1. Map the `userPricipalName` to `emails[type eq "work"].value` and `mailNickname` to
`userName`.
1. Click on `Synchronize Azure Active Directory Users to AppName`, to configure the attribute mapping.
1. Click **Delete** next to the `mail` mapping.
1. Map `userPrincipalName` to `emails[type eq "work"].value` and change it's **Matching precedence** to `2`.
1. Map `mailNickname` to `userName`.
1. Create a new mapping by clicking **Add New Mapping** then set **Source attribute** to `objectId`, **Target attribute** to `id`, **Match objects using this attribute** to `Yes`, and **Matching precedence** to `1`.
1. Create a new mapping by clicking **Add New Mapping** then set **Source attribute** to `objectId`, and **Target attribute** to `externalId`.
1. Click the `userPrincipalName` mapping and change **Match objects using this attribute** to `No`.
Example configuration:
Save your changes and you should have the following configuration:
![Azure's attribute mapping configuration](img/scim_attribute_mapping.png)
1. Click on **Show advanced options > Edit attribute list for AppName**.
NOTE: **Note:** If you used a unique identifier **other than** `objectId`, be sure to map it instead to both `id` and `externalId`.
1. Below the mapping list click on **Show advanced options > Edit attribute list for AppName**.
1. Leave the `id` as the primary and only required field.
NOTE: **Note:**
......@@ -99,12 +106,14 @@ You can then test the connection clicking on `Test Connection`.
![Azure's attribute advanced configuration](img/scim_advanced.png)
1. Save all the screens and, in the **Provisioning** step, set
the `Provisioning Status` to `ON`.
the `Provisioning Status` to `On`.
![Provisioning status toggle switch](img/scim_provisioning_status.png)
NOTE: **Note:**
You can control what is actually synced by selecting the `Scope`. For example,
`Sync only assigned users and groups` will only sync the users assigned to
the application (`Users and groups`), otherwise it will sync the whole Active Directory.
the application (`Users and groups`), otherwise, it will sync the whole Active Directory.
Once enabled, the synchronization details and any errors will appear on the
bottom of the **Provisioning** screen, together with a link to the audit logs.
......
......@@ -136,26 +136,26 @@ Supported formats (named colors are not supported):
Color written inside backticks will be followed by a color "chip":
```markdown
`#F00`
`#F00A`
`#FF0000`
`#FF0000AA`
`RGB(0,255,0)`
`RGB(0%,100%,0%)`
`RGBA(0,255,0,0.3)`
`HSL(540,70%,50%)`
`HSLA(540,70%,50%,0.3)`
```
`#F00`
`#F00A`
`#FF0000`
`#FF0000AA`
`RGB(0,255,0)`
`RGB(0%,100%,0%)`
`RGBA(0,255,0,0.3)`
`HSL(540,70%,50%)`
`HSLA(540,70%,50%,0.3)`
`#F00`
`#F00A`
`#FF0000`
`#FF0000AA`
`RGB(0,255,0)`
`RGB(0%,100%,0%)`
`RGBA(0,255,0,0.3)`
`HSL(540,70%,50%)`
`HSLA(540,70%,50%,0.3)`
```
`#F00`
`#F00A`
`#FF0000`
`#FF0000AA`
`RGB(0,255,0)`
`RGB(0%,100%,0%)`
`RGBA(0,255,0,0.3)`
`HSL(540,70%,50%)`
`HSLA(540,70%,50%,0.3)`
### Diagrams and flowcharts using Mermaid
......@@ -397,6 +397,7 @@ unordered or ordered lists:
- [ ] Sub-task 1
- [x] Sub-task 2
- [ ] Sub-task 3
1. [x] Completed task
1. [ ] Incomplete task
1. [ ] Sub-task 1
......@@ -408,6 +409,7 @@ unordered or ordered lists:
- [ ] Sub-task 1
- [x] Sub-task 2
- [ ] Sub-task 3
1. [x] Completed task
1. [ ] Incomplete task
1. [ ] Sub-task 1
......@@ -976,7 +978,7 @@ after the `</summary>` tag and before the `</details>` tag, as shown in the exam
These details _will_ remain **hidden** until expanded.
PASTE LOGS HERE
PASTE LOGS HERE
</details>
```
......@@ -988,7 +990,7 @@ These details _will_ remain **hidden** until expanded.
These details <em>will</em> remain <b>hidden</b> until expanded.
PASTE LOGS HERE
PASTE LOGS HERE
</details>
......@@ -1047,14 +1049,14 @@ A new line due to the previous backslash.
First paragraph.
Another line in the same paragraph.
A third line in the same paragraph, but this time ending with two spaces.
A third line in the same paragraph, but this time ending with two spaces.
A new line directly under the first paragraph.
<!-- (Do *NOT* remove the two ending whitespaces in the second line) -->
<!-- (They are needed for the Markdown text to render correctly on docs.gitlab.com, the backslash works fine inside GitLab itself) -->
Second paragraph.
Another line, this time ending with a backslash.
Another line, this time ending with a backslash.
A new line due to the previous backslash.
### Links
......@@ -1135,13 +1137,13 @@ GFM will autolink almost any URL you put into your text:
### Lists
Ordered and unordered lists can be easily created. Add the number you want the list
to start with, like `1. ` (with a space) at the start of each line for ordered lists.
to start with, like `1.`, followed by a space, at the start of each line for ordered lists.
After the first number, it does not matter what number you use, ordered lists will be
numbered automatically by vertical order, so repeating `1. ` for all items in the
same list is common. If you start with a number other than `1. `, it will use that as the first
numbered automatically by vertical order, so repeating `1.` for all items in the
same list is common. If you start with a number other than `1.`, it will use that as the first
number, and count up from there.
Add a `* `, `- ` or `+ ` (with a space) at the start of each line for unordered lists, but
Add a `*`, `-` or `+`, followed by a space, at the start of each line for unordered lists, but
you should not use a mix of them.
Examples:
......@@ -1156,7 +1158,9 @@ Examples:
4. And another item.
* Unordered lists can use asterisks
- Or minuses
+ Or pluses
```
......@@ -1170,9 +1174,11 @@ Examples:
1. Next ordered sub-list item
1. And another item.
* Unordered lists can use asterisks
- Unordered lists can use asterisks
- Or minuses
+ Or pluses
- Or pluses
---
......
......@@ -47,6 +47,7 @@ The following table depicts the various user permission levels in a project.
| View approved/blacklisted licenses **(ULTIMATE)** | ✓ | ✓ | ✓ | ✓ | ✓ |
| View license management reports **(ULTIMATE)** | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ |
| View Security reports **(ULTIMATE)** | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ |
| View [Design Management](project/issues/design_management.md) pages **(PREMIUM)** | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ |
| View project code | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ |
| Pull project code | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ |
| View GitLab Pages protected by [access control](project/pages/introduction.md#gitlab-pages-access-control-core-only) | ✓ | ✓ | ✓ | ✓ | ✓ |
......@@ -74,6 +75,7 @@ The following table depicts the various user permission levels in a project.
| View Error Tracking list | | ✓ | ✓ | ✓ | ✓ |
| Pull from [Maven repository](project/packages/maven_repository.md) or [NPM registry](project/packages/npm_registry.md) **(PREMIUM)** | | ✓ | ✓ | ✓ | ✓ |
| Publish to [Maven repository](project/packages/maven_repository.md) or [NPM registry](project/packages/npm_registry.md) **(PREMIUM)** | | | ✓ | ✓ | ✓ ||
| Upload [Design Management](project/issues/design_management.md) files **(PREMIUM)** | | | ✓ | ✓ | ✓ |
| Create new branches | | | ✓ | ✓ | ✓ |
| Push to non-protected branches | | | ✓ | ✓ | ✓ |
| Force push to non-protected branches | | | ✓ | ✓ | ✓ |
......
# Deleting a User Account
# Deleting a User account
Users can be deleted from a GitLab instance, either by:
- The user themselves.
- An administrator.
NOTE: **Note:**
Deleting a user will delete all projects in that user namespace.
- As a user, you can delete your own account by navigating to **Settings** > **Account** and selecting **Delete account**
- As an admin, you can delete a user account by navigating to the **Admin Area**, selecting the **Users** tab, selecting a user, and clicking on **Delete user**
## As a user
As a user, you can delete your own account by:
1. Clicking on your avatar.
1. Navigating to **Settings > Account**.
1. Selecting **Delete account**.
## As an administrator
As an administrator, you can delete a user account by:
1. Navigating to **Admin Area > Overview > Users**.
1. Selecting a user.
1. Under the **Account** tab, clicking:
- **Delete user** to delete only the user but maintaining their
[associated records](#associated-records).
- **Delete user and contributions** to delete the user and
their associated records.
### Blocking a user
In addition to blocking a user
[via an abuse report](../../admin_area/abuse_reports.md#blocking-users),
a user can be blocked directly from the Admin area. To do this:
1. Navigate to **Admin Area > Overview > Users**.
1. Selecting a user.
1. Under the **Account** tab, click **Block user**.
## Associated Records
> Introduced for issues in [GitLab 9.0][ce-7393], and for merge requests, award
emoji, notes, and abuse reports in [GitLab 9.1][ce-10467].
Hard deletion from abuse reports and spam logs was introduced in
[GitLab 9.1][ce-10273], and from the API in [GitLab 9.3][ce-11853].
> - Introduced for issues in
> [GitLab 9.0](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7393).
> - Introduced for merge requests, award emoji, notes, and abuse reports in
> [GitLab 9.1](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/10467).
> - Hard deletion from abuse reports and spam logs was introduced in
> [GitLab 9.1](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/10273),
> and from the API in
> [GitLab 9.3](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/11853).
When a user account is deleted, not all associated records are deleted with it.
Here's a list of things that will **not** be deleted:
- Issues that the user created
- Merge requests that the user created
- Notes that the user created
- Abuse reports that the user reported
- Award emoji that the user created
- Issues that the user created.
- Merge requests that the user created.
- Notes that the user created.
- Abuse reports that the user reported.
- Award emoji that the user created.
Instead of being deleted, these records will be moved to a system-wide
user with the username "Ghost User", whose sole purpose is to act as a container for such records. Any commits made by a deleted user will still display the username of the original user.
When a user is deleted from an [abuse report](../../admin_area/abuse_reports.md) or spam log, these associated
records are not ghosted and will be removed, along with any groups the user
is a sole owner of. Administrators can also request this behaviour when
deleting users from the [API](../../../api/users.md#user-deletion) or the
admin area.
[ce-7393]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7393
[ce-10273]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/10273
[ce-10467]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/10467
[ce-11853]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/11853
user with the username "Ghost User", whose sole purpose is to act as a container
for such records. Any commits made by a deleted user will still display the
username of the original user.
When a user is deleted from an [abuse report](../../admin_area/abuse_reports.md)
or spam log, these associated records are not ghosted and will be removed, along
with any groups the user is a sole owner of.
Administrators can also request this behavior when deleting users from the
[API](../../../api/users.md#user-deletion) or the Admin Area.
......@@ -253,7 +253,7 @@ With RBAC disabled and services deployed,
[Auto DevOps](../../../../topics/autodevops/index.md) can now be leveraged
to build, test, and deploy the app.
[Enable Auto DevOps](../../../../topics/autodevops/index.md#enablingdisabling-auto-devops-at-the-project-level)
[Enable Auto DevOps](../../../../topics/autodevops/index.md#at-the-project-level)
if not already enabled. If a wildcard DNS entry was created resolving to the
Load Balancer, enter it in the `domain` field under the Auto DevOps settings.
Otherwise, the deployed app will not be externally available outside of the cluster.
......
# Monitoring HAProxy
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/12621) in GitLab 9.4
GitLab has support for automatically detecting and monitoring HAProxy. This is provided by leveraging the [HAProxy Exporter](https://github.com/prometheus/haproxy_exporter), which translates HAProxy statistics into a Prometheus readable form.
......
# Design Management **(PREMIUM)**
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/660) in [GitLab Premium](https://about.gitlab.com/pricing/) 12.2.
CAUTION: **Warning:**
This an __alpha__ feature and is subject to change at any time without
prior notice.
## Overview
Design Management allows you to upload design assets (wireframes, mockups, etc.)
to GitLab issues and keep them stored in one single place, accessed by the Design
Management's page within an issue, giving product designers, product managers, and engineers a
way to collaborate on designs over one single source of truth.
You can easily share mock-ups of designs with your team, or visual regressions can be easily
viewed and addressed.
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
For an overview, see the video [Design Management (GitLab 12.2)](https://www.youtube.com/watch?v=CCMtCqdK_aM).
## Requirements
Design Management requires
[Large File Storage (LFS)](../../../workflow/lfs/manage_large_binaries_with_git_lfs.md)
to be enabled:
- For GitLab.com, LFS is already enabled.
- For self-managed instances, a GitLab administrator must have
[enabled LFS globally](../../../workflow/lfs/lfs_administration.md).
- For both GitLab.com and self-managed instances: LFS must be enabled for the project itself.
If enabled globally, LFS will be enabled by default to all projects. To enable LFS on the
project level, navigate to your project's **Settings > General**, expand **Visibility, project features, permissions**
and enable **Git Large File Storage**.
## Limitations
- Files uploaded must have a file extension of either `png`, `jpg`, `jpeg`, `gif`, `bmp`, `tiff` or `ico`. The [`svg` extension is not yet supported](https://gitlab.com/gitlab-org/gitlab-ee/issues/12771).
- [Designs cannot yet be deleted](https://gitlab.com/gitlab-org/gitlab-ee/issues/11089).
- Design Management is [not yet supported in the project export](https://gitlab.com/gitlab-org/gitlab-ee/issues/11090).
## The Design Management page
Navigate to the **Design Management** page from any issue by clicking the **Designs** tab:
![Designs tab](img/design_management_v12_2.png)
## Adding designs
To upload design images, click the **Upload Designs** button and select images to upload.
Designs with the same filename as an existing uploaded design will create a new version
of the design, and will replace the previous version.
## Viewing designs
Images on the Design Management page can be enlarged by clicking on them.
......@@ -120,6 +120,12 @@ associated label or assignee will change to match that of the new column. The en
board can also be filtered to only include issues from a certain milestone or an overarching
label.
### Design Management **(PREMIUM)**
With [Design Management](design_management.md), you can upload design
assets to issues and view them all together to easily share and
collaborate with your team.
### Epics **(ULTIMATE)**
[Epics](../../group/epics/index.md) let you manage your portfolio of projects more
......
......@@ -9,8 +9,18 @@ in [GitLab Starter](https://about.gitlab.com/pricing/) 9.3.
With the help of [GitLab CI/CD](../../../ci/README.md), you can analyze your
source code quality using GitLab Code Quality.
Code Quality uses [Code Climate Engines](https://codeclimate.com), which are
free and open source. Code Quality doesn't require a Code Climate subscription.
Code Quality:
- Uses [Code Climate Engines](https://codeclimate.com), which are
free and open source. Code Quality doesn't require a Code Climate
subscription.
- Runs in [pipelines](../../../ci/pipelines.md) using an Docker image built in
[GitLab Code
Quality](https://gitlab.com/gitlab-org/security-products/codequality) project.
- Can make use of a [template](#template-and-examples).
- Is available with [Auto
DevOps](../../../topics/autodevops/index.md#auto-code-quality-starter).
Going a step further, GitLab can show the Code Quality report right
in the merge request widget area:
......@@ -21,22 +31,48 @@ in the merge request widget area:
For instance, consider the following workflow:
1. Your backend team member starts a new implementation for making a certain feature in your app faster
1. With Code Quality reports, they analyze how their implementation is impacting the code quality
1. The metrics show that their code degrade the quality in 10 points
1. You ask a co-worker to help them with this modification
1. They both work on the changes until Code Quality report displays no degradations, only improvements
1. You approve the merge request and authorize its deployment to staging
1. Once verified, their changes are deployed to production
1. Your backend team member starts a new implementation for making a certain
feature in your app faster.
1. With Code Quality reports, they analyze how their implementation is impacting
the code quality.
1. The metrics show that their code degrade the quality in 10 points.
1. You ask a co-worker to help them with this modification.
1. They both work on the changes until Code Quality report displays no
degradations, only improvements.
1. You approve the merge request and authorize its deployment to staging.
1. Once verified, their changes are deployed to production.
## Template and examples
For most GitLab instances, the supplied template is the preferred method of
implementing Code Quality. See
[Analyze your project's Code Quality](../../../ci/examples/code_quality.md) for:
- Information on the builtin GitLab Code Quality template.
- Examples of manual GitLab configuration for earlier GitLab versions.
## How it works
## Configuring jobs using variables
First of all, you need to define a job in your `.gitlab-ci.yml` file that generates the
[Code Quality report artifact](../../../ci/yaml/README.md#artifactsreportscodequality-starter).
The Code Quality job supports environment variables that users can set to
configure job execution at runtime.
The Code Quality report artifact is a subset of the
[Code Climate spec](https://github.com/codeclimate/spec/blob/master/SPEC.md#data-types).
It must be a JSON file containing an array of objects with the following properties:
For a list of available environment variables, see
[Environment variables](https://gitlab.com/gitlab-org/security-products/codequality/blob/master/README.md#environment-variables).
## Implementing a custom tool
It's possible to have a custom tool provide Code Quality reports in GitLab. To
do this:
1. Define a job in your `.gitlab-ci.yml` file that generates the
[Code Quality report
artifact](../../../ci/yaml/README.md#artifactsreportscodequality-starter).
1. Configure your tool to generate the Code Quality report artifact as a JSON
file that implements subset of the [Code Climate
spec](https://github.com/codeclimate/spec/blob/master/SPEC.md#data-types).
The Code Quality report artifact JSON file must contain an array of objects
with the following properties:
| Name | Description |
| ---------------------- | -------------------------------------------------------------------------------------- |
......@@ -63,13 +99,16 @@ Example:
```
NOTE: **Note:**
Although the Code Climate spec supports more properties, those are ignored by GitLab.
Although the Code Climate spec supports more properties, those are ignored by
GitLab.
## Code Quality reports
For more information on what the Code Quality job should look like, check the
example on [analyzing a project's code quality](../../../ci/examples/code_quality.md).
Once the Code Quality job has completed, GitLab:
GitLab then checks this report, compares the metrics between the source and target
branches, and shows the information right on the merge request.
- Checks the generated report.
- Compares the metrics between the source and target branches.
- Shows the information right on the merge request.
If multiple jobs in a pipeline generate a code quality artifact, only the artifact from
the last created job (the job with the largest job ID) is used. To avoid confusion,
......
......@@ -22,6 +22,9 @@ export default {
className() {
return `vulnerability-count-${this.severity}`;
},
qaSelector() {
return `vulnerability_count_${this.severity}`;
},
severityTitle() {
return SEVERITY_LEVELS[this.severity] || this.severity;
},
......@@ -32,7 +35,7 @@ export default {
<template>
<div class="vulnerability-count" :class="className">
<div class="vulnerability-count-header">{{ severityTitle }}</div>
<div class="vulnerability-count-body">
<div class="vulnerability-count-body" :data-qa-selector="qaSelector">
<span v-if="isLoading">&mdash;</span> <span v-else>{{ count }}</span>
</div>
</div>
......
......@@ -101,14 +101,14 @@ class Projects::FeatureFlagsController < Projects::ApplicationController
params.require(:operations_feature_flag)
.permit(:name, :description, :active,
scopes_attributes: [:environment_scope, :active,
strategies: [:name, parameters: [:groupId, :percentage]]])
strategies: [:name, parameters: [:groupId, :percentage, :userIds]]])
end
def update_params
params.require(:operations_feature_flag)
.permit(:name, :description, :active,
scopes_attributes: [:id, :environment_scope, :active, :_destroy,
strategies: [:name, parameters: [:groupId, :percentage]]])
strategies: [:name, parameters: [:groupId, :percentage, :userIds]]])
end
def feature_flag_json(feature_flag)
......
......@@ -144,8 +144,11 @@ module EE
::Group
end
# Column name used by RelativePositioning for scoping. This is not related to `parent_class` above.
def parent_column
def relative_positioning_query_base(epic)
in_parents(epic.parent_ids)
end
def relative_positioning_parent_column
:parent_id
end
......
......@@ -417,7 +417,7 @@ module EE
return if repository_read_only?
return if repository_storage == new_repository_storage_key
raise ArgumentError unless ::Gitlab.config.repositories.storages.keys.include?(new_repository_storage_key)
raise ArgumentError unless ::Gitlab.config.repositories.storages.key?(new_repository_storage_key)
run_after_commit { ProjectUpdateRepositoryStorageWorker.perform_async(id, new_repository_storage_key) }
self.repository_read_only = true
......
......@@ -13,11 +13,11 @@ class EpicIssue < ApplicationRecord
scope :in_epic, ->(epic_id) { where(epic_id: epic_id) }
class << self
alias_method :in_parents, :in_epic
def self.relative_positioning_query_base(epic_issue)
in_epic(epic_issue.parent_ids)
end
def parent_column
:epic_id
end
def self.relative_positioning_parent_column
:epic_id
end
end
# frozen_string_literal: true
module Geo
class ContainerRepositoryUpdatedEvent < ApplicationRecord
include Geo::Model
include Geo::Eventable
belongs_to :container_repository
validates :container_repository, presence: true
end
end
......@@ -16,7 +16,8 @@ module Geo
Geo::HashedStorageAttachmentsEvent
Geo::LfsObjectDeletedEvent
Geo::JobArtifactDeletedEvent
Geo::UploadDeletedEvent].freeze
Geo::UploadDeletedEvent
Geo::ContainerRepositoryUpdatedEvent].freeze
belongs_to :cache_invalidation_event,
class_name: 'Geo::CacheInvalidationEvent',
......@@ -66,6 +67,10 @@ module Geo
class_name: 'Geo::ResetChecksumEvent',
foreign_key: :reset_checksum_event_id
belongs_to :container_repository_updated_event,
class_name: 'Geo::ContainerRepositoryUpdatedEvent',
foreign_key: :container_repository_updated_event_id
def self.latest_event
order(id: :desc).first
end
......@@ -97,7 +102,8 @@ module Geo
job_artifact_deleted_event ||
upload_deleted_event ||
reset_checksum_event ||
cache_invalidation_event
cache_invalidation_event ||
container_repository_updated_event
end
def project_id
......
# frozen_string_literal: true
module Geo
class ContainerRepositoryUpdatedEventStore < EventStore
self.event_type = :container_repository_updated_event
attr_reader :repository
def initialize(repository)
@repository = repository
end
private
def build_event
Geo::ContainerRepositoryUpdatedEvent.new(
container_repository: repository
)
end
# This is called by ProjectLogHelpers to build json log with context info
#
# @see ::Gitlab::Geo::ProjectLogHelpers
def base_log_data(message)
{
class: self.class.name,
container_repository_id: repository.try(:id),
message: message
}.compact
end
end
end
......@@ -3,6 +3,14 @@
class FeatureFlagStrategiesValidator < ActiveModel::EachValidator
STRATEGY_DEFAULT = 'default'.freeze
STRATEGY_GRADUALROLLOUTUSERID = 'gradualRolloutUserId'.freeze
STRATEGY_USERWITHID = 'userWithId'.freeze
# Order key names alphabetically
STRATEGIES = {
STRATEGY_DEFAULT => [].freeze,
STRATEGY_GRADUALROLLOUTUSERID => %w[groupId percentage].freeze,
STRATEGY_USERWITHID => ['userIds'].freeze
}.freeze
USERID_MAX_LENGTH = 256
def validate_each(record, attribute, value)
return unless value
......@@ -12,20 +20,40 @@ class FeatureFlagStrategiesValidator < ActiveModel::EachValidator
strategy_validations(record, attribute, strategy)
end
else
record.errors.add(attribute, 'must be an array of strategy hashes')
error(record, attribute, 'must be an array of strategy hashes')
end
end
private
def strategy_validations(record, attribute, strategy)
validate_name(record, attribute, strategy) &&
validate_parameters_type(record, attribute, strategy) &&
validate_parameters_keys(record, attribute, strategy) &&
validate_parameters_values(record, attribute, strategy)
end
def validate_name(record, attribute, strategy)
STRATEGIES.key?(strategy['name']) || error(record, attribute, 'strategy name is invalid')
end
def validate_parameters_type(record, attribute, strategy)
strategy['parameters'].is_a?(Hash) || error(record, attribute, 'parameters are invalid')
end
def validate_parameters_keys(record, attribute, strategy)
name, parameters = strategy.values_at('name', 'parameters')
actual_keys = parameters.keys.sort
expected_keys = STRATEGIES[name]
expected_keys == actual_keys || error(record, attribute, 'parameters are invalid')
end
def validate_parameters_values(record, attribute, strategy)
case strategy['name']
when STRATEGY_DEFAULT
default_parameters_validation(record, attribute, strategy)
when STRATEGY_GRADUALROLLOUTUSERID
gradual_rollout_user_id_parameters_validation(record, attribute, strategy)
else
record.errors.add(attribute, 'strategy name is invalid')
when STRATEGY_USERWITHID
user_with_id_parameters_validation(record, attribute, strategy)
end
end
......@@ -34,17 +62,34 @@ class FeatureFlagStrategiesValidator < ActiveModel::EachValidator
group_id = strategy.dig('parameters', 'groupId')
unless percentage.is_a?(String) && percentage.match(/\A[1-9]?[0-9]\z|\A100\z/)
record.errors.add(attribute, 'percentage must be a string between 0 and 100 inclusive')
error(record, attribute, 'percentage must be a string between 0 and 100 inclusive')
end
unless group_id.is_a?(String) && group_id.match(/\A[a-z]{1,32}\z/)
record.errors.add(attribute, 'groupId parameter is invalid')
error(record, attribute, 'groupId parameter is invalid')
end
end
def default_parameters_validation(record, attribute, strategy)
unless strategy['parameters'] == {}
record.errors.add(attribute, 'parameters must be empty for default strategy')
def user_with_id_parameters_validation(record, attribute, strategy)
user_ids = strategy.dig('parameters', 'userIds')
unless user_ids.is_a?(String) && !user_ids.match(/[\n\r\t]|,,/) && valid_ids?(user_ids.split(","))
error(record, attribute, "userIds must be a string of unique comma separated values each #{USERID_MAX_LENGTH} characters or less")
end
end
def valid_ids?(user_ids)
user_ids.uniq.length == user_ids.length &&
user_ids.all? { |id| valid_id?(id) }
end
def valid_id?(user_id)
user_id.present? &&
user_id.strip == user_id &&
user_id.length <= USERID_MAX_LENGTH
end
def error(record, attribute, msg)
record.errors.add(attribute, msg)
false
end
end
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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