Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2020-04-24 09:09:44 +00:00
parent 81c3057591
commit b1b7c2f9a7
79 changed files with 917 additions and 334 deletions

View file

@ -1,5 +1,12 @@
Please view this file on the master branch, on stable branches it's out of date.
## 12.10.1 (2020-04-24)
### Changed (1 change)
- Move project deploy tokens section back to Repository settings. !29280
## 12.10.0 (2020-04-22)
### Fixed (6 changes, 1 of them is from the community)

View file

@ -1,17 +1,62 @@
<script>
import { GlEmptyState, GlButton, GlLoadingIcon } from '@gitlab/ui';
import { mapState } from 'vuex';
import { GlEmptyState, GlButton, GlLoadingIcon, GlTable, GlAlert } from '@gitlab/ui';
import { __ } from '~/locale';
const tdClass = 'table-col d-flex';
export default {
fields: [
{
key: 'severity',
label: __('Severity'),
tdClass,
},
{
key: 'start_time',
label: __('Start Time'),
tdClass,
},
{
key: 'end_time',
label: __('End Time'),
tdClass,
},
{
key: 'alert',
label: __('Alert'),
thClass: 'w-30p',
tdClass,
},
{
key: 'events',
label: __('Events'),
tdClass,
},
{
key: 'status',
label: __('Status'),
tdClass,
},
],
components: {
GlEmptyState,
GlButton,
GlLoadingIcon,
GlTable,
GlAlert,
},
props: {
indexPath: {
type: String,
required: true,
},
// TODO: Handle alertManagementEnabled depending on resolution - https://gitlab.com/gitlab-org/gitlab/-/merge_requests/30024.
alertManagementEnabled: {
type: Boolean,
required: false,
default: true,
},
enableAlertManagementPath: {
type: String,
required: true,
@ -23,19 +68,45 @@ export default {
},
data() {
return {
alerts: [],
loading: false,
isAlertDismissed: false,
};
},
computed: {
...mapState('list', ['alerts', 'loading']),
showNoAlertsMsg() {
return !this.alerts.length && !this.isAlertDismissed;
},
},
};
</script>
<template>
<div>
<div v-if="alerts.length > 0" class="alert-management-list">
<div v-if="alertManagementEnabled" class="alert-management-list">
<gl-alert v-if="showNoAlertsMsg" @dismiss="isAlertDismissed = true">
{{
__(
`No alerts available to display. If you think you're seeing this message in error, refresh the page.`,
)
}}
</gl-alert>
<div v-if="loading" class="py-3">
<gl-loading-icon size="md" />
</div>
<gl-table
class="mt-3"
:items="alerts"
:fields="$options.fields"
:show-empty="true"
fixed
stacked="sm"
tbody-tr-class="table-row mb-4"
>
<template #empty>
{{ __('No alerts to display.') }}
</template>
</gl-table>
</div>
<template v-else>
<gl-empty-state :title="__('Surface alerts in GitLab')" :svg-path="emptyAlertSvgPath">
@ -51,9 +122,9 @@ export default {
</a>
</div>
<div class="d-block center pt-4">
<gl-button category="primary" variant="success" :href="enableAlertManagementPath">{{
__('Authorize external service')
}}</gl-button>
<gl-button category="primary" variant="success" :href="enableAlertManagementPath">
{{ __('Authorize external service') }}
</gl-button>
</div>
</template>
</gl-empty-state>

View file

@ -1,4 +1,5 @@
import Vue from 'vue';
import store from './store';
import AlertManagementList from './components/alert_management_list.vue';
export default () => {
@ -12,6 +13,7 @@ export default () => {
components: {
AlertManagementList,
},
store,
render(createElement) {
return createElement('alert-management-list', {
props: {

View file

@ -0,0 +1,22 @@
import Vue from 'vue';
import Vuex from 'vuex';
import * as listActions from './list/actions';
import listMutations from './list/mutations';
import listState from './list/state';
Vue.use(Vuex);
export const createStore = () =>
new Vuex.Store({
modules: {
list: {
namespaced: true,
state: listState(),
actions: listActions,
mutations: listMutations,
},
},
});
export default createStore();

View file

@ -0,0 +1,9 @@
import * as types from './mutation_types';
export const setAlerts = ({ commit }, alerts) => {
commit(types.SET_ALERTS, alerts);
};
export const setLoading = ({ commit }, loading) => {
commit(types.SET_LOADING, loading);
};

View file

@ -0,0 +1,2 @@
export const SET_ALERTS = 'SET_ALERTS';
export const SET_LOADING = 'SET_LOADING';

View file

@ -0,0 +1,10 @@
import * as types from './mutation_types';
export default {
[types.SET_ALERTS](state, alerts) {
state.alerts = alerts;
},
[types.SET_LOADING](state, loading) {
state.loading = loading;
},
};

View file

@ -0,0 +1,4 @@
export default () => ({
alerts: [],
loading: false,
});

View file

@ -12,10 +12,6 @@ export default {
type: Boolean,
required: true,
},
disabled: {
type: Boolean,
required: true,
},
},
data() {
return {
@ -41,12 +37,7 @@ export default {
<div class="form-group row" role="group">
<label for="service[active]" class="col-form-label col-sm-2">{{ __('Active') }}</label>
<div class="col-sm-10 pt-1">
<gl-toggle
v-model="activated"
:disabled="disabled"
name="service[active]"
@change="onToggle"
/>
<gl-toggle v-model="activated" name="service[active]" @change="onToggle" />
</div>
</div>
</div>

View file

@ -7,10 +7,9 @@ export default el => {
return null;
}
const { showActive: showActiveStr, activated: activatedStr, disabled: disabledStr } = el.dataset;
const { showActive: showActiveStr, activated: activatedStr } = el.dataset;
const showActive = parseBoolean(showActiveStr);
const activated = parseBoolean(activatedStr);
const disabled = parseBoolean(disabledStr);
if (!showActive) {
return null;
@ -22,7 +21,6 @@ export default el => {
return createElement(ActiveToggle, {
props: {
initialActivated: activated,
disabled,
},
});
},

View file

@ -256,7 +256,7 @@ export default class Notes {
discussionNoteForm = $textarea.closest('.js-discussion-note-form');
if (discussionNoteForm.length) {
if ($textarea.val() !== '') {
if (!window.confirm(__('Are you sure you want to cancel creating this comment?'))) {
if (!window.confirm(__('Your comment will be discarded.'))) {
return;
}
}
@ -268,7 +268,7 @@ export default class Notes {
originalText = $textarea.closest('form').data('originalNote');
newText = $textarea.val();
if (originalText !== newText) {
if (!window.confirm(__('Are you sure you want to cancel editing this comment?'))) {
if (!window.confirm(__('Are you sure you want to discard this comment?'))) {
return;
}
}

View file

@ -52,7 +52,6 @@ export default {
.then(res => res.data)
.then(data => {
eventHub.$emit('UpdateWidgetData', data);
eventHub.$emit('MRWidgetUpdateRequested');
})
.catch(() => {
this.isCancellingAutoMerge = false;

View file

@ -123,15 +123,13 @@ export default class MergeRequestStore {
const currentUser = data.current_user;
if (currentUser) {
this.cherryPickInForkPath = currentUser.cherry_pick_in_fork_path;
this.revertInForkPath = currentUser.revert_in_fork_path;
this.cherryPickInForkPath = currentUser.cherry_pick_in_fork_path;
this.revertInForkPath = currentUser.revert_in_fork_path;
this.canRemoveSourceBranch = currentUser.can_remove_source_branch || false;
this.canCreateIssue = currentUser.can_create_issue || false;
this.canCherryPickInCurrentMR = currentUser.can_cherry_pick_on_current_merge_request || false;
this.canRevertInCurrentMR = currentUser.can_revert_on_current_merge_request || false;
}
this.canRemoveSourceBranch = currentUser.can_remove_source_branch || false;
this.canCreateIssue = currentUser.can_create_issue || false;
this.canCherryPickInCurrentMR = currentUser.can_cherry_pick_on_current_merge_request || false;
this.canRevertInCurrentMR = currentUser.can_revert_on_current_merge_request || false;
this.setState(data);
}

View file

@ -0,0 +1,26 @@
// these styles need to be deleted once GlTable component looks in GitLab same as in @gitlab/ui
.alert-management-list {
table {
color: $gray-700;
tr {
td,
th {
@include gl-p-4;
}
th {
background-color: transparent;
font-weight: $gl-font-weight-bold;
color: $gl-gray-600;
@include gl-border-b-1;
@include gl-border-b-solid;
border-color: $gray-100;
}
td {
@include gl-border-0;
}
}
}
}

View file

@ -335,15 +335,6 @@
}
}
.deprecated-service {
cursor: default;
a {
font-weight: $gl-font-weight-bold;
color: $white;
}
}
.personal-access-tokens-never-expires-label {
color: $note-disabled-comment-color;
}
@ -401,4 +392,3 @@
}
}
}

View file

@ -49,6 +49,10 @@ module GroupsHelper
can?(current_user, :change_visibility_level, group)
end
def can_update_default_branch_protection?(group)
can?(current_user, :update_default_branch_protection, group)
end
def can_change_share_with_group_lock?(group)
can?(current_user, :change_share_with_group_lock, group)
end

View file

@ -51,17 +51,13 @@ module ServicesHelper
end
end
def service_save_button(service)
button_tag(class: 'btn btn-success', type: 'submit', disabled: service.deprecated?, data: { qa_selector: 'save_changes_button' }) do
def service_save_button
button_tag(class: 'btn btn-success', type: 'submit', data: { qa_selector: 'save_changes_button' }) do
icon('spinner spin', class: 'hidden js-btn-spinner') +
content_tag(:span, 'Save changes', class: 'js-btn-label')
end
end
def disable_fields_service?(service)
!current_controller?("admin/services") && service.deprecated?
end
def scoped_integrations_path
if @project.present?
project_settings_integrations_path(@project)

View file

@ -105,7 +105,10 @@ class BroadcastMessage < ApplicationRecord
def matches_current_path(current_path)
return true if current_path.blank? || target_path.blank?
current_path.match(Regexp.escape(target_path).gsub('\\*', '.*'))
escaped = Regexp.escape(target_path).gsub('\\*', '.*')
regexp = Regexp.new "^#{escaped}$", Regexp::IGNORECASE
regexp.match(current_path)
end
def flush_redis_cache

View file

@ -8,6 +8,11 @@ module ReactiveCaching
InvalidateReactiveCache = Class.new(StandardError)
ExceededReactiveCacheLimit = Class.new(StandardError)
WORK_TYPE = {
default: ReactiveCachingWorker,
external_dependency: ExternalServiceReactiveCachingWorker
}.freeze
included do
extend ActiveModel::Naming
@ -16,6 +21,7 @@ module ReactiveCaching
class_attribute :reactive_cache_refresh_interval
class_attribute :reactive_cache_lifetime
class_attribute :reactive_cache_hard_limit
class_attribute :reactive_cache_work_type
class_attribute :reactive_cache_worker_finder
# defaults
@ -24,6 +30,7 @@ module ReactiveCaching
self.reactive_cache_refresh_interval = 1.minute
self.reactive_cache_lifetime = 10.minutes
self.reactive_cache_hard_limit = 1.megabyte
self.reactive_cache_work_type = :default
self.reactive_cache_worker_finder = ->(id, *_args) do
find_by(primary_key => id)
end
@ -112,7 +119,7 @@ module ReactiveCaching
def refresh_reactive_cache!(*args)
clear_reactive_cache!(*args)
keep_alive_reactive_cache!(*args)
ReactiveCachingWorker.perform_async(self.class, id, *args)
worker_class.perform_async(self.class, id, *args)
end
def keep_alive_reactive_cache!(*args)
@ -145,7 +152,11 @@ module ReactiveCaching
def enqueuing_update(*args)
yield
ReactiveCachingWorker.perform_in(self.class.reactive_cache_refresh_interval, self.class, id, *args)
worker_class.perform_in(self.class.reactive_cache_refresh_interval, self.class, id, *args)
end
def worker_class
WORK_TYPE.fetch(self.class.reactive_cache_work_type.to_sym)
end
def check_exceeded_reactive_cache_limit!(data)

View file

@ -345,14 +345,6 @@ class Service < ApplicationRecord
service
end
def deprecated?
false
end
def deprecation_message
nil
end
# override if needed
def supports_data_fields?
false

View file

@ -74,6 +74,10 @@ class GlobalPolicy < BasePolicy
enable :create_group
end
rule { can?(:create_group) }.policy do
enable :create_group_with_default_branch_protection
end
rule { can_create_fork }.policy do
enable :create_fork
end

View file

@ -123,6 +123,7 @@ class GroupPolicy < BasePolicy
enable :set_note_created_at
enable :set_emails_disabled
enable :update_default_branch_protection
end
rule { can?(:read_nested_project_resources) }.policy do

View file

@ -17,6 +17,7 @@ module Discussions
return unless result
position = result[:position]
return unless position
# Currently position data is copied across all notes of a discussion
# It makes sense to store a position only for the first note instead

View file

@ -38,6 +38,10 @@ module Groups
# overridden in EE
end
def remove_unallowed_params
params.delete(:default_branch_protection) unless can?(current_user, :create_group_with_default_branch_protection)
end
def create_chat_team?
Gitlab.config.mattermost.enabled && @chat_team && group.chat_team.nil?
end

View file

@ -66,6 +66,7 @@ module Groups
# overridden in EE
def remove_unallowed_params
params.delete(:emails_disabled) unless can?(current_user, :set_emails_disabled, group)
params.delete(:default_branch_protection) unless can?(current_user, :update_default_branch_protection, group)
end
def valid_share_with_group_lock_change?

View file

@ -1,3 +0,0 @@
.flash-container.flash-container-page
.flash-alert.deprecated-service
%span= @service.deprecation_message

View file

@ -2,6 +2,4 @@
- breadcrumb_title @service.title
- page_title @service.title, "Service Templates"
= render 'deprecated_message' if @service.deprecation_message
= render 'form'

View file

@ -0,0 +1,3 @@
- return unless can_update_default_branch_protection?(group)
= render 'shared/default_branch_protection', f: f, selected_level: group.default_branch_protection

View file

@ -33,7 +33,7 @@
= render_if_exists 'groups/settings/ip_restriction', f: f, group: @group
= render_if_exists 'groups/settings/allowed_email_domain', f: f, group: @group
= render 'groups/settings/lfs', f: f
= render 'shared/default_branch_protection', f: f, selected_level: @group.default_branch_protection
= render 'groups/settings/default_branch_protection', f: f, group: @group
= render 'groups/settings/project_creation_level', f: f, group: @group
= render 'groups/settings/subgroup_creation_level', f: f, group: @group
= render 'groups/settings/two_factor_auth', f: f

View file

@ -1,3 +0,0 @@
.flash-container.flash-container-page
.flash-alert.deprecated-service
%span= @service.deprecation_message

View file

@ -13,7 +13,7 @@
= form_for(@service, as: :service, url: scoped_integration_path(@service), method: :put, html: { class: 'gl-show-field-errors integration-settings-form js-integration-settings-form', data: { 'can-test' => @service.can_test?, 'test-url' => test_project_service_path(@project, @service) } }) do |form|
= render 'shared/service_settings', form: form, service: @service
.footer-block.row-content-block
= service_save_button(@service)
= service_save_button
&nbsp;
= link_to _('Cancel'), project_settings_integrations_path(@project), class: 'btn btn-cancel'

View file

@ -2,8 +2,6 @@
- add_to_breadcrumbs _('Integration Settings'), project_settings_integrations_path(@project)
- page_title @service.title, _('Integrations')
= render 'deprecated_message' if @service.deprecation_message
= render 'form'
- if @web_hook_logs
= render partial: 'projects/hook_logs/index', locals: { hook: @service.service_hook, hook_logs: @web_hook_logs, project: @project }

View file

@ -7,7 +7,6 @@
- choices = field[:choices]
- default_choice = field[:default_choice]
- help = field[:help]
- disabled = disable_fields_service?(@service)
.form-group.row
- if type == "password" && value.present?
@ -16,14 +15,14 @@
= form.label name, title, class: "col-form-label col-sm-2"
.col-sm-10
- if type == 'text'
= form.text_field name, class: "form-control", placeholder: placeholder, required: required, disabled: disabled, data: { qa_selector: "#{name.downcase.gsub('\s', '')}_field" }
= form.text_field name, class: "form-control", placeholder: placeholder, required: required, data: { qa_selector: "#{name.downcase.gsub('\s', '')}_field" }
- elsif type == 'textarea'
= form.text_area name, rows: 5, class: "form-control", placeholder: placeholder, required: required, disabled: disabled
= form.text_area name, rows: 5, class: "form-control", placeholder: placeholder, required: required
- elsif type == 'checkbox'
= form.check_box name, disabled: disabled
= form.check_box name
- elsif type == 'select'
= form.select name, options_for_select(choices, value ? value : default_choice), {}, { class: "form-control", disabled: disabled}
= form.select name, options_for_select(choices, value ? value : default_choice), {}, { class: "form-control"}
- elsif type == 'password'
= form.password_field name, autocomplete: "new-password", placeholder: placeholder, class: "form-control", required: value.blank? && required, disabled: disabled, data: { qa_selector: "#{name.downcase.gsub('\s', '')}_field" }
= form.password_field name, autocomplete: "new-password", placeholder: placeholder, class: "form-control", required: value.blank? && required, data: { qa_selector: "#{name.downcase.gsub('\s', '')}_field" }
- if help
%span.form-text.text-muted= help

View file

@ -8,7 +8,7 @@
= markdown @service.help
.service-settings
.js-vue-integration-settings{ data: { show_active: @service.show_active_box?.to_s, activated: (@service.active || @service.new_record?).to_s, disabled: disable_fields_service?(@service).to_s } }
.js-vue-integration-settings{ data: { show_active: @service.show_active_box?.to_s, activated: (@service.active || @service.new_record?).to_s } }
- if @service.configurable_events.present?
.form-group.row

View file

@ -10,5 +10,5 @@
- if integration.editable?
.footer-block.row-content-block
= service_save_button(integration)
= service_save_button
= link_to _('Cancel'), scoped_integration_path(integration), class: 'btn btn-cancel'

View file

@ -1060,6 +1060,13 @@
:resource_boundary: :cpu
:weight: 1
:idempotent:
- :name: external_service_reactive_caching
:feature_category: :not_owned
:has_external_dependencies: true
:urgency: :low
:resource_boundary: :unknown
:weight: 1
:idempotent:
- :name: file_hook
:feature_category: :integrations
:has_external_dependencies:

View file

@ -0,0 +1,33 @@
# frozen_string_literal: true
module ReactiveCacheableWorker
extend ActiveSupport::Concern
included do
include ApplicationWorker
feature_category_not_owned!
def self.context_for_arguments(arguments)
class_name, *_other_args = arguments
Gitlab::ApplicationContext.new(related_class: class_name.to_s)
end
end
def perform(class_name, id, *args)
klass = begin
class_name.constantize
rescue NameError
nil
end
return unless klass
klass
.reactive_cache_worker_finder
.call(id, *args)
.try(:exclusively_update_reactive_cache!, *args)
rescue ReactiveCaching::ExceededReactiveCacheLimit => e
Gitlab::ErrorTracking.track_exception(e)
end
end

View file

@ -0,0 +1,7 @@
# frozen_string_literal: true
class ExternalServiceReactiveCachingWorker # rubocop:disable Scalability/IdempotentWorker
include ReactiveCacheableWorker
worker_has_external_dependencies!
end

View file

@ -1,36 +1,8 @@
# frozen_string_literal: true
class ReactiveCachingWorker # rubocop:disable Scalability/IdempotentWorker
include ApplicationWorker
include ReactiveCacheableWorker
feature_category_not_owned!
# TODO: The reactive caching worker should be split into
# two different workers, one for high urgency jobs without external dependencies
# and another worker without high urgency, but with external dependencies
# https://gitlab.com/gitlab-com/gl-infra/scalability/issues/34
# This worker should also have `worker_has_external_dependencies!` enabled
urgency :high
worker_resource_boundary :cpu
def self.context_for_arguments(arguments)
class_name, *_other_args = arguments
Gitlab::ApplicationContext.new(related_class: class_name.to_s)
end
def perform(class_name, id, *args)
klass = begin
class_name.constantize
rescue NameError
nil
end
return unless klass
klass
.reactive_cache_worker_finder
.call(id, *args)
.try(:exclusively_update_reactive_cache!, *args)
rescue ReactiveCaching::ExceededReactiveCacheLimit => e
Gitlab::ErrorTracking.track_exception(e)
end
end

View file

@ -0,0 +1,5 @@
---
title: Add policies for managing 'default_branch_protection' setting in groups
merge_request: 29879
author:
type: added

View file

@ -0,0 +1,5 @@
---
title: Empty state for alerts list
merge_request: 30215
author:
type: added

View file

@ -0,0 +1,5 @@
---
title: Update the cancel comment note text to a less ambiguous statement.
merge_request: 30189
author:
type: changed

View file

@ -0,0 +1,5 @@
---
title: Use stricter regex for broadcast target path
merge_request: 30210
author:
type: changed

View file

@ -0,0 +1,5 @@
---
title: Clean up refresh fix for cancel automatic merge
merge_request: 29844
author:
type: other

View file

@ -100,6 +100,8 @@
- 1
- - export_csv
- 1
- - external_service_reactive_caching
- 1
- - file_hook
- 1
- - gcp_cluster

View file

@ -0,0 +1,29 @@
# frozen_string_literal: true
class MigrateSamlIdentitiesToScimIdentities < ActiveRecord::Migration[6.0]
DOWNTIME = false
class Identity < ActiveRecord::Base
self.table_name = 'identities'
include ::EachBatch
end
def up
Identity
.joins('INNER JOIN saml_providers ON saml_providers.id = identities.saml_provider_id')
.where('saml_providers.group_id IN (SELECT group_id FROM scim_oauth_access_tokens)')
.select('identities.extern_uid, identities.user_id, saml_providers.group_id, TRUE AS active,
identities.created_at, CURRENT_TIMESTAMP AS updated_at')
.each_batch do |batch|
data_to_insert = batch.map do |record|
record.attributes.extract!("extern_uid", "user_id", "group_id", "active", "created_at", "updated_at")
end
Gitlab::Database.bulk_insert(:scim_identities, data_to_insert, on_conflict: :do_nothing)
end
end
def down
end
end

View file

@ -13285,6 +13285,7 @@ COPY "schema_migrations" (version) FROM STDIN;
20200310135818
20200310135823
20200310145304
20200310215714
20200311074438
20200311082301
20200311084025

View file

@ -209,6 +209,7 @@ Piwik
PgBouncer
plaintext
PostgreSQL
precompile
preconfigure
preconfigured
preconfigures

View file

@ -1,6 +1,8 @@
# Integrity Check Rake Task
# Integrity check Rake task
## Repository Integrity
GitLab provides Rake tasks to check the integrity of various components.
## Repository integrity
Even though Git is very resilient and tries to prevent data integrity issues,
there are times when things go wrong. The following Rake tasks intend to
@ -43,7 +45,7 @@ sudo gitlab-rake gitlab:git:fsck
sudo -u git -H bundle exec rake gitlab:git:fsck RAILS_ENV=production
```
## Uploaded Files Integrity
## Uploaded files integrity
Various types of files can be uploaded to a GitLab installation by users.
These integrity checks can detect missing files. Additionally, for locally
@ -127,7 +129,7 @@ Checking integrity of Uploads
Done!
```
## LDAP Check
## LDAP check
The LDAP check Rake task will test the bind DN and password credentials
(if configured) and will list a sample of LDAP users. This task is also

View file

@ -1,9 +1,11 @@
# Geo Rake Tasks **(PREMIUM ONLY)**
The following Rake tasks are for [Geo installations](../geo/replication/index.md).
## Git housekeeping
There are few tasks you can run to schedule a Git housekeeping to start at the
next repository sync in a **Secondary node**:
next repository sync in a **secondary** node:
### Incremental Repack

View file

@ -9,7 +9,7 @@ which will become the owner of the project. You can resume an import
with the same command.
Bear in mind that the syntax is very specific. Remove any spaces within the argument block and
before/after the brackets. Also, Some shells (e.g., zsh) can interpret the open/close brackets
before/after the brackets. Also, some shells (for example, `zsh`) can interpret the open/close brackets
(`[]`) separately. You may need to either escape the brackets or use double quotes.
## Importing multiple projects

View file

@ -1,4 +1,6 @@
# LDAP Rake Tasks
# LDAP Rake tasks
The following are LDAP-related Rake tasks.
## Check
@ -26,7 +28,7 @@ limit by passing a number to the check task:
rake gitlab:ldap:check[50]
```
## Run a Group Sync
## Run a group sync
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/14735) in [GitLab Starter](https://about.gitlab.com/pricing/) 12.2.

View file

@ -1,8 +1,11 @@
# Maintenance Rake Tasks
# Maintenance Rake tasks
## Gather information about GitLab and the system it runs on
GitLab provides Rake tasks for general maintenance.
This command gathers information about your GitLab installation and the System it runs on. These may be useful when asking for help or reporting issues.
## Gather GitLab and system information
This command gathers information about your GitLab installation and the system it runs on.
These may be useful when asking for help or reporting issues.
**Omnibus Installation**
@ -50,20 +53,23 @@ Git: /usr/bin/git
## Check GitLab configuration
Runs the following Rake tasks:
The `gitlab:check` Rake task runs the following Rake tasks:
- `gitlab:gitlab_shell:check`
- `gitlab:gitaly:check`
- `gitlab:sidekiq:check`
- `gitlab:app:check`
It will check that each component was set up according to the installation guide and suggest fixes for issues found.
This command must be run from your app server and will not work correctly on component servers like [Gitaly](../gitaly/index.md#running-gitaly-on-its-own-server).
It will check that each component was set up according to the installation guide and suggest fixes
for issues found. This command must be run from your application server and will not work correctly on
component servers like [Gitaly](../gitaly/index.md#running-gitaly-on-its-own-server).
You may also have a look at our Troubleshooting Guides:
You may also have a look at our troubleshooting guides for:
- [Troubleshooting Guide (GitLab)](../index.md#troubleshooting)
- [Troubleshooting Guide (Omnibus GitLab)](https://docs.gitlab.com/omnibus/README.html#troubleshooting)
- [GitLab](../index.md#troubleshooting)
- [Omnibus GitLab](https://docs.gitlab.com/omnibus/README.html#troubleshooting)
To run `gitlab:check`, run:
**Omnibus Installation**
@ -77,7 +83,8 @@ sudo gitlab-rake gitlab:check
bundle exec rake gitlab:check RAILS_ENV=production
```
NOTE: Use `SANITIZE=true` for `gitlab:check` if you want to omit project names from the output.
NOTE: **Note:**
Use `SANITIZE=true` for `gitlab:check` if you want to omit project names from the output.
Example output:
@ -126,7 +133,7 @@ Checking GitLab ... Finished
## Rebuild authorized_keys file
In some case it is necessary to rebuild the `authorized_keys` file.
In some case it is necessary to rebuild the `authorized_keys` file. To do this, run:
**Omnibus Installation**
@ -141,6 +148,8 @@ cd /home/git/gitlab
sudo -u git -H bundle exec rake gitlab:shell:setup RAILS_ENV=production
```
Example output:
```plaintext
This will rebuild an authorized_keys file.
You will lose any data stored in authorized_keys file.
@ -149,8 +158,8 @@ Do you want to continue (yes/no)? yes
## Clear Redis cache
If for some reason the dashboard shows wrong information you might want to
clear Redis' cache.
If for some reason the dashboard displays the wrong information, you might want to
clear Redis' cache. To do this, run:
**Omnibus Installation**
@ -170,7 +179,7 @@ sudo -u git -H bundle exec rake cache:clear RAILS_ENV=production
Sometimes during version upgrades you might end up with some wrong CSS or
missing some icons. In that case, try to precompile the assets again.
Note that this only applies to source installations and does NOT apply to
This only applies to source installations and does NOT apply to
Omnibus packages.
**Source Installation**
@ -193,6 +202,8 @@ GitLab provides a Rake task that lets you track deployments in GitLab
Performance Monitoring. This Rake task simply stores the current GitLab version
in the GitLab Performance Monitoring database.
To run `gitlab:track_deployment`:
**Omnibus Installation**
```shell

View file

@ -3,11 +3,13 @@
> - [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/issues/3050) in GitLab 8.9.
> - From GitLab 11.3, import/export can use object storage automatically.
See also:
GitLab provides Rake tasks relating to project import and export. For more information, see:
- [Project import/export documentation](../../user/project/settings/import_export.md).
- [Project import/export API](../../api/project_import_export.md).
## Import/export tasks
The GitLab import/export version can be checked by using the following command:
```shell
@ -28,8 +30,6 @@ sudo gitlab-rake gitlab:import_export:data
bundle exec rake gitlab:import_export:data RAILS_ENV=production
```
## Important notes
Note the following:
- Importing is only possible if the version of the import and export GitLab instances are

View file

@ -1,4 +1,4 @@
# Repository Storage Rake Tasks
# Repository storage Rake tasks
This is a collection of Rake tasks you can use to help you list and migrate
existing projects and attachments associated with it from Legacy storage to
@ -6,7 +6,7 @@ the new Hashed storage type.
You can read more about the storage types [here](../repository_storage_types.md).
## Migrate existing projects to Hashed storage
## Migrate existing projects to hashed storage
Before migrating your existing projects, you should
[enable hashed storage](../repository_storage_types.md#how-to-migrate-to-hashed-storage) for the new projects as well.
@ -34,9 +34,9 @@ export ID_FROM=20
export ID_TO=50
```
You can monitor the progress in the **Admin Area > Monitoring > Background Jobs** page.
There is a specific Queue you can watch to see how long it will take to finish:
`hashed_storage:hashed_storage_project_migrate`
You can monitor the progress in the **{admin}** **Admin Area > Monitoring > Background Jobs** page.
There is a specific queue you can watch to see how long it will take to finish:
`hashed_storage:hashed_storage_project_migrate`.
After it reaches zero, you can confirm every project has been migrated by running the commands bellow.
If you find it necessary, you can run this migration script again to schedule missing projects.
@ -44,16 +44,18 @@ If you find it necessary, you can run this migration script again to schedule mi
Any error or warning will be logged in Sidekiq's log file.
NOTE: **Note:**
If Geo is enabled, each project that is successfully migrated generates an event to replicate the changes on any **secondary** nodes.
If [Geo](../geo/replication/index.md) is enabled, each project that is successfully migrated
generates an event to replicate the changes on any **secondary** nodes.
You only need the `gitlab:storage:migrate_to_hashed` Rake task to migrate your repositories, but we have additional
commands below that helps you inspect projects and attachments in both legacy and hashed storage.
## Rollback from Hashed storage to Legacy storage
## Rollback from hashed storage to legacy storage
If you need to rollback the storage migration for any reason, you can follow the steps described here.
NOTE: **Note:** Hashed Storage will be required in future version of GitLab.
NOTE: **Note:**
Hashed storage will be required in future version of GitLab.
To prevent new projects from being created in the Hashed storage,
you need to undo the [enable hashed storage](../repository_storage_types.md#how-to-migrate-to-hashed-storage) changes.
@ -81,7 +83,7 @@ export ID_FROM=20
export ID_TO=50
```
You can monitor the progress in the **Admin Area > Monitoring > Background Jobs** page.
You can monitor the progress in the **{admin}** **Admin Area > Monitoring > Background Jobs** page.
On the **Queues** tab, you can watch the `hashed_storage:hashed_storage_project_rollback` queue to see how long the process will take to finish.
After it reaches zero, you can confirm every project has been rolled back by running the commands bellow.
@ -89,9 +91,13 @@ If some projects weren't rolled back, you can run this rollback script again to
Any error or warning will be logged in Sidekiq's log file.
## List projects on Legacy storage
## List projects
To have a simple summary of projects using **Legacy** storage:
The following are Rake tasks for listing projects.
### List projects on legacy storage
To have a simple summary of projects using legacy storage:
**Omnibus Installation**
@ -105,7 +111,7 @@ sudo gitlab-rake gitlab:storage:legacy_projects
sudo -u git -H bundle exec rake gitlab:storage:legacy_projects RAILS_ENV=production
```
To list projects using **Legacy** storage:
To list projects using legacy storage:
**Omnibus Installation**
@ -120,9 +126,9 @@ sudo -u git -H bundle exec rake gitlab:storage:list_legacy_projects RAILS_ENV=pr
```
## List projects on Hashed storage
### List projects on hashed storage
To have a simple summary of projects using **Hashed** storage:
To have a simple summary of projects using hashed storage:
**Omnibus Installation**
@ -136,7 +142,7 @@ sudo gitlab-rake gitlab:storage:hashed_projects
sudo -u git -H bundle exec rake gitlab:storage:hashed_projects RAILS_ENV=production
```
To list projects using **Hashed** storage:
To list projects using hashed storage:
**Omnibus Installation**
@ -150,9 +156,13 @@ sudo gitlab-rake gitlab:storage:list_hashed_projects
sudo -u git -H bundle exec rake gitlab:storage:list_hashed_projects RAILS_ENV=production
```
## List attachments on Legacy storage
## List attachments
To have a simple summary of project attachments using **Legacy** storage:
The following are Rake tasks for listing attachments.
### List attachments on legacy storage
To have a simple summary of project attachments using legacy storage:
**Omnibus Installation**
@ -166,7 +176,7 @@ sudo gitlab-rake gitlab:storage:legacy_attachments
sudo -u git -H bundle exec rake gitlab:storage:legacy_attachments RAILS_ENV=production
```
To list project attachments using **Legacy** storage:
To list project attachments using legacy storage:
**Omnibus Installation**
@ -180,9 +190,9 @@ sudo gitlab-rake gitlab:storage:list_legacy_attachments
sudo -u git -H bundle exec rake gitlab:storage:list_legacy_attachments RAILS_ENV=production
```
## List attachments on Hashed storage
### List attachments on hashed storage
To have a simple summary of project attachments using **Hashed** storage:
To have a simple summary of project attachments using hashed storage:
**Omnibus Installation**
@ -196,7 +206,7 @@ sudo gitlab-rake gitlab:storage:hashed_attachments
sudo -u git -H bundle exec rake gitlab:storage:hashed_attachments RAILS_ENV=production
```
To list project attachments using **Hashed** storage:
To list project attachments using hashed storage:
**Omnibus Installation**

View file

@ -2,7 +2,7 @@
## List all deploy keys
Get a list of all deploy keys across all projects of the GitLab instance. This endpoint requires admin access.
Get a list of all deploy keys across all projects of the GitLab instance. This endpoint requires admin access and is not available on GitLab.com.
```plaintext
GET /deploy_keys

View file

@ -97,7 +97,6 @@ The following table lists available parameters for jobs:
| [`services`](#services) | Use docker services images. Also available: `services:name`, `services:alias`, `services:entrypoint`, and `services:command`. |
| [`before_script`](#before_script-and-after_script) | Override a set of commands that are executed before job. |
| [`after_script`](#before_script-and-after_script) | Override a set of commands that are executed after job. |
| [`stages`](#stages) | Define stages in a pipeline. |
| [`stage`](#stage) | Defines a job stage (default: `test`). |
| [`only`](#onlyexcept-basic) | Limit when jobs are created. Also available: [`only:refs`, `only:kubernetes`, `only:variables`, and `only:changes`](#onlyexcept-advanced). |
| [`except`](#onlyexcept-basic) | Limit when jobs are not created. Also available: [`except:refs`, `except:kubernetes`, `except:variables`, and `except:changes`](#onlyexcept-advanced). |

View file

@ -85,6 +85,9 @@ The ReactiveCaching concern can be used in models as well as `project_services`
1. Implement the `calculate_reactive_cache` method in your model/service.
1. Call `with_reactive_cache` in your model/service where the cached value is needed.
1. If the `calculate_reactive_cache` method above submits requests to external services
(e.g. Prometheus, K8s), make sure to change the
[`reactive_cache_work_type` accordingly](#selfreactive_cache_work_type).
### In controllers
@ -244,6 +247,13 @@ and will silently raise `ReactiveCaching::ExceededReactiveCacheLimit` on Sentry.
self.reactive_cache_hard_limit = 5.megabytes
```
#### `self.reactive_cache_work_type`
- This is the type of work performed by the `calculate_reactive_cache` method. Based on this attribute,
it's able to pick the right worker to process the caching job. Make sure to
set it as `:external_dependency` if the work performs any external request
(e.g. Kubernetes, Sentry).
#### `self.reactive_cache_worker_finder`
- This is the method used by the background worker to find or generate the object on

View file

@ -1,61 +1,63 @@
# Import bare repositories into your GitLab instance
# Import bare repositories
## Notes
Rake tasks are available to import bare repositories into a GitLab instance.
- The owner of the project will be the first admin
- The groups will be created as needed, including subgroups
- The owner of the group will be the first admin
- Existing projects will be skipped
- Projects in hashed storage may be skipped (see [Importing bare repositories from hashed storage](#importing-bare-repositories-from-hashed-storage))
- The existing Git repos will be moved from disk (removed from the original path)
Note that:
## How to use
- The owner of the project will be the first administrator.
- The groups will be created as needed, including subgroups.
- The owner of the group will be the first administrator.
- Existing projects will be skipped.
- Projects in hashed storage may be skipped. For more information, see
[Importing bare repositories from hashed storage](#importing-bare-repositories-from-hashed-storage).
- The existing Git repositories will be moved from disk (removed from the original path).
### Create a new folder to import your Git repositories from
To import bare repositories into a GitLab instance:
The new folder needs to have Git user ownership and read/write/execute access for Git user and its group:
1. Create a new folder to import your Git repositories from. The new folder needs to have Git user
ownership and read/write/execute access for Git user and its group:
```shell
sudo -u git mkdir -p /var/opt/gitlab/git-data/repository-import-<date>/new_group
```
```shell
sudo -u git mkdir -p /var/opt/gitlab/git-data/repository-import-<date>/new_group
```
### Copy your bare repositories inside this newly created folder
1. Copy your bare repositories inside this newly created folder. Note:
- Any `.git` repositories found on any of the subfolders will be imported as projects
- Groups will be created as needed, these could be nested folders. Example:
- Any `.git` repositories found on any of the subfolders will be imported as projects.
- Groups will be created as needed, these could be nested folders.
If we copy the repos to `/var/opt/gitlab/git-data/repository-import-<date>`, and repo A needs to be under the groups G1 and G2, it will
have to be created under those folders: `/var/opt/gitlab/git-data/repository-import-<date>/G1/G2/A.git`.
For example, if we copy the repositories to `/var/opt/gitlab/git-data/repository-import-<date>`,
and repository `A` needs to be under the groups `G1` and `G2`, it must be created under those folders:
`/var/opt/gitlab/git-data/repository-import-<date>/G1/G2/A.git`.
```shell
sudo cp -r /old/git/foo.git /var/opt/gitlab/git-data/repository-import-<date>/new_group/
```shell
sudo cp -r /old/git/foo.git /var/opt/gitlab/git-data/repository-import-<date>/new_group/
# Do this once when you are done copying git repositories
sudo chown -R git:git /var/opt/gitlab/git-data/repository-import-<date>
```
# Do this once when you are done copying git repositories
sudo chown -R git:git /var/opt/gitlab/git-data/repository-import-<date>
```
`foo.git` needs to be owned by the `git` user and `git` users group.
`foo.git` needs to be owned by the `git` user and `git` users group.
If you are using an installation from source, replace `/var/opt/gitlab/` with `/home/git`.
If you are using an installation from source, replace `/var/opt/gitlab/` with `/home/git`.
### Run the command below depending on your type of installation
1. Run the following command depending on your type of installation:
#### Omnibus Installation
- Omnibus Installation
```shell
sudo gitlab-rake gitlab:import:repos['/var/opt/gitlab/git-data/repository-import-<date>']
```
```shell
sudo gitlab-rake gitlab:import:repos['/var/opt/gitlab/git-data/repository-import-<date>']
```
#### Installation from source
- Installation from source. Before running this command you need to change to the directory where
your GitLab installation is located:
Before running this command you need to change the directory to where your GitLab installation is located:
```shell
cd /home/git/gitlab
sudo -u git -H bundle exec rake gitlab:import:repos['/var/opt/gitlab/git-data/repository-import-<date>'] RAILS_ENV=production
```
```shell
cd /home/git/gitlab
sudo -u git -H bundle exec rake gitlab:import:repos['/var/opt/gitlab/git-data/repository-import-<date>'] RAILS_ENV=production
```
#### Example output
## Example output
```plaintext
Processing /var/opt/gitlab/git-data/repository-import-1/a/b/c/blah.git
@ -73,8 +75,6 @@ Processing /var/opt/gitlab/git-data/repository-import-1/group/xyz.git
## Importing bare repositories from hashed storage
### Background
Projects in legacy storage have a directory structure that mirrors their full
project path in GitLab, including their namespace structure. This information is
leveraged by the bare repository importer to import projects into their proper
@ -86,17 +86,17 @@ improved performance and data integrity. See
[Repository Storage Types](../administration/repository_storage_types.md) for
more details.
### Which repositories are importable?
The repositories that are importable depends on the version of GitLab.
#### GitLab 10.3 or earlier
### GitLab 10.3 or earlier
Importing bare repositories from hashed storage is unsupported.
#### GitLab 10.4 and later
### GitLab 10.4 and later
To support importing bare repositories from hashed storage, GitLab 10.4 and
later stores the full project path with each repository, in a special section of
the Git repository's config file. This section is formatted as follows:
the Git repository's configuration file. This section is formatted as follows:
```ini
[gitlab]

View file

@ -1,7 +1,8 @@
# Listing repository directories
You can print a list of all Git repositories on disk managed by
GitLab with the following command:
You can print a list of all Git repositories on disk managed by GitLab.
To print a list, run the following command:
```shell
# Omnibus
@ -12,10 +13,13 @@ cd /home/git/gitlab
sudo -u git -H bundle exec rake gitlab:list_repos RAILS_ENV=production
```
If you only want to list projects with recent activity you can pass
a date with the 'SINCE' environment variable. The time you specify
is parsed by the Rails [TimeZone#parse
function](https://api.rubyonrails.org/classes/ActiveSupport/TimeZone.html#method-i-parse).
NOTE: **Note:**
The results use the default ordering of the GitLab Rails application.
## Limit search results
To list only projects with recent activity, pass a date with the `SINCE` environment variable. The
time you specify is parsed by the Rails [TimeZone#parse function](https://api.rubyonrails.org/classes/ActiveSupport/TimeZone.html#method-i-parse).
```shell
# Omnibus
@ -25,6 +29,3 @@ sudo gitlab-rake gitlab:list_repos SINCE='Sep 1 2015'
cd /home/git/gitlab
sudo -u git -H bundle exec rake gitlab:list_repos RAILS_ENV=production SINCE='Sep 1 2015'
```
Note that the projects listed are NOT sorted by activity; they use
the default ordering of the GitLab Rails application.

View file

@ -128,9 +128,9 @@ before deploying one.
NOTE: **Note:**
The [`runner/gitlab-runner`](https://gitlab.com/gitlab-org/charts/gitlab-runner)
chart is used to install this application with a
[`values.yaml`](https://gitlab.com/gitlab-org/gitlab/blob/master/vendor/runner/values.yaml)
file. Customizing installation by modifying this file is not supported.
chart is used to install this application, using
[a preconfigured `values.yaml`](https://gitlab.com/gitlab-org/charts/gitlab-runner/-/blob/master/values.yaml)
file. Customizing the installation by modifying this file is not supported.
### Ingress

View file

@ -2420,9 +2420,6 @@ msgstr ""
msgid "Are you sure that you want to unarchive this project?"
msgstr ""
msgid "Are you sure you want to cancel creating this comment?"
msgstr ""
msgid "Are you sure you want to cancel editing this comment?"
msgstr ""
@ -2456,6 +2453,9 @@ msgstr ""
msgid "Are you sure you want to deploy this environment?"
msgstr ""
msgid "Are you sure you want to discard this comment?"
msgstr ""
msgid "Are you sure you want to erase this build?"
msgstr ""
@ -7835,6 +7835,9 @@ msgstr ""
msgid "Encountered an error while rendering: %{err}"
msgstr ""
msgid "End Time"
msgstr ""
msgid "End date"
msgstr ""
@ -13661,6 +13664,12 @@ msgstr ""
msgid "No activities found"
msgstr ""
msgid "No alerts available to display. If you think you're seeing this message in error, refresh the page."
msgstr ""
msgid "No alerts to display."
msgstr ""
msgid "No application_settings found"
msgstr ""
@ -18768,6 +18777,9 @@ msgstr ""
msgid "Settings to prevent self-approval across all projects in the instance. Only an administrator can modify these settings."
msgstr ""
msgid "Severity"
msgstr ""
msgid "Severity: %{severity}"
msgstr ""
@ -19550,6 +19562,9 @@ msgstr ""
msgid "Start GitLab Ultimate trial"
msgstr ""
msgid "Start Time"
msgstr ""
msgid "Start Web Terminal"
msgstr ""
@ -24252,6 +24267,9 @@ msgstr ""
msgid "Your comment could not be updated! Please check your network connection and try again."
msgstr ""
msgid "Your comment will be discarded."
msgstr ""
msgid "Your custom stage '%{title}' was created"
msgstr ""

View file

@ -270,6 +270,37 @@ describe GroupsController do
it { expect(subject).to render_template(:new) }
end
context 'when creating a group with `default_branch_protection` attribute' do
before do
sign_in(user)
end
subject do
post :create, params: { group: { name: 'new_group', path: 'new_group', default_branch_protection: Gitlab::Access::PROTECTION_NONE } }
end
context 'for users who have the ability to create a group with `default_branch_protection`' do
it 'creates group with the specified branch protection level' do
subject
expect(response).to have_gitlab_http_status(:found)
expect(Group.last.default_branch_protection).to eq(Gitlab::Access::PROTECTION_NONE)
end
end
context 'for users who do not have the ability to create a group with `default_branch_protection`' do
it 'does not create the group with the specified branch protection level' do
allow(Ability).to receive(:allowed?).and_call_original
allow(Ability).to receive(:allowed?).with(user, :create_group_with_default_branch_protection) { false }
subject
expect(response).to have_gitlab_http_status(:found)
expect(Group.last.default_branch_protection).not_to eq(Gitlab::Access::PROTECTION_NONE)
end
end
end
end
describe 'GET #index' do
@ -423,11 +454,31 @@ describe GroupsController do
expect(group.reload.project_creation_level).to eq(::Gitlab::Access::MAINTAINER_PROJECT_ACCESS)
end
it 'updates the default_branch_protection successfully' do
post :update, params: { id: group.to_param, group: { default_branch_protection: ::Gitlab::Access::PROTECTION_DEV_CAN_MERGE } }
context 'updating default_branch_protection' do
subject do
put :update, params: { id: group.to_param, group: { default_branch_protection: ::Gitlab::Access::PROTECTION_DEV_CAN_MERGE } }
end
expect(response).to have_gitlab_http_status(:found)
expect(group.reload.default_branch_protection).to eq(::Gitlab::Access::PROTECTION_DEV_CAN_MERGE)
context 'for users who have the ability to update default_branch_protection' do
it 'updates the attribute' do
subject
expect(response).to have_gitlab_http_status(:found)
expect(group.reload.default_branch_protection).to eq(::Gitlab::Access::PROTECTION_DEV_CAN_MERGE)
end
end
context 'for users who do not have the ability to update default_branch_protection' do
it 'does not update the attribute' do
allow(Ability).to receive(:allowed?).and_call_original
allow(Ability).to receive(:allowed?).with(user, :update_default_branch_protection, group) { false }
subject
expect(response).to have_gitlab_http_status(:found)
expect(group.reload.default_branch_protection).not_to eq(::Gitlab::Access::PROTECTION_DEV_CAN_MERGE)
end
end
end
context 'when a project inside the group has container repositories' do

View file

@ -2768,10 +2768,6 @@ Service
updates the has_external_issue_tracker boolean
on update
updates the has_external_issue_tracker boolean
#deprecated?
should return false by default
#deprecation_message
should be empty by default
#api_field_names
filters out sensitive fields

View file

@ -1,17 +1,28 @@
import { mount } from '@vue/test-utils';
import { GlEmptyState } from '@gitlab/ui';
import { createLocalVue, mount } from '@vue/test-utils';
import { GlEmptyState, GlTable, GlAlert } from '@gitlab/ui';
import Vuex from 'vuex';
import stubChildren from 'helpers/stub_children';
import AlertManagementList from '~/alert_management/components/alert_management_list.vue';
const localVue = createLocalVue();
localVue.use(Vuex);
describe('AlertManagementList', () => {
let wrapper;
let store;
function mountComponent({ stubs = {} } = {}) {
const findAlertsTable = () => wrapper.find(GlTable);
const findAlert = () => wrapper.find(GlAlert);
function mountComponent({ stubs = {}, alertManagementEnabled = false } = {}) {
wrapper = mount(AlertManagementList, {
localVue,
store,
propsData: {
indexPath: '/path',
enableAlertManagementPath: '/link',
emptyAlertSvgPath: 'illustration/path',
alertManagementEnabled,
},
stubs: {
...stubChildren(AlertManagementList),
@ -20,19 +31,43 @@ describe('AlertManagementList', () => {
});
}
beforeEach(() => {
store = new Vuex.Store({
modules: {
list: {
namespaced: true,
},
},
});
mountComponent();
});
afterEach(() => {
if (wrapper) {
wrapper.destroy();
store = null;
}
});
describe('alert management feature renders empty state', () => {
beforeEach(() => {
mountComponent();
});
it('shows empty state', () => {
expect(wrapper.find(GlEmptyState).exists()).toBe(true);
});
});
describe('Alerts table', () => {
it('shows empty list', () => {
store.state.list = {
alerts: [],
loading: false,
};
mountComponent({ alertManagementEnabled: true });
return wrapper.vm.$nextTick().then(() => {
expect(findAlertsTable().exists()).toBe(true);
expect(findAlert().text()).toContain('No alerts available to display');
});
});
});
});

View file

@ -340,4 +340,31 @@ describe GroupsHelper do
end
end
end
describe '#can_update_default_branch_protection?' do
let(:current_user) { create(:user) }
let(:group) { create(:group) }
subject { helper.can_update_default_branch_protection?(group) }
before do
allow(helper).to receive(:current_user) { current_user }
end
context 'for users who can update default branch protection of the group' do
before do
allow(helper).to receive(:can?).with(current_user, :update_default_branch_protection, group) { true }
end
it { is_expected.to be_truthy }
end
context 'for users who cannot update default branch protection of the group' do
before do
allow(helper).to receive(:can?).with(current_user, :update_default_branch_protection, group) { false }
end
it { is_expected.to be_falsey }
end
end
end

View file

@ -143,6 +143,24 @@ describe BroadcastMessage do
expect(subject.call('/group/groupname/issues').length).to eq(0)
end
it 'does not return message if target path has no wild card at the end' do
create(:broadcast_message, target_path: "*/issues", broadcast_type: broadcast_type)
expect(subject.call('/group/issues/test').length).to eq(0)
end
it 'does not return message if target path has wild card at the end' do
create(:broadcast_message, target_path: "/issues/*", broadcast_type: broadcast_type)
expect(subject.call('/group/issues/test').length).to eq(0)
end
it 'does return message if target path has wild card at the beginning and the end' do
create(:broadcast_message, target_path: "*/issues/*", broadcast_type: broadcast_type)
expect(subject.call('/group/issues/test').length).to eq(1)
end
end
describe '.current', :use_clean_rails_memory_store_caching do

View file

@ -6,39 +6,47 @@ describe ReactiveCaching, :use_clean_rails_memory_store_caching do
include ExclusiveLeaseHelpers
include ReactiveCachingHelpers
class CacheTest
include ReactiveCaching
let(:cache_class_test) do
Class.new do
include ReactiveCaching
self.reactive_cache_key = ->(thing) { ["foo", thing.id] }
self.reactive_cache_key = ->(thing) { ["foo", thing.id] }
self.reactive_cache_lifetime = 5.minutes
self.reactive_cache_refresh_interval = 15.seconds
self.reactive_cache_lifetime = 5.minutes
self.reactive_cache_refresh_interval = 15.seconds
attr_reader :id
attr_reader :id
def self.primary_key
:id
end
def initialize(id, &blk)
@id = id
@calculator = blk
end
def calculate_reactive_cache
@calculator.call
end
def result
with_reactive_cache do |data|
data
def self.primary_key
:id
end
def initialize(id, &blk)
@id = id
@calculator = blk
end
def calculate_reactive_cache
@calculator.call
end
def result
with_reactive_cache do |data|
data
end
end
end
end
let(:external_dependency_cache_class_test) do
Class.new(cache_class_test) do
self.reactive_cache_work_type = :external_dependency
end
end
let(:calculation) { -> { 2 + 2 } }
let(:cache_key) { "foo:666" }
let(:instance) { CacheTest.new(666, &calculation) }
let(:instance) { cache_class_test.new(666, &calculation) }
describe '#with_reactive_cache' do
before do
@ -47,6 +55,18 @@ describe ReactiveCaching, :use_clean_rails_memory_store_caching do
subject(:go!) { instance.result }
shared_examples 'reactive worker call' do |worker_class|
let(:instance) do
test_class.new(666, &calculation)
end
it 'performs caching with correct worker' do
expect(worker_class).to receive(:perform_async).with(test_class, 666)
go!
end
end
shared_examples 'a cacheable value' do |cached_value|
before do
stub_reactive_cache(instance, cached_value)
@ -73,10 +93,12 @@ describe ReactiveCaching, :use_clean_rails_memory_store_caching do
it { is_expected.to be_nil }
it 'refreshes cache' do
expect(ReactiveCachingWorker).to receive(:perform_async).with(CacheTest, 666)
it_behaves_like 'reactive worker call', ReactiveCachingWorker do
let(:test_class) { cache_class_test }
end
instance.with_reactive_cache { raise described_class::InvalidateReactiveCache }
it_behaves_like 'reactive worker call', ExternalServiceReactiveCachingWorker do
let(:test_class) { external_dependency_cache_class_test }
end
end
end
@ -84,10 +106,12 @@ describe ReactiveCaching, :use_clean_rails_memory_store_caching do
context 'when cache is empty' do
it { is_expected.to be_nil }
it 'enqueues a background worker to bootstrap the cache' do
expect(ReactiveCachingWorker).to receive(:perform_async).with(CacheTest, 666)
it_behaves_like 'reactive worker call', ReactiveCachingWorker do
let(:test_class) { cache_class_test }
end
go!
it_behaves_like 'reactive worker call', ExternalServiceReactiveCachingWorker do
let(:test_class) { external_dependency_cache_class_test }
end
it 'updates the cache lifespan' do
@ -168,12 +192,14 @@ describe ReactiveCaching, :use_clean_rails_memory_store_caching do
context 'with custom reactive_cache_worker_finder' do
let(:args) { %w(arg1 arg2) }
let(:instance) { CustomFinderCacheTest.new(666, &calculation) }
let(:instance) { custom_finder_cache_test.new(666, &calculation) }
class CustomFinderCacheTest < CacheTest
self.reactive_cache_worker_finder = ->(_id, *args) { from_cache(*args) }
let(:custom_finder_cache_test) do
Class.new(cache_class_test) do
self.reactive_cache_worker_finder = ->(_id, *args) { from_cache(*args) }
def self.from_cache(*args); end
def self.from_cache(*args); end
end
end
before do
@ -234,6 +260,18 @@ describe ReactiveCaching, :use_clean_rails_memory_store_caching do
go!
end
context 'when :external_dependency cache' do
let(:instance) do
external_dependency_cache_class_test.new(666, &calculation)
end
it 'enqueues a repeat worker' do
expect_reactive_cache_update_queued(instance, worker_klass: ExternalServiceReactiveCachingWorker)
go!
end
end
it 'calls a reactive_cache_updated only once if content did not change on subsequent update' do
expect(instance).to receive(:calculate_reactive_cache).twice
expect(instance).to receive(:reactive_cache_updated).once
@ -262,7 +300,7 @@ describe ReactiveCaching, :use_clean_rails_memory_store_caching do
it_behaves_like 'ExceededReactiveCacheLimit'
context 'when reactive_cache_hard_limit is overridden' do
let(:test_class) { Class.new(CacheTest) { self.reactive_cache_hard_limit = 3.megabytes } }
let(:test_class) { Class.new(cache_class_test) { self.reactive_cache_hard_limit = 3.megabytes } }
let(:instance) { test_class.new(666, &calculation) }
it_behaves_like 'successful cache'

View file

@ -523,24 +523,6 @@ describe Service do
end
end
describe "#deprecated?" do
let(:project) { create(:project, :repository) }
it 'returns false by default' do
service = create(:service, project: project)
expect(service.deprecated?).to be_falsy
end
end
describe "#deprecation_message" do
let(:project) { create(:project, :repository) }
it 'is empty by default' do
service = create(:service, project: project)
expect(service.deprecation_message).to be_nil
end
end
describe '#api_field_names' do
let(:fake_service) do
Class.new(Service) do

View file

@ -80,6 +80,34 @@ describe GlobalPolicy do
end
end
describe 'create group' do
context 'when user has the ability to create group' do
let(:current_user) { create(:user, can_create_group: true) }
it { is_expected.to be_allowed(:create_group) }
end
context 'when user does not have the ability to create group' do
let(:current_user) { create(:user, can_create_group: false) }
it { is_expected.not_to be_allowed(:create_group) }
end
end
describe 'create group with default branch protection' do
context 'when user has the ability to create group' do
let(:current_user) { create(:user, can_create_group: true) }
it { is_expected.to be_allowed(:create_group_with_default_branch_protection) }
end
context 'when user does not have the ability to create group' do
let(:current_user) { create(:user, can_create_group: false) }
it { is_expected.not_to be_allowed(:create_group_with_default_branch_protection) }
end
end
describe 'custom attributes' do
context 'regular user' do
it { is_expected.not_to be_allowed(:read_custom_attribute) }

View file

@ -642,6 +642,33 @@ describe API::Groups do
expect(json_response['default_branch_protection']).to eq(::Gitlab::Access::MAINTAINER_PROJECT_ACCESS)
end
context 'updating the `default_branch_protection` attribute' do
subject do
put api("/groups/#{group1.id}", user1), params: { default_branch_protection: ::Gitlab::Access::PROTECTION_NONE }
end
context 'for users who have the ability to update default_branch_protection' do
it 'updates the attribute' do
subject
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['default_branch_protection']).to eq(Gitlab::Access::PROTECTION_NONE)
end
end
context 'for users who does not have the ability to update default_branch_protection`' do
it 'does not update the attribute' do
allow(Ability).to receive(:allowed?).and_call_original
allow(Ability).to receive(:allowed?).with(user1, :update_default_branch_protection, group1) { false }
subject
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['default_branch_protection']).not_to eq(Gitlab::Access::PROTECTION_NONE)
end
end
end
context 'malicious group name' do
subject { put api("/groups/#{group1.id}", user1), params: { name: "<SCRIPT>alert('DOUBLE-ATTACK!')</SCRIPT>" } }
@ -1111,6 +1138,33 @@ describe API::Groups do
it { expect { subject }.not_to change { Group.count } }
end
context 'when creating a group with `default_branch_protection` attribute' do
let(:params) { attributes_for_group_api default_branch_protection: Gitlab::Access::PROTECTION_NONE }
subject { post api("/groups", user3), params: params }
context 'for users who have the ability to create a group with `default_branch_protection`' do
it 'creates group with the specified branch protection level' do
subject
expect(response).to have_gitlab_http_status(:created)
expect(json_response['default_branch_protection']).to eq(Gitlab::Access::PROTECTION_NONE)
end
end
context 'for users who do not have the ability to create a group with `default_branch_protection`' do
it 'does not create the group with the specified branch protection level' do
allow(Ability).to receive(:allowed?).and_call_original
allow(Ability).to receive(:allowed?).with(user3, :create_group_with_default_branch_protection) { false }
subject
expect(response).to have_gitlab_http_status(:created)
expect(json_response['default_branch_protection']).not_to eq(Gitlab::Access::PROTECTION_NONE)
end
end
end
it "does not create group, duplicate" do
post api("/groups", user3), params: { name: 'Duplicate Test', path: group2.path }

View file

@ -3,10 +3,11 @@
require 'spec_helper'
describe Discussions::CaptureDiffNotePositionService do
subject { described_class.new(note.noteable, paths) }
context 'image note on diff' do
let!(:note) { create(:image_diff_note_on_merge_request) }
subject { described_class.new(note.noteable, ['files/images/any_image.png']) }
let(:paths) { ['files/images/any_image.png'] }
it 'is note affected by the service' do
expect(Gitlab::Diff::PositionTracer).not_to receive(:new)
@ -18,8 +19,7 @@ describe Discussions::CaptureDiffNotePositionService do
context 'when empty paths are passed as a param' do
let!(:note) { create(:diff_note_on_merge_request) }
subject { described_class.new(note.noteable, []) }
let(:paths) { [] }
it 'does not calculate positons' do
expect(Gitlab::Diff::PositionTracer).not_to receive(:new)
@ -28,4 +28,19 @@ describe Discussions::CaptureDiffNotePositionService do
expect(note.diff_note_positions).to be_empty
end
end
context 'when position tracer returned nil position' do
let!(:note) { create(:diff_note_on_merge_request) }
let(:paths) { ['files/any_file.txt'] }
it 'does not create diff note position' do
expect(note.noteable).to receive(:merge_ref_head).and_return(double.as_null_object)
expect_next_instance_of(Gitlab::Diff::PositionTracer) do |tracer|
expect(tracer).to receive(:trace).and_return({ position: nil })
end
expect(subject.execute(note.discussion)).to eq(nil)
expect(note.diff_note_positions).to be_empty
end
end
end

View file

@ -24,6 +24,27 @@ describe Groups::CreateService, '#execute' do
end
end
context 'creating a group with `default_branch_protection` attribute' do
let(:params) { group_params.merge(default_branch_protection: Gitlab::Access::PROTECTION_NONE) }
let(:service) { described_class.new(user, params) }
let(:created_group) { service.execute }
context 'for users who have the ability to create a group with `default_branch_protection`' do
it 'creates group with the specified branch protection level' do
expect(created_group.default_branch_protection).to eq(Gitlab::Access::PROTECTION_NONE)
end
end
context 'for users who do not have the ability to create a group with `default_branch_protection`' do
it 'does not create the group with the specified branch protection level' do
allow(Ability).to receive(:allowed?).and_call_original
allow(Ability).to receive(:allowed?).with(user, :create_group_with_default_branch_protection) { false }
expect(created_group.default_branch_protection).not_to eq(Gitlab::Access::PROTECTION_NONE)
end
end
end
describe 'creating a top level group' do
let(:service) { described_class.new(user, group_params) }

View file

@ -148,6 +148,26 @@ describe Groups::UpdateService do
end
end
context 'updating default_branch_protection' do
let(:service) do
described_class.new(internal_group, user, default_branch_protection: Gitlab::Access::PROTECTION_NONE)
end
context 'for users who have the ability to update default_branch_protection' do
it 'updates the attribute' do
internal_group.add_owner(user)
expect { service.execute }.to change { internal_group.default_branch_protection }.to(Gitlab::Access::PROTECTION_NONE)
end
end
context 'for users who do not have the ability to update default_branch_protection' do
it 'does not update the attribute' do
expect { service.execute }.not_to change { internal_group.default_branch_protection }
end
end
end
context 'rename group' do
let!(:service) { described_class.new(internal_group, user, path: SecureRandom.hex) }

View file

@ -10,8 +10,11 @@ module ReactiveCachingHelpers
end
def stub_reactive_cache(subject = nil, data = nil, *qualifiers)
allow(ReactiveCachingWorker).to receive(:perform_async)
allow(ReactiveCachingWorker).to receive(:perform_in)
ReactiveCaching::WORK_TYPE.values.each do |worker|
allow(worker).to receive(:perform_async)
allow(worker).to receive(:perform_in)
end
write_reactive_cache(subject, data, *qualifiers) unless subject.nil?
end
@ -42,8 +45,8 @@ module ReactiveCachingHelpers
Rails.cache.write(alive_reactive_cache_key(subject, *qualifiers), true)
end
def expect_reactive_cache_update_queued(subject)
expect(ReactiveCachingWorker)
def expect_reactive_cache_update_queued(subject, worker_klass: ReactiveCachingWorker)
expect(worker_klass)
.to receive(:perform_in)
.with(subject.class.reactive_cache_refresh_interval, subject.class, subject.id)
end

View file

@ -35,7 +35,8 @@ RSpec.shared_context 'GroupPolicy context' do
:change_visibility_level,
:set_note_created_at,
:create_subgroup,
:read_statistics
:read_statistics,
:update_default_branch_protection
].compact
end

View file

@ -0,0 +1,59 @@
# frozen_string_literal: true
RSpec.shared_examples 'reactive cacheable worker' do
describe '#perform' do
context 'when reactive cache worker class is found' do
let!(:cluster) { create(:cluster, :project, :provided_by_gcp) }
let(:project) { cluster.project }
let!(:environment) { create(:environment, project: project) }
it 'calls #exclusively_update_reactive_cache!' do
expect_any_instance_of(Environment).to receive(:exclusively_update_reactive_cache!)
described_class.new.perform("Environment", environment.id)
end
context 'when ReactiveCaching::ExceededReactiveCacheLimit is raised' do
it 'avoids failing the job and tracks via Gitlab::ErrorTracking' do
allow_any_instance_of(Environment).to receive(:exclusively_update_reactive_cache!)
.and_raise(ReactiveCaching::ExceededReactiveCacheLimit)
expect(Gitlab::ErrorTracking).to receive(:track_exception)
.with(kind_of(ReactiveCaching::ExceededReactiveCacheLimit))
described_class.new.perform("Environment", environment.id)
end
end
end
context 'when reactive cache worker class is not found' do
it 'raises no error' do
expect { described_class.new.perform("Environment", -1) }.not_to raise_error
end
end
context 'when reactive cache worker class is invalid' do
it 'raises no error' do
expect { described_class.new.perform("FooBarKux", -1) }.not_to raise_error
end
end
end
describe 'worker context' do
it 'sets the related class on the job' do
described_class.perform_async('Environment', 1, 'other', 'argument')
scheduled_job = described_class.jobs.first
expect(scheduled_job).to include('meta.related_class' => 'Environment')
end
it 'sets the related class on the job when it was passed as a class' do
described_class.perform_async(Project, 1, 'other', 'argument')
scheduled_job = described_class.jobs.first
expect(scheduled_job).to include('meta.related_class' => 'Project')
end
end
end

View file

@ -0,0 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
describe ExternalServiceReactiveCachingWorker do
it_behaves_like 'reactive cacheable worker'
end

View file

@ -3,47 +3,5 @@
require 'spec_helper'
describe ReactiveCachingWorker do
describe '#perform' do
context 'when user configured kubernetes from CI/CD > Clusters' do
let!(:cluster) { create(:cluster, :project, :provided_by_gcp) }
let(:project) { cluster.project }
let!(:environment) { create(:environment, project: project) }
it 'calls #exclusively_update_reactive_cache!' do
expect_any_instance_of(Environment).to receive(:exclusively_update_reactive_cache!)
described_class.new.perform("Environment", environment.id)
end
context 'when ReactiveCaching::ExceededReactiveCacheLimit is raised' do
it 'avoids failing the job and tracks via Gitlab::ErrorTracking' do
allow_any_instance_of(Environment).to receive(:exclusively_update_reactive_cache!)
.and_raise(ReactiveCaching::ExceededReactiveCacheLimit)
expect(Gitlab::ErrorTracking).to receive(:track_exception)
.with(kind_of(ReactiveCaching::ExceededReactiveCacheLimit))
described_class.new.perform("Environment", environment.id)
end
end
end
end
describe 'worker context' do
it 'sets the related class on the job' do
described_class.perform_async('Environment', 1, 'other', 'argument')
scheduled_job = described_class.jobs.first
expect(scheduled_job).to include('meta.related_class' => 'Environment')
end
it 'sets the related class on the job when it was passed as a class' do
described_class.perform_async(Project, 1, 'other', 'argument')
scheduled_job = described_class.jobs.first
expect(scheduled_job).to include('meta.related_class' => 'Project')
end
end
it_behaves_like 'reactive cacheable worker'
end