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
|
# Browser detection
|
||||||
gem 'browser', '~> 4.2'
|
gem 'browser', '~> 4.2'
|
||||||
|
|
||||||
|
# OS detection for usage ping
|
||||||
|
gem 'ohai', '~> 16.10'
|
||||||
|
|
||||||
# GPG
|
# GPG
|
||||||
gem 'gpgme', '~> 2.0.19'
|
gem 'gpgme', '~> 2.0.19'
|
||||||
|
|
||||||
|
|
44
Gemfile.lock
44
Gemfile.lock
|
@ -172,6 +172,14 @@ GEM
|
||||||
cbor (0.5.9.6)
|
cbor (0.5.9.6)
|
||||||
character_set (1.4.0)
|
character_set (1.4.0)
|
||||||
charlock_holmes (0.7.7)
|
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)
|
childprocess (3.0.0)
|
||||||
chunky_png (1.3.5)
|
chunky_png (1.3.5)
|
||||||
citrus (3.0.2)
|
citrus (3.0.2)
|
||||||
|
@ -348,6 +356,8 @@ GEM
|
||||||
ffi-compiler (1.0.1)
|
ffi-compiler (1.0.1)
|
||||||
ffi (>= 1.0.0)
|
ffi (>= 1.0.0)
|
||||||
rake
|
rake
|
||||||
|
ffi-yajl (2.3.4)
|
||||||
|
libyajl2 (~> 1.2)
|
||||||
flipper (0.17.1)
|
flipper (0.17.1)
|
||||||
flipper-active_record (0.17.1)
|
flipper-active_record (0.17.1)
|
||||||
activerecord (>= 4.2, < 7)
|
activerecord (>= 4.2, < 7)
|
||||||
|
@ -403,6 +413,7 @@ GEM
|
||||||
fuubar (2.2.0)
|
fuubar (2.2.0)
|
||||||
rspec-core (~> 3.0)
|
rspec-core (~> 3.0)
|
||||||
ruby-progressbar (~> 1.4)
|
ruby-progressbar (~> 1.4)
|
||||||
|
fuzzyurl (0.9.0)
|
||||||
gemoji (3.0.1)
|
gemoji (3.0.1)
|
||||||
gemojione (3.3.0)
|
gemojione (3.3.0)
|
||||||
json
|
json
|
||||||
|
@ -668,6 +679,7 @@ GEM
|
||||||
actionmailer (>= 3.2)
|
actionmailer (>= 3.2)
|
||||||
letter_opener (~> 1.0)
|
letter_opener (~> 1.0)
|
||||||
railties (>= 3.2)
|
railties (>= 3.2)
|
||||||
|
libyajl2 (1.2.0)
|
||||||
license_finder (6.0.0)
|
license_finder (6.0.0)
|
||||||
bundler
|
bundler
|
||||||
rubyzip (>= 1, < 3)
|
rubyzip (>= 1, < 3)
|
||||||
|
@ -717,6 +729,12 @@ GEM
|
||||||
mini_mime (1.0.2)
|
mini_mime (1.0.2)
|
||||||
mini_portile2 (2.5.0)
|
mini_portile2 (2.5.0)
|
||||||
minitest (5.11.3)
|
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)
|
ms_rest (0.7.6)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
faraday (>= 0.9, < 2.0.0)
|
faraday (>= 0.9, < 2.0.0)
|
||||||
|
@ -741,6 +759,8 @@ GEM
|
||||||
connection_pool (~> 2.2)
|
connection_pool (~> 2.2)
|
||||||
net-ldap (0.16.3)
|
net-ldap (0.16.3)
|
||||||
net-ntp (2.1.3)
|
net-ntp (2.1.3)
|
||||||
|
net-scp (3.0.0)
|
||||||
|
net-ssh (>= 2.6.5, < 7.0.0)
|
||||||
net-ssh (6.0.0)
|
net-ssh (6.0.0)
|
||||||
netrc (0.11.0)
|
netrc (0.11.0)
|
||||||
nio4r (2.5.4)
|
nio4r (2.5.4)
|
||||||
|
@ -764,6 +784,19 @@ GEM
|
||||||
octokit (4.20.0)
|
octokit (4.20.0)
|
||||||
faraday (>= 0.9)
|
faraday (>= 0.9)
|
||||||
sawyer (~> 0.8.0, >= 0.5.3)
|
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)
|
oj (3.10.6)
|
||||||
omniauth (1.9.0)
|
omniauth (1.9.0)
|
||||||
hashie (>= 3.4.6, < 3.7.0)
|
hashie (>= 3.4.6, < 3.7.0)
|
||||||
|
@ -857,6 +890,7 @@ GEM
|
||||||
railties (>= 4.0.0)
|
railties (>= 4.0.0)
|
||||||
pg (1.2.3)
|
pg (1.2.3)
|
||||||
pg_query (1.3.0)
|
pg_query (1.3.0)
|
||||||
|
plist (3.6.0)
|
||||||
png_quantizator (0.2.1)
|
png_quantizator (0.2.1)
|
||||||
po_to_json (1.0.1)
|
po_to_json (1.0.1)
|
||||||
json (>= 1.6.0)
|
json (>= 1.6.0)
|
||||||
|
@ -1196,9 +1230,17 @@ GEM
|
||||||
parslet (~> 1.8.0)
|
parslet (~> 1.8.0)
|
||||||
toml-rb (1.0.0)
|
toml-rb (1.0.0)
|
||||||
citrus (~> 3.0, > 3.0)
|
citrus (~> 3.0, > 3.0)
|
||||||
|
tomlrb (1.3.0)
|
||||||
tpm-key_attestation (0.9.0)
|
tpm-key_attestation (0.9.0)
|
||||||
bindata (~> 2.4)
|
bindata (~> 2.4)
|
||||||
openssl-signature_algorithm (~> 0.4.0)
|
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)
|
truncato (0.7.11)
|
||||||
htmlentities (~> 4.3.1)
|
htmlentities (~> 4.3.1)
|
||||||
nokogiri (>= 1.7.0, <= 2.0)
|
nokogiri (>= 1.7.0, <= 2.0)
|
||||||
|
@ -1271,6 +1313,7 @@ GEM
|
||||||
expression_parser
|
expression_parser
|
||||||
rinku
|
rinku
|
||||||
with_env (1.1.0)
|
with_env (1.1.0)
|
||||||
|
wmi-lite (1.0.5)
|
||||||
xml-simple (1.1.5)
|
xml-simple (1.1.5)
|
||||||
xpath (3.2.0)
|
xpath (3.2.0)
|
||||||
nokogiri (~> 1.8)
|
nokogiri (~> 1.8)
|
||||||
|
@ -1443,6 +1486,7 @@ DEPENDENCIES
|
||||||
nokogiri (~> 1.11.1)
|
nokogiri (~> 1.11.1)
|
||||||
oauth2 (~> 1.4)
|
oauth2 (~> 1.4)
|
||||||
octokit (~> 4.15)
|
octokit (~> 4.15)
|
||||||
|
ohai (~> 16.10)
|
||||||
oj (~> 3.10.6)
|
oj (~> 3.10.6)
|
||||||
omniauth (~> 1.8)
|
omniauth (~> 1.8)
|
||||||
omniauth-atlassian-oauth2 (~> 0.2.0)
|
omniauth-atlassian-oauth2 (~> 0.2.0)
|
||||||
|
|
|
@ -10,7 +10,7 @@ import {
|
||||||
} from '@gitlab/ui';
|
} from '@gitlab/ui';
|
||||||
import { mapActions, mapState } from 'vuex';
|
import { mapActions, mapState } from 'vuex';
|
||||||
import { isListDraggable } from '~/boards/boards_util';
|
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 { BV_HIDE_TOOLTIP } from '~/lib/utils/constants';
|
||||||
import { n__, s__, __ } from '~/locale';
|
import { n__, s__, __ } from '~/locale';
|
||||||
import sidebarEventHub from '~/sidebar/event_hub';
|
import sidebarEventHub from '~/sidebar/event_hub';
|
||||||
|
@ -69,7 +69,7 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(['activeId']),
|
...mapState(['activeId', 'isEpicBoard']),
|
||||||
isLoggedIn() {
|
isLoggedIn() {
|
||||||
return Boolean(this.currentUserId);
|
return Boolean(this.currentUserId);
|
||||||
},
|
},
|
||||||
|
@ -97,11 +97,14 @@ export default {
|
||||||
showListDetails() {
|
showListDetails() {
|
||||||
return !this.list.collapsed || !this.isSwimlanesHeader;
|
return !this.list.collapsed || !this.isSwimlanesHeader;
|
||||||
},
|
},
|
||||||
issuesCount() {
|
itemsCount() {
|
||||||
return this.list.issuesCount;
|
return this.list.issuesCount;
|
||||||
},
|
},
|
||||||
issuesTooltipLabel() {
|
countIcon() {
|
||||||
return n__(`%d issue`, `%d issues`, this.issuesCount);
|
return 'issues';
|
||||||
|
},
|
||||||
|
itemsTooltipLabel() {
|
||||||
|
return n__(`%d issue`, `%d issues`, this.itemsCount);
|
||||||
},
|
},
|
||||||
chevronTooltip() {
|
chevronTooltip() {
|
||||||
return this.list.collapsed ? this.$options.i18n.expand : this.$options.i18n.collapse;
|
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';
|
return this.list.collapsed ? 'chevron-down' : 'chevron-right';
|
||||||
},
|
},
|
||||||
isNewIssueShown() {
|
isNewIssueShown() {
|
||||||
return this.listType === ListType.backlog || this.showListHeaderButton;
|
return (this.listType === ListType.backlog || this.showListHeaderButton) && !this.isEpicBoard;
|
||||||
},
|
},
|
||||||
isSettingsShown() {
|
isSettingsShown() {
|
||||||
return (
|
return (
|
||||||
|
@ -131,8 +134,14 @@ export default {
|
||||||
return !this.disabled && isListDraggable(this.list);
|
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: {
|
methods: {
|
||||||
...mapActions(['updateList', 'setActiveId']),
|
...mapActions(['updateList', 'setActiveId', 'toggleListCollapsed']),
|
||||||
openSidebarSettings() {
|
openSidebarSettings() {
|
||||||
if (this.activeId === inactiveId) {
|
if (this.activeId === inactiveId) {
|
||||||
sidebarEventHub.$emit('sidebar.closeAll');
|
sidebarEventHub.$emit('sidebar.closeAll');
|
||||||
|
@ -148,10 +157,10 @@ export default {
|
||||||
eventHub.$emit(`toggle-issue-form-${this.list.id}`);
|
eventHub.$emit(`toggle-issue-form-${this.list.id}`);
|
||||||
},
|
},
|
||||||
toggleExpanded() {
|
toggleExpanded() {
|
||||||
// eslint-disable-next-line vue/no-mutating-props
|
const collapsed = !this.list.collapsed;
|
||||||
this.list.collapsed = !this.list.collapsed;
|
this.toggleListCollapsed({ listId: this.list.id, collapsed });
|
||||||
|
|
||||||
if (!this.isLoggedIn) {
|
if (!this.isLoggedIn || this.isEpicBoard) {
|
||||||
this.addToLocalStorage();
|
this.addToLocalStorage();
|
||||||
} else {
|
} else {
|
||||||
this.updateListFunction();
|
this.updateListFunction();
|
||||||
|
@ -163,7 +172,7 @@ export default {
|
||||||
},
|
},
|
||||||
addToLocalStorage() {
|
addToLocalStorage() {
|
||||||
if (AccessorUtilities.isLocalStorageAccessSafe()) {
|
if (AccessorUtilities.isLocalStorageAccessSafe()) {
|
||||||
localStorage.setItem(`${this.uniqueKey}.expanded`, !this.list.collapsed);
|
localStorage.setItem(`${this.uniqueKey}.collapsed`, this.list.collapsed);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
updateListFunction() {
|
updateListFunction() {
|
||||||
|
@ -203,6 +212,7 @@ export default {
|
||||||
class="board-title-caret no-drag gl-cursor-pointer"
|
class="board-title-caret no-drag gl-cursor-pointer"
|
||||||
category="tertiary"
|
category="tertiary"
|
||||||
size="small"
|
size="small"
|
||||||
|
data-testid="board-title-caret"
|
||||||
@click="toggleExpanded"
|
@click="toggleExpanded"
|
||||||
/>
|
/>
|
||||||
<!-- EE start -->
|
<!-- EE start -->
|
||||||
|
@ -301,11 +311,11 @@ export default {
|
||||||
<div v-if="list.maxIssueCount !== 0">
|
<div v-if="list.maxIssueCount !== 0">
|
||||||
•
|
•
|
||||||
<gl-sprintf :message="__('%{issuesSize} with a limit of %{maxIssueCount}')">
|
<gl-sprintf :message="__('%{issuesSize} with a limit of %{maxIssueCount}')">
|
||||||
<template #issuesSize>{{ issuesTooltipLabel }}</template>
|
<template #issuesSize>{{ itemsTooltipLabel }}</template>
|
||||||
<template #maxIssueCount>{{ list.maxIssueCount }}</template>
|
<template #maxIssueCount>{{ list.maxIssueCount }}</template>
|
||||||
</gl-sprintf>
|
</gl-sprintf>
|
||||||
</div>
|
</div>
|
||||||
<div v-else>• {{ issuesTooltipLabel }}</div>
|
<div v-else>• {{ itemsTooltipLabel }}</div>
|
||||||
<div v-if="weightFeatureAvailable">
|
<div v-if="weightFeatureAvailable">
|
||||||
•
|
•
|
||||||
<gl-sprintf :message="__('%{totalWeight} total weight')">
|
<gl-sprintf :message="__('%{totalWeight} total weight')">
|
||||||
|
@ -323,13 +333,13 @@ export default {
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<span class="gl-display-inline-flex">
|
<span class="gl-display-inline-flex">
|
||||||
<gl-tooltip :target="() => $refs.issueCount" :title="issuesTooltipLabel" />
|
<gl-tooltip :target="() => $refs.itemCount" :title="itemsTooltipLabel" />
|
||||||
<span ref="issueCount" class="issue-count-badge-count">
|
<span ref="itemCount" class="issue-count-badge-count">
|
||||||
<gl-icon class="gl-mr-2" name="issues" />
|
<gl-icon class="gl-mr-2" :name="countIcon" />
|
||||||
<issue-count :issues-size="issuesCount" :max-issue-count="list.maxIssueCount" />
|
<issue-count :issues-size="itemsCount" :max-issue-count="list.maxIssueCount" />
|
||||||
</span>
|
</span>
|
||||||
<!-- EE start -->
|
<!-- EE start -->
|
||||||
<template v-if="weightFeatureAvailable">
|
<template v-if="weightFeatureAvailable && !isEpicBoard">
|
||||||
<gl-tooltip :target="() => $refs.weightTooltip" :title="weightCountToolTip" />
|
<gl-tooltip :target="() => $refs.weightTooltip" :title="weightCountToolTip" />
|
||||||
<span ref="weightTooltip" class="gl-display-inline-flex gl-ml-3">
|
<span ref="weightTooltip" class="gl-display-inline-flex gl-ml-3">
|
||||||
<gl-icon class="gl-mr-2" name="weight" />
|
<gl-icon class="gl-mr-2" name="weight" />
|
||||||
|
|
|
@ -86,7 +86,11 @@ export default {
|
||||||
<template>
|
<template>
|
||||||
<span>
|
<span>
|
||||||
<span ref="issueDueDate" :class="cssClass" class="board-card-info card-number">
|
<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">{{
|
<time :class="{ 'text-danger': isPastDue }" datetime="date" class="board-card-info-text">{{
|
||||||
body
|
body
|
||||||
}}</time>
|
}}</time>
|
||||||
|
|
|
@ -37,7 +37,7 @@ export default {
|
||||||
<template>
|
<template>
|
||||||
<span>
|
<span>
|
||||||
<span ref="issueTimeEstimate" class="board-card-info card-number">
|
<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>
|
<time class="board-card-info-text">{{ timeEstimate }}</time>
|
||||||
</span>
|
</span>
|
||||||
<gl-tooltip
|
<gl-tooltip
|
||||||
|
|
|
@ -256,6 +256,10 @@ export default {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
toggleListCollapsed: ({ commit }, { listId, collapsed }) => {
|
||||||
|
commit(types.TOGGLE_LIST_COLLAPSED, { listId, collapsed });
|
||||||
|
},
|
||||||
|
|
||||||
removeList: ({ state, commit }, listId) => {
|
removeList: ({ state, commit }, listId) => {
|
||||||
const listsBackup = { ...state.boardLists };
|
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 RECEIVE_ADD_LIST_ERROR = 'RECEIVE_ADD_LIST_ERROR';
|
||||||
export const MOVE_LIST = 'MOVE_LIST';
|
export const MOVE_LIST = 'MOVE_LIST';
|
||||||
export const UPDATE_LIST_FAILURE = 'UPDATE_LIST_FAILURE';
|
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 = 'REMOVE_LIST';
|
||||||
export const REMOVE_LIST_FAILURE = 'REMOVE_LIST_FAILURE';
|
export const REMOVE_LIST_FAILURE = 'REMOVE_LIST_FAILURE';
|
||||||
export const REQUEST_ITEMS_FOR_LIST = 'REQUEST_ITEMS_FOR_LIST';
|
export const REQUEST_ITEMS_FOR_LIST = 'REQUEST_ITEMS_FOR_LIST';
|
||||||
|
|
|
@ -105,6 +105,10 @@ export default {
|
||||||
Vue.set(state, 'boardLists', backupList);
|
Vue.set(state, 'boardLists', backupList);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
[mutationTypes.TOGGLE_LIST_COLLAPSED]: (state, { listId, collapsed }) => {
|
||||||
|
Vue.set(state.boardLists[listId], 'collapsed', collapsed);
|
||||||
|
},
|
||||||
|
|
||||||
[mutationTypes.REMOVE_LIST]: (state, listId) => {
|
[mutationTypes.REMOVE_LIST]: (state, listId) => {
|
||||||
Vue.delete(state.boardLists, 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-input id="fork-name" v-model="fork.name" data-testid="fork-name-input" required />
|
||||||
</gl-form-group>
|
</gl-form-group>
|
||||||
|
|
||||||
<div class="gl-display-flex">
|
<div class="gl-md-display-flex">
|
||||||
<div class="gl-w-half">
|
<div class="gl-flex-basis-half">
|
||||||
<gl-form-group label="Project URL" label-for="fork-url" class="gl-pr-2">
|
<gl-form-group label="Project URL" label-for="fork-url" class="gl-md-mr-3">
|
||||||
<gl-form-input-group>
|
<gl-form-input-group>
|
||||||
<template #prepend>
|
<template #prepend>
|
||||||
<gl-input-group-text>
|
<gl-input-group-text>
|
||||||
|
@ -225,8 +225,8 @@ export default {
|
||||||
</gl-form-input-group>
|
</gl-form-input-group>
|
||||||
</gl-form-group>
|
</gl-form-group>
|
||||||
</div>
|
</div>
|
||||||
<div class="gl-w-half">
|
<div class="gl-flex-basis-half">
|
||||||
<gl-form-group label="Project slug" label-for="fork-slug" class="gl-pl-2">
|
<gl-form-group label="Project slug" label-for="fork-slug" class="gl-md-ml-3">
|
||||||
<gl-form-input
|
<gl-form-input
|
||||||
id="fork-slug"
|
id="fork-slug"
|
||||||
v-model="fork.slug"
|
v-model="fork.slug"
|
||||||
|
|
|
@ -55,7 +55,12 @@ export default {
|
||||||
>
|
>
|
||||||
<div class="d-flex align-items-center">
|
<div class="d-flex align-items-center">
|
||||||
<gl-icon :size="16" :name="option.icon" />
|
<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>
|
</div>
|
||||||
<template #help>{{
|
<template #help>{{
|
||||||
isProjectSnippet && option.description_project
|
isProjectSnippet && option.description_project
|
||||||
|
|
|
@ -35,5 +35,4 @@
|
||||||
@import './pages/sherlock';
|
@import './pages/sherlock';
|
||||||
@import './pages/storage_quota';
|
@import './pages/storage_quota';
|
||||||
@import './pages/tree';
|
@import './pages/tree';
|
||||||
@import './pages/trials';
|
|
||||||
@import './pages/users';
|
@import './pages/users';
|
||||||
|
|
|
@ -830,8 +830,8 @@ $ci-variable-remove-button-width: calc(1em + #{2 * $gl-padding});
|
||||||
/*
|
/*
|
||||||
GitLab Plans
|
GitLab Plans
|
||||||
*/
|
*/
|
||||||
$gl-gold-plan: #d4af37;
|
$gl-ultimate-plan: #d4af37;
|
||||||
$gl-silver-plan: #91a1ab;
|
$gl-premium-plan: #91a1ab;
|
||||||
$gl-bronze-plan: #cd7f32;
|
$gl-bronze-plan: #cd7f32;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -339,3 +339,19 @@
|
||||||
display: inline;
|
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%;
|
flex-basis: 25%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Will be moved to @gitlab/ui in https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1168
|
||||||
.gl-md-ml-3 {
|
.gl-md-ml-3 {
|
||||||
@media (min-width: $breakpoint-md) {
|
@media (min-width: $breakpoint-md) {
|
||||||
margin-left: $gl-spacing-scale-3;
|
margin-left: $gl-spacing-scale-3;
|
||||||
|
|
|
@ -237,7 +237,6 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
|
||||||
[
|
[
|
||||||
*::ApplicationSettingsHelper.visible_attributes,
|
*::ApplicationSettingsHelper.visible_attributes,
|
||||||
*::ApplicationSettingsHelper.external_authorization_service_attributes,
|
*::ApplicationSettingsHelper.external_authorization_service_attributes,
|
||||||
*ApplicationSetting.repository_storages_weighted_attributes,
|
|
||||||
*ApplicationSetting.kroki_formats_attributes.keys.map { |key| "kroki_formats_#{key}".to_sym },
|
*ApplicationSetting.kroki_formats_attributes.keys.map { |key| "kroki_formats_#{key}".to_sym },
|
||||||
:lets_encrypt_notification_email,
|
:lets_encrypt_notification_email,
|
||||||
:lets_encrypt_terms_of_service_accepted,
|
:lets_encrypt_terms_of_service_accepted,
|
||||||
|
@ -248,8 +247,8 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
|
||||||
:default_branch_name,
|
:default_branch_name,
|
||||||
disabled_oauth_sign_in_sources: [],
|
disabled_oauth_sign_in_sources: [],
|
||||||
import_sources: [],
|
import_sources: [],
|
||||||
repository_storages: [],
|
restricted_visibility_levels: [],
|
||||||
restricted_visibility_levels: []
|
repository_storages_weighted: {}
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -5,35 +5,38 @@ module Mutations
|
||||||
module Issues
|
module Issues
|
||||||
class IssueMoveList < Mutations::Issues::Base
|
class IssueMoveList < Mutations::Issues::Base
|
||||||
graphql_name 'IssueMoveList'
|
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,
|
required: true,
|
||||||
loads: Types::BoardType,
|
loads: Types::BoardType,
|
||||||
description: 'Global ID of the board that the issue is in.'
|
description: 'Global ID of the board that the issue is in.'
|
||||||
|
|
||||||
argument :project_path, GraphQL::ID_TYPE,
|
argument :project_path, GraphQL::ID_TYPE,
|
||||||
required: true,
|
required: true,
|
||||||
description: 'Project the issue to mutate is in.'
|
description: 'Project the issue to mutate is in.'
|
||||||
|
|
||||||
argument :iid, GraphQL::STRING_TYPE,
|
argument :iid, GraphQL::STRING_TYPE,
|
||||||
required: true,
|
required: true,
|
||||||
description: 'IID of the issue to mutate.'
|
description: 'IID of the issue to mutate.'
|
||||||
|
|
||||||
argument :from_list_id, GraphQL::ID_TYPE,
|
argument :from_list_id, ListID,
|
||||||
required: false,
|
required: false,
|
||||||
description: 'ID of the board list that the issue will be moved from.'
|
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,
|
required: false,
|
||||||
description: 'ID of the board list that the issue will be moved to.'
|
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,
|
required: false,
|
||||||
description: 'ID of issue that should be placed before the current issue.'
|
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,
|
required: false,
|
||||||
description: 'ID of issue that should be placed after the current issue.'
|
description: 'ID of issue that should be placed after the current issue.'
|
||||||
|
|
||||||
def ready?(**args)
|
def ready?(**args)
|
||||||
if move_arguments(args).blank?
|
if move_arguments(args).blank?
|
||||||
|
|
|
@ -37,13 +37,8 @@ module ApplicationSettingsHelper
|
||||||
end
|
end
|
||||||
|
|
||||||
def storage_weights
|
def storage_weights
|
||||||
ApplicationSetting.repository_storages_weighted_attributes.map do |attribute|
|
Gitlab.config.repositories.storages.keys.each_with_object(OpenStruct.new) do |storage, weights|
|
||||||
storage = attribute.to_s.delete_prefix('repository_storages_weighted_')
|
weights[storage.to_sym] = @application_setting.repository_storages_weighted[storage] || 0
|
||||||
{
|
|
||||||
name: attribute,
|
|
||||||
label: storage,
|
|
||||||
value: @application_setting.repository_storages_weighted[storage] || 0
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -25,10 +25,6 @@ class ApplicationSetting < ApplicationRecord
|
||||||
alias_attribute :instance_group_id, :instance_administrators_group_id
|
alias_attribute :instance_group_id, :instance_administrators_group_id
|
||||||
alias_attribute :instance_administrators_group, :instance_group
|
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
|
def self.kroki_formats_attributes
|
||||||
{
|
{
|
||||||
blockdiag: {
|
blockdiag: {
|
||||||
|
@ -44,7 +40,6 @@ class ApplicationSetting < ApplicationRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
store_accessor :kroki_formats, *ApplicationSetting.kroki_formats_attributes.keys, prefix: true
|
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
|
# Include here so it can override methods from
|
||||||
# `add_authentication_token_field`
|
# `add_authentication_token_field`
|
||||||
|
@ -58,8 +53,9 @@ class ApplicationSetting < ApplicationRecord
|
||||||
serialize :domain_allowlist, Array # rubocop:disable Cop/ActiveRecordSerialize
|
serialize :domain_allowlist, Array # rubocop:disable Cop/ActiveRecordSerialize
|
||||||
serialize :domain_denylist, Array # rubocop:disable Cop/ActiveRecordSerialize
|
serialize :domain_denylist, Array # rubocop:disable Cop/ActiveRecordSerialize
|
||||||
serialize :repository_storages # 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
|
# 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
|
serialize :asset_proxy_whitelist, Array # rubocop:disable Cop/ActiveRecordSerialize
|
||||||
|
|
||||||
cache_markdown_field :sign_in_text
|
cache_markdown_field :sign_in_text
|
||||||
|
@ -502,6 +498,7 @@ class ApplicationSetting < ApplicationRecord
|
||||||
inclusion: { in: [true, false], message: _('must be a boolean value') }
|
inclusion: { in: [true, false], message: _('must be a boolean value') }
|
||||||
|
|
||||||
before_validation :ensure_uuid!
|
before_validation :ensure_uuid!
|
||||||
|
before_validation :coerce_repository_storages_weighted, if: :repository_storages_weighted_changed?
|
||||||
|
|
||||||
before_save :ensure_runners_registration_token
|
before_save :ensure_runners_registration_token
|
||||||
before_save :ensure_health_check_access_token
|
before_save :ensure_health_check_access_token
|
||||||
|
@ -582,12 +579,6 @@ class ApplicationSetting < ApplicationRecord
|
||||||
recaptcha_enabled || login_recaptcha_protection_enabled
|
recaptcha_enabled || login_recaptcha_protection_enabled
|
||||||
end
|
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|
|
kroki_formats_attributes.keys.each do |key|
|
||||||
define_method :"kroki_formats_#{key}=" do |value|
|
define_method :"kroki_formats_#{key}=" do |value|
|
||||||
super(::Gitlab::Utils.to_boolean(value))
|
super(::Gitlab::Utils.to_boolean(value))
|
||||||
|
|
|
@ -280,23 +280,24 @@ module ApplicationSettingImplementation
|
||||||
self.notes_create_limit_allowlist = strings_to_array(values).map(&:downcase)
|
self.notes_create_limit_allowlist = strings_to_array(values).map(&:downcase)
|
||||||
end
|
end
|
||||||
|
|
||||||
def asset_proxy_allowlist=(values)
|
def asset_proxy_whitelist=(values)
|
||||||
values = strings_to_array(values) if values.is_a?(String)
|
values = strings_to_array(values) if values.is_a?(String)
|
||||||
|
|
||||||
# make sure we always allow the running host
|
# make sure we always allow the running host
|
||||||
values << Gitlab.config.gitlab.host unless values.include?(Gitlab.config.gitlab.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
|
end
|
||||||
|
|
||||||
def repository_storages
|
def repository_storages
|
||||||
Array(read_attribute(:repository_storages))
|
Array(read_attribute(:repository_storages))
|
||||||
end
|
end
|
||||||
|
|
||||||
def repository_storages_weighted
|
|
||||||
read_attribute(:repository_storages_weighted)
|
|
||||||
end
|
|
||||||
|
|
||||||
def commit_email_hostname
|
def commit_email_hostname
|
||||||
super.presence || self.class.default_commit_email_hostname
|
super.presence || self.class.default_commit_email_hostname
|
||||||
end
|
end
|
||||||
|
@ -328,9 +329,10 @@ module ApplicationSettingImplementation
|
||||||
|
|
||||||
def normalized_repository_storage_weights
|
def normalized_repository_storage_weights
|
||||||
strong_memoize(:normalized_repository_storage_weights) do
|
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
|
next w if weights_total == 0
|
||||||
|
|
||||||
w.to_f / weights_total
|
w.to_f / weights_total
|
||||||
|
@ -468,16 +470,20 @@ module ApplicationSettingImplementation
|
||||||
invalid.empty?
|
invalid.empty?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def coerce_repository_storages_weighted
|
||||||
|
repository_storages_weighted.transform_values!(&:to_i)
|
||||||
|
end
|
||||||
|
|
||||||
def check_repository_storages_weighted
|
def check_repository_storages_weighted
|
||||||
invalid = repository_storages_weighted.keys - Gitlab.config.repositories.storages.keys
|
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?
|
invalid.empty?
|
||||||
|
|
||||||
repository_storages_weighted.each do |key, val|
|
repository_storages_weighted.each do |key, val|
|
||||||
next unless val.present?
|
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, _("value for '%{storage}' must be an integer") % { storage: key }) 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 between 0 and 100") % { storage: key }) unless val.between?(0, 100)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ module ApplicationSettings
|
||||||
|
|
||||||
attr_reader :params, :application_setting
|
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
|
def execute
|
||||||
result = update_settings
|
result = update_settings
|
||||||
|
|
|
@ -31,6 +31,7 @@ module MergeRequests
|
||||||
old_mentioned_users = old_associations.fetch(:mentioned_users, [])
|
old_mentioned_users = old_associations.fetch(:mentioned_users, [])
|
||||||
old_assignees = old_associations.fetch(:assignees, [])
|
old_assignees = old_associations.fetch(:assignees, [])
|
||||||
old_reviewers = old_associations.fetch(:reviewers, [])
|
old_reviewers = old_associations.fetch(:reviewers, [])
|
||||||
|
old_timelogs = old_associations.fetch(:timelogs, [])
|
||||||
changed_fields = merge_request.previous_changes.keys
|
changed_fields = merge_request.previous_changes.keys
|
||||||
|
|
||||||
resolve_todos(merge_request, old_labels, old_assignees, old_reviewers)
|
resolve_todos(merge_request, old_labels, old_assignees, old_reviewers)
|
||||||
|
@ -48,6 +49,7 @@ module MergeRequests
|
||||||
|
|
||||||
track_title_and_desc_edits(changed_fields)
|
track_title_and_desc_edits(changed_fields)
|
||||||
track_discussion_lock_toggle(merge_request, 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_labels_added(merge_request, old_labels)
|
||||||
notify_if_mentions_added(merge_request, old_mentioned_users)
|
notify_if_mentions_added(merge_request, old_mentioned_users)
|
||||||
|
@ -106,6 +108,11 @@ module MergeRequests
|
||||||
end
|
end
|
||||||
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)
|
def notify_if_labels_added(merge_request, old_labels)
|
||||||
added_labels = merge_request.labels - old_labels
|
added_labels = merge_request.labels - old_labels
|
||||||
|
|
||||||
|
|
|
@ -18,8 +18,9 @@
|
||||||
= _('Enter weights for storages for new repositories.')
|
= _('Enter weights for storages for new repositories.')
|
||||||
= link_to sprite_icon('question-o'), help_page_path('administration/repository_storage_paths')
|
= link_to sprite_icon('question-o'), help_page_path('administration/repository_storage_paths')
|
||||||
.form-check
|
.form-check
|
||||||
- storage_weights.each do |attribute|
|
= f.fields_for :repository_storages_weighted, storage_weights do |storage_form|
|
||||||
= f.text_field attribute[:name], class: 'form-text-input', value: attribute[:value]
|
- Gitlab.config.repositories.storages.keys.each do |storage|
|
||||||
= f.label attribute[:label], attribute[:label], class: 'label-bold form-check-label'
|
= storage_form.text_field storage, class: 'form-text-input'
|
||||||
%br
|
= 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"
|
= 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/import.css"
|
||||||
config.assets.precompile << "page_bundles/incident_management_list.css"
|
config.assets.precompile << "page_bundles/incident_management_list.css"
|
||||||
config.assets.precompile << "page_bundles/issues_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.css"
|
||||||
config.assets.precompile << "page_bundles/jira_connect_users.css"
|
config.assets.precompile << "page_bundles/jira_connect_users.css"
|
||||||
config.assets.precompile << "page_bundles/learn_gitlab.css"
|
config.assets.precompile << "page_bundles/learn_gitlab.css"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
---
|
---
|
||||||
name: ci_reduce_queries_when_ticking_runner_queue
|
name: ci_reduce_queries_when_ticking_runner_queue
|
||||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55496
|
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'
|
milestone: '13.10'
|
||||||
type: development
|
type: development
|
||||||
group: group::continuous integration
|
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,
|
invisible_captcha_enabled boolean DEFAULT false NOT NULL,
|
||||||
enforce_ssh_key_expiration boolean DEFAULT false NOT NULL,
|
enforce_ssh_key_expiration boolean DEFAULT false NOT NULL,
|
||||||
git_two_factor_session_expiry integer DEFAULT 15 NOT NULL,
|
git_two_factor_session_expiry integer DEFAULT 15 NOT NULL,
|
||||||
asset_proxy_allowlist text,
|
|
||||||
keep_latest_artifact boolean DEFAULT true NOT NULL,
|
keep_latest_artifact boolean DEFAULT true NOT NULL,
|
||||||
notes_create_limit integer DEFAULT 300 NOT NULL,
|
notes_create_limit integer DEFAULT 300 NOT NULL,
|
||||||
notes_create_limit_allowlist text[] DEFAULT '{}'::text[] NOT NULL,
|
notes_create_limit_allowlist text[] DEFAULT '{}'::text[] NOT NULL,
|
||||||
kroki_formats jsonb DEFAULT '{}'::jsonb NOT NULL,
|
kroki_formats jsonb DEFAULT '{}'::jsonb NOT NULL,
|
||||||
in_product_marketing_emails_enabled boolean DEFAULT true 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_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 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)),
|
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` | |
|
| `tier` | |
|
||||||
| `skip_validation` | true |
|
| `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`
|
## `redis_hll_counters.code_review.i_code_review_user_toggled_task_item_status_monthly`
|
||||||
|
|
||||||
Missing description
|
Missing description
|
||||||
|
@ -20846,6 +20926,26 @@ Is encrypted LDAP secrets configured?
|
||||||
| `tier` | free, premium, ultimate |
|
| `tier` | free, premium, ultimate |
|
||||||
| `skip_validation` | true |
|
| `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`
|
## `signup_enabled`
|
||||||
|
|
||||||
Whether public signup is enabled
|
Whether public signup is enabled
|
||||||
|
|
|
@ -5,7 +5,7 @@ module Gitlab
|
||||||
module Reports
|
module Reports
|
||||||
class CodequalityReportsComparer < ReportsComparer
|
class CodequalityReportsComparer < ReportsComparer
|
||||||
def initialize(base_report, head_report)
|
def initialize(base_report, head_report)
|
||||||
@base_report = base_report || CodequalityReports.new
|
@base_report = base_report
|
||||||
@head_report = head_report
|
@head_report = head_report
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -15,12 +15,16 @@ module Gitlab
|
||||||
|
|
||||||
def existing_errors
|
def existing_errors
|
||||||
strong_memoize(:existing_errors) do
|
strong_memoize(:existing_errors) do
|
||||||
|
next [] if not_found?
|
||||||
|
|
||||||
base_report.all_degradations & head_report.all_degradations
|
base_report.all_degradations & head_report.all_degradations
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def new_errors
|
def new_errors
|
||||||
strong_memoize(:new_errors) do
|
strong_memoize(:new_errors) do
|
||||||
|
next [] if not_found?
|
||||||
|
|
||||||
fingerprints = head_report.degradations.keys - base_report.degradations.keys
|
fingerprints = head_report.degradations.keys - base_report.degradations.keys
|
||||||
head_report.degradations.fetch_values(*fingerprints)
|
head_report.degradations.fetch_values(*fingerprints)
|
||||||
end
|
end
|
||||||
|
@ -28,6 +32,8 @@ module Gitlab
|
||||||
|
|
||||||
def resolved_errors
|
def resolved_errors
|
||||||
strong_memoize(:resolved_errors) do
|
strong_memoize(:resolved_errors) do
|
||||||
|
next [] if not_found?
|
||||||
|
|
||||||
fingerprints = base_report.degradations.keys - head_report.degradations.keys
|
fingerprints = base_report.degradations.keys - head_report.degradations.keys
|
||||||
base_report.degradations.fetch_values(*fingerprints)
|
base_report.degradations.fetch_values(*fingerprints)
|
||||||
end
|
end
|
||||||
|
|
|
@ -18,10 +18,10 @@ module Gitlab
|
||||||
end
|
end
|
||||||
|
|
||||||
def status
|
def status
|
||||||
if success?
|
if base_report.nil? || head_report.nil?
|
||||||
STATUS_SUCCESS
|
|
||||||
elsif base_report.nil? || head_report.nil?
|
|
||||||
STATUS_NOT_FOUND
|
STATUS_NOT_FOUND
|
||||||
|
elsif success?
|
||||||
|
STATUS_SUCCESS
|
||||||
else
|
else
|
||||||
STATUS_FAILED
|
STATUS_FAILED
|
||||||
end
|
end
|
||||||
|
@ -54,6 +54,10 @@ module Gitlab
|
||||||
def total_count
|
def total_count
|
||||||
existing_errors.size + new_errors.size
|
existing_errors.size + new_errors.size
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def not_found?
|
||||||
|
status == STATUS_NOT_FOUND
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -77,12 +77,22 @@ module Gitlab
|
||||||
def add_approval!(user_id)
|
def add_approval!(user_id)
|
||||||
return unless review.review_type == 'APPROVED'
|
return unless review.review_type == 'APPROVED'
|
||||||
|
|
||||||
add_approval_system_note!(user_id)
|
approval_attribues = {
|
||||||
|
merge_request_id: merge_request.id,
|
||||||
merge_request.approvals.create!(
|
|
||||||
user_id: user_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
|
end
|
||||||
|
|
||||||
def add_approval_system_note!(user_id)
|
def add_approval_system_note!(user_id)
|
||||||
|
|
|
@ -242,7 +242,8 @@ module Gitlab
|
||||||
def system_usage_data_settings
|
def system_usage_data_settings
|
||||||
{
|
{
|
||||||
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
|
end
|
||||||
|
@ -505,6 +506,17 @@ module Gitlab
|
||||||
end
|
end
|
||||||
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)
|
def last_28_days_time_period(column: :created_at)
|
||||||
{ column => 30.days.ago..2.days.ago }
|
{ column => 30.days.ago..2.days.ago }
|
||||||
end
|
end
|
||||||
|
|
|
@ -44,7 +44,9 @@
|
||||||
'i_code_review_user_toggled_task_item_status',
|
'i_code_review_user_toggled_task_item_status',
|
||||||
'i_code_review_user_create_mr_from_issue',
|
'i_code_review_user_create_mr_from_issue',
|
||||||
'i_code_review_user_mr_discussion_locked',
|
'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
|
- name: code_review_category_monthly_active_users
|
||||||
operator: OR
|
operator: OR
|
||||||
|
@ -82,7 +84,9 @@
|
||||||
'i_code_review_user_toggled_task_item_status',
|
'i_code_review_user_toggled_task_item_status',
|
||||||
'i_code_review_user_create_mr_from_issue',
|
'i_code_review_user_create_mr_from_issue',
|
||||||
'i_code_review_user_mr_discussion_locked',
|
'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
|
- name: code_review_extension_category_monthly_active_users
|
||||||
operator: OR
|
operator: OR
|
||||||
|
|
|
@ -174,3 +174,13 @@
|
||||||
category: code_review
|
category: code_review
|
||||||
aggregation: weekly
|
aggregation: weekly
|
||||||
feature_flag: usage_data_i_code_review_user_mr_discussion_unlocked
|
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_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_LOCKED_ACTION = 'i_code_review_user_mr_discussion_locked'
|
||||||
MR_DISCUSSION_UNLOCKED_ACTION = 'i_code_review_user_mr_discussion_unlocked'
|
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
|
class << self
|
||||||
def track_mr_diffs_action(merge_request:)
|
def track_mr_diffs_action(merge_request:)
|
||||||
|
@ -163,6 +165,14 @@ module Gitlab
|
||||||
track_unique_action_by_user(MR_DISCUSSION_UNLOCKED_ACTION, user)
|
track_unique_action_by_user(MR_DISCUSSION_UNLOCKED_ACTION, user)
|
||||||
end
|
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
|
private
|
||||||
|
|
||||||
def track_unique_action_by_merge_request(action, merge_request)
|
def track_unique_action_by_merge_request(action, merge_request)
|
||||||
|
|
|
@ -173,6 +173,11 @@ msgid_plural "%d days"
|
||||||
msgstr[0] ""
|
msgstr[0] ""
|
||||||
msgstr[1] ""
|
msgstr[1] ""
|
||||||
|
|
||||||
|
msgid "%d epic"
|
||||||
|
msgid_plural "%d epics"
|
||||||
|
msgstr[0] ""
|
||||||
|
msgstr[1] ""
|
||||||
|
|
||||||
msgid "%d error"
|
msgid "%d error"
|
||||||
msgid_plural "%d errors"
|
msgid_plural "%d errors"
|
||||||
msgstr[0] ""
|
msgstr[0] ""
|
||||||
|
@ -559,6 +564,9 @@ msgstr ""
|
||||||
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
|
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "%{itemsCount} issues with a limit of %{maxIssueCount}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
|
msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -34912,6 +34920,9 @@ msgstr ""
|
||||||
msgid "can't be enabled because signed commits are required for this project"
|
msgid "can't be enabled because signed commits are required for this project"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "can't include: %{invalid_storages}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "cannot be a date in the past"
|
msgid "cannot be a date in the past"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -36318,6 +36329,12 @@ msgstr ""
|
||||||
msgid "v%{version} published %{timeAgo}"
|
msgid "v%{version} published %{timeAgo}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "value for '%{storage}' must be an integer"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "value for '%{storage}' must be between 0 and 100"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "verify ownership"
|
msgid "verify ownership"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,10 @@ module QA
|
||||||
# This 'element' is here only to ensure the changes in the view source aren't mistakenly changed
|
# 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
|
element :_, "qa_selector = local_assigns.fetch(:qa_selector, '')" # rubocop:disable QA/ElementWithPattern
|
||||||
end
|
end
|
||||||
|
|
||||||
|
base.view 'app/assets/javascripts/snippets/components/snippet_visibility_edit.vue' do
|
||||||
|
element :visibility_content
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def fill_title(title)
|
def fill_title(title)
|
||||||
|
@ -44,7 +48,7 @@ module QA
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_visibility(visibility)
|
def set_visibility(visibility)
|
||||||
choose visibility
|
click_element(:visibility_content, visibility: visibility)
|
||||||
end
|
end
|
||||||
|
|
||||||
def fill_file_name(name, file_number = nil)
|
def fill_file_name(name, file_number = nil)
|
||||||
|
|
|
@ -30,6 +30,7 @@ UsageData/LargeTable:
|
||||||
- :Settings
|
- :Settings
|
||||||
- :CE_MEMOIZED_VALUES
|
- :CE_MEMOIZED_VALUES
|
||||||
- :EE_MEMOIZED_VALUES
|
- :EE_MEMOIZED_VALUES
|
||||||
|
- :Ohai::System
|
||||||
CountMethods:
|
CountMethods:
|
||||||
- :count
|
- :count
|
||||||
- :distinct_count
|
- :distinct_count
|
||||||
|
|
|
@ -144,10 +144,10 @@ RSpec.describe Admin::ApplicationSettingsController do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'updates repository_storages_weighted setting' do
|
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(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
|
end
|
||||||
|
|
||||||
it 'updates kroki_formats setting' do
|
it 'updates kroki_formats setting' do
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
FactoryBot.define do
|
FactoryBot.define do
|
||||||
factory :ci_build_trace_chunk, class: 'Ci::BuildTraceChunk' do
|
factory :ci_build_trace_chunk, class: 'Ci::BuildTraceChunk' do
|
||||||
build factory: :ci_build
|
build factory: :ci_build
|
||||||
chunk_index { 0 }
|
chunk_index { generate(:iid) }
|
||||||
data_store { :redis }
|
data_store { :redis }
|
||||||
|
|
||||||
trait :redis_with_data do
|
trait :redis_with_data do
|
||||||
|
|
|
@ -384,7 +384,20 @@ RSpec.describe 'Admin updates settings' do
|
||||||
click_button 'Save changes'
|
click_button 'Save changes'
|
||||||
end
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { shallowMount, createLocalVue } from '@vue/test-utils';
|
import { shallowMount, createLocalVue } from '@vue/test-utils';
|
||||||
import Vuex from 'vuex';
|
import Vuex from 'vuex';
|
||||||
|
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
|
||||||
|
|
||||||
import { mockLabelList } from 'jest/boards/mock_data';
|
import { mockLabelList } from 'jest/boards/mock_data';
|
||||||
import BoardListHeader from '~/boards/components/board_list_header.vue';
|
import BoardListHeader from '~/boards/components/board_list_header.vue';
|
||||||
|
@ -14,6 +15,7 @@ describe('Board List Header Component', () => {
|
||||||
let store;
|
let store;
|
||||||
|
|
||||||
const updateListSpy = jest.fn();
|
const updateListSpy = jest.fn();
|
||||||
|
const toggleListCollapsedSpy = jest.fn();
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
wrapper.destroy();
|
wrapper.destroy();
|
||||||
|
@ -43,38 +45,39 @@ describe('Board List Header Component', () => {
|
||||||
|
|
||||||
if (withLocalStorage) {
|
if (withLocalStorage) {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
`boards.${boardId}.${listMock.listType}.${listMock.id}.expanded`,
|
`boards.${boardId}.${listMock.listType}.${listMock.id}.collapsed`,
|
||||||
(!collapsed).toString(),
|
collapsed.toString(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
store = new Vuex.Store({
|
store = new Vuex.Store({
|
||||||
state: {},
|
state: {},
|
||||||
actions: { updateList: updateListSpy },
|
actions: { updateList: updateListSpy, toggleListCollapsed: toggleListCollapsedSpy },
|
||||||
getters: {},
|
getters: {},
|
||||||
});
|
});
|
||||||
|
|
||||||
wrapper = shallowMount(BoardListHeader, {
|
wrapper = extendedWrapper(
|
||||||
store,
|
shallowMount(BoardListHeader, {
|
||||||
localVue,
|
store,
|
||||||
propsData: {
|
localVue,
|
||||||
disabled: false,
|
propsData: {
|
||||||
list: listMock,
|
disabled: false,
|
||||||
},
|
list: listMock,
|
||||||
provide: {
|
},
|
||||||
boardId,
|
provide: {
|
||||||
weightFeatureAvailable: false,
|
boardId,
|
||||||
currentUserId,
|
weightFeatureAvailable: false,
|
||||||
},
|
currentUserId,
|
||||||
});
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const isCollapsed = () => wrapper.vm.list.collapsed;
|
const isCollapsed = () => wrapper.vm.list.collapsed;
|
||||||
const isExpanded = () => !isCollapsed;
|
|
||||||
|
|
||||||
const findAddIssueButton = () => wrapper.find({ ref: 'newIssueBtn' });
|
const findAddIssueButton = () => wrapper.find({ ref: 'newIssueBtn' });
|
||||||
const findTitle = () => wrapper.find('.board-title');
|
const findTitle = () => wrapper.find('.board-title');
|
||||||
const findCaret = () => wrapper.find('.board-title-caret');
|
const findCaret = () => wrapper.findByTestId('board-title-caret');
|
||||||
|
|
||||||
describe('Add issue button', () => {
|
describe('Add issue button', () => {
|
||||||
const hasNoAddButton = [ListType.closed];
|
const hasNoAddButton = [ListType.closed];
|
||||||
|
@ -114,40 +117,29 @@ describe('Board List Header Component', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('expanding / collapsing the column', () => {
|
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();
|
createComponent();
|
||||||
|
|
||||||
expect(isCollapsed()).toBe(false);
|
const icon = findCaret();
|
||||||
|
|
||||||
wrapper.find('[data-testid="board-list-header"]').trigger('click');
|
expect(icon.props('icon')).toBe('chevron-right');
|
||||||
|
|
||||||
await wrapper.vm.$nextTick();
|
|
||||||
|
|
||||||
expect(isCollapsed()).toBe(false);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('collapses expanded Column when clicking the collapse icon', async () => {
|
it('should display expand icon when column is collapsed', 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 () => {
|
|
||||||
createComponent({ collapsed: true });
|
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');
|
findCaret().vm.$emit('click');
|
||||||
|
|
||||||
await wrapper.vm.$nextTick();
|
await wrapper.vm.$nextTick();
|
||||||
|
expect(toggleListCollapsedSpy).toHaveBeenCalledTimes(1);
|
||||||
expect(isCollapsed()).toBe(false);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("when logged in it calls list update and doesn't set localStorage", async () => {
|
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();
|
await wrapper.vm.$nextTick();
|
||||||
|
|
||||||
expect(updateListSpy).toHaveBeenCalledTimes(1);
|
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 () => {
|
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();
|
await wrapper.vm.$nextTick();
|
||||||
|
|
||||||
expect(updateListSpy).not.toHaveBeenCalled();
|
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', () => {
|
describe('removeList', () => {
|
||||||
let state;
|
let state;
|
||||||
const list = mockLists[0];
|
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', () => {
|
describe('REMOVE_LIST', () => {
|
||||||
it('removes list from boardLists', () => {
|
it('removes list from boardLists', () => {
|
||||||
const [list, secondList] = mockLists;
|
const [list, secondList] = mockLists;
|
||||||
|
|
|
@ -46,6 +46,8 @@ exports[`Snippet Visibility Edit component rendering matches the snapshot 1`] =
|
||||||
|
|
||||||
<span
|
<span
|
||||||
class="font-weight-bold ml-1 js-visibility-option"
|
class="font-weight-bold ml-1 js-visibility-option"
|
||||||
|
data-qa-selector="visibility_content"
|
||||||
|
data-qa-visibility="Private"
|
||||||
>
|
>
|
||||||
Private
|
Private
|
||||||
</span>
|
</span>
|
||||||
|
@ -65,6 +67,8 @@ exports[`Snippet Visibility Edit component rendering matches the snapshot 1`] =
|
||||||
|
|
||||||
<span
|
<span
|
||||||
class="font-weight-bold ml-1 js-visibility-option"
|
class="font-weight-bold ml-1 js-visibility-option"
|
||||||
|
data-qa-selector="visibility_content"
|
||||||
|
data-qa-visibility="Internal"
|
||||||
>
|
>
|
||||||
Internal
|
Internal
|
||||||
</span>
|
</span>
|
||||||
|
@ -84,6 +88,8 @@ exports[`Snippet Visibility Edit component rendering matches the snapshot 1`] =
|
||||||
|
|
||||||
<span
|
<span
|
||||||
class="font-weight-bold ml-1 js-visibility-option"
|
class="font-weight-bold ml-1 js-visibility-option"
|
||||||
|
data-qa-selector="visibility_content"
|
||||||
|
data-qa-visibility="Public"
|
||||||
>
|
>
|
||||||
Public
|
Public
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -130,20 +130,15 @@ RSpec.describe ApplicationSettingsHelper do
|
||||||
before do
|
before do
|
||||||
helper.instance_variable_set(:@application_setting, application_setting)
|
helper.instance_variable_set(:@application_setting, application_setting)
|
||||||
stub_storage_settings({ 'default': {}, 'storage_1': {}, 'storage_2': {} })
|
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 })
|
stub_application_setting(repository_storages_weighted: { 'default' => 100, 'storage_1' => 50, 'storage_2' => nil })
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns storages correctly' do
|
it 'returns storages correctly' do
|
||||||
expect(helper.storage_weights).to eq([
|
expect(helper.storage_weights).to eq(OpenStruct.new(
|
||||||
{ name: :repository_storages_weighted_default, label: 'default', value: 100 },
|
default: 100,
|
||||||
{ name: :repository_storages_weighted_storage_1, label: 'storage_1', value: 50 },
|
storage_1: 50,
|
||||||
{ name: :repository_storages_weighted_storage_2, label: 'storage_2', value: 0 }
|
storage_2: 0
|
||||||
])
|
))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,22 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReportsComparer do
|
||||||
expect(report_status).to eq(described_class::STATUS_SUCCESS)
|
expect(report_status).to eq(described_class::STATUS_SUCCESS)
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|
||||||
describe '#errors_count' do
|
describe '#errors_count' do
|
||||||
|
@ -93,6 +109,14 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReportsComparer do
|
||||||
expect(resolved_count).to be_zero
|
expect(resolved_count).to be_zero
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|
||||||
describe '#total_count' do
|
describe '#total_count' do
|
||||||
|
@ -140,6 +164,14 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReportsComparer do
|
||||||
expect(total_count).to eq(2)
|
expect(total_count).to eq(2)
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|
||||||
describe '#existing_errors' do
|
describe '#existing_errors' do
|
||||||
|
@ -177,6 +209,14 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReportsComparer do
|
||||||
expect(existing_errors).to be_empty
|
expect(existing_errors).to be_empty
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|
||||||
describe '#new_errors' do
|
describe '#new_errors' do
|
||||||
|
@ -213,6 +253,14 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReportsComparer do
|
||||||
expect(new_errors).to eq([degradation_1])
|
expect(new_errors).to eq([degradation_1])
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|
||||||
describe '#resolved_errors' do
|
describe '#resolved_errors' do
|
||||||
|
@ -250,5 +298,13 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReportsComparer do
|
||||||
expect(resolved_errors).to be_empty
|
expect(resolved_errors).to be_empty
|
||||||
end
|
end
|
||||||
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
|
||||||
end
|
end
|
||||||
|
|
|
@ -49,10 +49,6 @@ RSpec.describe Gitlab::Ci::Reports::ReportsComparer do
|
||||||
context 'when base_report is nil' do
|
context 'when base_report is nil' do
|
||||||
let(:base_report) { nil }
|
let(:base_report) { nil }
|
||||||
|
|
||||||
before do
|
|
||||||
allow(comparer).to receive(:success?).and_return(false)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns status not_found' do
|
it 'returns status not_found' do
|
||||||
expect(status).to eq('not_found')
|
expect(status).to eq('not_found')
|
||||||
end
|
end
|
||||||
|
@ -61,10 +57,6 @@ RSpec.describe Gitlab::Ci::Reports::ReportsComparer do
|
||||||
context 'when head_report is nil' do
|
context 'when head_report is nil' do
|
||||||
let(:head_report) { nil }
|
let(:head_report) { nil }
|
||||||
|
|
||||||
before do
|
|
||||||
allow(comparer).to receive(:success?).and_return(false)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns status not_found' do
|
it 'returns status not_found' do
|
||||||
expect(status).to eq('not_found')
|
expect(status).to eq('not_found')
|
||||||
end
|
end
|
||||||
|
@ -118,4 +110,22 @@ RSpec.describe Gitlab::Ci::Reports::ReportsComparer do
|
||||||
expect { total_count }.to raise_error(NotImplementedError)
|
expect { total_count }.to raise_error(NotImplementedError)
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|
|
@ -19,8 +19,10 @@ RSpec.describe Gitlab::GithubImport::Importer::PullRequestReviewImporter, :clean
|
||||||
context 'when the review is "APPROVED"' do
|
context 'when the review is "APPROVED"' do
|
||||||
let(:review) { create_review(type: 'APPROVED', note: '') }
|
let(:review) { create_review(type: 'APPROVED', note: '') }
|
||||||
|
|
||||||
it 'creates a note for the review' do
|
it 'creates a note for the review and approves the Merge Request' do
|
||||||
expect { subject.execute }.to change(Note, :count)
|
expect { subject.execute }
|
||||||
|
.to change(Note, :count).by(1)
|
||||||
|
.and change(Approval, :count).by(1)
|
||||||
|
|
||||||
last_note = merge_request.notes.last
|
last_note = merge_request.notes.last
|
||||||
expect(last_note.note).to eq('approved this merge request')
|
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.approved_by_users.reload).to include(author)
|
||||||
expect(merge_request.approvals.last.created_at).to eq(submitted_at)
|
expect(merge_request.approvals.last.created_at).to eq(submitted_at)
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
context 'when the review is "COMMENTED"' do
|
context 'when the review is "COMMENTED"' do
|
||||||
|
|
|
@ -4,8 +4,8 @@ require 'spec_helper'
|
||||||
|
|
||||||
RSpec.describe Gitlab::ImportExport do
|
RSpec.describe Gitlab::ImportExport do
|
||||||
describe 'export filename' do
|
describe 'export filename' do
|
||||||
let(:group) { create(:group, :nested) }
|
let(:group) { build(:group, path: 'child', parent: build(:group, path: 'parent')) }
|
||||||
let(:project) { create(:project, :public, path: 'project-path', namespace: group) }
|
let(:project) { build(:project, :public, path: 'project-path', namespace: group) }
|
||||||
|
|
||||||
it 'contains the project path' do
|
it 'contains the project path' do
|
||||||
expect(described_class.export_filename(exportable: project)).to include(project.path)
|
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 }
|
let(:action) { described_class::MR_DISCUSSION_UNLOCKED_ACTION }
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|
|
@ -1129,12 +1129,40 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
|
||||||
end
|
end
|
||||||
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
|
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 }
|
subject { described_class.system_usage_data_settings }
|
||||||
|
|
||||||
it 'gathers settings usage data', :aggregate_failures do
|
it 'gathers settings usage data', :aggregate_failures do
|
||||||
expect(subject[:settings][:ldap_encrypted_secrets_enabled]).to eq(Gitlab::Auth::Ldap::Config.encrypted_secrets.active?)
|
expect(subject[:settings][:ldap_encrypted_secrets_enabled]).to eq(Gitlab::Auth::Ldap::Config.encrypted_secrets.active?)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'populates operating system information' do
|
||||||
|
expect(subject[:settings][:operating_system]).to eq('ubuntu-20.04')
|
||||||
|
end
|
||||||
end
|
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(false).for(:hashed_storage_enabled) }
|
||||||
|
|
||||||
it { is_expected.not_to allow_value(101).for(:repository_storages_weighted_default) }
|
it { is_expected.to allow_value('default' => 0).for(:repository_storages_weighted) }
|
||||||
it { is_expected.to allow_value('90').for(:repository_storages_weighted_default) }
|
it { is_expected.to allow_value('default' => 50).for(:repository_storages_weighted) }
|
||||||
it { is_expected.not_to allow_value(-1).for(:repository_storages_weighted_default) }
|
it { is_expected.to allow_value('default' => 100).for(:repository_storages_weighted) }
|
||||||
it { is_expected.to allow_value(100).for(:repository_storages_weighted_default) }
|
it { is_expected.to allow_value('default' => '90').for(:repository_storages_weighted) }
|
||||||
it { is_expected.to allow_value(0).for(:repository_storages_weighted_default) }
|
it { is_expected.to allow_value('default' => nil).for(:repository_storages_weighted) }
|
||||||
it { is_expected.to allow_value(50).for(:repository_storages_weighted_default) }
|
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.to allow_value(nil).for(:repository_storages_weighted_default) }
|
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) }
|
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.to allow_value(400).for(:notes_create_limit) }
|
||||||
it { is_expected.not_to allow_value('two').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
|
||||||
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
|
describe '#asset_proxy_allowlist' do
|
||||||
context 'when given an Array' do
|
context 'when given an Array' do
|
||||||
it 'sets the domains and adds current running host' 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'
|
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
|
describe 'kroki_format_supported?' do
|
||||||
it 'returns true when Excalidraw is enabled' do
|
it 'returns true when Excalidraw is enabled' do
|
||||||
subject.kroki_formats_excalidraw = true
|
subject.kroki_formats_excalidraw = true
|
||||||
|
@ -1007,11 +1027,4 @@ RSpec.describe ApplicationSetting do
|
||||||
expect(subject.kroki_formats_excalidraw).to eq(true)
|
expect(subject.kroki_formats_excalidraw).to eq(true)
|
||||||
end
|
end
|
||||||
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
|
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_url: 'http://test.com' }
|
||||||
it_behaves_like 'invalidates markdown cache', { asset_proxy_secret_key: 'another secret' }
|
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_allowlist: ['domain.com'] }
|
||||||
|
it_behaves_like 'invalidates markdown cache', { asset_proxy_whitelist: ['domain.com'] }
|
||||||
|
|
||||||
context 'when also setting the local_markdown_version' do
|
context 'when also setting the local_markdown_version' do
|
||||||
let(:params) { { asset_proxy_enabled: true, local_markdown_version: 12 } }
|
let(:params) { { asset_proxy_enabled: true, local_markdown_version: 12 } }
|
||||||
|
|
|
@ -169,6 +169,23 @@ RSpec.describe MergeRequests::UpdateService, :mailer do
|
||||||
end
|
end
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
context 'updating milestone' do
|
context 'updating milestone' do
|
||||||
|
|
|
@ -31,9 +31,34 @@ RSpec.describe Projects::DestroyService, :aggregate_failures do
|
||||||
end
|
end
|
||||||
|
|
||||||
shared_examples 'deleting the project with pipeline and build' do
|
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!(: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'
|
it_behaves_like 'deleting the project'
|
||||||
end
|
end
|
||||||
|
|
|
@ -289,6 +289,7 @@ RSpec.shared_examples 'application settings examples' do
|
||||||
|
|
||||||
describe '#pick_repository_storage' do
|
describe '#pick_repository_storage' do
|
||||||
before 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 })
|
allow(setting).to receive(:repository_storages_weighted).and_return({ 'default' => 20, 'backup' => 80 })
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -304,15 +305,19 @@ RSpec.shared_examples 'application settings examples' do
|
||||||
describe '#normalized_repository_storage_weights' do
|
describe '#normalized_repository_storage_weights' do
|
||||||
using RSpec::Parameterized::TableSyntax
|
using RSpec::Parameterized::TableSyntax
|
||||||
|
|
||||||
where(:storages, :normalized) do
|
where(:config_storages, :storages, :normalized) do
|
||||||
{ 'default' => 0, 'backup' => 100 } | { 'default' => 0.0, 'backup' => 1.0 }
|
%w(default backup) | { 'default' => 0, 'backup' => 100 } | { 'default' => 0.0, 'backup' => 1.0 }
|
||||||
{ 'default' => 100, 'backup' => 100 } | { 'default' => 0.5, 'backup' => 0.5 }
|
%w(default backup) | { 'default' => 100, 'backup' => 100 } | { 'default' => 0.5, 'backup' => 0.5 }
|
||||||
{ 'default' => 20, 'backup' => 80 } | { 'default' => 0.2, 'backup' => 0.8 }
|
%w(default backup) | { 'default' => 20, 'backup' => 80 } | { 'default' => 0.2, 'backup' => 0.8 }
|
||||||
{ 'default' => 0, 'backup' => 0 } | { 'default' => 0.0, 'backup' => 0.0 }
|
%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
|
end
|
||||||
|
|
||||||
with_them do
|
with_them do
|
||||||
before 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)
|
allow(setting).to receive(:repository_storages_weighted).and_return(storages)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -3,34 +3,49 @@
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
RSpec.describe 'admin/application_settings/_repository_storage.html.haml' do
|
RSpec.describe 'admin/application_settings/_repository_storage.html.haml' do
|
||||||
let(:app_settings) { create(:application_setting) }
|
let(:app_settings) { build(:application_setting, repository_storages_weighted: repository_storages_weighted) }
|
||||||
let(:repository_storages_weighted_attributes) { [:repository_storages_weighted_default, :repository_storages_weighted_mepmep, :repository_storages_weighted_foobar]}
|
|
||||||
let(:repository_storages_weighted) do
|
|
||||||
{
|
|
||||||
"default" => 100,
|
|
||||||
"mepmep" => 50
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
before do
|
before do
|
||||||
allow(app_settings).to receive(:repository_storages_weighted).and_return(repository_storages_weighted)
|
stub_storage_settings({ 'default': {}, 'mepmep': {}, 'foobar': {} })
|
||||||
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)
|
assign(:application_setting, app_settings)
|
||||||
allow(ApplicationSetting).to receive(:repository_storages_weighted_attributes).and_return(repository_storages_weighted_attributes)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when multiple storages are available' do
|
context 'additional storage config' do
|
||||||
|
let(:repository_storages_weighted) do
|
||||||
|
{
|
||||||
|
'default' => 100,
|
||||||
|
'mepmep' => 50
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
it 'lists them all' do
|
it 'lists them all' do
|
||||||
render
|
render
|
||||||
|
|
||||||
# lists storages that are saved with weights
|
Gitlab.config.repositories.storages.keys.each do |storage_name|
|
||||||
repository_storages_weighted.each do |storage_name, storage_weight|
|
|
||||||
expect(rendered).to have_content(storage_name)
|
expect(rendered).to have_content(storage_name)
|
||||||
end
|
end
|
||||||
|
|
||||||
# lists storage not saved with weight
|
|
||||||
expect(rendered).to have_content('foobar')
|
expect(rendered).to have_content('foobar')
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|
Loading…
Reference in New Issue