diff --git a/Gemfile b/Gemfile
index dbeda69ef98..d35cbcf73a7 100644
--- a/Gemfile
+++ b/Gemfile
@@ -74,6 +74,9 @@ gem 'acme-client', '~> 2.0', '>= 2.0.6'
# Browser detection
gem 'browser', '~> 4.2'
+# OS detection for usage ping
+gem 'ohai', '~> 16.10'
+
# GPG
gem 'gpgme', '~> 2.0.19'
diff --git a/Gemfile.lock b/Gemfile.lock
index 5e06975ec87..a70aa8185ed 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -172,6 +172,14 @@ GEM
cbor (0.5.9.6)
character_set (1.4.0)
charlock_holmes (0.7.7)
+ chef-config (16.10.17)
+ addressable
+ chef-utils (= 16.10.17)
+ fuzzyurl
+ mixlib-config (>= 2.2.12, < 4.0)
+ mixlib-shellout (>= 2.0, < 4.0)
+ tomlrb (~> 1.2)
+ chef-utils (16.10.17)
childprocess (3.0.0)
chunky_png (1.3.5)
citrus (3.0.2)
@@ -348,6 +356,8 @@ GEM
ffi-compiler (1.0.1)
ffi (>= 1.0.0)
rake
+ ffi-yajl (2.3.4)
+ libyajl2 (~> 1.2)
flipper (0.17.1)
flipper-active_record (0.17.1)
activerecord (>= 4.2, < 7)
@@ -403,6 +413,7 @@ GEM
fuubar (2.2.0)
rspec-core (~> 3.0)
ruby-progressbar (~> 1.4)
+ fuzzyurl (0.9.0)
gemoji (3.0.1)
gemojione (3.3.0)
json
@@ -668,6 +679,7 @@ GEM
actionmailer (>= 3.2)
letter_opener (~> 1.0)
railties (>= 3.2)
+ libyajl2 (1.2.0)
license_finder (6.0.0)
bundler
rubyzip (>= 1, < 3)
@@ -717,6 +729,12 @@ GEM
mini_mime (1.0.2)
mini_portile2 (2.5.0)
minitest (5.11.3)
+ mixlib-cli (2.1.8)
+ mixlib-config (3.0.9)
+ tomlrb
+ mixlib-log (3.0.9)
+ mixlib-shellout (3.2.5)
+ chef-utils
ms_rest (0.7.6)
concurrent-ruby (~> 1.0)
faraday (>= 0.9, < 2.0.0)
@@ -741,6 +759,8 @@ GEM
connection_pool (~> 2.2)
net-ldap (0.16.3)
net-ntp (2.1.3)
+ net-scp (3.0.0)
+ net-ssh (>= 2.6.5, < 7.0.0)
net-ssh (6.0.0)
netrc (0.11.0)
nio4r (2.5.4)
@@ -764,6 +784,19 @@ GEM
octokit (4.20.0)
faraday (>= 0.9)
sawyer (~> 0.8.0, >= 0.5.3)
+ ohai (16.10.6)
+ chef-config (>= 12.8, < 17)
+ chef-utils (>= 16.0, < 17)
+ ffi (~> 1.9)
+ ffi-yajl (~> 2.2)
+ ipaddress
+ mixlib-cli (>= 1.7.0)
+ mixlib-config (>= 2.0, < 4.0)
+ mixlib-log (>= 2.0.1, < 4.0)
+ mixlib-shellout (>= 2.0, < 4.0)
+ plist (~> 3.1)
+ train-core
+ wmi-lite (~> 1.0)
oj (3.10.6)
omniauth (1.9.0)
hashie (>= 3.4.6, < 3.7.0)
@@ -857,6 +890,7 @@ GEM
railties (>= 4.0.0)
pg (1.2.3)
pg_query (1.3.0)
+ plist (3.6.0)
png_quantizator (0.2.1)
po_to_json (1.0.1)
json (>= 1.6.0)
@@ -1196,9 +1230,17 @@ GEM
parslet (~> 1.8.0)
toml-rb (1.0.0)
citrus (~> 3.0, > 3.0)
+ tomlrb (1.3.0)
tpm-key_attestation (0.9.0)
bindata (~> 2.4)
openssl-signature_algorithm (~> 0.4.0)
+ train-core (3.4.9)
+ addressable (~> 2.5)
+ ffi (!= 1.13.0)
+ json (>= 1.8, < 3.0)
+ mixlib-shellout (>= 2.0, < 4.0)
+ net-scp (>= 1.2, < 4.0)
+ net-ssh (>= 2.9, < 7.0)
truncato (0.7.11)
htmlentities (~> 4.3.1)
nokogiri (>= 1.7.0, <= 2.0)
@@ -1271,6 +1313,7 @@ GEM
expression_parser
rinku
with_env (1.1.0)
+ wmi-lite (1.0.5)
xml-simple (1.1.5)
xpath (3.2.0)
nokogiri (~> 1.8)
@@ -1443,6 +1486,7 @@ DEPENDENCIES
nokogiri (~> 1.11.1)
oauth2 (~> 1.4)
octokit (~> 4.15)
+ ohai (~> 16.10)
oj (~> 3.10.6)
omniauth (~> 1.8)
omniauth-atlassian-oauth2 (~> 0.2.0)
diff --git a/app/assets/javascripts/boards/components/board_list_header.vue b/app/assets/javascripts/boards/components/board_list_header.vue
index a933370427c..7a92aed720d 100644
--- a/app/assets/javascripts/boards/components/board_list_header.vue
+++ b/app/assets/javascripts/boards/components/board_list_header.vue
@@ -10,7 +10,7 @@ import {
} from '@gitlab/ui';
import { mapActions, mapState } from 'vuex';
import { isListDraggable } from '~/boards/boards_util';
-import { isScopedLabel } from '~/lib/utils/common_utils';
+import { isScopedLabel, parseBoolean } from '~/lib/utils/common_utils';
import { BV_HIDE_TOOLTIP } from '~/lib/utils/constants';
import { n__, s__, __ } from '~/locale';
import sidebarEventHub from '~/sidebar/event_hub';
@@ -69,7 +69,7 @@ export default {
},
},
computed: {
- ...mapState(['activeId']),
+ ...mapState(['activeId', 'isEpicBoard']),
isLoggedIn() {
return Boolean(this.currentUserId);
},
@@ -97,11 +97,14 @@ export default {
showListDetails() {
return !this.list.collapsed || !this.isSwimlanesHeader;
},
- issuesCount() {
+ itemsCount() {
return this.list.issuesCount;
},
- issuesTooltipLabel() {
- return n__(`%d issue`, `%d issues`, this.issuesCount);
+ countIcon() {
+ return 'issues';
+ },
+ itemsTooltipLabel() {
+ return n__(`%d issue`, `%d issues`, this.itemsCount);
},
chevronTooltip() {
return this.list.collapsed ? this.$options.i18n.expand : this.$options.i18n.collapse;
@@ -110,7 +113,7 @@ export default {
return this.list.collapsed ? 'chevron-down' : 'chevron-right';
},
isNewIssueShown() {
- return this.listType === ListType.backlog || this.showListHeaderButton;
+ return (this.listType === ListType.backlog || this.showListHeaderButton) && !this.isEpicBoard;
},
isSettingsShown() {
return (
@@ -131,8 +134,14 @@ export default {
return !this.disabled && isListDraggable(this.list);
},
},
+ created() {
+ const localCollapsed = parseBoolean(localStorage.getItem(`${this.uniqueKey}.collapsed`));
+ if ((!this.isLoggedIn || this.isEpicBoard) && localCollapsed) {
+ this.toggleListCollapsed({ listId: this.list.id, collapsed: true });
+ }
+ },
methods: {
- ...mapActions(['updateList', 'setActiveId']),
+ ...mapActions(['updateList', 'setActiveId', 'toggleListCollapsed']),
openSidebarSettings() {
if (this.activeId === inactiveId) {
sidebarEventHub.$emit('sidebar.closeAll');
@@ -148,10 +157,10 @@ export default {
eventHub.$emit(`toggle-issue-form-${this.list.id}`);
},
toggleExpanded() {
- // eslint-disable-next-line vue/no-mutating-props
- this.list.collapsed = !this.list.collapsed;
+ const collapsed = !this.list.collapsed;
+ this.toggleListCollapsed({ listId: this.list.id, collapsed });
- if (!this.isLoggedIn) {
+ if (!this.isLoggedIn || this.isEpicBoard) {
this.addToLocalStorage();
} else {
this.updateListFunction();
@@ -163,7 +172,7 @@ export default {
},
addToLocalStorage() {
if (AccessorUtilities.isLocalStorageAccessSafe()) {
- localStorage.setItem(`${this.uniqueKey}.expanded`, !this.list.collapsed);
+ localStorage.setItem(`${this.uniqueKey}.collapsed`, this.list.collapsed);
}
},
updateListFunction() {
@@ -203,6 +212,7 @@ export default {
class="board-title-caret no-drag gl-cursor-pointer"
category="tertiary"
size="small"
+ data-testid="board-title-caret"
@click="toggleExpanded"
/>
@@ -301,11 +311,11 @@ export default {
•
- {{ issuesTooltipLabel }}
+ {{ itemsTooltipLabel }}
{{ list.maxIssueCount }}
- • {{ issuesTooltipLabel }}
+ • {{ itemsTooltipLabel }}
•
@@ -323,13 +333,13 @@ export default {
}"
>
-
-
-
-
+
+
+
+
-
+
diff --git a/app/assets/javascripts/boards/components/issue_due_date.vue b/app/assets/javascripts/boards/components/issue_due_date.vue
index 7e3f36c8a17..73ec008c2b6 100644
--- a/app/assets/javascripts/boards/components/issue_due_date.vue
+++ b/app/assets/javascripts/boards/components/issue_due_date.vue
@@ -86,7 +86,11 @@ export default {
-
+
diff --git a/app/assets/javascripts/boards/components/issue_time_estimate.vue b/app/assets/javascripts/boards/components/issue_time_estimate.vue
index 42d187b9b40..1ab7deebfaf 100644
--- a/app/assets/javascripts/boards/components/issue_time_estimate.vue
+++ b/app/assets/javascripts/boards/components/issue_time_estimate.vue
@@ -37,7 +37,7 @@ export default {
-
+
{
+ commit(types.TOGGLE_LIST_COLLAPSED, { listId, collapsed });
+ },
+
removeList: ({ state, commit }, listId) => {
const listsBackup = { ...state.boardLists };
diff --git a/app/assets/javascripts/boards/stores/mutation_types.js b/app/assets/javascripts/boards/stores/mutation_types.js
index 2a748d122d9..e7c034fb087 100644
--- a/app/assets/javascripts/boards/stores/mutation_types.js
+++ b/app/assets/javascripts/boards/stores/mutation_types.js
@@ -14,6 +14,7 @@ export const RECEIVE_ADD_LIST_SUCCESS = 'RECEIVE_ADD_LIST_SUCCESS';
export const RECEIVE_ADD_LIST_ERROR = 'RECEIVE_ADD_LIST_ERROR';
export const MOVE_LIST = 'MOVE_LIST';
export const UPDATE_LIST_FAILURE = 'UPDATE_LIST_FAILURE';
+export const TOGGLE_LIST_COLLAPSED = 'TOGGLE_LIST_COLLAPSED';
export const REMOVE_LIST = 'REMOVE_LIST';
export const REMOVE_LIST_FAILURE = 'REMOVE_LIST_FAILURE';
export const REQUEST_ITEMS_FOR_LIST = 'REQUEST_ITEMS_FOR_LIST';
diff --git a/app/assets/javascripts/boards/stores/mutations.js b/app/assets/javascripts/boards/stores/mutations.js
index 5b8bf5b5357..99a3f3c8f86 100644
--- a/app/assets/javascripts/boards/stores/mutations.js
+++ b/app/assets/javascripts/boards/stores/mutations.js
@@ -105,6 +105,10 @@ export default {
Vue.set(state, 'boardLists', backupList);
},
+ [mutationTypes.TOGGLE_LIST_COLLAPSED]: (state, { listId, collapsed }) => {
+ Vue.set(state.boardLists[listId], 'collapsed', collapsed);
+ },
+
[mutationTypes.REMOVE_LIST]: (state, listId) => {
Vue.delete(state.boardLists, listId);
},
diff --git a/app/assets/javascripts/pages/projects/forks/new/components/fork_form.vue b/app/assets/javascripts/pages/projects/forks/new/components/fork_form.vue
index a59a8afa4de..fd35c16ab63 100644
--- a/app/assets/javascripts/pages/projects/forks/new/components/fork_form.vue
+++ b/app/assets/javascripts/pages/projects/forks/new/components/fork_form.vue
@@ -200,9 +200,9 @@ export default {
-
-
-
+
+
+
@@ -225,8 +225,8 @@ export default {
-
-
+
+
- {{ option.label }}
+ {{ option.label }}
{{
isProjectSnippet && option.description_project
diff --git a/app/assets/stylesheets/_page_specific_files.scss b/app/assets/stylesheets/_page_specific_files.scss
index 20ff78d32d3..e55654e84d8 100644
--- a/app/assets/stylesheets/_page_specific_files.scss
+++ b/app/assets/stylesheets/_page_specific_files.scss
@@ -35,5 +35,4 @@
@import './pages/sherlock';
@import './pages/storage_quota';
@import './pages/tree';
-@import './pages/trials';
@import './pages/users';
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index 089f130843f..1aa4177c902 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -830,8 +830,8 @@ $ci-variable-remove-button-width: calc(1em + #{2 * $gl-padding});
/*
GitLab Plans
*/
-$gl-gold-plan: #d4af37;
-$gl-silver-plan: #91a1ab;
+$gl-ultimate-plan: #d4af37;
+$gl-premium-plan: #91a1ab;
$gl-bronze-plan: #cd7f32;
/*
diff --git a/app/assets/stylesheets/lazy_bundles/select2_overrides.scss b/app/assets/stylesheets/lazy_bundles/select2_overrides.scss
index b148cc8f0e7..0d40159f6de 100644
--- a/app/assets/stylesheets/lazy_bundles/select2_overrides.scss
+++ b/app/assets/stylesheets/lazy_bundles/select2_overrides.scss
@@ -339,3 +339,19 @@
display: inline;
}
}
+
+.gl-select2-html5-required-fix {
+ .select2-container {
+ + .select2 {
+ @include gl-opacity-0;
+ @include gl-border-0;
+ @include gl-bg-none;
+ @include gl-bg-transparent;
+ display: block !important;
+ width: 1px;
+ height: 1px;
+ z-index: -1;
+ margin: -3px auto 0;
+ }
+ }
+}
diff --git a/app/assets/stylesheets/pages/trials.scss b/app/assets/stylesheets/pages/trials.scss
deleted file mode 100644
index 55f323b7df7..00000000000
--- a/app/assets/stylesheets/pages/trials.scss
+++ /dev/null
@@ -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;
-}
diff --git a/app/assets/stylesheets/utilities.scss b/app/assets/stylesheets/utilities.scss
index d49b58df69b..024162eba3e 100644
--- a/app/assets/stylesheets/utilities.scss
+++ b/app/assets/stylesheets/utilities.scss
@@ -117,6 +117,7 @@
flex-basis: 25%;
}
+// Will be moved to @gitlab/ui in https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1168
.gl-md-ml-3 {
@media (min-width: $breakpoint-md) {
margin-left: $gl-spacing-scale-3;
diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb
index 7f7d38a09c5..7c6a444ce7a 100644
--- a/app/controllers/admin/application_settings_controller.rb
+++ b/app/controllers/admin/application_settings_controller.rb
@@ -237,7 +237,6 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
[
*::ApplicationSettingsHelper.visible_attributes,
*::ApplicationSettingsHelper.external_authorization_service_attributes,
- *ApplicationSetting.repository_storages_weighted_attributes,
*ApplicationSetting.kroki_formats_attributes.keys.map { |key| "kroki_formats_#{key}".to_sym },
:lets_encrypt_notification_email,
:lets_encrypt_terms_of_service_accepted,
@@ -248,8 +247,8 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:default_branch_name,
disabled_oauth_sign_in_sources: [],
import_sources: [],
- repository_storages: [],
- restricted_visibility_levels: []
+ restricted_visibility_levels: [],
+ repository_storages_weighted: {}
]
end
diff --git a/app/graphql/mutations/boards/issues/issue_move_list.rb b/app/graphql/mutations/boards/issues/issue_move_list.rb
index 6df4250fc67..096ac89db1c 100644
--- a/app/graphql/mutations/boards/issues/issue_move_list.rb
+++ b/app/graphql/mutations/boards/issues/issue_move_list.rb
@@ -5,35 +5,38 @@ module Mutations
module Issues
class IssueMoveList < Mutations::Issues::Base
graphql_name 'IssueMoveList'
+ BoardGID = ::Types::GlobalIDType[::Board]
+ ListID = ::GraphQL::ID_TYPE
+ IssueID = ::GraphQL::ID_TYPE
- argument :board_id, GraphQL::ID_TYPE,
- required: true,
- loads: Types::BoardType,
- description: 'Global ID of the board that the issue is in.'
+ argument :board_id, BoardGID,
+ required: true,
+ loads: Types::BoardType,
+ description: 'Global ID of the board that the issue is in.'
argument :project_path, GraphQL::ID_TYPE,
- required: true,
- description: 'Project the issue to mutate is in.'
+ required: true,
+ description: 'Project the issue to mutate is in.'
argument :iid, GraphQL::STRING_TYPE,
- required: true,
- description: 'IID of the issue to mutate.'
+ required: true,
+ description: 'IID of the issue to mutate.'
- argument :from_list_id, GraphQL::ID_TYPE,
- required: false,
- description: 'ID of the board list that the issue will be moved from.'
+ argument :from_list_id, ListID,
+ required: false,
+ description: 'ID of the board list that the issue will be moved from.'
- argument :to_list_id, GraphQL::ID_TYPE,
- required: false,
- description: 'ID of the board list that the issue will be moved to.'
+ argument :to_list_id, ListID,
+ required: false,
+ description: 'ID of the board list that the issue will be moved to.'
- argument :move_before_id, GraphQL::ID_TYPE,
- required: false,
- description: 'ID of issue that should be placed before the current issue.'
+ argument :move_before_id, IssueID,
+ required: false,
+ description: 'ID of issue that should be placed before the current issue.'
- argument :move_after_id, GraphQL::ID_TYPE,
- required: false,
- description: 'ID of issue that should be placed after the current issue.'
+ argument :move_after_id, IssueID,
+ required: false,
+ description: 'ID of issue that should be placed after the current issue.'
def ready?(**args)
if move_arguments(args).blank?
diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb
index 244238e246d..551e08d1e31 100644
--- a/app/helpers/application_settings_helper.rb
+++ b/app/helpers/application_settings_helper.rb
@@ -37,13 +37,8 @@ module ApplicationSettingsHelper
end
def storage_weights
- ApplicationSetting.repository_storages_weighted_attributes.map do |attribute|
- storage = attribute.to_s.delete_prefix('repository_storages_weighted_')
- {
- name: attribute,
- label: storage,
- value: @application_setting.repository_storages_weighted[storage] || 0
- }
+ Gitlab.config.repositories.storages.keys.each_with_object(OpenStruct.new) do |storage, weights|
+ weights[storage.to_sym] = @application_setting.repository_storages_weighted[storage] || 0
end
end
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index 33c058dab96..44eb2fefb3f 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -25,10 +25,6 @@ class ApplicationSetting < ApplicationRecord
alias_attribute :instance_group_id, :instance_administrators_group_id
alias_attribute :instance_administrators_group, :instance_group
- def self.repository_storages_weighted_attributes
- @repository_storages_weighted_atributes ||= Gitlab.config.repositories.storages.keys.map { |k| "repository_storages_weighted_#{k}".to_sym }.freeze
- end
-
def self.kroki_formats_attributes
{
blockdiag: {
@@ -44,7 +40,6 @@ class ApplicationSetting < ApplicationRecord
end
store_accessor :kroki_formats, *ApplicationSetting.kroki_formats_attributes.keys, prefix: true
- store_accessor :repository_storages_weighted, *Gitlab.config.repositories.storages.keys, prefix: true
# Include here so it can override methods from
# `add_authentication_token_field`
@@ -58,8 +53,9 @@ class ApplicationSetting < ApplicationRecord
serialize :domain_allowlist, Array # rubocop:disable Cop/ActiveRecordSerialize
serialize :domain_denylist, Array # rubocop:disable Cop/ActiveRecordSerialize
serialize :repository_storages # rubocop:disable Cop/ActiveRecordSerialize
- serialize :asset_proxy_allowlist, Array # rubocop:disable Cop/ActiveRecordSerialize
+
# See https://gitlab.com/gitlab-org/gitlab/-/issues/300916
+ serialize :asset_proxy_allowlist, Array # rubocop:disable Cop/ActiveRecordSerialize
serialize :asset_proxy_whitelist, Array # rubocop:disable Cop/ActiveRecordSerialize
cache_markdown_field :sign_in_text
@@ -502,6 +498,7 @@ class ApplicationSetting < ApplicationRecord
inclusion: { in: [true, false], message: _('must be a boolean value') }
before_validation :ensure_uuid!
+ before_validation :coerce_repository_storages_weighted, if: :repository_storages_weighted_changed?
before_save :ensure_runners_registration_token
before_save :ensure_health_check_access_token
@@ -582,12 +579,6 @@ class ApplicationSetting < ApplicationRecord
recaptcha_enabled || login_recaptcha_protection_enabled
end
- repository_storages_weighted_attributes.each do |attribute|
- define_method :"#{attribute}=" do |value|
- super(value.to_i)
- end
- end
-
kroki_formats_attributes.keys.each do |key|
define_method :"kroki_formats_#{key}=" do |value|
super(::Gitlab::Utils.to_boolean(value))
diff --git a/app/models/application_setting_implementation.rb b/app/models/application_setting_implementation.rb
index 2911ae6b1c8..731a3b5fed8 100644
--- a/app/models/application_setting_implementation.rb
+++ b/app/models/application_setting_implementation.rb
@@ -280,23 +280,24 @@ module ApplicationSettingImplementation
self.notes_create_limit_allowlist = strings_to_array(values).map(&:downcase)
end
- def asset_proxy_allowlist=(values)
+ def asset_proxy_whitelist=(values)
values = strings_to_array(values) if values.is_a?(String)
# make sure we always allow the running host
values << Gitlab.config.gitlab.host unless values.include?(Gitlab.config.gitlab.host)
- self[:asset_proxy_allowlist] = values
+ self[:asset_proxy_whitelist] = values
+ end
+ alias_method :asset_proxy_allowlist=, :asset_proxy_whitelist=
+
+ def asset_proxy_allowlist
+ read_attribute(:asset_proxy_whitelist)
end
def repository_storages
Array(read_attribute(:repository_storages))
end
- def repository_storages_weighted
- read_attribute(:repository_storages_weighted)
- end
-
def commit_email_hostname
super.presence || self.class.default_commit_email_hostname
end
@@ -328,9 +329,10 @@ module ApplicationSettingImplementation
def normalized_repository_storage_weights
strong_memoize(:normalized_repository_storage_weights) do
- weights_total = repository_storages_weighted.values.reduce(:+)
+ repository_storages_weights = repository_storages_weighted.slice(*Gitlab.config.repositories.storages.keys)
+ weights_total = repository_storages_weights.values.reduce(:+)
- repository_storages_weighted.transform_values do |w|
+ repository_storages_weights.transform_values do |w|
next w if weights_total == 0
w.to_f / weights_total
@@ -468,16 +470,20 @@ module ApplicationSettingImplementation
invalid.empty?
end
+ def coerce_repository_storages_weighted
+ repository_storages_weighted.transform_values!(&:to_i)
+ end
+
def check_repository_storages_weighted
invalid = repository_storages_weighted.keys - Gitlab.config.repositories.storages.keys
- errors.add(:repository_storages_weighted, "can't include: %{invalid_storages}" % { invalid_storages: invalid.join(", ") }) unless
+ errors.add(:repository_storages_weighted, _("can't include: %{invalid_storages}") % { invalid_storages: invalid.join(", ") }) unless
invalid.empty?
repository_storages_weighted.each do |key, val|
next unless val.present?
- errors.add(:"repository_storages_weighted_#{key}", "value must be an integer") unless val.is_a?(Integer)
- errors.add(:"repository_storages_weighted_#{key}", "value must be between 0 and 100") unless val.between?(0, 100)
+ errors.add(:repository_storages_weighted, _("value for '%{storage}' must be an integer") % { storage: key }) unless val.is_a?(Integer)
+ errors.add(:repository_storages_weighted, _("value for '%{storage}' must be between 0 and 100") % { storage: key }) unless val.between?(0, 100)
end
end
diff --git a/app/services/application_settings/update_service.rb b/app/services/application_settings/update_service.rb
index 5e5c8ae2177..7792b811b4e 100644
--- a/app/services/application_settings/update_service.rb
+++ b/app/services/application_settings/update_service.rb
@@ -6,7 +6,7 @@ module ApplicationSettings
attr_reader :params, :application_setting
- MARKDOWN_CACHE_INVALIDATING_PARAMS = %w(asset_proxy_enabled asset_proxy_url asset_proxy_secret_key asset_proxy_allowlist).freeze
+ MARKDOWN_CACHE_INVALIDATING_PARAMS = %w(asset_proxy_enabled asset_proxy_url asset_proxy_secret_key asset_proxy_whitelist).freeze
def execute
result = update_settings
diff --git a/app/services/merge_requests/update_service.rb b/app/services/merge_requests/update_service.rb
index 217679e733c..4bc7823faaa 100644
--- a/app/services/merge_requests/update_service.rb
+++ b/app/services/merge_requests/update_service.rb
@@ -31,6 +31,7 @@ module MergeRequests
old_mentioned_users = old_associations.fetch(:mentioned_users, [])
old_assignees = old_associations.fetch(:assignees, [])
old_reviewers = old_associations.fetch(:reviewers, [])
+ old_timelogs = old_associations.fetch(:timelogs, [])
changed_fields = merge_request.previous_changes.keys
resolve_todos(merge_request, old_labels, old_assignees, old_reviewers)
@@ -48,6 +49,7 @@ module MergeRequests
track_title_and_desc_edits(changed_fields)
track_discussion_lock_toggle(merge_request, changed_fields)
+ track_time_estimate_and_spend_edits(merge_request, old_timelogs, changed_fields)
notify_if_labels_added(merge_request, old_labels)
notify_if_mentions_added(merge_request, old_mentioned_users)
@@ -106,6 +108,11 @@ module MergeRequests
end
end
+ def track_time_estimate_and_spend_edits(merge_request, old_timelogs, changed_fields)
+ merge_request_activity_counter.track_time_estimate_changed_action(user: current_user) if changed_fields.include?('time_estimate')
+ merge_request_activity_counter.track_time_spent_changed_action(user: current_user) if old_timelogs != merge_request.timelogs
+ end
+
def notify_if_labels_added(merge_request, old_labels)
added_labels = merge_request.labels - old_labels
diff --git a/app/views/admin/application_settings/_repository_storage.html.haml b/app/views/admin/application_settings/_repository_storage.html.haml
index 0862d1bf0b6..437189cf47f 100644
--- a/app/views/admin/application_settings/_repository_storage.html.haml
+++ b/app/views/admin/application_settings/_repository_storage.html.haml
@@ -18,8 +18,9 @@
= _('Enter weights for storages for new repositories.')
= link_to sprite_icon('question-o'), help_page_path('administration/repository_storage_paths')
.form-check
- - storage_weights.each do |attribute|
- = f.text_field attribute[:name], class: 'form-text-input', value: attribute[:value]
- = f.label attribute[:label], attribute[:label], class: 'label-bold form-check-label'
- %br
+ = f.fields_for :repository_storages_weighted, storage_weights do |storage_form|
+ - Gitlab.config.repositories.storages.keys.each do |storage|
+ = storage_form.text_field storage, class: 'form-text-input'
+ = storage_form.label storage, storage, class: 'label-bold form-check-label'
+ %br
= f.submit _('Save changes'), class: "gl-button btn btn-success qa-save-changes-button"
diff --git a/changelogs/unreleased/292128-fix-icon-spacing.yml b/changelogs/unreleased/292128-fix-icon-spacing.yml
new file mode 100644
index 00000000000..d832dc9beee
--- /dev/null
+++ b/changelogs/unreleased/292128-fix-icon-spacing.yml
@@ -0,0 +1,5 @@
+---
+title: Add space next to icons in epic issue list
+merge_request: 54138
+author: Yogi (@yo)
+type: fixed
diff --git a/changelogs/unreleased/292824-track-mr-time-changes.yml b/changelogs/unreleased/292824-track-mr-time-changes.yml
new file mode 100644
index 00000000000..89721e8f8c5
--- /dev/null
+++ b/changelogs/unreleased/292824-track-mr-time-changes.yml
@@ -0,0 +1,5 @@
+---
+title: Add tracking to merge request time estimate/spent changes
+merge_request: 55046
+author:
+type: other
diff --git a/changelogs/unreleased/297020-linux-distribution-details-in-usage-ping.yml b/changelogs/unreleased/297020-linux-distribution-details-in-usage-ping.yml
new file mode 100644
index 00000000000..644973f4b01
--- /dev/null
+++ b/changelogs/unreleased/297020-linux-distribution-details-in-usage-ping.yml
@@ -0,0 +1,5 @@
+---
+title: Add Operating System details to usage ping
+merge_request: 54778
+author:
+type: added
diff --git a/changelogs/unreleased/322991-asset_proxy_allowlist-errors-in-13-9-for-geo-with-downtime-upgrade.yml b/changelogs/unreleased/322991-asset_proxy_allowlist-errors-in-13-9-for-geo-with-downtime-upgrade.yml
new file mode 100644
index 00000000000..1092f68a358
--- /dev/null
+++ b/changelogs/unreleased/322991-asset_proxy_allowlist-errors-in-13-9-for-geo-with-downtime-upgrade.yml
@@ -0,0 +1,5 @@
+---
+title: Rename asset_proxy_allowlist column
+merge_request: 55419
+author:
+type: fixed
diff --git a/changelogs/unreleased/fix_repo_storage_weights_admin.yml b/changelogs/unreleased/fix_repo_storage_weights_admin.yml
new file mode 100644
index 00000000000..c82ef578e01
--- /dev/null
+++ b/changelogs/unreleased/fix_repo_storage_weights_admin.yml
@@ -0,0 +1,5 @@
+---
+title: Allow saving repository weights after a storage has been removed
+merge_request: 53803
+author:
+type: fixed
diff --git a/changelogs/unreleased/kassio-githubimporter-avoid-approval-exception.yml b/changelogs/unreleased/kassio-githubimporter-avoid-approval-exception.yml
new file mode 100644
index 00000000000..cc994d50f4e
--- /dev/null
+++ b/changelogs/unreleased/kassio-githubimporter-avoid-approval-exception.yml
@@ -0,0 +1,5 @@
+---
+title: 'GithubImporter: Add Merge request approval only if it does not exists yet'
+merge_request: 55376
+author:
+type: fixed
diff --git a/config/application.rb b/config/application.rb
index 83f5f83d120..f3dfa95ae1a 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -193,7 +193,6 @@ module Gitlab
config.assets.precompile << "page_bundles/import.css"
config.assets.precompile << "page_bundles/incident_management_list.css"
config.assets.precompile << "page_bundles/issues_list.css"
- config.assets.precompile << "page_bundles/iterations.css"
config.assets.precompile << "page_bundles/jira_connect.css"
config.assets.precompile << "page_bundles/jira_connect_users.css"
config.assets.precompile << "page_bundles/learn_gitlab.css"
diff --git a/config/feature_flags/development/ci_reduce_queries_when_ticking_runner_queue.yml b/config/feature_flags/development/ci_reduce_queries_when_ticking_runner_queue.yml
index 906eb83aa01..92acb98969b 100644
--- a/config/feature_flags/development/ci_reduce_queries_when_ticking_runner_queue.yml
+++ b/config/feature_flags/development/ci_reduce_queries_when_ticking_runner_queue.yml
@@ -1,7 +1,7 @@
---
name: ci_reduce_queries_when_ticking_runner_queue
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55496
-rollout_issue_url:
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/323328
milestone: '13.10'
type: development
group: group::continuous integration
diff --git a/config/feature_flags/development/usage_data_i_code_review_user_time_estimate_changed.yml b/config/feature_flags/development/usage_data_i_code_review_user_time_estimate_changed.yml
new file mode 100644
index 00000000000..81096288a8d
--- /dev/null
+++ b/config/feature_flags/development/usage_data_i_code_review_user_time_estimate_changed.yml
@@ -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
diff --git a/config/feature_flags/development/usage_data_i_code_review_user_time_spent_changed.yml b/config/feature_flags/development/usage_data_i_code_review_user_time_spent_changed.yml
new file mode 100644
index 00000000000..56329f6b48b
--- /dev/null
+++ b/config/feature_flags/development/usage_data_i_code_review_user_time_spent_changed.yml
@@ -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
diff --git a/config/metrics/counts_28d/20210301102134_i_code_review_user_time_estimate_changed_monthly.yml b/config/metrics/counts_28d/20210301102134_i_code_review_user_time_estimate_changed_monthly.yml
new file mode 100644
index 00000000000..69ad9237f0d
--- /dev/null
+++ b/config/metrics/counts_28d/20210301102134_i_code_review_user_time_estimate_changed_monthly.yml
@@ -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
diff --git a/config/metrics/counts_28d/20210301102204_i_code_review_user_time_spent_changed_monthly.yml b/config/metrics/counts_28d/20210301102204_i_code_review_user_time_spent_changed_monthly.yml
new file mode 100644
index 00000000000..36972b7e168
--- /dev/null
+++ b/config/metrics/counts_28d/20210301102204_i_code_review_user_time_spent_changed_monthly.yml
@@ -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
diff --git a/config/metrics/counts_7d/20210302103539_i_code_review_user_time_estimate_changed_weekly.yml b/config/metrics/counts_7d/20210302103539_i_code_review_user_time_estimate_changed_weekly.yml
new file mode 100644
index 00000000000..fb47deaba92
--- /dev/null
+++ b/config/metrics/counts_7d/20210302103539_i_code_review_user_time_estimate_changed_weekly.yml
@@ -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
diff --git a/config/metrics/counts_7d/20210302103615_i_code_review_user_time_spent_changed_weekly.yml b/config/metrics/counts_7d/20210302103615_i_code_review_user_time_spent_changed_weekly.yml
new file mode 100644
index 00000000000..91c88c00af8
--- /dev/null
+++ b/config/metrics/counts_7d/20210302103615_i_code_review_user_time_spent_changed_weekly.yml
@@ -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
diff --git a/config/metrics/settings/20210225045628_operating_system.yml b/config/metrics/settings/20210225045628_operating_system.yml
new file mode 100644
index 00000000000..e88b8f8c827
--- /dev/null
+++ b/config/metrics/settings/20210225045628_operating_system.yml
@@ -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
diff --git a/db/migrate/20210301200601_rename_asset_proxy_allowlist_on_application_settings.rb b/db/migrate/20210301200601_rename_asset_proxy_allowlist_on_application_settings.rb
new file mode 100644
index 00000000000..8a9acd8b86e
--- /dev/null
+++ b/db/migrate/20210301200601_rename_asset_proxy_allowlist_on_application_settings.rb
@@ -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
diff --git a/db/post_migrate/20210301200959_clean_up_asset_proxy_allowlist_rename_on_application_settings.rb b/db/post_migrate/20210301200959_clean_up_asset_proxy_allowlist_rename_on_application_settings.rb
new file mode 100644
index 00000000000..d0b372b84f0
--- /dev/null
+++ b/db/post_migrate/20210301200959_clean_up_asset_proxy_allowlist_rename_on_application_settings.rb
@@ -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
diff --git a/db/schema_migrations/20210301200601 b/db/schema_migrations/20210301200601
new file mode 100644
index 00000000000..13907f9de8b
--- /dev/null
+++ b/db/schema_migrations/20210301200601
@@ -0,0 +1 @@
+21ae7ea7cbf1d34c7b9dc300a641eaf975ed1e33f5bc519494cd37c4a661bec8
\ No newline at end of file
diff --git a/db/schema_migrations/20210301200959 b/db/schema_migrations/20210301200959
new file mode 100644
index 00000000000..8c5efabd76d
--- /dev/null
+++ b/db/schema_migrations/20210301200959
@@ -0,0 +1 @@
+28b90c9b7c2e4f2e2b12088f5aee85c16dfb567f89ed6a8e771f2c5d91c818d9
\ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index c56ee01c08e..ea2b9de88d0 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -9394,12 +9394,12 @@ CREATE TABLE application_settings (
invisible_captcha_enabled boolean DEFAULT false NOT NULL,
enforce_ssh_key_expiration boolean DEFAULT false NOT NULL,
git_two_factor_session_expiry integer DEFAULT 15 NOT NULL,
- asset_proxy_allowlist text,
keep_latest_artifact boolean DEFAULT true NOT NULL,
notes_create_limit integer DEFAULT 300 NOT NULL,
notes_create_limit_allowlist text[] DEFAULT '{}'::text[] NOT NULL,
kroki_formats jsonb DEFAULT '{}'::jsonb NOT NULL,
in_product_marketing_emails_enabled boolean DEFAULT true NOT NULL,
+ asset_proxy_whitelist text,
CONSTRAINT app_settings_container_reg_cleanup_tags_max_list_size_positive CHECK ((container_registry_cleanup_tags_service_max_list_size >= 0)),
CONSTRAINT app_settings_registry_exp_policies_worker_capacity_positive CHECK ((container_registry_expiration_policies_worker_capacity >= 0)),
CONSTRAINT check_17d9558205 CHECK ((char_length(kroki_url) <= 1024)),
diff --git a/doc/development/usage_ping/dictionary.md b/doc/development/usage_ping/dictionary.md
index 93ce26fceb5..16ab43eff1a 100644
--- a/doc/development/usage_ping/dictionary.md
+++ b/doc/development/usage_ping/dictionary.md
@@ -13730,6 +13730,86 @@ Count of unique users per week|month with diffs viewed file by file
| `tier` | |
| `skip_validation` | true |
+## `redis_hll_counters.code_review.i_code_review_user_time_estimate_changed_monthly`
+
+Count of unique users per month who changed time estimate of a MR
+
+| field | value |
+| --- | --- |
+| `key_path` | **`redis_hll_counters.code_review.i_code_review_user_time_estimate_changed_monthly`** |
+| `product_section` | dev |
+| `product_stage` | create |
+| `product_group` | `group::code review` |
+| `product_category` | `code_review` |
+| `value_type` | number |
+| `status` | implemented |
+| `milestone` | 13.10 |
+| `introduced_by_url` | [Introduced by](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55046) |
+| `time_frame` | 28d |
+| `data_source` | Redis_hll |
+| `distribution` | ce, ee |
+| `tier` | free, premium, ultimate |
+
+## `redis_hll_counters.code_review.i_code_review_user_time_estimate_changed_weekly`
+
+Count of unique users per week who changed time estimate of a MR
+
+| field | value |
+| --- | --- |
+| `key_path` | **`redis_hll_counters.code_review.i_code_review_user_time_estimate_changed_weekly`** |
+| `product_section` | dev |
+| `product_stage` | create |
+| `product_group` | `group::code review` |
+| `product_category` | `code_review` |
+| `value_type` | number |
+| `status` | implemented |
+| `milestone` | 13.10 |
+| `introduced_by_url` | [Introduced by](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55046) |
+| `time_frame` | 7d |
+| `data_source` | Redis_hll |
+| `distribution` | ce, ee |
+| `tier` | free, premium, ultimate |
+
+## `redis_hll_counters.code_review.i_code_review_user_time_spent_changed_monthly`
+
+Count of unique users per month who changed time spent on a MR
+
+| field | value |
+| --- | --- |
+| `key_path` | **`redis_hll_counters.code_review.i_code_review_user_time_spent_changed_monthly`** |
+| `product_section` | dev |
+| `product_stage` | create |
+| `product_group` | `group::code review` |
+| `product_category` | `code_review` |
+| `value_type` | number |
+| `status` | implemented |
+| `milestone` | 13.10 |
+| `introduced_by_url` | [Introduced by](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55046) |
+| `time_frame` | 28d |
+| `data_source` | Redis_hll |
+| `distribution` | ce, ee |
+| `tier` | free, premium, ultimate |
+
+## `redis_hll_counters.code_review.i_code_review_user_time_spent_changed_weekly`
+
+Count of unique users per week who changed time spent on a MR
+
+| field | value |
+| --- | --- |
+| `key_path` | **`redis_hll_counters.code_review.i_code_review_user_time_spent_changed_weekly`** |
+| `product_section` | dev |
+| `product_stage` | create |
+| `product_group` | `group::code review` |
+| `product_category` | `code_review` |
+| `value_type` | number |
+| `status` | implemented |
+| `milestone` | 13.10 |
+| `introduced_by_url` | [Introduced by](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55046) |
+| `time_frame` | 7d |
+| `data_source` | Redis_hll |
+| `distribution` | ce, ee |
+| `tier` | free, premium, ultimate |
+
## `redis_hll_counters.code_review.i_code_review_user_toggled_task_item_status_monthly`
Missing description
@@ -20846,6 +20926,26 @@ Is encrypted LDAP secrets configured?
| `tier` | free, premium, ultimate |
| `skip_validation` | true |
+## `settings.operating_system`
+
+Information about the operating system running GitLab
+
+| field | value |
+| --- | --- |
+| `key_path` | **`settings.operating_system`** |
+| `product_section` | enablement |
+| `product_stage` | enablement |
+| `product_group` | `group::distribution` |
+| `product_category` | `collection` |
+| `value_type` | string |
+| `status` | implemented |
+| `milestone` | 13.10 |
+| `introduced_by_url` | [Introduced by](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/54778) |
+| `time_frame` | none |
+| `data_source` | Ruby |
+| `distribution` | ce, ee |
+| `tier` | free, premium, ultimate |
+
## `signup_enabled`
Whether public signup is enabled
diff --git a/lib/gitlab/ci/reports/codequality_reports_comparer.rb b/lib/gitlab/ci/reports/codequality_reports_comparer.rb
index 88e02cd9004..10748b8ca02 100644
--- a/lib/gitlab/ci/reports/codequality_reports_comparer.rb
+++ b/lib/gitlab/ci/reports/codequality_reports_comparer.rb
@@ -5,7 +5,7 @@ module Gitlab
module Reports
class CodequalityReportsComparer < ReportsComparer
def initialize(base_report, head_report)
- @base_report = base_report || CodequalityReports.new
+ @base_report = base_report
@head_report = head_report
end
@@ -15,12 +15,16 @@ module Gitlab
def existing_errors
strong_memoize(:existing_errors) do
+ next [] if not_found?
+
base_report.all_degradations & head_report.all_degradations
end
end
def new_errors
strong_memoize(:new_errors) do
+ next [] if not_found?
+
fingerprints = head_report.degradations.keys - base_report.degradations.keys
head_report.degradations.fetch_values(*fingerprints)
end
@@ -28,6 +32,8 @@ module Gitlab
def resolved_errors
strong_memoize(:resolved_errors) do
+ next [] if not_found?
+
fingerprints = base_report.degradations.keys - head_report.degradations.keys
base_report.degradations.fetch_values(*fingerprints)
end
diff --git a/lib/gitlab/ci/reports/reports_comparer.rb b/lib/gitlab/ci/reports/reports_comparer.rb
index 5667998e535..16a7f6478b7 100644
--- a/lib/gitlab/ci/reports/reports_comparer.rb
+++ b/lib/gitlab/ci/reports/reports_comparer.rb
@@ -18,10 +18,10 @@ module Gitlab
end
def status
- if success?
- STATUS_SUCCESS
- elsif base_report.nil? || head_report.nil?
+ if base_report.nil? || head_report.nil?
STATUS_NOT_FOUND
+ elsif success?
+ STATUS_SUCCESS
else
STATUS_FAILED
end
@@ -54,6 +54,10 @@ module Gitlab
def total_count
existing_errors.size + new_errors.size
end
+
+ def not_found?
+ status == STATUS_NOT_FOUND
+ end
end
end
end
diff --git a/lib/gitlab/github_import/importer/pull_request_review_importer.rb b/lib/gitlab/github_import/importer/pull_request_review_importer.rb
index 14ee69ba089..9f495913897 100644
--- a/lib/gitlab/github_import/importer/pull_request_review_importer.rb
+++ b/lib/gitlab/github_import/importer/pull_request_review_importer.rb
@@ -77,12 +77,22 @@ module Gitlab
def add_approval!(user_id)
return unless review.review_type == 'APPROVED'
- add_approval_system_note!(user_id)
-
- merge_request.approvals.create!(
+ approval_attribues = {
+ merge_request_id: merge_request.id,
user_id: user_id,
- created_at: review.submitted_at
+ created_at: review.submitted_at,
+ updated_at: review.submitted_at
+ }
+
+ result = ::Approval.insert(
+ approval_attribues,
+ returning: [:id],
+ unique_by: [:user_id, :merge_request_id]
)
+
+ if result.rows.present?
+ add_approval_system_note!(user_id)
+ end
end
def add_approval_system_note!(user_id)
diff --git a/lib/gitlab/usage_data.rb b/lib/gitlab/usage_data.rb
index 500c4ab17c6..32b67a73fa3 100644
--- a/lib/gitlab/usage_data.rb
+++ b/lib/gitlab/usage_data.rb
@@ -242,7 +242,8 @@ module Gitlab
def system_usage_data_settings
{
settings: {
- ldap_encrypted_secrets_enabled: alt_usage_data(fallback: nil) { Gitlab::Auth::Ldap::Config.encrypted_secrets.active? }
+ ldap_encrypted_secrets_enabled: alt_usage_data(fallback: nil) { Gitlab::Auth::Ldap::Config.encrypted_secrets.active? },
+ operating_system: alt_usage_data(fallback: nil) { operating_system }
}
}
end
@@ -505,6 +506,17 @@ module Gitlab
end
end
+ def operating_system
+ ohai_data = Ohai::System.new.tap do |oh|
+ oh.all_plugins(['platform'])
+ end.data
+
+ platform = ohai_data['platform']
+ platform = 'raspbian' if ohai_data['platform'] == 'debian' && /armv/.match?(ohai_data['kernel']['machine'])
+
+ "#{platform}-#{ohai_data['platform_version']}"
+ end
+
def last_28_days_time_period(column: :created_at)
{ column => 30.days.ago..2.days.ago }
end
diff --git a/lib/gitlab/usage_data_counters/aggregated_metrics/code_review.yml b/lib/gitlab/usage_data_counters/aggregated_metrics/code_review.yml
index c856d0a5eee..f19de850490 100644
--- a/lib/gitlab/usage_data_counters/aggregated_metrics/code_review.yml
+++ b/lib/gitlab/usage_data_counters/aggregated_metrics/code_review.yml
@@ -44,7 +44,9 @@
'i_code_review_user_toggled_task_item_status',
'i_code_review_user_create_mr_from_issue',
'i_code_review_user_mr_discussion_locked',
- 'i_code_review_user_mr_discussion_unlocked'
+ 'i_code_review_user_mr_discussion_unlocked',
+ 'i_code_review_user_time_estimate_changed',
+ 'i_code_review_user_time_spent_changed'
]
- name: code_review_category_monthly_active_users
operator: OR
@@ -82,7 +84,9 @@
'i_code_review_user_toggled_task_item_status',
'i_code_review_user_create_mr_from_issue',
'i_code_review_user_mr_discussion_locked',
- 'i_code_review_user_mr_discussion_unlocked'
+ 'i_code_review_user_mr_discussion_unlocked',
+ 'i_code_review_user_time_estimate_changed',
+ 'i_code_review_user_time_spent_changed'
]
- name: code_review_extension_category_monthly_active_users
operator: OR
diff --git a/lib/gitlab/usage_data_counters/known_events/code_review_events.yml b/lib/gitlab/usage_data_counters/known_events/code_review_events.yml
index 21613740142..83a932456f9 100644
--- a/lib/gitlab/usage_data_counters/known_events/code_review_events.yml
+++ b/lib/gitlab/usage_data_counters/known_events/code_review_events.yml
@@ -174,3 +174,13 @@
category: code_review
aggregation: weekly
feature_flag: usage_data_i_code_review_user_mr_discussion_unlocked
+- name: i_code_review_user_time_estimate_changed
+ redis_slot: code_review
+ category: code_review
+ aggregation: weekly
+ feature_flag: usage_data_i_code_review_user_time_estimate_changed
+- name: i_code_review_user_time_spent_changed
+ redis_slot: code_review
+ category: code_review
+ aggregation: weekly
+ feature_flag: usage_data_i_code_review_user_time_spent_changed
diff --git a/lib/gitlab/usage_data_counters/merge_request_activity_unique_counter.rb b/lib/gitlab/usage_data_counters/merge_request_activity_unique_counter.rb
index b94caa32bf7..daa4c34ae1f 100644
--- a/lib/gitlab/usage_data_counters/merge_request_activity_unique_counter.rb
+++ b/lib/gitlab/usage_data_counters/merge_request_activity_unique_counter.rb
@@ -37,6 +37,8 @@ module Gitlab
MR_CREATE_FROM_ISSUE_ACTION = 'i_code_review_user_create_mr_from_issue'
MR_DISCUSSION_LOCKED_ACTION = 'i_code_review_user_mr_discussion_locked'
MR_DISCUSSION_UNLOCKED_ACTION = 'i_code_review_user_mr_discussion_unlocked'
+ MR_TIME_ESTIMATE_CHANGED_ACTION = 'i_code_review_user_time_estimate_changed'
+ MR_TIME_SPENT_CHANGED_ACTION = 'i_code_review_user_time_spent_changed'
class << self
def track_mr_diffs_action(merge_request:)
@@ -163,6 +165,14 @@ module Gitlab
track_unique_action_by_user(MR_DISCUSSION_UNLOCKED_ACTION, user)
end
+ def track_time_estimate_changed_action(user:)
+ track_unique_action_by_user(MR_TIME_ESTIMATE_CHANGED_ACTION, user)
+ end
+
+ def track_time_spent_changed_action(user:)
+ track_unique_action_by_user(MR_TIME_SPENT_CHANGED_ACTION, user)
+ end
+
private
def track_unique_action_by_merge_request(action, merge_request)
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index d8e05fd9baf..45ddfe952bb 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -173,6 +173,11 @@ msgid_plural "%d days"
msgstr[0] ""
msgstr[1] ""
+msgid "%d epic"
+msgid_plural "%d epics"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d error"
msgid_plural "%d errors"
msgstr[0] ""
@@ -559,6 +564,9 @@ msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{itemsCount} issues with a limit of %{maxIssueCount}"
+msgstr ""
+
msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
msgstr ""
@@ -34912,6 +34920,9 @@ msgstr ""
msgid "can't be enabled because signed commits are required for this project"
msgstr ""
+msgid "can't include: %{invalid_storages}"
+msgstr ""
+
msgid "cannot be a date in the past"
msgstr ""
@@ -36318,6 +36329,12 @@ msgstr ""
msgid "v%{version} published %{timeAgo}"
msgstr ""
+msgid "value for '%{storage}' must be an integer"
+msgstr ""
+
+msgid "value for '%{storage}' must be between 0 and 100"
+msgstr ""
+
msgid "verify ownership"
msgstr ""
diff --git a/qa/qa/page/component/new_snippet.rb b/qa/qa/page/component/new_snippet.rb
index 741a3feb73b..673bc7ba44c 100644
--- a/qa/qa/page/component/new_snippet.rb
+++ b/qa/qa/page/component/new_snippet.rb
@@ -32,6 +32,10 @@ module QA
# This 'element' is here only to ensure the changes in the view source aren't mistakenly changed
element :_, "qa_selector = local_assigns.fetch(:qa_selector, '')" # rubocop:disable QA/ElementWithPattern
end
+
+ base.view 'app/assets/javascripts/snippets/components/snippet_visibility_edit.vue' do
+ element :visibility_content
+ end
end
def fill_title(title)
@@ -44,7 +48,7 @@ module QA
end
def set_visibility(visibility)
- choose visibility
+ click_element(:visibility_content, visibility: visibility)
end
def fill_file_name(name, file_number = nil)
diff --git a/rubocop/rubocop-usage-data.yml b/rubocop/rubocop-usage-data.yml
index bbc4d590ddc..d0dd6a2e4da 100644
--- a/rubocop/rubocop-usage-data.yml
+++ b/rubocop/rubocop-usage-data.yml
@@ -30,6 +30,7 @@ UsageData/LargeTable:
- :Settings
- :CE_MEMOIZED_VALUES
- :EE_MEMOIZED_VALUES
+ - :Ohai::System
CountMethods:
- :count
- :distinct_count
diff --git a/spec/controllers/admin/application_settings_controller_spec.rb b/spec/controllers/admin/application_settings_controller_spec.rb
index 71abf3191b8..2b562e2dd64 100644
--- a/spec/controllers/admin/application_settings_controller_spec.rb
+++ b/spec/controllers/admin/application_settings_controller_spec.rb
@@ -144,10 +144,10 @@ RSpec.describe Admin::ApplicationSettingsController do
end
it 'updates repository_storages_weighted setting' do
- put :update, params: { application_setting: { repository_storages_weighted_default: 75 } }
+ put :update, params: { application_setting: { repository_storages_weighted: { default: 75 } } }
expect(response).to redirect_to(general_admin_application_settings_path)
- expect(ApplicationSetting.current.repository_storages_weighted_default).to eq(75)
+ expect(ApplicationSetting.current.repository_storages_weighted).to eq('default' => 75)
end
it 'updates kroki_formats setting' do
diff --git a/spec/factories/ci/build_trace_chunks.rb b/spec/factories/ci/build_trace_chunks.rb
index d996b41b648..115eb32111c 100644
--- a/spec/factories/ci/build_trace_chunks.rb
+++ b/spec/factories/ci/build_trace_chunks.rb
@@ -3,7 +3,7 @@
FactoryBot.define do
factory :ci_build_trace_chunk, class: 'Ci::BuildTraceChunk' do
build factory: :ci_build
- chunk_index { 0 }
+ chunk_index { generate(:iid) }
data_store { :redis }
trait :redis_with_data do
diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb
index 52f39f65bd0..249621f5835 100644
--- a/spec/features/admin/admin_settings_spec.rb
+++ b/spec/features/admin/admin_settings_spec.rb
@@ -384,7 +384,20 @@ RSpec.describe 'Admin updates settings' do
click_button 'Save changes'
end
- expect(current_settings.repository_storages_weighted_default).to be 50
+ expect(current_settings.repository_storages_weighted).to eq('default' => 50)
+ end
+
+ it 'still saves when settings are outdated' do
+ current_settings.update_attribute :repository_storages_weighted, { 'default' => 100, 'outdated' => 100 }
+
+ visit repository_admin_application_settings_path
+
+ page.within('.as-repository-storage') do
+ fill_in 'application_setting_repository_storages_weighted_default', with: 50
+ click_button 'Save changes'
+ end
+
+ expect(current_settings.repository_storages_weighted).to eq('default' => 50)
end
end
diff --git a/spec/frontend/boards/components/board_list_header_spec.js b/spec/frontend/boards/components/board_list_header_spec.js
index f30e3792435..7472eeb4396 100644
--- a/spec/frontend/boards/components/board_list_header_spec.js
+++ b/spec/frontend/boards/components/board_list_header_spec.js
@@ -1,5 +1,6 @@
import { shallowMount, createLocalVue } from '@vue/test-utils';
import Vuex from 'vuex';
+import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import { mockLabelList } from 'jest/boards/mock_data';
import BoardListHeader from '~/boards/components/board_list_header.vue';
@@ -14,6 +15,7 @@ describe('Board List Header Component', () => {
let store;
const updateListSpy = jest.fn();
+ const toggleListCollapsedSpy = jest.fn();
afterEach(() => {
wrapper.destroy();
@@ -43,38 +45,39 @@ describe('Board List Header Component', () => {
if (withLocalStorage) {
localStorage.setItem(
- `boards.${boardId}.${listMock.listType}.${listMock.id}.expanded`,
- (!collapsed).toString(),
+ `boards.${boardId}.${listMock.listType}.${listMock.id}.collapsed`,
+ collapsed.toString(),
);
}
store = new Vuex.Store({
state: {},
- actions: { updateList: updateListSpy },
+ actions: { updateList: updateListSpy, toggleListCollapsed: toggleListCollapsedSpy },
getters: {},
});
- wrapper = shallowMount(BoardListHeader, {
- store,
- localVue,
- propsData: {
- disabled: false,
- list: listMock,
- },
- provide: {
- boardId,
- weightFeatureAvailable: false,
- currentUserId,
- },
- });
+ wrapper = extendedWrapper(
+ shallowMount(BoardListHeader, {
+ store,
+ localVue,
+ propsData: {
+ disabled: false,
+ list: listMock,
+ },
+ provide: {
+ boardId,
+ weightFeatureAvailable: false,
+ currentUserId,
+ },
+ }),
+ );
};
const isCollapsed = () => wrapper.vm.list.collapsed;
- const isExpanded = () => !isCollapsed;
const findAddIssueButton = () => wrapper.find({ ref: 'newIssueBtn' });
const findTitle = () => wrapper.find('.board-title');
- const findCaret = () => wrapper.find('.board-title-caret');
+ const findCaret = () => wrapper.findByTestId('board-title-caret');
describe('Add issue button', () => {
const hasNoAddButton = [ListType.closed];
@@ -114,40 +117,29 @@ describe('Board List Header Component', () => {
});
describe('expanding / collapsing the column', () => {
- it('does not collapse when clicking the header', async () => {
+ it('should display collapse icon when column is expanded', async () => {
createComponent();
- expect(isCollapsed()).toBe(false);
+ const icon = findCaret();
- wrapper.find('[data-testid="board-list-header"]').trigger('click');
-
- await wrapper.vm.$nextTick();
-
- expect(isCollapsed()).toBe(false);
+ expect(icon.props('icon')).toBe('chevron-right');
});
- it('collapses expanded Column when clicking the collapse icon', async () => {
- createComponent();
-
- expect(isCollapsed()).toBe(false);
-
- findCaret().vm.$emit('click');
-
- await wrapper.vm.$nextTick();
-
- expect(isCollapsed()).toBe(true);
- });
-
- it('expands collapsed Column when clicking the expand icon', async () => {
+ it('should display expand icon when column is collapsed', async () => {
createComponent({ collapsed: true });
- expect(isCollapsed()).toBe(true);
+ const icon = findCaret();
+
+ expect(icon.props('icon')).toBe('chevron-down');
+ });
+
+ it('should dispatch toggleListCollapse when clicking the collapse icon', async () => {
+ createComponent();
findCaret().vm.$emit('click');
await wrapper.vm.$nextTick();
-
- expect(isCollapsed()).toBe(false);
+ expect(toggleListCollapsedSpy).toHaveBeenCalledTimes(1);
});
it("when logged in it calls list update and doesn't set localStorage", async () => {
@@ -157,7 +149,7 @@ describe('Board List Header Component', () => {
await wrapper.vm.$nextTick();
expect(updateListSpy).toHaveBeenCalledTimes(1);
- expect(localStorage.getItem(`${wrapper.vm.uniqueKey}.expanded`)).toBe(null);
+ expect(localStorage.getItem(`${wrapper.vm.uniqueKey}.collapsed`)).toBe(null);
});
it("when logged out it doesn't call list update and sets localStorage", async () => {
@@ -167,7 +159,7 @@ describe('Board List Header Component', () => {
await wrapper.vm.$nextTick();
expect(updateListSpy).not.toHaveBeenCalled();
- expect(localStorage.getItem(`${wrapper.vm.uniqueKey}.expanded`)).toBe(String(isExpanded()));
+ expect(localStorage.getItem(`${wrapper.vm.uniqueKey}.collapsed`)).toBe(String(isCollapsed()));
});
});
diff --git a/spec/frontend/boards/stores/actions_spec.js b/spec/frontend/boards/stores/actions_spec.js
index 622401c1c09..9e1b5018cc1 100644
--- a/spec/frontend/boards/stores/actions_spec.js
+++ b/spec/frontend/boards/stores/actions_spec.js
@@ -452,6 +452,22 @@ describe('updateList', () => {
});
});
+describe('toggleListCollapsed', () => {
+ it('should commit TOGGLE_LIST_COLLAPSED mutation', async () => {
+ const payload = { listId: 'gid://gitlab/List/1', collapsed: true };
+ await testAction({
+ action: actions.toggleListCollapsed,
+ payload,
+ expectedMutations: [
+ {
+ type: types.TOGGLE_LIST_COLLAPSED,
+ payload,
+ },
+ ],
+ });
+ });
+});
+
describe('removeList', () => {
let state;
const list = mockLists[0];
diff --git a/spec/frontend/boards/stores/mutations_spec.js b/spec/frontend/boards/stores/mutations_spec.js
index 3a94bd9160f..29a205e2ffb 100644
--- a/spec/frontend/boards/stores/mutations_spec.js
+++ b/spec/frontend/boards/stores/mutations_spec.js
@@ -202,6 +202,24 @@ describe('Board Store Mutations', () => {
});
});
+ describe('TOGGLE_LIST_COLLAPSED', () => {
+ it('updates collapsed attribute of list in boardLists state', () => {
+ const listId = 'gid://gitlab/List/1';
+ state = {
+ ...state,
+ boardLists: {
+ [listId]: mockLists[0],
+ },
+ };
+
+ expect(state.boardLists[listId].collapsed).toEqual(false);
+
+ mutations.TOGGLE_LIST_COLLAPSED(state, { listId, collapsed: true });
+
+ expect(state.boardLists[listId].collapsed).toEqual(true);
+ });
+ });
+
describe('REMOVE_LIST', () => {
it('removes list from boardLists', () => {
const [list, secondList] = mockLists;
diff --git a/spec/frontend/snippets/components/__snapshots__/snippet_visibility_edit_spec.js.snap b/spec/frontend/snippets/components/__snapshots__/snippet_visibility_edit_spec.js.snap
index 8446f0f50c4..95da67c2bbf 100644
--- a/spec/frontend/snippets/components/__snapshots__/snippet_visibility_edit_spec.js.snap
+++ b/spec/frontend/snippets/components/__snapshots__/snippet_visibility_edit_spec.js.snap
@@ -46,6 +46,8 @@ exports[`Snippet Visibility Edit component rendering matches the snapshot 1`] =
Private
@@ -65,6 +67,8 @@ exports[`Snippet Visibility Edit component rendering matches the snapshot 1`] =
Internal
@@ -84,6 +88,8 @@ exports[`Snippet Visibility Edit component rendering matches the snapshot 1`] =
Public
diff --git a/spec/helpers/application_settings_helper_spec.rb b/spec/helpers/application_settings_helper_spec.rb
index 2cd01451e0d..c74ee3ce0ec 100644
--- a/spec/helpers/application_settings_helper_spec.rb
+++ b/spec/helpers/application_settings_helper_spec.rb
@@ -130,20 +130,15 @@ RSpec.describe ApplicationSettingsHelper do
before do
helper.instance_variable_set(:@application_setting, application_setting)
stub_storage_settings({ 'default': {}, 'storage_1': {}, 'storage_2': {} })
- allow(ApplicationSetting).to receive(:repository_storages_weighted_attributes).and_return(
- [:repository_storages_weighted_default,
- :repository_storages_weighted_storage_1,
- :repository_storages_weighted_storage_2])
-
stub_application_setting(repository_storages_weighted: { 'default' => 100, 'storage_1' => 50, 'storage_2' => nil })
end
it 'returns storages correctly' do
- expect(helper.storage_weights).to eq([
- { name: :repository_storages_weighted_default, label: 'default', value: 100 },
- { name: :repository_storages_weighted_storage_1, label: 'storage_1', value: 50 },
- { name: :repository_storages_weighted_storage_2, label: 'storage_2', value: 0 }
- ])
+ expect(helper.storage_weights).to eq(OpenStruct.new(
+ default: 100,
+ storage_1: 50,
+ storage_2: 0
+ ))
end
end
diff --git a/spec/lib/gitlab/ci/reports/codequality_reports_comparer_spec.rb b/spec/lib/gitlab/ci/reports/codequality_reports_comparer_spec.rb
index 90188b56f5a..b322e55cb5a 100644
--- a/spec/lib/gitlab/ci/reports/codequality_reports_comparer_spec.rb
+++ b/spec/lib/gitlab/ci/reports/codequality_reports_comparer_spec.rb
@@ -27,6 +27,22 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReportsComparer do
expect(report_status).to eq(described_class::STATUS_SUCCESS)
end
end
+
+ context 'when head report does not exist' do
+ let(:head_report) { nil }
+
+ it 'returns status not found' do
+ expect(report_status).to eq(described_class::STATUS_NOT_FOUND)
+ end
+ end
+
+ context 'when base report does not exist' do
+ let(:base_report) { nil }
+
+ it 'returns status success' do
+ expect(report_status).to eq(described_class::STATUS_NOT_FOUND)
+ end
+ end
end
describe '#errors_count' do
@@ -93,6 +109,14 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReportsComparer do
expect(resolved_count).to be_zero
end
end
+
+ context 'when base report is nil' do
+ let(:base_report) { nil }
+
+ it 'returns zero' do
+ expect(resolved_count).to be_zero
+ end
+ end
end
describe '#total_count' do
@@ -140,6 +164,14 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReportsComparer do
expect(total_count).to eq(2)
end
end
+
+ context 'when base report is nil' do
+ let(:base_report) { nil }
+
+ it 'returns zero' do
+ expect(total_count).to be_zero
+ end
+ end
end
describe '#existing_errors' do
@@ -177,6 +209,14 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReportsComparer do
expect(existing_errors).to be_empty
end
end
+
+ context 'when base report is nil' do
+ let(:base_report) { nil }
+
+ it 'returns an empty array' do
+ expect(existing_errors).to be_empty
+ end
+ end
end
describe '#new_errors' do
@@ -213,6 +253,14 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReportsComparer do
expect(new_errors).to eq([degradation_1])
end
end
+
+ context 'when base report is nil' do
+ let(:base_report) { nil }
+
+ it 'returns an empty array' do
+ expect(new_errors).to be_empty
+ end
+ end
end
describe '#resolved_errors' do
@@ -250,5 +298,13 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReportsComparer do
expect(resolved_errors).to be_empty
end
end
+
+ context 'when base report is nil' do
+ let(:base_report) { nil }
+
+ it 'returns an empty array' do
+ expect(resolved_errors).to be_empty
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/ci/reports/reports_comparer_spec.rb b/spec/lib/gitlab/ci/reports/reports_comparer_spec.rb
index 8eb2258a335..7ed9270e9a0 100644
--- a/spec/lib/gitlab/ci/reports/reports_comparer_spec.rb
+++ b/spec/lib/gitlab/ci/reports/reports_comparer_spec.rb
@@ -49,10 +49,6 @@ RSpec.describe Gitlab::Ci::Reports::ReportsComparer do
context 'when base_report is nil' do
let(:base_report) { nil }
- before do
- allow(comparer).to receive(:success?).and_return(false)
- end
-
it 'returns status not_found' do
expect(status).to eq('not_found')
end
@@ -61,10 +57,6 @@ RSpec.describe Gitlab::Ci::Reports::ReportsComparer do
context 'when head_report is nil' do
let(:head_report) { nil }
- before do
- allow(comparer).to receive(:success?).and_return(false)
- end
-
it 'returns status not_found' do
expect(status).to eq('not_found')
end
@@ -118,4 +110,22 @@ RSpec.describe Gitlab::Ci::Reports::ReportsComparer do
expect { total_count }.to raise_error(NotImplementedError)
end
end
+
+ describe '#not_found?' do
+ subject(:not_found) { comparer.not_found? }
+
+ context 'when base report is nil' do
+ let(:base_report) { nil }
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when base report exists' do
+ before do
+ allow(comparer).to receive(:success?).and_return(true)
+ end
+
+ it { is_expected.to be_falsey }
+ end
+ end
end
diff --git a/spec/lib/gitlab/github_import/importer/pull_request_review_importer_spec.rb b/spec/lib/gitlab/github_import/importer/pull_request_review_importer_spec.rb
index b2f993ac47c..290f3f51202 100644
--- a/spec/lib/gitlab/github_import/importer/pull_request_review_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/pull_request_review_importer_spec.rb
@@ -19,8 +19,10 @@ RSpec.describe Gitlab::GithubImport::Importer::PullRequestReviewImporter, :clean
context 'when the review is "APPROVED"' do
let(:review) { create_review(type: 'APPROVED', note: '') }
- it 'creates a note for the review' do
- expect { subject.execute }.to change(Note, :count)
+ it 'creates a note for the review and approves the Merge Request' do
+ expect { subject.execute }
+ .to change(Note, :count).by(1)
+ .and change(Approval, :count).by(1)
last_note = merge_request.notes.last
expect(last_note.note).to eq('approved this merge request')
@@ -31,6 +33,14 @@ RSpec.describe Gitlab::GithubImport::Importer::PullRequestReviewImporter, :clean
expect(merge_request.approved_by_users.reload).to include(author)
expect(merge_request.approvals.last.created_at).to eq(submitted_at)
end
+
+ it 'does nothing if the user already approved the merge request' do
+ create(:approval, merge_request: merge_request, user: author)
+
+ expect { subject.execute }
+ .to change(Note, :count).by(0)
+ .and change(Approval, :count).by(0)
+ end
end
context 'when the review is "COMMENTED"' do
diff --git a/spec/lib/gitlab/import_export/import_export_spec.rb b/spec/lib/gitlab/import_export/import_export_spec.rb
index 62b4717fc96..87757b07572 100644
--- a/spec/lib/gitlab/import_export/import_export_spec.rb
+++ b/spec/lib/gitlab/import_export/import_export_spec.rb
@@ -4,8 +4,8 @@ require 'spec_helper'
RSpec.describe Gitlab::ImportExport do
describe 'export filename' do
- let(:group) { create(:group, :nested) }
- let(:project) { create(:project, :public, path: 'project-path', namespace: group) }
+ let(:group) { build(:group, path: 'child', parent: build(:group, path: 'parent')) }
+ let(:project) { build(:project, :public, path: 'project-path', namespace: group) }
it 'contains the project path' do
expect(described_class.export_filename(exportable: project)).to include(project.path)
diff --git a/spec/lib/gitlab/usage_data_counters/merge_request_activity_unique_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/merge_request_activity_unique_counter_spec.rb
index 6bc42430889..d740e19ae7b 100644
--- a/spec/lib/gitlab/usage_data_counters/merge_request_activity_unique_counter_spec.rb
+++ b/spec/lib/gitlab/usage_data_counters/merge_request_activity_unique_counter_spec.rb
@@ -300,4 +300,20 @@ RSpec.describe Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter, :cl
let(:action) { described_class::MR_DISCUSSION_UNLOCKED_ACTION }
end
end
+
+ describe '.track_time_estimate_changed_action' do
+ subject { described_class.track_time_estimate_changed_action(user: user) }
+
+ it_behaves_like 'a tracked merge request unique event' do
+ let(:action) { described_class::MR_TIME_ESTIMATE_CHANGED_ACTION }
+ end
+ end
+
+ describe '.track_time_spent_changed_action' do
+ subject { described_class.track_time_spent_changed_action(user: user) }
+
+ it_behaves_like 'a tracked merge request unique event' do
+ let(:action) { described_class::MR_TIME_SPENT_CHANGED_ACTION }
+ end
+ end
end
diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb
index 602f6640d72..1aa2cbb5485 100644
--- a/spec/lib/gitlab/usage_data_spec.rb
+++ b/spec/lib/gitlab/usage_data_spec.rb
@@ -1129,12 +1129,40 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
end
end
+ describe ".operating_system" do
+ let(:ohai_data) { { "platform" => "ubuntu", "platform_version" => "20.04" } }
+
+ before do
+ allow_next_instance_of(Ohai::System) do |ohai|
+ allow(ohai).to receive(:data).and_return(ohai_data)
+ end
+ end
+
+ subject { described_class.operating_system }
+
+ it { is_expected.to eq("ubuntu-20.04") }
+
+ context 'when on Debian with armv architecture' do
+ let(:ohai_data) { { "platform" => "debian", "platform_version" => "10", 'kernel' => { 'machine' => 'armv' } } }
+
+ it { is_expected.to eq("raspbian-10") }
+ end
+ end
+
describe ".system_usage_data_settings" do
+ before do
+ allow(described_class).to receive(:operating_system).and_return('ubuntu-20.04')
+ end
+
subject { described_class.system_usage_data_settings }
it 'gathers settings usage data', :aggregate_failures do
expect(subject[:settings][:ldap_encrypted_secrets_enabled]).to eq(Gitlab::Auth::Ldap::Config.encrypted_secrets.active?)
end
+
+ it 'populates operating system information' do
+ expect(subject[:settings][:operating_system]).to eq('ubuntu-20.04')
+ end
end
end
diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb
index 8f63f44e8af..808932ce7e4 100644
--- a/spec/models/application_setting_spec.rb
+++ b/spec/models/application_setting_spec.rb
@@ -105,14 +105,14 @@ RSpec.describe ApplicationSetting do
it { is_expected.not_to allow_value(false).for(:hashed_storage_enabled) }
- it { is_expected.not_to allow_value(101).for(:repository_storages_weighted_default) }
- it { is_expected.to allow_value('90').for(:repository_storages_weighted_default) }
- it { is_expected.not_to allow_value(-1).for(:repository_storages_weighted_default) }
- it { is_expected.to allow_value(100).for(:repository_storages_weighted_default) }
- it { is_expected.to allow_value(0).for(:repository_storages_weighted_default) }
- it { is_expected.to allow_value(50).for(:repository_storages_weighted_default) }
- it { is_expected.to allow_value(nil).for(:repository_storages_weighted_default) }
- it { is_expected.not_to allow_value({ default: 100, shouldntexist: 50 }).for(:repository_storages_weighted) }
+ it { is_expected.to allow_value('default' => 0).for(:repository_storages_weighted) }
+ it { is_expected.to allow_value('default' => 50).for(:repository_storages_weighted) }
+ it { is_expected.to allow_value('default' => 100).for(:repository_storages_weighted) }
+ it { is_expected.to allow_value('default' => '90').for(:repository_storages_weighted) }
+ it { is_expected.to allow_value('default' => nil).for(:repository_storages_weighted) }
+ it { is_expected.not_to allow_value('default' => -1).for(:repository_storages_weighted).with_message("value for 'default' must be between 0 and 100") }
+ it { is_expected.not_to allow_value('default' => 101).for(:repository_storages_weighted).with_message("value for 'default' must be between 0 and 100") }
+ it { is_expected.not_to allow_value('default' => 100, shouldntexist: 50).for(:repository_storages_weighted).with_message("can't include: shouldntexist") }
it { is_expected.to allow_value(400).for(:notes_create_limit) }
it { is_expected.not_to allow_value('two').for(:notes_create_limit) }
@@ -650,6 +650,32 @@ RSpec.describe ApplicationSetting do
end
end
+ describe '#asset_proxy_whitelist' do
+ context 'when given an Array' do
+ it 'sets the domains and adds current running host' do
+ setting.asset_proxy_whitelist = ['example.com', 'assets.example.com']
+ expect(setting.asset_proxy_whitelist).to eq(['example.com', 'assets.example.com', 'localhost'])
+ end
+ end
+
+ context 'when given a String' do
+ it 'sets multiple domains with spaces' do
+ setting.asset_proxy_whitelist = 'example.com *.example.com'
+ expect(setting.asset_proxy_whitelist).to eq(['example.com', '*.example.com', 'localhost'])
+ end
+
+ it 'sets multiple domains with newlines and a space' do
+ setting.asset_proxy_whitelist = "example.com\n *.example.com"
+ expect(setting.asset_proxy_whitelist).to eq(['example.com', '*.example.com', 'localhost'])
+ end
+
+ it 'sets multiple domains with commas' do
+ setting.asset_proxy_whitelist = "example.com, *.example.com"
+ expect(setting.asset_proxy_whitelist).to eq(['example.com', '*.example.com', 'localhost'])
+ end
+ end
+ end
+
describe '#asset_proxy_allowlist' do
context 'when given an Array' do
it 'sets the domains and adds current running host' do
@@ -958,12 +984,6 @@ RSpec.describe ApplicationSetting do
it_behaves_like 'application settings examples'
- describe 'repository_storages_weighted_attributes' do
- it 'returns the keys for repository_storages_weighted' do
- expect(subject.class.repository_storages_weighted_attributes).to eq([:repository_storages_weighted_default])
- end
- end
-
describe 'kroki_format_supported?' do
it 'returns true when Excalidraw is enabled' do
subject.kroki_formats_excalidraw = true
@@ -1007,11 +1027,4 @@ RSpec.describe ApplicationSetting do
expect(subject.kroki_formats_excalidraw).to eq(true)
end
end
-
- it 'does not allow to set weight for non existing storage' do
- setting.repository_storages_weighted = { invalid_storage: 100 }
-
- expect(setting).not_to be_valid
- expect(setting.errors.messages[:repository_storages_weighted]).to match_array(["can't include: invalid_storage"])
- end
end
diff --git a/spec/services/application_settings/update_service_spec.rb b/spec/services/application_settings/update_service_spec.rb
index 1352a595ba4..258b3d25aee 100644
--- a/spec/services/application_settings/update_service_spec.rb
+++ b/spec/services/application_settings/update_service_spec.rb
@@ -123,6 +123,7 @@ RSpec.describe ApplicationSettings::UpdateService do
it_behaves_like 'invalidates markdown cache', { asset_proxy_url: 'http://test.com' }
it_behaves_like 'invalidates markdown cache', { asset_proxy_secret_key: 'another secret' }
it_behaves_like 'invalidates markdown cache', { asset_proxy_allowlist: ['domain.com'] }
+ it_behaves_like 'invalidates markdown cache', { asset_proxy_whitelist: ['domain.com'] }
context 'when also setting the local_markdown_version' do
let(:params) { { asset_proxy_enabled: true, local_markdown_version: 12 } }
diff --git a/spec/services/merge_requests/update_service_spec.rb b/spec/services/merge_requests/update_service_spec.rb
index e9ec3bccda3..30d94a2e948 100644
--- a/spec/services/merge_requests/update_service_spec.rb
+++ b/spec/services/merge_requests/update_service_spec.rb
@@ -169,6 +169,23 @@ RSpec.describe MergeRequests::UpdateService, :mailer do
end
end
end
+
+ it 'tracks time estimate and spend time changes' do
+ expect(Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter)
+ .to receive(:track_time_estimate_changed_action).once.with(user: user)
+
+ expect(Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter)
+ .to receive(:track_time_spent_changed_action).once.with(user: user)
+
+ opts[:time_estimate] = 86400
+ opts[:spend_time] = {
+ duration: 3600,
+ user_id: user.id,
+ spent_at: Date.parse('2021-02-24')
+ }
+
+ MergeRequests::UpdateService.new(project, user, opts).execute(merge_request)
+ end
end
context 'updating milestone' do
diff --git a/spec/services/projects/destroy_service_spec.rb b/spec/services/projects/destroy_service_spec.rb
index 75d1c98923a..6fa3d6efbb5 100644
--- a/spec/services/projects/destroy_service_spec.rb
+++ b/spec/services/projects/destroy_service_spec.rb
@@ -31,9 +31,34 @@ RSpec.describe Projects::DestroyService, :aggregate_failures do
end
shared_examples 'deleting the project with pipeline and build' do
- context 'with pipeline and build', :sidekiq_inline do # which has optimistic locking
+ context 'with pipeline and build related records', :sidekiq_inline do # which has optimistic locking
let!(:pipeline) { create(:ci_pipeline, project: project) }
- let!(:build) { create(:ci_build, :artifacts, pipeline: pipeline) }
+ let!(:build) { create(:ci_build, :artifacts, :with_runner_session, pipeline: pipeline) }
+ let!(:trace_chunks) { create(:ci_build_trace_chunk, build: build) }
+ let!(:job_variables) { create(:ci_job_variable, job: build) }
+ let!(:report_result) { create(:ci_build_report_result, build: build) }
+ let!(:pending_state) { create(:ci_build_pending_state, build: build) }
+
+ it 'deletes build related records' do
+ expect { destroy_project(project, user, {}) }.to change { Ci::Build.count }.by(-1)
+ .and change { Ci::BuildTraceChunk.count }.by(-1)
+ .and change { Ci::JobArtifact.count }.by(-2)
+ .and change { Ci::JobVariable.count }.by(-1)
+ .and change { Ci::BuildPendingState.count }.by(-1)
+ .and change { Ci::BuildReportResult.count }.by(-1)
+ .and change { Ci::BuildRunnerSession.count }.by(-1)
+ end
+
+ it 'avoids N+1 queries', skip: 'skipped until fixed in https://gitlab.com/gitlab-org/gitlab/-/issues/24644' do
+ recorder = ActiveRecord::QueryRecorder.new { destroy_project(project, user, {}) }
+
+ project = create(:project, :repository, namespace: user.namespace)
+ pipeline = create(:ci_pipeline, project: project)
+ builds = create_list(:ci_build, 3, :artifacts, pipeline: pipeline)
+ create_list(:ci_build_trace_chunk, 3, build: builds[0])
+
+ expect { destroy_project(project, project.owner, {}) }.not_to exceed_query_limit(recorder)
+ end
it_behaves_like 'deleting the project'
end
diff --git a/spec/support/shared_examples/models/application_setting_shared_examples.rb b/spec/support/shared_examples/models/application_setting_shared_examples.rb
index 92fd4363134..60a02d85a1e 100644
--- a/spec/support/shared_examples/models/application_setting_shared_examples.rb
+++ b/spec/support/shared_examples/models/application_setting_shared_examples.rb
@@ -289,6 +289,7 @@ RSpec.shared_examples 'application settings examples' do
describe '#pick_repository_storage' do
before do
+ allow(Gitlab.config.repositories.storages).to receive(:keys).and_return(%w(default backup))
allow(setting).to receive(:repository_storages_weighted).and_return({ 'default' => 20, 'backup' => 80 })
end
@@ -304,15 +305,19 @@ RSpec.shared_examples 'application settings examples' do
describe '#normalized_repository_storage_weights' do
using RSpec::Parameterized::TableSyntax
- where(:storages, :normalized) do
- { 'default' => 0, 'backup' => 100 } | { 'default' => 0.0, 'backup' => 1.0 }
- { 'default' => 100, 'backup' => 100 } | { 'default' => 0.5, 'backup' => 0.5 }
- { 'default' => 20, 'backup' => 80 } | { 'default' => 0.2, 'backup' => 0.8 }
- { 'default' => 0, 'backup' => 0 } | { 'default' => 0.0, 'backup' => 0.0 }
+ where(:config_storages, :storages, :normalized) do
+ %w(default backup) | { 'default' => 0, 'backup' => 100 } | { 'default' => 0.0, 'backup' => 1.0 }
+ %w(default backup) | { 'default' => 100, 'backup' => 100 } | { 'default' => 0.5, 'backup' => 0.5 }
+ %w(default backup) | { 'default' => 20, 'backup' => 80 } | { 'default' => 0.2, 'backup' => 0.8 }
+ %w(default backup) | { 'default' => 0, 'backup' => 0 } | { 'default' => 0.0, 'backup' => 0.0 }
+ %w(default) | { 'default' => 0, 'backup' => 100 } | { 'default' => 0.0 }
+ %w(default) | { 'default' => 100, 'backup' => 100 } | { 'default' => 1.0 }
+ %w(default) | { 'default' => 20, 'backup' => 80 } | { 'default' => 1.0 }
end
with_them do
before do
+ allow(Gitlab.config.repositories.storages).to receive(:keys).and_return(config_storages)
allow(setting).to receive(:repository_storages_weighted).and_return(storages)
end
diff --git a/spec/views/admin/application_settings/_repository_storage.html.haml_spec.rb b/spec/views/admin/application_settings/_repository_storage.html.haml_spec.rb
index 2915fe1964f..dc8f259eb56 100644
--- a/spec/views/admin/application_settings/_repository_storage.html.haml_spec.rb
+++ b/spec/views/admin/application_settings/_repository_storage.html.haml_spec.rb
@@ -3,34 +3,49 @@
require 'spec_helper'
RSpec.describe 'admin/application_settings/_repository_storage.html.haml' do
- let(:app_settings) { create(:application_setting) }
- let(:repository_storages_weighted_attributes) { [:repository_storages_weighted_default, :repository_storages_weighted_mepmep, :repository_storages_weighted_foobar]}
- let(:repository_storages_weighted) do
- {
- "default" => 100,
- "mepmep" => 50
- }
- end
+ let(:app_settings) { build(:application_setting, repository_storages_weighted: repository_storages_weighted) }
before do
- allow(app_settings).to receive(:repository_storages_weighted).and_return(repository_storages_weighted)
- allow(app_settings).to receive(:repository_storages_weighted_mepmep).and_return(100)
- allow(app_settings).to receive(:repository_storages_weighted_foobar).and_return(50)
+ stub_storage_settings({ 'default': {}, 'mepmep': {}, 'foobar': {} })
assign(:application_setting, app_settings)
- allow(ApplicationSetting).to receive(:repository_storages_weighted_attributes).and_return(repository_storages_weighted_attributes)
end
- context 'when multiple storages are available' do
+ context 'additional storage config' do
+ let(:repository_storages_weighted) do
+ {
+ 'default' => 100,
+ 'mepmep' => 50
+ }
+ end
+
it 'lists them all' do
render
- # lists storages that are saved with weights
- repository_storages_weighted.each do |storage_name, storage_weight|
+ Gitlab.config.repositories.storages.keys.each do |storage_name|
expect(rendered).to have_content(storage_name)
end
- # lists storage not saved with weight
expect(rendered).to have_content('foobar')
end
end
+
+ context 'fewer storage configs' do
+ let(:repository_storages_weighted) do
+ {
+ 'default' => 100,
+ 'mepmep' => 50,
+ 'something_old' => 100
+ }
+ end
+
+ it 'lists only configured storages' do
+ render
+
+ Gitlab.config.repositories.storages.keys.each do |storage_name|
+ expect(rendered).to have_content(storage_name)
+ end
+
+ expect(rendered).not_to have_content('something_old')
+ end
+ end
end