Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
b5820a6bcd
commit
7fcda12793
3
Gemfile
3
Gemfile
|
@ -74,6 +74,9 @@ gem 'acme-client', '~> 2.0', '>= 2.0.6'
|
|||
# Browser detection
|
||||
gem 'browser', '~> 4.2'
|
||||
|
||||
# OS detection for usage ping
|
||||
gem 'ohai', '~> 16.10'
|
||||
|
||||
# GPG
|
||||
gem 'gpgme', '~> 2.0.19'
|
||||
|
||||
|
|
44
Gemfile.lock
44
Gemfile.lock
|
@ -172,6 +172,14 @@ GEM
|
|||
cbor (0.5.9.6)
|
||||
character_set (1.4.0)
|
||||
charlock_holmes (0.7.7)
|
||||
chef-config (16.10.17)
|
||||
addressable
|
||||
chef-utils (= 16.10.17)
|
||||
fuzzyurl
|
||||
mixlib-config (>= 2.2.12, < 4.0)
|
||||
mixlib-shellout (>= 2.0, < 4.0)
|
||||
tomlrb (~> 1.2)
|
||||
chef-utils (16.10.17)
|
||||
childprocess (3.0.0)
|
||||
chunky_png (1.3.5)
|
||||
citrus (3.0.2)
|
||||
|
@ -348,6 +356,8 @@ GEM
|
|||
ffi-compiler (1.0.1)
|
||||
ffi (>= 1.0.0)
|
||||
rake
|
||||
ffi-yajl (2.3.4)
|
||||
libyajl2 (~> 1.2)
|
||||
flipper (0.17.1)
|
||||
flipper-active_record (0.17.1)
|
||||
activerecord (>= 4.2, < 7)
|
||||
|
@ -403,6 +413,7 @@ GEM
|
|||
fuubar (2.2.0)
|
||||
rspec-core (~> 3.0)
|
||||
ruby-progressbar (~> 1.4)
|
||||
fuzzyurl (0.9.0)
|
||||
gemoji (3.0.1)
|
||||
gemojione (3.3.0)
|
||||
json
|
||||
|
@ -668,6 +679,7 @@ GEM
|
|||
actionmailer (>= 3.2)
|
||||
letter_opener (~> 1.0)
|
||||
railties (>= 3.2)
|
||||
libyajl2 (1.2.0)
|
||||
license_finder (6.0.0)
|
||||
bundler
|
||||
rubyzip (>= 1, < 3)
|
||||
|
@ -717,6 +729,12 @@ GEM
|
|||
mini_mime (1.0.2)
|
||||
mini_portile2 (2.5.0)
|
||||
minitest (5.11.3)
|
||||
mixlib-cli (2.1.8)
|
||||
mixlib-config (3.0.9)
|
||||
tomlrb
|
||||
mixlib-log (3.0.9)
|
||||
mixlib-shellout (3.2.5)
|
||||
chef-utils
|
||||
ms_rest (0.7.6)
|
||||
concurrent-ruby (~> 1.0)
|
||||
faraday (>= 0.9, < 2.0.0)
|
||||
|
@ -741,6 +759,8 @@ GEM
|
|||
connection_pool (~> 2.2)
|
||||
net-ldap (0.16.3)
|
||||
net-ntp (2.1.3)
|
||||
net-scp (3.0.0)
|
||||
net-ssh (>= 2.6.5, < 7.0.0)
|
||||
net-ssh (6.0.0)
|
||||
netrc (0.11.0)
|
||||
nio4r (2.5.4)
|
||||
|
@ -764,6 +784,19 @@ GEM
|
|||
octokit (4.20.0)
|
||||
faraday (>= 0.9)
|
||||
sawyer (~> 0.8.0, >= 0.5.3)
|
||||
ohai (16.10.6)
|
||||
chef-config (>= 12.8, < 17)
|
||||
chef-utils (>= 16.0, < 17)
|
||||
ffi (~> 1.9)
|
||||
ffi-yajl (~> 2.2)
|
||||
ipaddress
|
||||
mixlib-cli (>= 1.7.0)
|
||||
mixlib-config (>= 2.0, < 4.0)
|
||||
mixlib-log (>= 2.0.1, < 4.0)
|
||||
mixlib-shellout (>= 2.0, < 4.0)
|
||||
plist (~> 3.1)
|
||||
train-core
|
||||
wmi-lite (~> 1.0)
|
||||
oj (3.10.6)
|
||||
omniauth (1.9.0)
|
||||
hashie (>= 3.4.6, < 3.7.0)
|
||||
|
@ -857,6 +890,7 @@ GEM
|
|||
railties (>= 4.0.0)
|
||||
pg (1.2.3)
|
||||
pg_query (1.3.0)
|
||||
plist (3.6.0)
|
||||
png_quantizator (0.2.1)
|
||||
po_to_json (1.0.1)
|
||||
json (>= 1.6.0)
|
||||
|
@ -1196,9 +1230,17 @@ GEM
|
|||
parslet (~> 1.8.0)
|
||||
toml-rb (1.0.0)
|
||||
citrus (~> 3.0, > 3.0)
|
||||
tomlrb (1.3.0)
|
||||
tpm-key_attestation (0.9.0)
|
||||
bindata (~> 2.4)
|
||||
openssl-signature_algorithm (~> 0.4.0)
|
||||
train-core (3.4.9)
|
||||
addressable (~> 2.5)
|
||||
ffi (!= 1.13.0)
|
||||
json (>= 1.8, < 3.0)
|
||||
mixlib-shellout (>= 2.0, < 4.0)
|
||||
net-scp (>= 1.2, < 4.0)
|
||||
net-ssh (>= 2.9, < 7.0)
|
||||
truncato (0.7.11)
|
||||
htmlentities (~> 4.3.1)
|
||||
nokogiri (>= 1.7.0, <= 2.0)
|
||||
|
@ -1271,6 +1313,7 @@ GEM
|
|||
expression_parser
|
||||
rinku
|
||||
with_env (1.1.0)
|
||||
wmi-lite (1.0.5)
|
||||
xml-simple (1.1.5)
|
||||
xpath (3.2.0)
|
||||
nokogiri (~> 1.8)
|
||||
|
@ -1443,6 +1486,7 @@ DEPENDENCIES
|
|||
nokogiri (~> 1.11.1)
|
||||
oauth2 (~> 1.4)
|
||||
octokit (~> 4.15)
|
||||
ohai (~> 16.10)
|
||||
oj (~> 3.10.6)
|
||||
omniauth (~> 1.8)
|
||||
omniauth-atlassian-oauth2 (~> 0.2.0)
|
||||
|
|
|
@ -10,7 +10,7 @@ import {
|
|||
} from '@gitlab/ui';
|
||||
import { mapActions, mapState } from 'vuex';
|
||||
import { isListDraggable } from '~/boards/boards_util';
|
||||
import { isScopedLabel } from '~/lib/utils/common_utils';
|
||||
import { isScopedLabel, parseBoolean } from '~/lib/utils/common_utils';
|
||||
import { BV_HIDE_TOOLTIP } from '~/lib/utils/constants';
|
||||
import { n__, s__, __ } from '~/locale';
|
||||
import sidebarEventHub from '~/sidebar/event_hub';
|
||||
|
@ -69,7 +69,7 @@ export default {
|
|||
},
|
||||
},
|
||||
computed: {
|
||||
...mapState(['activeId']),
|
||||
...mapState(['activeId', 'isEpicBoard']),
|
||||
isLoggedIn() {
|
||||
return Boolean(this.currentUserId);
|
||||
},
|
||||
|
@ -97,11 +97,14 @@ export default {
|
|||
showListDetails() {
|
||||
return !this.list.collapsed || !this.isSwimlanesHeader;
|
||||
},
|
||||
issuesCount() {
|
||||
itemsCount() {
|
||||
return this.list.issuesCount;
|
||||
},
|
||||
issuesTooltipLabel() {
|
||||
return n__(`%d issue`, `%d issues`, this.issuesCount);
|
||||
countIcon() {
|
||||
return 'issues';
|
||||
},
|
||||
itemsTooltipLabel() {
|
||||
return n__(`%d issue`, `%d issues`, this.itemsCount);
|
||||
},
|
||||
chevronTooltip() {
|
||||
return this.list.collapsed ? this.$options.i18n.expand : this.$options.i18n.collapse;
|
||||
|
@ -110,7 +113,7 @@ export default {
|
|||
return this.list.collapsed ? 'chevron-down' : 'chevron-right';
|
||||
},
|
||||
isNewIssueShown() {
|
||||
return this.listType === ListType.backlog || this.showListHeaderButton;
|
||||
return (this.listType === ListType.backlog || this.showListHeaderButton) && !this.isEpicBoard;
|
||||
},
|
||||
isSettingsShown() {
|
||||
return (
|
||||
|
@ -131,8 +134,14 @@ export default {
|
|||
return !this.disabled && isListDraggable(this.list);
|
||||
},
|
||||
},
|
||||
created() {
|
||||
const localCollapsed = parseBoolean(localStorage.getItem(`${this.uniqueKey}.collapsed`));
|
||||
if ((!this.isLoggedIn || this.isEpicBoard) && localCollapsed) {
|
||||
this.toggleListCollapsed({ listId: this.list.id, collapsed: true });
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['updateList', 'setActiveId']),
|
||||
...mapActions(['updateList', 'setActiveId', 'toggleListCollapsed']),
|
||||
openSidebarSettings() {
|
||||
if (this.activeId === inactiveId) {
|
||||
sidebarEventHub.$emit('sidebar.closeAll');
|
||||
|
@ -148,10 +157,10 @@ export default {
|
|||
eventHub.$emit(`toggle-issue-form-${this.list.id}`);
|
||||
},
|
||||
toggleExpanded() {
|
||||
// eslint-disable-next-line vue/no-mutating-props
|
||||
this.list.collapsed = !this.list.collapsed;
|
||||
const collapsed = !this.list.collapsed;
|
||||
this.toggleListCollapsed({ listId: this.list.id, collapsed });
|
||||
|
||||
if (!this.isLoggedIn) {
|
||||
if (!this.isLoggedIn || this.isEpicBoard) {
|
||||
this.addToLocalStorage();
|
||||
} else {
|
||||
this.updateListFunction();
|
||||
|
@ -163,7 +172,7 @@ export default {
|
|||
},
|
||||
addToLocalStorage() {
|
||||
if (AccessorUtilities.isLocalStorageAccessSafe()) {
|
||||
localStorage.setItem(`${this.uniqueKey}.expanded`, !this.list.collapsed);
|
||||
localStorage.setItem(`${this.uniqueKey}.collapsed`, this.list.collapsed);
|
||||
}
|
||||
},
|
||||
updateListFunction() {
|
||||
|
@ -203,6 +212,7 @@ export default {
|
|||
class="board-title-caret no-drag gl-cursor-pointer"
|
||||
category="tertiary"
|
||||
size="small"
|
||||
data-testid="board-title-caret"
|
||||
@click="toggleExpanded"
|
||||
/>
|
||||
<!-- EE start -->
|
||||
|
@ -301,11 +311,11 @@ export default {
|
|||
<div v-if="list.maxIssueCount !== 0">
|
||||
•
|
||||
<gl-sprintf :message="__('%{issuesSize} with a limit of %{maxIssueCount}')">
|
||||
<template #issuesSize>{{ issuesTooltipLabel }}</template>
|
||||
<template #issuesSize>{{ itemsTooltipLabel }}</template>
|
||||
<template #maxIssueCount>{{ list.maxIssueCount }}</template>
|
||||
</gl-sprintf>
|
||||
</div>
|
||||
<div v-else>• {{ issuesTooltipLabel }}</div>
|
||||
<div v-else>• {{ itemsTooltipLabel }}</div>
|
||||
<div v-if="weightFeatureAvailable">
|
||||
•
|
||||
<gl-sprintf :message="__('%{totalWeight} total weight')">
|
||||
|
@ -323,13 +333,13 @@ export default {
|
|||
}"
|
||||
>
|
||||
<span class="gl-display-inline-flex">
|
||||
<gl-tooltip :target="() => $refs.issueCount" :title="issuesTooltipLabel" />
|
||||
<span ref="issueCount" class="issue-count-badge-count">
|
||||
<gl-icon class="gl-mr-2" name="issues" />
|
||||
<issue-count :issues-size="issuesCount" :max-issue-count="list.maxIssueCount" />
|
||||
<gl-tooltip :target="() => $refs.itemCount" :title="itemsTooltipLabel" />
|
||||
<span ref="itemCount" class="issue-count-badge-count">
|
||||
<gl-icon class="gl-mr-2" :name="countIcon" />
|
||||
<issue-count :issues-size="itemsCount" :max-issue-count="list.maxIssueCount" />
|
||||
</span>
|
||||
<!-- EE start -->
|
||||
<template v-if="weightFeatureAvailable">
|
||||
<template v-if="weightFeatureAvailable && !isEpicBoard">
|
||||
<gl-tooltip :target="() => $refs.weightTooltip" :title="weightCountToolTip" />
|
||||
<span ref="weightTooltip" class="gl-display-inline-flex gl-ml-3">
|
||||
<gl-icon class="gl-mr-2" name="weight" />
|
||||
|
|
|
@ -86,7 +86,11 @@ export default {
|
|||
<template>
|
||||
<span>
|
||||
<span ref="issueDueDate" :class="cssClass" class="board-card-info card-number">
|
||||
<gl-icon :class="{ 'text-danger': isPastDue }" class="board-card-info-icon" name="calendar" />
|
||||
<gl-icon
|
||||
:class="{ 'text-danger': isPastDue }"
|
||||
class="board-card-info-icon gl-mr-2"
|
||||
name="calendar"
|
||||
/>
|
||||
<time :class="{ 'text-danger': isPastDue }" datetime="date" class="board-card-info-text">{{
|
||||
body
|
||||
}}</time>
|
||||
|
|
|
@ -37,7 +37,7 @@ export default {
|
|||
<template>
|
||||
<span>
|
||||
<span ref="issueTimeEstimate" class="board-card-info card-number">
|
||||
<gl-icon name="hourglass" class="board-card-info-icon" />
|
||||
<gl-icon name="hourglass" class="board-card-info-icon gl-mr-2" />
|
||||
<time class="board-card-info-text">{{ timeEstimate }}</time>
|
||||
</span>
|
||||
<gl-tooltip
|
||||
|
|
|
@ -256,6 +256,10 @@ export default {
|
|||
});
|
||||
},
|
||||
|
||||
toggleListCollapsed: ({ commit }, { listId, collapsed }) => {
|
||||
commit(types.TOGGLE_LIST_COLLAPSED, { listId, collapsed });
|
||||
},
|
||||
|
||||
removeList: ({ state, commit }, listId) => {
|
||||
const listsBackup = { ...state.boardLists };
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ export const RECEIVE_ADD_LIST_SUCCESS = 'RECEIVE_ADD_LIST_SUCCESS';
|
|||
export const RECEIVE_ADD_LIST_ERROR = 'RECEIVE_ADD_LIST_ERROR';
|
||||
export const MOVE_LIST = 'MOVE_LIST';
|
||||
export const UPDATE_LIST_FAILURE = 'UPDATE_LIST_FAILURE';
|
||||
export const TOGGLE_LIST_COLLAPSED = 'TOGGLE_LIST_COLLAPSED';
|
||||
export const REMOVE_LIST = 'REMOVE_LIST';
|
||||
export const REMOVE_LIST_FAILURE = 'REMOVE_LIST_FAILURE';
|
||||
export const REQUEST_ITEMS_FOR_LIST = 'REQUEST_ITEMS_FOR_LIST';
|
||||
|
|
|
@ -105,6 +105,10 @@ export default {
|
|||
Vue.set(state, 'boardLists', backupList);
|
||||
},
|
||||
|
||||
[mutationTypes.TOGGLE_LIST_COLLAPSED]: (state, { listId, collapsed }) => {
|
||||
Vue.set(state.boardLists[listId], 'collapsed', collapsed);
|
||||
},
|
||||
|
||||
[mutationTypes.REMOVE_LIST]: (state, listId) => {
|
||||
Vue.delete(state.boardLists, listId);
|
||||
},
|
||||
|
|
|
@ -200,9 +200,9 @@ export default {
|
|||
<gl-form-input id="fork-name" v-model="fork.name" data-testid="fork-name-input" required />
|
||||
</gl-form-group>
|
||||
|
||||
<div class="gl-display-flex">
|
||||
<div class="gl-w-half">
|
||||
<gl-form-group label="Project URL" label-for="fork-url" class="gl-pr-2">
|
||||
<div class="gl-md-display-flex">
|
||||
<div class="gl-flex-basis-half">
|
||||
<gl-form-group label="Project URL" label-for="fork-url" class="gl-md-mr-3">
|
||||
<gl-form-input-group>
|
||||
<template #prepend>
|
||||
<gl-input-group-text>
|
||||
|
@ -225,8 +225,8 @@ export default {
|
|||
</gl-form-input-group>
|
||||
</gl-form-group>
|
||||
</div>
|
||||
<div class="gl-w-half">
|
||||
<gl-form-group label="Project slug" label-for="fork-slug" class="gl-pl-2">
|
||||
<div class="gl-flex-basis-half">
|
||||
<gl-form-group label="Project slug" label-for="fork-slug" class="gl-md-ml-3">
|
||||
<gl-form-input
|
||||
id="fork-slug"
|
||||
v-model="fork.slug"
|
||||
|
|
|
@ -55,7 +55,12 @@ export default {
|
|||
>
|
||||
<div class="d-flex align-items-center">
|
||||
<gl-icon :size="16" :name="option.icon" />
|
||||
<span class="font-weight-bold ml-1 js-visibility-option">{{ option.label }}</span>
|
||||
<span
|
||||
class="font-weight-bold ml-1 js-visibility-option"
|
||||
data-qa-selector="visibility_content"
|
||||
:data-qa-visibility="option.label"
|
||||
>{{ option.label }}</span
|
||||
>
|
||||
</div>
|
||||
<template #help>{{
|
||||
isProjectSnippet && option.description_project
|
||||
|
|
|
@ -35,5 +35,4 @@
|
|||
@import './pages/sherlock';
|
||||
@import './pages/storage_quota';
|
||||
@import './pages/tree';
|
||||
@import './pages/trials';
|
||||
@import './pages/users';
|
||||
|
|
|
@ -830,8 +830,8 @@ $ci-variable-remove-button-width: calc(1em + #{2 * $gl-padding});
|
|||
/*
|
||||
GitLab Plans
|
||||
*/
|
||||
$gl-gold-plan: #d4af37;
|
||||
$gl-silver-plan: #91a1ab;
|
||||
$gl-ultimate-plan: #d4af37;
|
||||
$gl-premium-plan: #91a1ab;
|
||||
$gl-bronze-plan: #cd7f32;
|
||||
|
||||
/*
|
||||
|
|
|
@ -339,3 +339,19 @@
|
|||
display: inline;
|
||||
}
|
||||
}
|
||||
|
||||
.gl-select2-html5-required-fix {
|
||||
.select2-container {
|
||||
+ .select2 {
|
||||
@include gl-opacity-0;
|
||||
@include gl-border-0;
|
||||
@include gl-bg-none;
|
||||
@include gl-bg-transparent;
|
||||
display: block !important;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
z-index: -1;
|
||||
margin: -3px auto 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
/*
|
||||
* A CSS cross-browser fix for Select2 failire to display HTML5 required warnings
|
||||
* MR link https://gitlab.com/gitlab-org/gitlab/-/merge_requests/22716
|
||||
*/
|
||||
.gl-select2-html5-required-fix div.select2-container+select.select2 {
|
||||
@include gl-opacity-0;
|
||||
@include gl-border-0;
|
||||
@include gl-bg-none;
|
||||
@include gl-bg-transparent;
|
||||
display: block !important;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
z-index: -1;
|
||||
margin: -3px auto 0;
|
||||
}
|
|
@ -117,6 +117,7 @@
|
|||
flex-basis: 25%;
|
||||
}
|
||||
|
||||
// Will be moved to @gitlab/ui in https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1168
|
||||
.gl-md-ml-3 {
|
||||
@media (min-width: $breakpoint-md) {
|
||||
margin-left: $gl-spacing-scale-3;
|
||||
|
|
|
@ -237,7 +237,6 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
|
|||
[
|
||||
*::ApplicationSettingsHelper.visible_attributes,
|
||||
*::ApplicationSettingsHelper.external_authorization_service_attributes,
|
||||
*ApplicationSetting.repository_storages_weighted_attributes,
|
||||
*ApplicationSetting.kroki_formats_attributes.keys.map { |key| "kroki_formats_#{key}".to_sym },
|
||||
:lets_encrypt_notification_email,
|
||||
:lets_encrypt_terms_of_service_accepted,
|
||||
|
@ -248,8 +247,8 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
|
|||
:default_branch_name,
|
||||
disabled_oauth_sign_in_sources: [],
|
||||
import_sources: [],
|
||||
repository_storages: [],
|
||||
restricted_visibility_levels: []
|
||||
restricted_visibility_levels: [],
|
||||
repository_storages_weighted: {}
|
||||
]
|
||||
end
|
||||
|
||||
|
|
|
@ -5,8 +5,11 @@ module Mutations
|
|||
module Issues
|
||||
class IssueMoveList < Mutations::Issues::Base
|
||||
graphql_name 'IssueMoveList'
|
||||
BoardGID = ::Types::GlobalIDType[::Board]
|
||||
ListID = ::GraphQL::ID_TYPE
|
||||
IssueID = ::GraphQL::ID_TYPE
|
||||
|
||||
argument :board_id, GraphQL::ID_TYPE,
|
||||
argument :board_id, BoardGID,
|
||||
required: true,
|
||||
loads: Types::BoardType,
|
||||
description: 'Global ID of the board that the issue is in.'
|
||||
|
@ -19,19 +22,19 @@ module Mutations
|
|||
required: true,
|
||||
description: 'IID of the issue to mutate.'
|
||||
|
||||
argument :from_list_id, GraphQL::ID_TYPE,
|
||||
argument :from_list_id, ListID,
|
||||
required: false,
|
||||
description: 'ID of the board list that the issue will be moved from.'
|
||||
|
||||
argument :to_list_id, GraphQL::ID_TYPE,
|
||||
argument :to_list_id, ListID,
|
||||
required: false,
|
||||
description: 'ID of the board list that the issue will be moved to.'
|
||||
|
||||
argument :move_before_id, GraphQL::ID_TYPE,
|
||||
argument :move_before_id, IssueID,
|
||||
required: false,
|
||||
description: 'ID of issue that should be placed before the current issue.'
|
||||
|
||||
argument :move_after_id, GraphQL::ID_TYPE,
|
||||
argument :move_after_id, IssueID,
|
||||
required: false,
|
||||
description: 'ID of issue that should be placed after the current issue.'
|
||||
|
||||
|
|
|
@ -37,13 +37,8 @@ module ApplicationSettingsHelper
|
|||
end
|
||||
|
||||
def storage_weights
|
||||
ApplicationSetting.repository_storages_weighted_attributes.map do |attribute|
|
||||
storage = attribute.to_s.delete_prefix('repository_storages_weighted_')
|
||||
{
|
||||
name: attribute,
|
||||
label: storage,
|
||||
value: @application_setting.repository_storages_weighted[storage] || 0
|
||||
}
|
||||
Gitlab.config.repositories.storages.keys.each_with_object(OpenStruct.new) do |storage, weights|
|
||||
weights[storage.to_sym] = @application_setting.repository_storages_weighted[storage] || 0
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -25,10 +25,6 @@ class ApplicationSetting < ApplicationRecord
|
|||
alias_attribute :instance_group_id, :instance_administrators_group_id
|
||||
alias_attribute :instance_administrators_group, :instance_group
|
||||
|
||||
def self.repository_storages_weighted_attributes
|
||||
@repository_storages_weighted_atributes ||= Gitlab.config.repositories.storages.keys.map { |k| "repository_storages_weighted_#{k}".to_sym }.freeze
|
||||
end
|
||||
|
||||
def self.kroki_formats_attributes
|
||||
{
|
||||
blockdiag: {
|
||||
|
@ -44,7 +40,6 @@ class ApplicationSetting < ApplicationRecord
|
|||
end
|
||||
|
||||
store_accessor :kroki_formats, *ApplicationSetting.kroki_formats_attributes.keys, prefix: true
|
||||
store_accessor :repository_storages_weighted, *Gitlab.config.repositories.storages.keys, prefix: true
|
||||
|
||||
# Include here so it can override methods from
|
||||
# `add_authentication_token_field`
|
||||
|
@ -58,8 +53,9 @@ class ApplicationSetting < ApplicationRecord
|
|||
serialize :domain_allowlist, Array # rubocop:disable Cop/ActiveRecordSerialize
|
||||
serialize :domain_denylist, Array # rubocop:disable Cop/ActiveRecordSerialize
|
||||
serialize :repository_storages # rubocop:disable Cop/ActiveRecordSerialize
|
||||
serialize :asset_proxy_allowlist, Array # rubocop:disable Cop/ActiveRecordSerialize
|
||||
|
||||
# See https://gitlab.com/gitlab-org/gitlab/-/issues/300916
|
||||
serialize :asset_proxy_allowlist, Array # rubocop:disable Cop/ActiveRecordSerialize
|
||||
serialize :asset_proxy_whitelist, Array # rubocop:disable Cop/ActiveRecordSerialize
|
||||
|
||||
cache_markdown_field :sign_in_text
|
||||
|
@ -502,6 +498,7 @@ class ApplicationSetting < ApplicationRecord
|
|||
inclusion: { in: [true, false], message: _('must be a boolean value') }
|
||||
|
||||
before_validation :ensure_uuid!
|
||||
before_validation :coerce_repository_storages_weighted, if: :repository_storages_weighted_changed?
|
||||
|
||||
before_save :ensure_runners_registration_token
|
||||
before_save :ensure_health_check_access_token
|
||||
|
@ -582,12 +579,6 @@ class ApplicationSetting < ApplicationRecord
|
|||
recaptcha_enabled || login_recaptcha_protection_enabled
|
||||
end
|
||||
|
||||
repository_storages_weighted_attributes.each do |attribute|
|
||||
define_method :"#{attribute}=" do |value|
|
||||
super(value.to_i)
|
||||
end
|
||||
end
|
||||
|
||||
kroki_formats_attributes.keys.each do |key|
|
||||
define_method :"kroki_formats_#{key}=" do |value|
|
||||
super(::Gitlab::Utils.to_boolean(value))
|
||||
|
|
|
@ -280,23 +280,24 @@ module ApplicationSettingImplementation
|
|||
self.notes_create_limit_allowlist = strings_to_array(values).map(&:downcase)
|
||||
end
|
||||
|
||||
def asset_proxy_allowlist=(values)
|
||||
def asset_proxy_whitelist=(values)
|
||||
values = strings_to_array(values) if values.is_a?(String)
|
||||
|
||||
# make sure we always allow the running host
|
||||
values << Gitlab.config.gitlab.host unless values.include?(Gitlab.config.gitlab.host)
|
||||
|
||||
self[:asset_proxy_allowlist] = values
|
||||
self[:asset_proxy_whitelist] = values
|
||||
end
|
||||
alias_method :asset_proxy_allowlist=, :asset_proxy_whitelist=
|
||||
|
||||
def asset_proxy_allowlist
|
||||
read_attribute(:asset_proxy_whitelist)
|
||||
end
|
||||
|
||||
def repository_storages
|
||||
Array(read_attribute(:repository_storages))
|
||||
end
|
||||
|
||||
def repository_storages_weighted
|
||||
read_attribute(:repository_storages_weighted)
|
||||
end
|
||||
|
||||
def commit_email_hostname
|
||||
super.presence || self.class.default_commit_email_hostname
|
||||
end
|
||||
|
@ -328,9 +329,10 @@ module ApplicationSettingImplementation
|
|||
|
||||
def normalized_repository_storage_weights
|
||||
strong_memoize(:normalized_repository_storage_weights) do
|
||||
weights_total = repository_storages_weighted.values.reduce(:+)
|
||||
repository_storages_weights = repository_storages_weighted.slice(*Gitlab.config.repositories.storages.keys)
|
||||
weights_total = repository_storages_weights.values.reduce(:+)
|
||||
|
||||
repository_storages_weighted.transform_values do |w|
|
||||
repository_storages_weights.transform_values do |w|
|
||||
next w if weights_total == 0
|
||||
|
||||
w.to_f / weights_total
|
||||
|
@ -468,16 +470,20 @@ module ApplicationSettingImplementation
|
|||
invalid.empty?
|
||||
end
|
||||
|
||||
def coerce_repository_storages_weighted
|
||||
repository_storages_weighted.transform_values!(&:to_i)
|
||||
end
|
||||
|
||||
def check_repository_storages_weighted
|
||||
invalid = repository_storages_weighted.keys - Gitlab.config.repositories.storages.keys
|
||||
errors.add(:repository_storages_weighted, "can't include: %{invalid_storages}" % { invalid_storages: invalid.join(", ") }) unless
|
||||
errors.add(:repository_storages_weighted, _("can't include: %{invalid_storages}") % { invalid_storages: invalid.join(", ") }) unless
|
||||
invalid.empty?
|
||||
|
||||
repository_storages_weighted.each do |key, val|
|
||||
next unless val.present?
|
||||
|
||||
errors.add(:"repository_storages_weighted_#{key}", "value must be an integer") unless val.is_a?(Integer)
|
||||
errors.add(:"repository_storages_weighted_#{key}", "value must be between 0 and 100") unless val.between?(0, 100)
|
||||
errors.add(:repository_storages_weighted, _("value for '%{storage}' must be an integer") % { storage: key }) unless val.is_a?(Integer)
|
||||
errors.add(:repository_storages_weighted, _("value for '%{storage}' must be between 0 and 100") % { storage: key }) unless val.between?(0, 100)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ module ApplicationSettings
|
|||
|
||||
attr_reader :params, :application_setting
|
||||
|
||||
MARKDOWN_CACHE_INVALIDATING_PARAMS = %w(asset_proxy_enabled asset_proxy_url asset_proxy_secret_key asset_proxy_allowlist).freeze
|
||||
MARKDOWN_CACHE_INVALIDATING_PARAMS = %w(asset_proxy_enabled asset_proxy_url asset_proxy_secret_key asset_proxy_whitelist).freeze
|
||||
|
||||
def execute
|
||||
result = update_settings
|
||||
|
|
|
@ -31,6 +31,7 @@ module MergeRequests
|
|||
old_mentioned_users = old_associations.fetch(:mentioned_users, [])
|
||||
old_assignees = old_associations.fetch(:assignees, [])
|
||||
old_reviewers = old_associations.fetch(:reviewers, [])
|
||||
old_timelogs = old_associations.fetch(:timelogs, [])
|
||||
changed_fields = merge_request.previous_changes.keys
|
||||
|
||||
resolve_todos(merge_request, old_labels, old_assignees, old_reviewers)
|
||||
|
@ -48,6 +49,7 @@ module MergeRequests
|
|||
|
||||
track_title_and_desc_edits(changed_fields)
|
||||
track_discussion_lock_toggle(merge_request, changed_fields)
|
||||
track_time_estimate_and_spend_edits(merge_request, old_timelogs, changed_fields)
|
||||
|
||||
notify_if_labels_added(merge_request, old_labels)
|
||||
notify_if_mentions_added(merge_request, old_mentioned_users)
|
||||
|
@ -106,6 +108,11 @@ module MergeRequests
|
|||
end
|
||||
end
|
||||
|
||||
def track_time_estimate_and_spend_edits(merge_request, old_timelogs, changed_fields)
|
||||
merge_request_activity_counter.track_time_estimate_changed_action(user: current_user) if changed_fields.include?('time_estimate')
|
||||
merge_request_activity_counter.track_time_spent_changed_action(user: current_user) if old_timelogs != merge_request.timelogs
|
||||
end
|
||||
|
||||
def notify_if_labels_added(merge_request, old_labels)
|
||||
added_labels = merge_request.labels - old_labels
|
||||
|
||||
|
|
|
@ -18,8 +18,9 @@
|
|||
= _('Enter weights for storages for new repositories.')
|
||||
= link_to sprite_icon('question-o'), help_page_path('administration/repository_storage_paths')
|
||||
.form-check
|
||||
- storage_weights.each do |attribute|
|
||||
= f.text_field attribute[:name], class: 'form-text-input', value: attribute[:value]
|
||||
= f.label attribute[:label], attribute[:label], class: 'label-bold form-check-label'
|
||||
= f.fields_for :repository_storages_weighted, storage_weights do |storage_form|
|
||||
- Gitlab.config.repositories.storages.keys.each do |storage|
|
||||
= storage_form.text_field storage, class: 'form-text-input'
|
||||
= storage_form.label storage, storage, class: 'label-bold form-check-label'
|
||||
%br
|
||||
= f.submit _('Save changes'), class: "gl-button btn btn-success qa-save-changes-button"
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add space next to icons in epic issue list
|
||||
merge_request: 54138
|
||||
author: Yogi (@yo)
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add tracking to merge request time estimate/spent changes
|
||||
merge_request: 55046
|
||||
author:
|
||||
type: other
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add Operating System details to usage ping
|
||||
merge_request: 54778
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Rename asset_proxy_allowlist column
|
||||
merge_request: 55419
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Allow saving repository weights after a storage has been removed
|
||||
merge_request: 53803
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: 'GithubImporter: Add Merge request approval only if it does not exists yet'
|
||||
merge_request: 55376
|
||||
author:
|
||||
type: fixed
|
|
@ -193,7 +193,6 @@ module Gitlab
|
|||
config.assets.precompile << "page_bundles/import.css"
|
||||
config.assets.precompile << "page_bundles/incident_management_list.css"
|
||||
config.assets.precompile << "page_bundles/issues_list.css"
|
||||
config.assets.precompile << "page_bundles/iterations.css"
|
||||
config.assets.precompile << "page_bundles/jira_connect.css"
|
||||
config.assets.precompile << "page_bundles/jira_connect_users.css"
|
||||
config.assets.precompile << "page_bundles/learn_gitlab.css"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
name: ci_reduce_queries_when_ticking_runner_queue
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55496
|
||||
rollout_issue_url:
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/323328
|
||||
milestone: '13.10'
|
||||
type: development
|
||||
group: group::continuous integration
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: usage_data_i_code_review_user_time_estimate_changed
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55046
|
||||
rollout_issue_url:
|
||||
milestone: '13.10'
|
||||
type: development
|
||||
group: group::code review
|
||||
default_enabled: true
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: usage_data_i_code_review_user_time_spent_changed
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55046
|
||||
rollout_issue_url:
|
||||
milestone: '13.10'
|
||||
type: development
|
||||
group: group::code review
|
||||
default_enabled: true
|
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
key_path: redis_hll_counters.code_review.i_code_review_user_time_estimate_changed_monthly
|
||||
description: Count of unique users per month who changed time estimate of a MR
|
||||
product_section: dev
|
||||
product_stage: create
|
||||
product_group: group::code review
|
||||
product_category: code_review
|
||||
value_type: number
|
||||
status: implemented
|
||||
milestone: "13.10"
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55046
|
||||
time_frame: 28d
|
||||
data_source: redis_hll
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
key_path: redis_hll_counters.code_review.i_code_review_user_time_spent_changed_monthly
|
||||
description: Count of unique users per month who changed time spent on a MR
|
||||
product_section: dev
|
||||
product_stage: create
|
||||
product_group: group::code review
|
||||
product_category: code_review
|
||||
value_type: number
|
||||
status: implemented
|
||||
milestone: "13.10"
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55046
|
||||
time_frame: 28d
|
||||
data_source: redis_hll
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
key_path: redis_hll_counters.code_review.i_code_review_user_time_estimate_changed_weekly
|
||||
description: Count of unique users per week who changed time estimate of a MR
|
||||
product_section: dev
|
||||
product_stage: create
|
||||
product_group: group::code review
|
||||
product_category: code_review
|
||||
value_type: number
|
||||
status: implemented
|
||||
milestone: "13.10"
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55046
|
||||
time_frame: 7d
|
||||
data_source: redis_hll
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
key_path: redis_hll_counters.code_review.i_code_review_user_time_spent_changed_weekly
|
||||
description: Count of unique users per week who changed time spent on a MR
|
||||
product_section: dev
|
||||
product_stage: create
|
||||
product_group: group::code review
|
||||
product_category: code_review
|
||||
value_type: number
|
||||
status: implemented
|
||||
milestone: "13.10"
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55046
|
||||
time_frame: 7d
|
||||
data_source: redis_hll
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
key_path: settings.operating_system
|
||||
description: Information about the operating system running GitLab
|
||||
product_section: enablement
|
||||
product_stage: enablement
|
||||
product_group: group::distribution
|
||||
product_category: collection
|
||||
value_type: string
|
||||
status: implemented
|
||||
milestone: "13.10"
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/54778
|
||||
time_frame: none
|
||||
data_source: ruby
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
|
@ -0,0 +1,21 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class RenameAssetProxyAllowlistOnApplicationSettings < ActiveRecord::Migration[6.0]
|
||||
include Gitlab::Database::MigrationHelpers::V2
|
||||
|
||||
DOWNTIME = false
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
rename_column_concurrently :application_settings,
|
||||
:asset_proxy_allowlist,
|
||||
:asset_proxy_whitelist
|
||||
end
|
||||
|
||||
def down
|
||||
undo_rename_column_concurrently :application_settings,
|
||||
:asset_proxy_allowlist,
|
||||
:asset_proxy_whitelist
|
||||
end
|
||||
end
|
|
@ -0,0 +1,21 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class CleanUpAssetProxyAllowlistRenameOnApplicationSettings < ActiveRecord::Migration[6.0]
|
||||
include Gitlab::Database::MigrationHelpers::V2
|
||||
|
||||
DOWNTIME = false
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
cleanup_concurrent_column_rename :application_settings,
|
||||
:asset_proxy_allowlist,
|
||||
:asset_proxy_whitelist
|
||||
end
|
||||
|
||||
def down
|
||||
undo_cleanup_concurrent_column_rename :application_settings,
|
||||
:asset_proxy_allowlist,
|
||||
:asset_proxy_whitelist
|
||||
end
|
||||
end
|
|
@ -0,0 +1 @@
|
|||
21ae7ea7cbf1d34c7b9dc300a641eaf975ed1e33f5bc519494cd37c4a661bec8
|
|
@ -0,0 +1 @@
|
|||
28b90c9b7c2e4f2e2b12088f5aee85c16dfb567f89ed6a8e771f2c5d91c818d9
|
|
@ -9394,12 +9394,12 @@ CREATE TABLE application_settings (
|
|||
invisible_captcha_enabled boolean DEFAULT false NOT NULL,
|
||||
enforce_ssh_key_expiration boolean DEFAULT false NOT NULL,
|
||||
git_two_factor_session_expiry integer DEFAULT 15 NOT NULL,
|
||||
asset_proxy_allowlist text,
|
||||
keep_latest_artifact boolean DEFAULT true NOT NULL,
|
||||
notes_create_limit integer DEFAULT 300 NOT NULL,
|
||||
notes_create_limit_allowlist text[] DEFAULT '{}'::text[] NOT NULL,
|
||||
kroki_formats jsonb DEFAULT '{}'::jsonb NOT NULL,
|
||||
in_product_marketing_emails_enabled boolean DEFAULT true NOT NULL,
|
||||
asset_proxy_whitelist text,
|
||||
CONSTRAINT app_settings_container_reg_cleanup_tags_max_list_size_positive CHECK ((container_registry_cleanup_tags_service_max_list_size >= 0)),
|
||||
CONSTRAINT app_settings_registry_exp_policies_worker_capacity_positive CHECK ((container_registry_expiration_policies_worker_capacity >= 0)),
|
||||
CONSTRAINT check_17d9558205 CHECK ((char_length(kroki_url) <= 1024)),
|
||||
|
|
|
@ -13730,6 +13730,86 @@ Count of unique users per week|month with diffs viewed file by file
|
|||
| `tier` | |
|
||||
| `skip_validation` | true |
|
||||
|
||||
## `redis_hll_counters.code_review.i_code_review_user_time_estimate_changed_monthly`
|
||||
|
||||
Count of unique users per month who changed time estimate of a MR
|
||||
|
||||
| field | value |
|
||||
| --- | --- |
|
||||
| `key_path` | **`redis_hll_counters.code_review.i_code_review_user_time_estimate_changed_monthly`** |
|
||||
| `product_section` | dev |
|
||||
| `product_stage` | create |
|
||||
| `product_group` | `group::code review` |
|
||||
| `product_category` | `code_review` |
|
||||
| `value_type` | number |
|
||||
| `status` | implemented |
|
||||
| `milestone` | 13.10 |
|
||||
| `introduced_by_url` | [Introduced by](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55046) |
|
||||
| `time_frame` | 28d |
|
||||
| `data_source` | Redis_hll |
|
||||
| `distribution` | ce, ee |
|
||||
| `tier` | free, premium, ultimate |
|
||||
|
||||
## `redis_hll_counters.code_review.i_code_review_user_time_estimate_changed_weekly`
|
||||
|
||||
Count of unique users per week who changed time estimate of a MR
|
||||
|
||||
| field | value |
|
||||
| --- | --- |
|
||||
| `key_path` | **`redis_hll_counters.code_review.i_code_review_user_time_estimate_changed_weekly`** |
|
||||
| `product_section` | dev |
|
||||
| `product_stage` | create |
|
||||
| `product_group` | `group::code review` |
|
||||
| `product_category` | `code_review` |
|
||||
| `value_type` | number |
|
||||
| `status` | implemented |
|
||||
| `milestone` | 13.10 |
|
||||
| `introduced_by_url` | [Introduced by](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55046) |
|
||||
| `time_frame` | 7d |
|
||||
| `data_source` | Redis_hll |
|
||||
| `distribution` | ce, ee |
|
||||
| `tier` | free, premium, ultimate |
|
||||
|
||||
## `redis_hll_counters.code_review.i_code_review_user_time_spent_changed_monthly`
|
||||
|
||||
Count of unique users per month who changed time spent on a MR
|
||||
|
||||
| field | value |
|
||||
| --- | --- |
|
||||
| `key_path` | **`redis_hll_counters.code_review.i_code_review_user_time_spent_changed_monthly`** |
|
||||
| `product_section` | dev |
|
||||
| `product_stage` | create |
|
||||
| `product_group` | `group::code review` |
|
||||
| `product_category` | `code_review` |
|
||||
| `value_type` | number |
|
||||
| `status` | implemented |
|
||||
| `milestone` | 13.10 |
|
||||
| `introduced_by_url` | [Introduced by](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55046) |
|
||||
| `time_frame` | 28d |
|
||||
| `data_source` | Redis_hll |
|
||||
| `distribution` | ce, ee |
|
||||
| `tier` | free, premium, ultimate |
|
||||
|
||||
## `redis_hll_counters.code_review.i_code_review_user_time_spent_changed_weekly`
|
||||
|
||||
Count of unique users per week who changed time spent on a MR
|
||||
|
||||
| field | value |
|
||||
| --- | --- |
|
||||
| `key_path` | **`redis_hll_counters.code_review.i_code_review_user_time_spent_changed_weekly`** |
|
||||
| `product_section` | dev |
|
||||
| `product_stage` | create |
|
||||
| `product_group` | `group::code review` |
|
||||
| `product_category` | `code_review` |
|
||||
| `value_type` | number |
|
||||
| `status` | implemented |
|
||||
| `milestone` | 13.10 |
|
||||
| `introduced_by_url` | [Introduced by](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55046) |
|
||||
| `time_frame` | 7d |
|
||||
| `data_source` | Redis_hll |
|
||||
| `distribution` | ce, ee |
|
||||
| `tier` | free, premium, ultimate |
|
||||
|
||||
## `redis_hll_counters.code_review.i_code_review_user_toggled_task_item_status_monthly`
|
||||
|
||||
Missing description
|
||||
|
@ -20846,6 +20926,26 @@ Is encrypted LDAP secrets configured?
|
|||
| `tier` | free, premium, ultimate |
|
||||
| `skip_validation` | true |
|
||||
|
||||
## `settings.operating_system`
|
||||
|
||||
Information about the operating system running GitLab
|
||||
|
||||
| field | value |
|
||||
| --- | --- |
|
||||
| `key_path` | **`settings.operating_system`** |
|
||||
| `product_section` | enablement |
|
||||
| `product_stage` | enablement |
|
||||
| `product_group` | `group::distribution` |
|
||||
| `product_category` | `collection` |
|
||||
| `value_type` | string |
|
||||
| `status` | implemented |
|
||||
| `milestone` | 13.10 |
|
||||
| `introduced_by_url` | [Introduced by](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/54778) |
|
||||
| `time_frame` | none |
|
||||
| `data_source` | Ruby |
|
||||
| `distribution` | ce, ee |
|
||||
| `tier` | free, premium, ultimate |
|
||||
|
||||
## `signup_enabled`
|
||||
|
||||
Whether public signup is enabled
|
||||
|
|
|
@ -5,7 +5,7 @@ module Gitlab
|
|||
module Reports
|
||||
class CodequalityReportsComparer < ReportsComparer
|
||||
def initialize(base_report, head_report)
|
||||
@base_report = base_report || CodequalityReports.new
|
||||
@base_report = base_report
|
||||
@head_report = head_report
|
||||
end
|
||||
|
||||
|
@ -15,12 +15,16 @@ module Gitlab
|
|||
|
||||
def existing_errors
|
||||
strong_memoize(:existing_errors) do
|
||||
next [] if not_found?
|
||||
|
||||
base_report.all_degradations & head_report.all_degradations
|
||||
end
|
||||
end
|
||||
|
||||
def new_errors
|
||||
strong_memoize(:new_errors) do
|
||||
next [] if not_found?
|
||||
|
||||
fingerprints = head_report.degradations.keys - base_report.degradations.keys
|
||||
head_report.degradations.fetch_values(*fingerprints)
|
||||
end
|
||||
|
@ -28,6 +32,8 @@ module Gitlab
|
|||
|
||||
def resolved_errors
|
||||
strong_memoize(:resolved_errors) do
|
||||
next [] if not_found?
|
||||
|
||||
fingerprints = base_report.degradations.keys - head_report.degradations.keys
|
||||
base_report.degradations.fetch_values(*fingerprints)
|
||||
end
|
||||
|
|
|
@ -18,10 +18,10 @@ module Gitlab
|
|||
end
|
||||
|
||||
def status
|
||||
if success?
|
||||
STATUS_SUCCESS
|
||||
elsif base_report.nil? || head_report.nil?
|
||||
if base_report.nil? || head_report.nil?
|
||||
STATUS_NOT_FOUND
|
||||
elsif success?
|
||||
STATUS_SUCCESS
|
||||
else
|
||||
STATUS_FAILED
|
||||
end
|
||||
|
@ -54,6 +54,10 @@ module Gitlab
|
|||
def total_count
|
||||
existing_errors.size + new_errors.size
|
||||
end
|
||||
|
||||
def not_found?
|
||||
status == STATUS_NOT_FOUND
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -77,12 +77,22 @@ module Gitlab
|
|||
def add_approval!(user_id)
|
||||
return unless review.review_type == 'APPROVED'
|
||||
|
||||
add_approval_system_note!(user_id)
|
||||
|
||||
merge_request.approvals.create!(
|
||||
approval_attribues = {
|
||||
merge_request_id: merge_request.id,
|
||||
user_id: user_id,
|
||||
created_at: review.submitted_at
|
||||
created_at: review.submitted_at,
|
||||
updated_at: review.submitted_at
|
||||
}
|
||||
|
||||
result = ::Approval.insert(
|
||||
approval_attribues,
|
||||
returning: [:id],
|
||||
unique_by: [:user_id, :merge_request_id]
|
||||
)
|
||||
|
||||
if result.rows.present?
|
||||
add_approval_system_note!(user_id)
|
||||
end
|
||||
end
|
||||
|
||||
def add_approval_system_note!(user_id)
|
||||
|
|
|
@ -242,7 +242,8 @@ module Gitlab
|
|||
def system_usage_data_settings
|
||||
{
|
||||
settings: {
|
||||
ldap_encrypted_secrets_enabled: alt_usage_data(fallback: nil) { Gitlab::Auth::Ldap::Config.encrypted_secrets.active? }
|
||||
ldap_encrypted_secrets_enabled: alt_usage_data(fallback: nil) { Gitlab::Auth::Ldap::Config.encrypted_secrets.active? },
|
||||
operating_system: alt_usage_data(fallback: nil) { operating_system }
|
||||
}
|
||||
}
|
||||
end
|
||||
|
@ -505,6 +506,17 @@ module Gitlab
|
|||
end
|
||||
end
|
||||
|
||||
def operating_system
|
||||
ohai_data = Ohai::System.new.tap do |oh|
|
||||
oh.all_plugins(['platform'])
|
||||
end.data
|
||||
|
||||
platform = ohai_data['platform']
|
||||
platform = 'raspbian' if ohai_data['platform'] == 'debian' && /armv/.match?(ohai_data['kernel']['machine'])
|
||||
|
||||
"#{platform}-#{ohai_data['platform_version']}"
|
||||
end
|
||||
|
||||
def last_28_days_time_period(column: :created_at)
|
||||
{ column => 30.days.ago..2.days.ago }
|
||||
end
|
||||
|
|
|
@ -44,7 +44,9 @@
|
|||
'i_code_review_user_toggled_task_item_status',
|
||||
'i_code_review_user_create_mr_from_issue',
|
||||
'i_code_review_user_mr_discussion_locked',
|
||||
'i_code_review_user_mr_discussion_unlocked'
|
||||
'i_code_review_user_mr_discussion_unlocked',
|
||||
'i_code_review_user_time_estimate_changed',
|
||||
'i_code_review_user_time_spent_changed'
|
||||
]
|
||||
- name: code_review_category_monthly_active_users
|
||||
operator: OR
|
||||
|
@ -82,7 +84,9 @@
|
|||
'i_code_review_user_toggled_task_item_status',
|
||||
'i_code_review_user_create_mr_from_issue',
|
||||
'i_code_review_user_mr_discussion_locked',
|
||||
'i_code_review_user_mr_discussion_unlocked'
|
||||
'i_code_review_user_mr_discussion_unlocked',
|
||||
'i_code_review_user_time_estimate_changed',
|
||||
'i_code_review_user_time_spent_changed'
|
||||
]
|
||||
- name: code_review_extension_category_monthly_active_users
|
||||
operator: OR
|
||||
|
|
|
@ -174,3 +174,13 @@
|
|||
category: code_review
|
||||
aggregation: weekly
|
||||
feature_flag: usage_data_i_code_review_user_mr_discussion_unlocked
|
||||
- name: i_code_review_user_time_estimate_changed
|
||||
redis_slot: code_review
|
||||
category: code_review
|
||||
aggregation: weekly
|
||||
feature_flag: usage_data_i_code_review_user_time_estimate_changed
|
||||
- name: i_code_review_user_time_spent_changed
|
||||
redis_slot: code_review
|
||||
category: code_review
|
||||
aggregation: weekly
|
||||
feature_flag: usage_data_i_code_review_user_time_spent_changed
|
||||
|
|
|
@ -37,6 +37,8 @@ module Gitlab
|
|||
MR_CREATE_FROM_ISSUE_ACTION = 'i_code_review_user_create_mr_from_issue'
|
||||
MR_DISCUSSION_LOCKED_ACTION = 'i_code_review_user_mr_discussion_locked'
|
||||
MR_DISCUSSION_UNLOCKED_ACTION = 'i_code_review_user_mr_discussion_unlocked'
|
||||
MR_TIME_ESTIMATE_CHANGED_ACTION = 'i_code_review_user_time_estimate_changed'
|
||||
MR_TIME_SPENT_CHANGED_ACTION = 'i_code_review_user_time_spent_changed'
|
||||
|
||||
class << self
|
||||
def track_mr_diffs_action(merge_request:)
|
||||
|
@ -163,6 +165,14 @@ module Gitlab
|
|||
track_unique_action_by_user(MR_DISCUSSION_UNLOCKED_ACTION, user)
|
||||
end
|
||||
|
||||
def track_time_estimate_changed_action(user:)
|
||||
track_unique_action_by_user(MR_TIME_ESTIMATE_CHANGED_ACTION, user)
|
||||
end
|
||||
|
||||
def track_time_spent_changed_action(user:)
|
||||
track_unique_action_by_user(MR_TIME_SPENT_CHANGED_ACTION, user)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def track_unique_action_by_merge_request(action, merge_request)
|
||||
|
|
|
@ -173,6 +173,11 @@ msgid_plural "%d days"
|
|||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
msgid "%d epic"
|
||||
msgid_plural "%d epics"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
msgid "%d error"
|
||||
msgid_plural "%d errors"
|
||||
msgstr[0] ""
|
||||
|
@ -559,6 +564,9 @@ msgstr ""
|
|||
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
|
||||
msgstr ""
|
||||
|
||||
msgid "%{itemsCount} issues with a limit of %{maxIssueCount}"
|
||||
msgstr ""
|
||||
|
||||
msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
|
||||
msgstr ""
|
||||
|
||||
|
@ -34912,6 +34920,9 @@ msgstr ""
|
|||
msgid "can't be enabled because signed commits are required for this project"
|
||||
msgstr ""
|
||||
|
||||
msgid "can't include: %{invalid_storages}"
|
||||
msgstr ""
|
||||
|
||||
msgid "cannot be a date in the past"
|
||||
msgstr ""
|
||||
|
||||
|
@ -36318,6 +36329,12 @@ msgstr ""
|
|||
msgid "v%{version} published %{timeAgo}"
|
||||
msgstr ""
|
||||
|
||||
msgid "value for '%{storage}' must be an integer"
|
||||
msgstr ""
|
||||
|
||||
msgid "value for '%{storage}' must be between 0 and 100"
|
||||
msgstr ""
|
||||
|
||||
msgid "verify ownership"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -32,6 +32,10 @@ module QA
|
|||
# This 'element' is here only to ensure the changes in the view source aren't mistakenly changed
|
||||
element :_, "qa_selector = local_assigns.fetch(:qa_selector, '')" # rubocop:disable QA/ElementWithPattern
|
||||
end
|
||||
|
||||
base.view 'app/assets/javascripts/snippets/components/snippet_visibility_edit.vue' do
|
||||
element :visibility_content
|
||||
end
|
||||
end
|
||||
|
||||
def fill_title(title)
|
||||
|
@ -44,7 +48,7 @@ module QA
|
|||
end
|
||||
|
||||
def set_visibility(visibility)
|
||||
choose visibility
|
||||
click_element(:visibility_content, visibility: visibility)
|
||||
end
|
||||
|
||||
def fill_file_name(name, file_number = nil)
|
||||
|
|
|
@ -30,6 +30,7 @@ UsageData/LargeTable:
|
|||
- :Settings
|
||||
- :CE_MEMOIZED_VALUES
|
||||
- :EE_MEMOIZED_VALUES
|
||||
- :Ohai::System
|
||||
CountMethods:
|
||||
- :count
|
||||
- :distinct_count
|
||||
|
|
|
@ -144,10 +144,10 @@ RSpec.describe Admin::ApplicationSettingsController do
|
|||
end
|
||||
|
||||
it 'updates repository_storages_weighted setting' do
|
||||
put :update, params: { application_setting: { repository_storages_weighted_default: 75 } }
|
||||
put :update, params: { application_setting: { repository_storages_weighted: { default: 75 } } }
|
||||
|
||||
expect(response).to redirect_to(general_admin_application_settings_path)
|
||||
expect(ApplicationSetting.current.repository_storages_weighted_default).to eq(75)
|
||||
expect(ApplicationSetting.current.repository_storages_weighted).to eq('default' => 75)
|
||||
end
|
||||
|
||||
it 'updates kroki_formats setting' do
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
FactoryBot.define do
|
||||
factory :ci_build_trace_chunk, class: 'Ci::BuildTraceChunk' do
|
||||
build factory: :ci_build
|
||||
chunk_index { 0 }
|
||||
chunk_index { generate(:iid) }
|
||||
data_store { :redis }
|
||||
|
||||
trait :redis_with_data do
|
||||
|
|
|
@ -384,7 +384,20 @@ RSpec.describe 'Admin updates settings' do
|
|||
click_button 'Save changes'
|
||||
end
|
||||
|
||||
expect(current_settings.repository_storages_weighted_default).to be 50
|
||||
expect(current_settings.repository_storages_weighted).to eq('default' => 50)
|
||||
end
|
||||
|
||||
it 'still saves when settings are outdated' do
|
||||
current_settings.update_attribute :repository_storages_weighted, { 'default' => 100, 'outdated' => 100 }
|
||||
|
||||
visit repository_admin_application_settings_path
|
||||
|
||||
page.within('.as-repository-storage') do
|
||||
fill_in 'application_setting_repository_storages_weighted_default', with: 50
|
||||
click_button 'Save changes'
|
||||
end
|
||||
|
||||
expect(current_settings.repository_storages_weighted).to eq('default' => 50)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { shallowMount, createLocalVue } from '@vue/test-utils';
|
||||
import Vuex from 'vuex';
|
||||
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
|
||||
|
||||
import { mockLabelList } from 'jest/boards/mock_data';
|
||||
import BoardListHeader from '~/boards/components/board_list_header.vue';
|
||||
|
@ -14,6 +15,7 @@ describe('Board List Header Component', () => {
|
|||
let store;
|
||||
|
||||
const updateListSpy = jest.fn();
|
||||
const toggleListCollapsedSpy = jest.fn();
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
|
@ -43,18 +45,19 @@ describe('Board List Header Component', () => {
|
|||
|
||||
if (withLocalStorage) {
|
||||
localStorage.setItem(
|
||||
`boards.${boardId}.${listMock.listType}.${listMock.id}.expanded`,
|
||||
(!collapsed).toString(),
|
||||
`boards.${boardId}.${listMock.listType}.${listMock.id}.collapsed`,
|
||||
collapsed.toString(),
|
||||
);
|
||||
}
|
||||
|
||||
store = new Vuex.Store({
|
||||
state: {},
|
||||
actions: { updateList: updateListSpy },
|
||||
actions: { updateList: updateListSpy, toggleListCollapsed: toggleListCollapsedSpy },
|
||||
getters: {},
|
||||
});
|
||||
|
||||
wrapper = shallowMount(BoardListHeader, {
|
||||
wrapper = extendedWrapper(
|
||||
shallowMount(BoardListHeader, {
|
||||
store,
|
||||
localVue,
|
||||
propsData: {
|
||||
|
@ -66,15 +69,15 @@ describe('Board List Header Component', () => {
|
|||
weightFeatureAvailable: false,
|
||||
currentUserId,
|
||||
},
|
||||
});
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
const isCollapsed = () => wrapper.vm.list.collapsed;
|
||||
const isExpanded = () => !isCollapsed;
|
||||
|
||||
const findAddIssueButton = () => wrapper.find({ ref: 'newIssueBtn' });
|
||||
const findTitle = () => wrapper.find('.board-title');
|
||||
const findCaret = () => wrapper.find('.board-title-caret');
|
||||
const findCaret = () => wrapper.findByTestId('board-title-caret');
|
||||
|
||||
describe('Add issue button', () => {
|
||||
const hasNoAddButton = [ListType.closed];
|
||||
|
@ -114,40 +117,29 @@ describe('Board List Header Component', () => {
|
|||
});
|
||||
|
||||
describe('expanding / collapsing the column', () => {
|
||||
it('does not collapse when clicking the header', async () => {
|
||||
it('should display collapse icon when column is expanded', async () => {
|
||||
createComponent();
|
||||
|
||||
expect(isCollapsed()).toBe(false);
|
||||
const icon = findCaret();
|
||||
|
||||
wrapper.find('[data-testid="board-list-header"]').trigger('click');
|
||||
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
expect(isCollapsed()).toBe(false);
|
||||
expect(icon.props('icon')).toBe('chevron-right');
|
||||
});
|
||||
|
||||
it('collapses expanded Column when clicking the collapse icon', async () => {
|
||||
createComponent();
|
||||
|
||||
expect(isCollapsed()).toBe(false);
|
||||
|
||||
findCaret().vm.$emit('click');
|
||||
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
expect(isCollapsed()).toBe(true);
|
||||
});
|
||||
|
||||
it('expands collapsed Column when clicking the expand icon', async () => {
|
||||
it('should display expand icon when column is collapsed', async () => {
|
||||
createComponent({ collapsed: true });
|
||||
|
||||
expect(isCollapsed()).toBe(true);
|
||||
const icon = findCaret();
|
||||
|
||||
expect(icon.props('icon')).toBe('chevron-down');
|
||||
});
|
||||
|
||||
it('should dispatch toggleListCollapse when clicking the collapse icon', async () => {
|
||||
createComponent();
|
||||
|
||||
findCaret().vm.$emit('click');
|
||||
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
expect(isCollapsed()).toBe(false);
|
||||
expect(toggleListCollapsedSpy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("when logged in it calls list update and doesn't set localStorage", async () => {
|
||||
|
@ -157,7 +149,7 @@ describe('Board List Header Component', () => {
|
|||
await wrapper.vm.$nextTick();
|
||||
|
||||
expect(updateListSpy).toHaveBeenCalledTimes(1);
|
||||
expect(localStorage.getItem(`${wrapper.vm.uniqueKey}.expanded`)).toBe(null);
|
||||
expect(localStorage.getItem(`${wrapper.vm.uniqueKey}.collapsed`)).toBe(null);
|
||||
});
|
||||
|
||||
it("when logged out it doesn't call list update and sets localStorage", async () => {
|
||||
|
@ -167,7 +159,7 @@ describe('Board List Header Component', () => {
|
|||
await wrapper.vm.$nextTick();
|
||||
|
||||
expect(updateListSpy).not.toHaveBeenCalled();
|
||||
expect(localStorage.getItem(`${wrapper.vm.uniqueKey}.expanded`)).toBe(String(isExpanded()));
|
||||
expect(localStorage.getItem(`${wrapper.vm.uniqueKey}.collapsed`)).toBe(String(isCollapsed()));
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -452,6 +452,22 @@ describe('updateList', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('toggleListCollapsed', () => {
|
||||
it('should commit TOGGLE_LIST_COLLAPSED mutation', async () => {
|
||||
const payload = { listId: 'gid://gitlab/List/1', collapsed: true };
|
||||
await testAction({
|
||||
action: actions.toggleListCollapsed,
|
||||
payload,
|
||||
expectedMutations: [
|
||||
{
|
||||
type: types.TOGGLE_LIST_COLLAPSED,
|
||||
payload,
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('removeList', () => {
|
||||
let state;
|
||||
const list = mockLists[0];
|
||||
|
|
|
@ -202,6 +202,24 @@ describe('Board Store Mutations', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('TOGGLE_LIST_COLLAPSED', () => {
|
||||
it('updates collapsed attribute of list in boardLists state', () => {
|
||||
const listId = 'gid://gitlab/List/1';
|
||||
state = {
|
||||
...state,
|
||||
boardLists: {
|
||||
[listId]: mockLists[0],
|
||||
},
|
||||
};
|
||||
|
||||
expect(state.boardLists[listId].collapsed).toEqual(false);
|
||||
|
||||
mutations.TOGGLE_LIST_COLLAPSED(state, { listId, collapsed: true });
|
||||
|
||||
expect(state.boardLists[listId].collapsed).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('REMOVE_LIST', () => {
|
||||
it('removes list from boardLists', () => {
|
||||
const [list, secondList] = mockLists;
|
||||
|
|
|
@ -46,6 +46,8 @@ exports[`Snippet Visibility Edit component rendering matches the snapshot 1`] =
|
|||
|
||||
<span
|
||||
class="font-weight-bold ml-1 js-visibility-option"
|
||||
data-qa-selector="visibility_content"
|
||||
data-qa-visibility="Private"
|
||||
>
|
||||
Private
|
||||
</span>
|
||||
|
@ -65,6 +67,8 @@ exports[`Snippet Visibility Edit component rendering matches the snapshot 1`] =
|
|||
|
||||
<span
|
||||
class="font-weight-bold ml-1 js-visibility-option"
|
||||
data-qa-selector="visibility_content"
|
||||
data-qa-visibility="Internal"
|
||||
>
|
||||
Internal
|
||||
</span>
|
||||
|
@ -84,6 +88,8 @@ exports[`Snippet Visibility Edit component rendering matches the snapshot 1`] =
|
|||
|
||||
<span
|
||||
class="font-weight-bold ml-1 js-visibility-option"
|
||||
data-qa-selector="visibility_content"
|
||||
data-qa-visibility="Public"
|
||||
>
|
||||
Public
|
||||
</span>
|
||||
|
|
|
@ -130,20 +130,15 @@ RSpec.describe ApplicationSettingsHelper do
|
|||
before do
|
||||
helper.instance_variable_set(:@application_setting, application_setting)
|
||||
stub_storage_settings({ 'default': {}, 'storage_1': {}, 'storage_2': {} })
|
||||
allow(ApplicationSetting).to receive(:repository_storages_weighted_attributes).and_return(
|
||||
[:repository_storages_weighted_default,
|
||||
:repository_storages_weighted_storage_1,
|
||||
:repository_storages_weighted_storage_2])
|
||||
|
||||
stub_application_setting(repository_storages_weighted: { 'default' => 100, 'storage_1' => 50, 'storage_2' => nil })
|
||||
end
|
||||
|
||||
it 'returns storages correctly' do
|
||||
expect(helper.storage_weights).to eq([
|
||||
{ name: :repository_storages_weighted_default, label: 'default', value: 100 },
|
||||
{ name: :repository_storages_weighted_storage_1, label: 'storage_1', value: 50 },
|
||||
{ name: :repository_storages_weighted_storage_2, label: 'storage_2', value: 0 }
|
||||
])
|
||||
expect(helper.storage_weights).to eq(OpenStruct.new(
|
||||
default: 100,
|
||||
storage_1: 50,
|
||||
storage_2: 0
|
||||
))
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -27,6 +27,22 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReportsComparer do
|
|||
expect(report_status).to eq(described_class::STATUS_SUCCESS)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when head report does not exist' do
|
||||
let(:head_report) { nil }
|
||||
|
||||
it 'returns status not found' do
|
||||
expect(report_status).to eq(described_class::STATUS_NOT_FOUND)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when base report does not exist' do
|
||||
let(:base_report) { nil }
|
||||
|
||||
it 'returns status success' do
|
||||
expect(report_status).to eq(described_class::STATUS_NOT_FOUND)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#errors_count' do
|
||||
|
@ -93,6 +109,14 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReportsComparer do
|
|||
expect(resolved_count).to be_zero
|
||||
end
|
||||
end
|
||||
|
||||
context 'when base report is nil' do
|
||||
let(:base_report) { nil }
|
||||
|
||||
it 'returns zero' do
|
||||
expect(resolved_count).to be_zero
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#total_count' do
|
||||
|
@ -140,6 +164,14 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReportsComparer do
|
|||
expect(total_count).to eq(2)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when base report is nil' do
|
||||
let(:base_report) { nil }
|
||||
|
||||
it 'returns zero' do
|
||||
expect(total_count).to be_zero
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#existing_errors' do
|
||||
|
@ -177,6 +209,14 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReportsComparer do
|
|||
expect(existing_errors).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
context 'when base report is nil' do
|
||||
let(:base_report) { nil }
|
||||
|
||||
it 'returns an empty array' do
|
||||
expect(existing_errors).to be_empty
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#new_errors' do
|
||||
|
@ -213,6 +253,14 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReportsComparer do
|
|||
expect(new_errors).to eq([degradation_1])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when base report is nil' do
|
||||
let(:base_report) { nil }
|
||||
|
||||
it 'returns an empty array' do
|
||||
expect(new_errors).to be_empty
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#resolved_errors' do
|
||||
|
@ -250,5 +298,13 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReportsComparer do
|
|||
expect(resolved_errors).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
context 'when base report is nil' do
|
||||
let(:base_report) { nil }
|
||||
|
||||
it 'returns an empty array' do
|
||||
expect(resolved_errors).to be_empty
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -49,10 +49,6 @@ RSpec.describe Gitlab::Ci::Reports::ReportsComparer do
|
|||
context 'when base_report is nil' do
|
||||
let(:base_report) { nil }
|
||||
|
||||
before do
|
||||
allow(comparer).to receive(:success?).and_return(false)
|
||||
end
|
||||
|
||||
it 'returns status not_found' do
|
||||
expect(status).to eq('not_found')
|
||||
end
|
||||
|
@ -61,10 +57,6 @@ RSpec.describe Gitlab::Ci::Reports::ReportsComparer do
|
|||
context 'when head_report is nil' do
|
||||
let(:head_report) { nil }
|
||||
|
||||
before do
|
||||
allow(comparer).to receive(:success?).and_return(false)
|
||||
end
|
||||
|
||||
it 'returns status not_found' do
|
||||
expect(status).to eq('not_found')
|
||||
end
|
||||
|
@ -118,4 +110,22 @@ RSpec.describe Gitlab::Ci::Reports::ReportsComparer do
|
|||
expect { total_count }.to raise_error(NotImplementedError)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#not_found?' do
|
||||
subject(:not_found) { comparer.not_found? }
|
||||
|
||||
context 'when base report is nil' do
|
||||
let(:base_report) { nil }
|
||||
|
||||
it { is_expected.to be_truthy }
|
||||
end
|
||||
|
||||
context 'when base report exists' do
|
||||
before do
|
||||
allow(comparer).to receive(:success?).and_return(true)
|
||||
end
|
||||
|
||||
it { is_expected.to be_falsey }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -19,8 +19,10 @@ RSpec.describe Gitlab::GithubImport::Importer::PullRequestReviewImporter, :clean
|
|||
context 'when the review is "APPROVED"' do
|
||||
let(:review) { create_review(type: 'APPROVED', note: '') }
|
||||
|
||||
it 'creates a note for the review' do
|
||||
expect { subject.execute }.to change(Note, :count)
|
||||
it 'creates a note for the review and approves the Merge Request' do
|
||||
expect { subject.execute }
|
||||
.to change(Note, :count).by(1)
|
||||
.and change(Approval, :count).by(1)
|
||||
|
||||
last_note = merge_request.notes.last
|
||||
expect(last_note.note).to eq('approved this merge request')
|
||||
|
@ -31,6 +33,14 @@ RSpec.describe Gitlab::GithubImport::Importer::PullRequestReviewImporter, :clean
|
|||
expect(merge_request.approved_by_users.reload).to include(author)
|
||||
expect(merge_request.approvals.last.created_at).to eq(submitted_at)
|
||||
end
|
||||
|
||||
it 'does nothing if the user already approved the merge request' do
|
||||
create(:approval, merge_request: merge_request, user: author)
|
||||
|
||||
expect { subject.execute }
|
||||
.to change(Note, :count).by(0)
|
||||
.and change(Approval, :count).by(0)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the review is "COMMENTED"' do
|
||||
|
|
|
@ -4,8 +4,8 @@ require 'spec_helper'
|
|||
|
||||
RSpec.describe Gitlab::ImportExport do
|
||||
describe 'export filename' do
|
||||
let(:group) { create(:group, :nested) }
|
||||
let(:project) { create(:project, :public, path: 'project-path', namespace: group) }
|
||||
let(:group) { build(:group, path: 'child', parent: build(:group, path: 'parent')) }
|
||||
let(:project) { build(:project, :public, path: 'project-path', namespace: group) }
|
||||
|
||||
it 'contains the project path' do
|
||||
expect(described_class.export_filename(exportable: project)).to include(project.path)
|
||||
|
|
|
@ -300,4 +300,20 @@ RSpec.describe Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter, :cl
|
|||
let(:action) { described_class::MR_DISCUSSION_UNLOCKED_ACTION }
|
||||
end
|
||||
end
|
||||
|
||||
describe '.track_time_estimate_changed_action' do
|
||||
subject { described_class.track_time_estimate_changed_action(user: user) }
|
||||
|
||||
it_behaves_like 'a tracked merge request unique event' do
|
||||
let(:action) { described_class::MR_TIME_ESTIMATE_CHANGED_ACTION }
|
||||
end
|
||||
end
|
||||
|
||||
describe '.track_time_spent_changed_action' do
|
||||
subject { described_class.track_time_spent_changed_action(user: user) }
|
||||
|
||||
it_behaves_like 'a tracked merge request unique event' do
|
||||
let(:action) { described_class::MR_TIME_SPENT_CHANGED_ACTION }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1129,12 +1129,40 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
|
|||
end
|
||||
end
|
||||
|
||||
describe ".operating_system" do
|
||||
let(:ohai_data) { { "platform" => "ubuntu", "platform_version" => "20.04" } }
|
||||
|
||||
before do
|
||||
allow_next_instance_of(Ohai::System) do |ohai|
|
||||
allow(ohai).to receive(:data).and_return(ohai_data)
|
||||
end
|
||||
end
|
||||
|
||||
subject { described_class.operating_system }
|
||||
|
||||
it { is_expected.to eq("ubuntu-20.04") }
|
||||
|
||||
context 'when on Debian with armv architecture' do
|
||||
let(:ohai_data) { { "platform" => "debian", "platform_version" => "10", 'kernel' => { 'machine' => 'armv' } } }
|
||||
|
||||
it { is_expected.to eq("raspbian-10") }
|
||||
end
|
||||
end
|
||||
|
||||
describe ".system_usage_data_settings" do
|
||||
before do
|
||||
allow(described_class).to receive(:operating_system).and_return('ubuntu-20.04')
|
||||
end
|
||||
|
||||
subject { described_class.system_usage_data_settings }
|
||||
|
||||
it 'gathers settings usage data', :aggregate_failures do
|
||||
expect(subject[:settings][:ldap_encrypted_secrets_enabled]).to eq(Gitlab::Auth::Ldap::Config.encrypted_secrets.active?)
|
||||
end
|
||||
|
||||
it 'populates operating system information' do
|
||||
expect(subject[:settings][:operating_system]).to eq('ubuntu-20.04')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -105,14 +105,14 @@ RSpec.describe ApplicationSetting do
|
|||
|
||||
it { is_expected.not_to allow_value(false).for(:hashed_storage_enabled) }
|
||||
|
||||
it { is_expected.not_to allow_value(101).for(:repository_storages_weighted_default) }
|
||||
it { is_expected.to allow_value('90').for(:repository_storages_weighted_default) }
|
||||
it { is_expected.not_to allow_value(-1).for(:repository_storages_weighted_default) }
|
||||
it { is_expected.to allow_value(100).for(:repository_storages_weighted_default) }
|
||||
it { is_expected.to allow_value(0).for(:repository_storages_weighted_default) }
|
||||
it { is_expected.to allow_value(50).for(:repository_storages_weighted_default) }
|
||||
it { is_expected.to allow_value(nil).for(:repository_storages_weighted_default) }
|
||||
it { is_expected.not_to allow_value({ default: 100, shouldntexist: 50 }).for(:repository_storages_weighted) }
|
||||
it { is_expected.to allow_value('default' => 0).for(:repository_storages_weighted) }
|
||||
it { is_expected.to allow_value('default' => 50).for(:repository_storages_weighted) }
|
||||
it { is_expected.to allow_value('default' => 100).for(:repository_storages_weighted) }
|
||||
it { is_expected.to allow_value('default' => '90').for(:repository_storages_weighted) }
|
||||
it { is_expected.to allow_value('default' => nil).for(:repository_storages_weighted) }
|
||||
it { is_expected.not_to allow_value('default' => -1).for(:repository_storages_weighted).with_message("value for 'default' must be between 0 and 100") }
|
||||
it { is_expected.not_to allow_value('default' => 101).for(:repository_storages_weighted).with_message("value for 'default' must be between 0 and 100") }
|
||||
it { is_expected.not_to allow_value('default' => 100, shouldntexist: 50).for(:repository_storages_weighted).with_message("can't include: shouldntexist") }
|
||||
|
||||
it { is_expected.to allow_value(400).for(:notes_create_limit) }
|
||||
it { is_expected.not_to allow_value('two').for(:notes_create_limit) }
|
||||
|
@ -650,6 +650,32 @@ RSpec.describe ApplicationSetting do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#asset_proxy_whitelist' do
|
||||
context 'when given an Array' do
|
||||
it 'sets the domains and adds current running host' do
|
||||
setting.asset_proxy_whitelist = ['example.com', 'assets.example.com']
|
||||
expect(setting.asset_proxy_whitelist).to eq(['example.com', 'assets.example.com', 'localhost'])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when given a String' do
|
||||
it 'sets multiple domains with spaces' do
|
||||
setting.asset_proxy_whitelist = 'example.com *.example.com'
|
||||
expect(setting.asset_proxy_whitelist).to eq(['example.com', '*.example.com', 'localhost'])
|
||||
end
|
||||
|
||||
it 'sets multiple domains with newlines and a space' do
|
||||
setting.asset_proxy_whitelist = "example.com\n *.example.com"
|
||||
expect(setting.asset_proxy_whitelist).to eq(['example.com', '*.example.com', 'localhost'])
|
||||
end
|
||||
|
||||
it 'sets multiple domains with commas' do
|
||||
setting.asset_proxy_whitelist = "example.com, *.example.com"
|
||||
expect(setting.asset_proxy_whitelist).to eq(['example.com', '*.example.com', 'localhost'])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#asset_proxy_allowlist' do
|
||||
context 'when given an Array' do
|
||||
it 'sets the domains and adds current running host' do
|
||||
|
@ -958,12 +984,6 @@ RSpec.describe ApplicationSetting do
|
|||
|
||||
it_behaves_like 'application settings examples'
|
||||
|
||||
describe 'repository_storages_weighted_attributes' do
|
||||
it 'returns the keys for repository_storages_weighted' do
|
||||
expect(subject.class.repository_storages_weighted_attributes).to eq([:repository_storages_weighted_default])
|
||||
end
|
||||
end
|
||||
|
||||
describe 'kroki_format_supported?' do
|
||||
it 'returns true when Excalidraw is enabled' do
|
||||
subject.kroki_formats_excalidraw = true
|
||||
|
@ -1007,11 +1027,4 @@ RSpec.describe ApplicationSetting do
|
|||
expect(subject.kroki_formats_excalidraw).to eq(true)
|
||||
end
|
||||
end
|
||||
|
||||
it 'does not allow to set weight for non existing storage' do
|
||||
setting.repository_storages_weighted = { invalid_storage: 100 }
|
||||
|
||||
expect(setting).not_to be_valid
|
||||
expect(setting.errors.messages[:repository_storages_weighted]).to match_array(["can't include: invalid_storage"])
|
||||
end
|
||||
end
|
||||
|
|
|
@ -123,6 +123,7 @@ RSpec.describe ApplicationSettings::UpdateService do
|
|||
it_behaves_like 'invalidates markdown cache', { asset_proxy_url: 'http://test.com' }
|
||||
it_behaves_like 'invalidates markdown cache', { asset_proxy_secret_key: 'another secret' }
|
||||
it_behaves_like 'invalidates markdown cache', { asset_proxy_allowlist: ['domain.com'] }
|
||||
it_behaves_like 'invalidates markdown cache', { asset_proxy_whitelist: ['domain.com'] }
|
||||
|
||||
context 'when also setting the local_markdown_version' do
|
||||
let(:params) { { asset_proxy_enabled: true, local_markdown_version: 12 } }
|
||||
|
|
|
@ -169,6 +169,23 @@ RSpec.describe MergeRequests::UpdateService, :mailer do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
it 'tracks time estimate and spend time changes' do
|
||||
expect(Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter)
|
||||
.to receive(:track_time_estimate_changed_action).once.with(user: user)
|
||||
|
||||
expect(Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter)
|
||||
.to receive(:track_time_spent_changed_action).once.with(user: user)
|
||||
|
||||
opts[:time_estimate] = 86400
|
||||
opts[:spend_time] = {
|
||||
duration: 3600,
|
||||
user_id: user.id,
|
||||
spent_at: Date.parse('2021-02-24')
|
||||
}
|
||||
|
||||
MergeRequests::UpdateService.new(project, user, opts).execute(merge_request)
|
||||
end
|
||||
end
|
||||
|
||||
context 'updating milestone' do
|
||||
|
|
|
@ -31,9 +31,34 @@ RSpec.describe Projects::DestroyService, :aggregate_failures do
|
|||
end
|
||||
|
||||
shared_examples 'deleting the project with pipeline and build' do
|
||||
context 'with pipeline and build', :sidekiq_inline do # which has optimistic locking
|
||||
context 'with pipeline and build related records', :sidekiq_inline do # which has optimistic locking
|
||||
let!(:pipeline) { create(:ci_pipeline, project: project) }
|
||||
let!(:build) { create(:ci_build, :artifacts, pipeline: pipeline) }
|
||||
let!(:build) { create(:ci_build, :artifacts, :with_runner_session, pipeline: pipeline) }
|
||||
let!(:trace_chunks) { create(:ci_build_trace_chunk, build: build) }
|
||||
let!(:job_variables) { create(:ci_job_variable, job: build) }
|
||||
let!(:report_result) { create(:ci_build_report_result, build: build) }
|
||||
let!(:pending_state) { create(:ci_build_pending_state, build: build) }
|
||||
|
||||
it 'deletes build related records' do
|
||||
expect { destroy_project(project, user, {}) }.to change { Ci::Build.count }.by(-1)
|
||||
.and change { Ci::BuildTraceChunk.count }.by(-1)
|
||||
.and change { Ci::JobArtifact.count }.by(-2)
|
||||
.and change { Ci::JobVariable.count }.by(-1)
|
||||
.and change { Ci::BuildPendingState.count }.by(-1)
|
||||
.and change { Ci::BuildReportResult.count }.by(-1)
|
||||
.and change { Ci::BuildRunnerSession.count }.by(-1)
|
||||
end
|
||||
|
||||
it 'avoids N+1 queries', skip: 'skipped until fixed in https://gitlab.com/gitlab-org/gitlab/-/issues/24644' do
|
||||
recorder = ActiveRecord::QueryRecorder.new { destroy_project(project, user, {}) }
|
||||
|
||||
project = create(:project, :repository, namespace: user.namespace)
|
||||
pipeline = create(:ci_pipeline, project: project)
|
||||
builds = create_list(:ci_build, 3, :artifacts, pipeline: pipeline)
|
||||
create_list(:ci_build_trace_chunk, 3, build: builds[0])
|
||||
|
||||
expect { destroy_project(project, project.owner, {}) }.not_to exceed_query_limit(recorder)
|
||||
end
|
||||
|
||||
it_behaves_like 'deleting the project'
|
||||
end
|
||||
|
|
|
@ -289,6 +289,7 @@ RSpec.shared_examples 'application settings examples' do
|
|||
|
||||
describe '#pick_repository_storage' do
|
||||
before do
|
||||
allow(Gitlab.config.repositories.storages).to receive(:keys).and_return(%w(default backup))
|
||||
allow(setting).to receive(:repository_storages_weighted).and_return({ 'default' => 20, 'backup' => 80 })
|
||||
end
|
||||
|
||||
|
@ -304,15 +305,19 @@ RSpec.shared_examples 'application settings examples' do
|
|||
describe '#normalized_repository_storage_weights' do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
where(:storages, :normalized) do
|
||||
{ 'default' => 0, 'backup' => 100 } | { 'default' => 0.0, 'backup' => 1.0 }
|
||||
{ 'default' => 100, 'backup' => 100 } | { 'default' => 0.5, 'backup' => 0.5 }
|
||||
{ 'default' => 20, 'backup' => 80 } | { 'default' => 0.2, 'backup' => 0.8 }
|
||||
{ 'default' => 0, 'backup' => 0 } | { 'default' => 0.0, 'backup' => 0.0 }
|
||||
where(:config_storages, :storages, :normalized) do
|
||||
%w(default backup) | { 'default' => 0, 'backup' => 100 } | { 'default' => 0.0, 'backup' => 1.0 }
|
||||
%w(default backup) | { 'default' => 100, 'backup' => 100 } | { 'default' => 0.5, 'backup' => 0.5 }
|
||||
%w(default backup) | { 'default' => 20, 'backup' => 80 } | { 'default' => 0.2, 'backup' => 0.8 }
|
||||
%w(default backup) | { 'default' => 0, 'backup' => 0 } | { 'default' => 0.0, 'backup' => 0.0 }
|
||||
%w(default) | { 'default' => 0, 'backup' => 100 } | { 'default' => 0.0 }
|
||||
%w(default) | { 'default' => 100, 'backup' => 100 } | { 'default' => 1.0 }
|
||||
%w(default) | { 'default' => 20, 'backup' => 80 } | { 'default' => 1.0 }
|
||||
end
|
||||
|
||||
with_them do
|
||||
before do
|
||||
allow(Gitlab.config.repositories.storages).to receive(:keys).and_return(config_storages)
|
||||
allow(setting).to receive(:repository_storages_weighted).and_return(storages)
|
||||
end
|
||||
|
||||
|
|
|
@ -3,34 +3,49 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe 'admin/application_settings/_repository_storage.html.haml' do
|
||||
let(:app_settings) { create(:application_setting) }
|
||||
let(:repository_storages_weighted_attributes) { [:repository_storages_weighted_default, :repository_storages_weighted_mepmep, :repository_storages_weighted_foobar]}
|
||||
let(:app_settings) { build(:application_setting, repository_storages_weighted: repository_storages_weighted) }
|
||||
|
||||
before do
|
||||
stub_storage_settings({ 'default': {}, 'mepmep': {}, 'foobar': {} })
|
||||
assign(:application_setting, app_settings)
|
||||
end
|
||||
|
||||
context 'additional storage config' do
|
||||
let(:repository_storages_weighted) do
|
||||
{
|
||||
"default" => 100,
|
||||
"mepmep" => 50
|
||||
'default' => 100,
|
||||
'mepmep' => 50
|
||||
}
|
||||
end
|
||||
|
||||
before do
|
||||
allow(app_settings).to receive(:repository_storages_weighted).and_return(repository_storages_weighted)
|
||||
allow(app_settings).to receive(:repository_storages_weighted_mepmep).and_return(100)
|
||||
allow(app_settings).to receive(:repository_storages_weighted_foobar).and_return(50)
|
||||
assign(:application_setting, app_settings)
|
||||
allow(ApplicationSetting).to receive(:repository_storages_weighted_attributes).and_return(repository_storages_weighted_attributes)
|
||||
end
|
||||
|
||||
context 'when multiple storages are available' do
|
||||
it 'lists them all' do
|
||||
render
|
||||
|
||||
# lists storages that are saved with weights
|
||||
repository_storages_weighted.each do |storage_name, storage_weight|
|
||||
Gitlab.config.repositories.storages.keys.each do |storage_name|
|
||||
expect(rendered).to have_content(storage_name)
|
||||
end
|
||||
|
||||
# lists storage not saved with weight
|
||||
expect(rendered).to have_content('foobar')
|
||||
end
|
||||
end
|
||||
|
||||
context 'fewer storage configs' do
|
||||
let(:repository_storages_weighted) do
|
||||
{
|
||||
'default' => 100,
|
||||
'mepmep' => 50,
|
||||
'something_old' => 100
|
||||
}
|
||||
end
|
||||
|
||||
it 'lists only configured storages' do
|
||||
render
|
||||
|
||||
Gitlab.config.repositories.storages.keys.each do |storage_name|
|
||||
expect(rendered).to have_content(storage_name)
|
||||
end
|
||||
|
||||
expect(rendered).not_to have_content('something_old')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue