Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-07-19 21:08:23 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-07-19 21:08:23 +0300
commit3f0db3db2ad99a74c3969bf2e930814004ccf1ec (patch)
treec8b4123b3b4b422b14211430b85556e64eb68a26
parent4a721269429a178957e8ce7c6d0a75d3307c9830 (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--.gitlab/CODEOWNERS1
-rw-r--r--.gitlab/ci/frontend.gitlab-ci.yml9
-rw-r--r--app/assets/javascripts/admin/users/components/user_actions.vue117
-rw-r--r--app/assets/javascripts/admin/users/constants.js2
-rw-r--r--app/assets/javascripts/admin/users/index.js16
-rw-r--r--app/assets/javascripts/pages/admin/identities/index.js3
-rw-r--r--app/assets/javascripts/pages/admin/impersonation_tokens/index.js3
-rw-r--r--app/assets/javascripts/pages/admin/users/index.js3
-rw-r--r--app/assets/javascripts/pipelines/components/parsing_utils.js17
-rw-r--r--app/finders/members_finder.rb5
-rw-r--r--app/helpers/users_helper.rb127
-rw-r--r--app/models/members/group_member.rb2
-rw-r--r--app/services/groups/group_links/create_service.rb2
-rw-r--r--app/services/groups/group_links/destroy_service.rb2
-rw-r--r--app/services/groups/group_links/update_service.rb2
-rw-r--r--app/services/jira_import/users_mapper_service.rb2
-rw-r--r--app/views/admin/identities/index.html.haml2
-rw-r--r--app/views/admin/impersonation_tokens/index.html.haml2
-rw-r--r--app/views/admin/users/_approve_user.html.haml7
-rw-r--r--app/views/admin/users/_ban_user.html.haml9
-rw-r--r--app/views/admin/users/_block_user.html.haml8
-rw-r--r--app/views/admin/users/_head.html.haml66
-rw-r--r--app/views/admin/users/_reject_pending_user.html.haml7
-rw-r--r--app/views/admin/users/_user_activation_effects.html.haml6
-rw-r--r--app/views/admin/users/_user_approve_effects.html.haml11
-rw-r--r--app/views/admin/users/_user_detail_note.html.haml4
-rw-r--r--app/views/admin/users/_user_reject_effects.html.haml10
-rw-r--r--app/views/admin/users/keys.html.haml1
-rw-r--r--app/views/admin/users/projects.html.haml2
-rw-r--r--app/views/admin/users/show.html.haml118
-rw-r--r--config/initializers/active_record_build_select.rb6
-rw-r--r--db/migrate/20210707171554_create_vulnerability_flags.rb30
-rw-r--r--db/schema_migrations/202107071715541
-rw-r--r--db/structure.sql31
-rw-r--r--doc/administration/geo/replication/docker_registry.md5
-rw-r--r--doc/api/projects.md10
-rw-r--r--doc/development/code_review.md2
-rw-r--r--doc/development/fe_guide/style/html.md2
-rw-r--r--doc/development/fe_guide/style/scss.md2
-rw-r--r--doc/development/feature_flags/controls.md2
-rw-r--r--doc/development/feature_flags/index.md2
-rw-r--r--doc/development/foreign_keys.md2
-rw-r--r--doc/development/geo.md8
-rw-r--r--doc/development/git_object_deduplication.md2
-rw-r--r--doc/development/github_importer.md2
-rw-r--r--doc/development/go_guide/index.md6
-rw-r--r--doc/development/jh_features_review.md7
-rw-r--r--doc/development/logging.md6
-rw-r--r--doc/development/merge_request_performance_guidelines.md8
-rw-r--r--doc/development/migration_style_guide.md4
-rw-r--r--doc/development/module_with_instance_variables.md4
-rw-r--r--doc/development/multi_version_compatibility.md2
-rw-r--r--doc/development/omnibus.md2
-rw-r--r--doc/development/packages.md2
-rw-r--r--doc/development/performance.md2
-rw-r--r--doc/development/pipelines.md20
-rw-r--r--doc/development/policies.md4
-rw-r--r--doc/development/polymorphic_associations.md2
-rw-r--r--doc/development/pry_debugging.md2
-rw-r--r--doc/development/query_recorder.md2
-rw-r--r--doc/development/rails_initializers.md2
-rw-r--r--doc/development/reactive_caching.md2
-rw-r--r--doc/development/refactoring_guide/index.md2
-rw-r--r--doc/development/renaming_features.md2
-rw-r--r--doc/development/reusing_abstractions.md2
-rw-r--r--doc/development/scalability.md4
-rw-r--r--doc/development/secure_coding_guidelines.md6
-rw-r--r--doc/development/serializing_data.md6
-rw-r--r--doc/subscriptions/gitlab_com/index.md32
-rw-r--r--doc/user/admin_area/approving_users.md3
-rw-r--r--doc/user/admin_area/moderate_users.md30
-rw-r--r--doc/user/project/import/index.md4
-rw-r--r--doc/user/project/quick_actions.md2
-rw-r--r--doc/user/project/repository/branches/default.md23
-rw-r--r--lib/gitlab/ci/reports/security/scanner.rb2
-rw-r--r--locale/gitlab.pot94
-rw-r--r--qa/qa/page/admin/overview/users/show.rb19
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/login/register_spec.rb2
-rw-r--r--spec/controllers/groups/group_links_controller_spec.rb6
-rw-r--r--spec/features/admin/admin_mode/workers_spec.rb5
-rw-r--r--spec/features/admin/users/user_spec.rb46
-rw-r--r--spec/features/admin/users/users_spec.rb18
-rw-r--r--spec/frontend/admin/users/components/user_actions_spec.js45
-rw-r--r--spec/frontend/admin/users/index_spec.js36
-rw-r--r--spec/frontend/admin/users/mock_data.js2
-rw-r--r--spec/helpers/users_helper_spec.rb18
-rw-r--r--spec/lib/gitlab/ci/reports/security/scanner_spec.rb1
-rw-r--r--spec/services/groups/group_links/destroy_service_spec.rb6
-rw-r--r--spec/services/groups/group_links/update_service_spec.rb2
-rw-r--r--spec/support/helpers/features/admin_users_helpers.rb25
-rw-r--r--spec/workers/remove_expired_group_links_worker_spec.rb2
91 files changed, 577 insertions, 618 deletions
diff --git a/.gitlab/CODEOWNERS b/.gitlab/CODEOWNERS
index a34c1b354c2..c7da1c5bebc 100644
--- a/.gitlab/CODEOWNERS
+++ b/.gitlab/CODEOWNERS
@@ -236,6 +236,7 @@ Dangerfile @gl-quality/eng-prod
/ee/lib/gitlab/ci/reports/dependency_list/ @gitlab-org/secure/composition-analysis-be
/ee/lib/gitlab/ci/reports/license_scanning/ @gitlab-org/secure/composition-analysis-be
/ee/lib/gitlab/ci/reports/security/ @gitlab-org/secure/composition-analysis-be @gitlab-org/secure/dynamic-analysis-be @gitlab-org/secure/static-analysis-be @gitlab-org/secure/fuzzing-be
+/ee/app/services/ci/run_dast_scan_service.rb @gitlab-org/secure/dynamic-analysis-be
[Container Security]
/ee/app/views/projects/threat_monitoring/** @gitlab-org/protect/container-security-frontend
diff --git a/.gitlab/ci/frontend.gitlab-ci.yml b/.gitlab/ci/frontend.gitlab-ci.yml
index 1e60e6684cb..3aa8ab1591a 100644
--- a/.gitlab/ci/frontend.gitlab-ci.yml
+++ b/.gitlab/ci/frontend.gitlab-ci.yml
@@ -39,6 +39,15 @@ compile-production-assets:
- public/assets/
- webpack-report/
when: always
+ before_script:
+ - if [ -n "$CI_MERGE_REQUEST_SOURCE_BRANCH_SHA" ]; then
+ echo "Checking out \$CI_MERGE_REQUEST_SOURCE_BRANCH_SHA ($CI_MERGE_REQUEST_SOURCE_BRANCH_SHA) instead of \$CI_COMMIT_SHA (merge result commit $CI_COMMIT_SHA) so that GitLab assets image tag actually reflect the commit for which assets were compiled.";
+ git checkout -f ${CI_MERGE_REQUEST_SOURCE_BRANCH_SHA};
+ else
+ echo "Building the image from \$CI_COMMIT_SHA ($CI_COMMIT_SHA) for this non-merge result pipeline.";
+ fi;
+ - echo "See https://docs.gitlab.com/ee/development/testing_guide/end_to_end/index.html#with-pipeline-for-merged-results for more details.";
+ - !reference [.default-before_script, before_script]
after_script:
- rm -f /etc/apt/sources.list.d/google*.list # We don't need to update Chrome here
diff --git a/app/assets/javascripts/admin/users/components/user_actions.vue b/app/assets/javascripts/admin/users/components/user_actions.vue
index b782526e6be..c076e0bedf0 100644
--- a/app/assets/javascripts/admin/users/components/user_actions.vue
+++ b/app/assets/javascripts/admin/users/components/user_actions.vue
@@ -5,6 +5,7 @@ import {
GlDropdownItem,
GlDropdownSectionHeader,
GlDropdownDivider,
+ GlTooltipDirective,
} from '@gitlab/ui';
import { convertArrayToCamelCase } from '~/lib/utils/common_utils';
import { capitalizeFirstCharacter } from '~/lib/utils/text_utility';
@@ -21,6 +22,9 @@ export default {
GlDropdownDivider,
...Actions,
},
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
props: {
user: {
type: Object,
@@ -30,6 +34,11 @@ export default {
type: Object,
required: true,
},
+ showButtonLabels: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
computed: {
userActions() {
@@ -56,6 +65,13 @@ export default {
userPaths() {
return generateUserPaths(this.paths, this.user.username);
},
+ editButtonAttrs() {
+ return {
+ 'data-testid': 'edit',
+ icon: 'pencil-square',
+ href: this.userPaths.edit,
+ };
+ },
},
methods: {
isLdapAction(action) {
@@ -70,51 +86,68 @@ export default {
</script>
<template>
- <div class="gl-display-flex gl-justify-content-end" :data-testid="`user-actions-${user.id}`">
- <gl-button v-if="hasEditAction" data-testid="edit" :href="userPaths.edit">{{
- $options.i18n.edit
- }}</gl-button>
+ <div
+ class="gl-display-flex gl-justify-content-end gl-my-n2 gl-mx-n2"
+ :data-testid="`user-actions-${user.id}`"
+ >
+ <div v-if="hasEditAction" class="gl-p-2">
+ <gl-button v-if="showButtonLabels" v-bind="editButtonAttrs">{{
+ $options.i18n.edit
+ }}</gl-button>
+ <gl-button
+ v-else
+ v-gl-tooltip="$options.i18n.edit"
+ v-bind="editButtonAttrs"
+ :aria-label="$options.i18n.edit"
+ />
+ </div>
- <gl-dropdown
- v-if="hasDropdownActions"
- data-testid="dropdown-toggle"
- right
- class="gl-ml-2"
- icon="settings"
- >
- <gl-dropdown-section-header>{{ $options.i18n.settings }}</gl-dropdown-section-header>
+ <div v-if="hasDropdownActions" class="gl-p-2">
+ <gl-dropdown
+ data-testid="dropdown-toggle"
+ right
+ :text="$options.i18n.userAdministration"
+ :text-sr-only="!showButtonLabels"
+ icon="settings"
+ data-qa-selector="user_actions_dropdown_toggle"
+ :data-qa-username="user.username"
+ >
+ <gl-dropdown-section-header>{{
+ $options.i18n.userAdministration
+ }}</gl-dropdown-section-header>
- <template v-for="action in dropdownSafeActions">
- <component
- :is="getActionComponent(action)"
- v-if="getActionComponent(action)"
- :key="action"
- :path="userPaths[action]"
- :username="user.name"
- :data-testid="action"
- >
- {{ $options.i18n[action] }}
- </component>
- <gl-dropdown-item v-else-if="isLdapAction(action)" :key="action" :data-testid="action">
- {{ $options.i18n[action] }}
- </gl-dropdown-item>
- </template>
+ <template v-for="action in dropdownSafeActions">
+ <component
+ :is="getActionComponent(action)"
+ v-if="getActionComponent(action)"
+ :key="action"
+ :path="userPaths[action]"
+ :username="user.name"
+ :data-testid="action"
+ >
+ {{ $options.i18n[action] }}
+ </component>
+ <gl-dropdown-item v-else-if="isLdapAction(action)" :key="action" :data-testid="action">
+ {{ $options.i18n[action] }}
+ </gl-dropdown-item>
+ </template>
- <gl-dropdown-divider v-if="hasDeleteActions" />
+ <gl-dropdown-divider v-if="hasDeleteActions" />
- <template v-for="action in dropdownDeleteActions">
- <component
- :is="getActionComponent(action)"
- v-if="getActionComponent(action)"
- :key="action"
- :paths="userPaths"
- :username="user.name"
- :oncall-schedules="user.oncallSchedules"
- :data-testid="`delete-${action}`"
- >
- {{ $options.i18n[action] }}
- </component>
- </template>
- </gl-dropdown>
+ <template v-for="action in dropdownDeleteActions">
+ <component
+ :is="getActionComponent(action)"
+ v-if="getActionComponent(action)"
+ :key="action"
+ :paths="userPaths"
+ :username="user.name"
+ :oncall-schedules="user.oncallSchedules"
+ :data-testid="`delete-${action}`"
+ >
+ {{ $options.i18n[action] }}
+ </component>
+ </template>
+ </gl-dropdown>
+ </div>
</div>
</template>
diff --git a/app/assets/javascripts/admin/users/constants.js b/app/assets/javascripts/admin/users/constants.js
index 33ee7e1cb0d..4636c8705a5 100644
--- a/app/assets/javascripts/admin/users/constants.js
+++ b/app/assets/javascripts/admin/users/constants.js
@@ -6,7 +6,7 @@ export const LENGTH_OF_USER_NOTE_TOOLTIP = 100;
export const I18N_USER_ACTIONS = {
edit: __('Edit'),
- settings: __('Settings'),
+ userAdministration: s__('AdminUsers|User administration'),
unlock: __('Unlock'),
block: s__('AdminUsers|Block'),
unblock: s__('AdminUsers|Unblock'),
diff --git a/app/assets/javascripts/admin/users/index.js b/app/assets/javascripts/admin/users/index.js
index 05f8469e61a..852b253d25a 100644
--- a/app/assets/javascripts/admin/users/index.js
+++ b/app/assets/javascripts/admin/users/index.js
@@ -5,6 +5,7 @@ import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import csrf from '~/lib/utils/csrf';
import AdminUsersApp from './components/app.vue';
import ModalManager from './components/modals/user_modal_manager.vue';
+import UserActions from './components/user_actions.vue';
import {
CONFIRM_DELETE_BUTTON_SELECTOR,
MODAL_TEXTS_CONTAINER_SELECTOR,
@@ -17,26 +18,33 @@ const apolloProvider = new VueApollo({
defaultClient: createDefaultClient({}, { assumeImmutableResults: true }),
});
-export const initAdminUsersApp = (el = document.querySelector('#js-admin-users-app')) => {
+const initApp = (el, component, userPropKey, props = {}) => {
if (!el) {
return false;
}
- const { users, paths } = el.dataset;
+ const { [userPropKey]: user, paths } = el.dataset;
return new Vue({
el,
apolloProvider,
render: (createElement) =>
- createElement(AdminUsersApp, {
+ createElement(component, {
props: {
- users: convertObjectPropsToCamelCase(JSON.parse(users), { deep: true }),
+ [userPropKey]: convertObjectPropsToCamelCase(JSON.parse(user), { deep: true }),
paths: convertObjectPropsToCamelCase(JSON.parse(paths)),
+ ...props,
},
}),
});
};
+export const initAdminUsersApp = (el = document.querySelector('#js-admin-users-app')) =>
+ initApp(el, AdminUsersApp, 'users');
+
+export const initAdminUserActions = (el = document.querySelector('#js-admin-user-actions')) =>
+ initApp(el, UserActions, 'user', { showButtonLabels: true });
+
export const initDeleteUserModals = () => {
const modalsMountElement = document.querySelector(MODAL_TEXTS_CONTAINER_SELECTOR);
diff --git a/app/assets/javascripts/pages/admin/identities/index.js b/app/assets/javascripts/pages/admin/identities/index.js
index 868c8e33077..a9f5f00cb9b 100644
--- a/app/assets/javascripts/pages/admin/identities/index.js
+++ b/app/assets/javascripts/pages/admin/identities/index.js
@@ -1,3 +1,6 @@
+import { initAdminUserActions, initDeleteUserModals } from '~/admin/users';
import initConfirmModal from '~/confirm_modal';
+initAdminUserActions();
+initDeleteUserModals();
initConfirmModal();
diff --git a/app/assets/javascripts/pages/admin/impersonation_tokens/index.js b/app/assets/javascripts/pages/admin/impersonation_tokens/index.js
index 3f9e4e9d591..8fbc8dc17bc 100644
--- a/app/assets/javascripts/pages/admin/impersonation_tokens/index.js
+++ b/app/assets/javascripts/pages/admin/impersonation_tokens/index.js
@@ -1,5 +1,8 @@
import { initExpiresAtField } from '~/access_tokens';
+import { initAdminUserActions, initDeleteUserModals } from '~/admin/users';
import initConfirmModal from '~/confirm_modal';
+initAdminUserActions();
+initDeleteUserModals();
initExpiresAtField();
initConfirmModal();
diff --git a/app/assets/javascripts/pages/admin/users/index.js b/app/assets/javascripts/pages/admin/users/index.js
index 99bf842ef2d..41e99a3baf5 100644
--- a/app/assets/javascripts/pages/admin/users/index.js
+++ b/app/assets/javascripts/pages/admin/users/index.js
@@ -1,6 +1,7 @@
-import { initAdminUsersApp, initDeleteUserModals } from '~/admin/users';
+import { initAdminUsersApp, initDeleteUserModals, initAdminUserActions } from '~/admin/users';
import initConfirmModal from '~/confirm_modal';
initAdminUsersApp();
+initAdminUserActions();
initDeleteUserModals();
initConfirmModal();
diff --git a/app/assets/javascripts/pipelines/components/parsing_utils.js b/app/assets/javascripts/pipelines/components/parsing_utils.js
index f1d9ced807b..b36c9c0d049 100644
--- a/app/assets/javascripts/pipelines/components/parsing_utils.js
+++ b/app/assets/javascripts/pipelines/components/parsing_utils.js
@@ -1,4 +1,4 @@
-import { isEqual, memoize, uniqWith } from 'lodash';
+import { memoize } from 'lodash';
import { createSankey } from './dag/drawing_utils';
/*
@@ -113,11 +113,24 @@ export const filterByAncestors = (links, nodeDict) =>
return !allAncestors.includes(source);
});
+/*
+ A peformant alternative to lodash's isEqual. Because findIndex always finds
+ the first instance of a match, if the found index is not the first, we know
+ it is in fact a duplicate.
+*/
+const deduplicate = (item, itemIndex, arr) => {
+ const foundIdx = arr.findIndex((test) => {
+ return test.source === item.source && test.target === item.target;
+ });
+
+ return foundIdx === itemIndex;
+};
+
export const parseData = (nodes) => {
const nodeDict = createNodeDict(nodes);
const allLinks = makeLinksFromNodes(nodes, nodeDict);
const filteredLinks = filterByAncestors(allLinks, nodeDict);
- const links = uniqWith(filteredLinks, isEqual);
+ const links = filteredLinks.filter(deduplicate);
return { nodes, links };
};
diff --git a/app/finders/members_finder.rb b/app/finders/members_finder.rb
index 1ff2ad01b63..ea101cf1dcd 100644
--- a/app/finders/members_finder.rb
+++ b/app/finders/members_finder.rb
@@ -83,7 +83,10 @@ class MembersFinder
union = Gitlab::SQL::Union.new(union_members, remove_duplicates: false) # rubocop: disable Gitlab/Union
sql = distinct_on(union)
- Member.includes(:user).from([Arel.sql("(#{sql}) AS #{Member.table_name}")]) # rubocop: disable CodeReuse/ActiveRecord
+ # enumerate the columns here since we are enumerating them in the union and want to be immune to
+ # column caching issues when adding/removing columns
+ Member.select(*Member.column_names)
+ .includes(:user).from([Arel.sql("(#{sql}) AS #{Member.table_name}")]) # rubocop: disable CodeReuse/ActiveRecord
end
def distinct_on(union)
diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb
index 2af932f8e8c..93a0166f43e 100644
--- a/app/helpers/users_helper.rb
+++ b/app/helpers/users_helper.rb
@@ -123,114 +123,10 @@ module UsersHelper
!user.confirmed?
end
- def user_block_data(user, message)
- {
- path: block_admin_user_path(user),
- method: 'put',
- modal_attributes: {
- title: s_('AdminUsers|Block user %{username}?') % { username: sanitize_name(user.name) },
- messageHtml: message,
- okVariant: 'warning',
- okTitle: s_('AdminUsers|Block')
- }.to_json
- }
- end
-
- def user_unblock_data(user)
- {
- path: unblock_admin_user_path(user),
- method: 'put',
- modal_attributes: {
- title: s_('AdminUsers|Unblock user %{username}?') % { username: sanitize_name(user.name) },
- message: s_('AdminUsers|You can always block their account again if needed.'),
- okVariant: 'info',
- okTitle: s_('AdminUsers|Unblock')
- }.to_json
- }
- end
-
- def user_block_effects
- header = tag.p s_('AdminUsers|Blocking user has the following effects:')
-
- list = tag.ul do
- concat tag.li s_('AdminUsers|User will not be able to login')
- concat tag.li s_('AdminUsers|User will not be able to access git repositories')
- concat tag.li s_('AdminUsers|Personal projects will be left')
- concat tag.li s_('AdminUsers|Owned groups will be left')
- end
-
- header + list
- end
-
- def user_ban_data(user)
- {
- path: ban_admin_user_path(user),
- method: 'put',
- modal_attributes: {
- title: s_('AdminUsers|Ban user %{username}?') % { username: sanitize_name(user.name) },
- message: s_('AdminUsers|You can unban their account in the future. Their data remains intact.'),
- okVariant: 'warning',
- okTitle: s_('AdminUsers|Ban')
- }.to_json
- }
- end
-
- def user_unban_data(user)
- {
- path: unban_admin_user_path(user),
- method: 'put',
- modal_attributes: {
- title: s_('AdminUsers|Unban %{username}?') % { username: sanitize_name(user.name) },
- message: s_('AdminUsers|You ban their account in the future if necessary.'),
- okVariant: 'info',
- okTitle: s_('AdminUsers|Unban')
- }.to_json
- }
- end
-
- def user_ban_effects
- header = tag.p s_('AdminUsers|Banning the user has the following effects:')
-
- list = tag.ul do
- concat tag.li s_('AdminUsers|User will be blocked')
- end
-
- link_start = '<a href="%{url}" target="_blank">'.html_safe % { url: help_page_path("user/admin_area/moderate_users", anchor: "ban-a-user") }
- info = tag.p s_('AdminUsers|Learn more about %{link_start}banned users.%{link_end}').html_safe % { link_start: link_start, link_end: '</a>'.html_safe }
-
- header + list + info
- end
-
def ban_feature_available?
Feature.enabled?(:ban_user_feature_flag)
end
- def user_deactivation_data(user, message)
- {
- path: deactivate_admin_user_path(user),
- method: 'put',
- modal_attributes: {
- title: s_('AdminUsers|Deactivate user %{username}?') % { username: sanitize_name(user.name) },
- messageHtml: message,
- okVariant: 'warning',
- okTitle: s_('AdminUsers|Deactivate')
- }.to_json
- }
- end
-
- def user_activation_data(user)
- {
- path: activate_admin_user_path(user),
- method: 'put',
- modal_attributes: {
- title: s_('AdminUsers|Activate user %{username}?') % { username: sanitize_name(user.name) },
- message: s_('AdminUsers|You can always deactivate their account again if needed.'),
- okVariant: 'info',
- okTitle: s_('AdminUsers|Activate')
- }.to_json
- }
- end
-
def confirm_user_data(user)
message = if user.unconfirmed_email.present?
_('This user has an unconfirmed email address (%{email}). You may force a confirmation.') % { email: user.unconfirmed_email }
@@ -259,22 +155,6 @@ module UsersHelper
}
end
- def user_deactivation_effects
- header = tag.p s_('AdminUsers|Deactivating a user has the following effects:')
-
- list = tag.ul do
- concat tag.li s_('AdminUsers|The user will be logged out')
- concat tag.li s_('AdminUsers|The user will not be able to access git repositories')
- concat tag.li s_('AdminUsers|The user will not be able to access the API')
- concat tag.li s_('AdminUsers|The user will not receive any notifications')
- concat tag.li s_('AdminUsers|The user will not be able to use slash commands')
- concat tag.li s_('AdminUsers|When the user logs back in, their account will reactivate as a fully active account')
- concat tag.li s_('AdminUsers|Personal projects, group and user history will be left intact')
- end
-
- header + list
- end
-
def user_display_name(user)
return s_('UserProfile|Blocked user') if user.blocked?
@@ -284,6 +164,13 @@ module UsersHelper
user.name
end
+ def admin_user_actions_data_attributes(user)
+ {
+ user: Admin::UserEntity.represent(user, { current_user: current_user }).to_json,
+ paths: admin_users_paths.to_json
+ }
+ end
+
private
def admin_users_paths
diff --git a/app/models/members/group_member.rb b/app/models/members/group_member.rb
index ab044b80133..cf5906a4cbf 100644
--- a/app/models/members/group_member.rb
+++ b/app/models/members/group_member.rb
@@ -28,6 +28,8 @@ class GroupMember < Member
attr_accessor :last_owner, :last_blocked_owner
+ self.enumerate_columns_in_select_statements = true
+
def self.access_level_roles
Gitlab::Access.options_with_owner
end
diff --git a/app/services/groups/group_links/create_service.rb b/app/services/groups/group_links/create_service.rb
index 5f81e5972b0..8c3ba0a63f2 100644
--- a/app/services/groups/group_links/create_service.rb
+++ b/app/services/groups/group_links/create_service.rb
@@ -24,7 +24,7 @@ module Groups
)
if link.save
- shared_with_group.refresh_members_authorized_projects(direct_members_only: true)
+ shared_with_group.refresh_members_authorized_projects(blocking: false, direct_members_only: true)
success(link: link)
else
error(link.errors.full_messages.to_sentence, 409)
diff --git a/app/services/groups/group_links/destroy_service.rb b/app/services/groups/group_links/destroy_service.rb
index 05504a80f46..0e7fd7e0817 100644
--- a/app/services/groups/group_links/destroy_service.rb
+++ b/app/services/groups/group_links/destroy_service.rb
@@ -16,7 +16,7 @@ module Groups
groups_to_refresh = links.map(&:shared_with_group)
groups_to_refresh.uniq.each do |group|
- group.refresh_members_authorized_projects(direct_members_only: true)
+ group.refresh_members_authorized_projects(blocking: false, direct_members_only: true)
end
else
Gitlab::AppLogger.info(
diff --git a/app/services/groups/group_links/update_service.rb b/app/services/groups/group_links/update_service.rb
index 3703d535482..a1411de36d6 100644
--- a/app/services/groups/group_links/update_service.rb
+++ b/app/services/groups/group_links/update_service.rb
@@ -13,7 +13,7 @@ module Groups
group_link.update!(group_link_params)
if requires_authorization_refresh?(group_link_params)
- group_link.shared_with_group.refresh_members_authorized_projects(direct_members_only: true)
+ group_link.shared_with_group.refresh_members_authorized_projects(blocking: false, direct_members_only: true)
end
end
diff --git a/app/services/jira_import/users_mapper_service.rb b/app/services/jira_import/users_mapper_service.rb
index 760f06a1cfb..13e0dd5120e 100644
--- a/app/services/jira_import/users_mapper_service.rb
+++ b/app/services/jira_import/users_mapper_service.rb
@@ -77,7 +77,7 @@ module JiraImport
end
def project_member_ids
- @project_member_ids ||= MembersFinder.new(project, current_user).execute.select(:user_id)
+ @project_member_ids ||= MembersFinder.new(project, current_user).execute.reselect(:user_id)
end
end
end
diff --git a/app/views/admin/identities/index.html.haml b/app/views/admin/identities/index.html.haml
index a6d562dad31..d85ab476693 100644
--- a/app/views/admin/identities/index.html.haml
+++ b/app/views/admin/identities/index.html.haml
@@ -15,3 +15,5 @@
= render @identities
- else
%h4= _('This user has no identities')
+
+= render partial: 'admin/users/modals'
diff --git a/app/views/admin/impersonation_tokens/index.html.haml b/app/views/admin/impersonation_tokens/index.html.haml
index 465025ba684..1609687fc8d 100644
--- a/app/views/admin/impersonation_tokens/index.html.haml
+++ b/app/views/admin/impersonation_tokens/index.html.haml
@@ -28,3 +28,5 @@
impersonation: true,
active_tokens: @active_impersonation_tokens,
revoke_route_helper: ->(token) { revoke_admin_user_impersonation_token_path(token.user, token) }
+
+= render partial: 'admin/users/modals'
diff --git a/app/views/admin/users/_approve_user.html.haml b/app/views/admin/users/_approve_user.html.haml
deleted file mode 100644
index f61c9fa4b80..00000000000
--- a/app/views/admin/users/_approve_user.html.haml
+++ /dev/null
@@ -1,7 +0,0 @@
-.card.border-info
- .card-header.gl-bg-blue-500.gl-text-white
- = s_('AdminUsers|This user has requested access')
- .card-body
- = render partial: 'admin/users/user_approve_effects'
- %br
- = link_to s_('AdminUsers|Approve user'), approve_admin_user_path(user), method: :put, class: "btn gl-button btn-info", data: { confirm: s_('AdminUsers|Are you sure?'), qa_selector: 'approve_user_button' }
diff --git a/app/views/admin/users/_ban_user.html.haml b/app/views/admin/users/_ban_user.html.haml
deleted file mode 100644
index 229c88adb7f..00000000000
--- a/app/views/admin/users/_ban_user.html.haml
+++ /dev/null
@@ -1,9 +0,0 @@
-- if ban_feature_available?
- .card.border-warning
- .card-header.bg-warning.gl-text-white
- = s_('AdminUsers|Ban user')
- .card-body
- = user_ban_effects
- %br
- %button.btn.gl-button.btn-warning.js-confirm-modal-button{ data: user_ban_data(user) }
- = s_('AdminUsers|Ban user')
diff --git a/app/views/admin/users/_block_user.html.haml b/app/views/admin/users/_block_user.html.haml
deleted file mode 100644
index 29029986345..00000000000
--- a/app/views/admin/users/_block_user.html.haml
+++ /dev/null
@@ -1,8 +0,0 @@
-.card.border-warning
- .card-header.bg-warning.text-white
- = s_('AdminUsers|Block this user')
- .card-body
- = user_block_effects
- %br
- %button.btn.gl-button.btn-warning.js-confirm-modal-button{ data: user_block_data(user, s_('AdminUsers|You can always unblock their account, their data will remain intact.')) }
- = s_('AdminUsers|Block user')
diff --git a/app/views/admin/users/_head.html.haml b/app/views/admin/users/_head.html.haml
index 9ef2d7b2f22..b7b712e078d 100644
--- a/app/views/admin/users/_head.html.haml
+++ b/app/views/admin/users/_head.html.haml
@@ -1,38 +1,38 @@
-%h3.page-title
- = @user.name
- - if @user.blocked_pending_approval?
- %span.cred
- = s_('AdminUsers|(Pending approval)')
- - elsif @user.banned?
- %span.cred
- = s_('AdminUsers|(Banned)')
- - elsif @user.blocked?
- %span.cred
- = s_('AdminUsers|(Blocked)')
- - if @user.internal?
- %span.cred
- = s_('AdminUsers|(Internal)')
- - if @user.admin
- %span.cred
- = s_('AdminUsers|(Admin)')
- - if @user.deactivated?
- %span.cred
- = s_('AdminUsers|(Deactivated)')
- = render_if_exists 'admin/users/auditor_user_badge'
- = render_if_exists 'admin/users/gma_user_badge'
+.gl-display-flex.gl-flex-wrap.gl-justify-content-space-between.gl-align-items-center.gl-py-3.gl-mb-5.gl-border-b-solid.gl-border-gray-100.gl-border-b-1
+ .gl-my-3
+ %h3.page-title.gl-m-0
+ = @user.name
+ - if @user.blocked_pending_approval?
+ %span.cred
+ = s_('AdminUsers|(Pending approval)')
+ - elsif @user.banned?
+ %span.cred
+ = s_('AdminUsers|(Banned)')
+ - elsif @user.blocked?
+ %span.cred
+ = s_('AdminUsers|(Blocked)')
+ - if @user.internal?
+ %span.cred
+ = s_('AdminUsers|(Internal)')
+ - if @user.admin
+ %span.cred
+ = s_('AdminUsers|(Admin)')
+ - if @user.deactivated?
+ %span.cred
+ = s_('AdminUsers|(Deactivated)')
+ = render_if_exists 'admin/users/auditor_user_badge'
+ = render_if_exists 'admin/users/gma_user_badge'
- .float-right
- = link_to edit_admin_user_path(@user), class: "btn btn-default gl-button btn-grouped" do
- = sprite_icon('pencil-square', css_class: 'gl-icon gl-button-icon')
- = _('Edit')
+ .gl-my-3.gl-display-flex.gl-flex-wrap.gl-my-n2.gl-mx-n2
+ .gl-p-2
+ #js-admin-user-actions{ data: admin_user_actions_data_attributes(@user) }
- if @user != current_user
- - if impersonation_enabled? && @user.can?(:log_in)
- = link_to _('Impersonate'), impersonate_admin_user_path(@user), method: :post, class: "btn btn-default gl-button btn-grouped", data: { qa_selector: 'impersonate_user_link' }
- - if can_force_email_confirmation?(@user)
- %button.btn.gl-button.btn-info.btn-grouped.js-confirm-modal-button{ data: confirm_user_data(@user) }
- = _('Confirm user')
-
-%hr
+ .gl-p-2
+ - if impersonation_enabled? && @user.can?(:log_in)
+ = link_to _('Impersonate'), impersonate_admin_user_path(@user), method: :post, class: "btn btn-default gl-button", data: { qa_selector: 'impersonate_user_link' }
+ - if can_force_email_confirmation?(@user)
+ %button.btn.gl-button.btn-info.js-confirm-modal-button{ data: confirm_user_data(@user) }
+ = _('Confirm user')
%ul.nav-links.nav.nav-tabs
= nav_link(path: 'users#show') do
= link_to _("Account"), admin_user_path(@user)
diff --git a/app/views/admin/users/_reject_pending_user.html.haml b/app/views/admin/users/_reject_pending_user.html.haml
deleted file mode 100644
index 17108427330..00000000000
--- a/app/views/admin/users/_reject_pending_user.html.haml
+++ /dev/null
@@ -1,7 +0,0 @@
-.card.border-danger
- .card-header.bg-danger.gl-text-white
- = s_('AdminUsers|This user has requested access')
- .card-body
- = render partial: 'admin/users/user_reject_effects'
- %br
- = link_to s_('AdminUsers|Reject request'), reject_admin_user_path(user), method: :delete, class: "btn gl-button btn-danger", data: { confirm: s_('AdminUsers|Are you sure?') }
diff --git a/app/views/admin/users/_user_activation_effects.html.haml b/app/views/admin/users/_user_activation_effects.html.haml
deleted file mode 100644
index 244836dac11..00000000000
--- a/app/views/admin/users/_user_activation_effects.html.haml
+++ /dev/null
@@ -1,6 +0,0 @@
-%p
- = s_('AdminUsers|Reactivating a user will:')
-%ul
- %li
- = s_('AdminUsers|Restore user access to the account, including web, Git and API.')
- = render_if_exists 'admin/users/user_activation_effects_on_seats'
diff --git a/app/views/admin/users/_user_approve_effects.html.haml b/app/views/admin/users/_user_approve_effects.html.haml
deleted file mode 100644
index 54e51bf3467..00000000000
--- a/app/views/admin/users/_user_approve_effects.html.haml
+++ /dev/null
@@ -1,11 +0,0 @@
-%p
- = s_('AdminUsers|Approved users can:')
-%ul
- %li
- = s_('AdminUsers|Log in')
- %li
- = s_('AdminUsers|Access Git repositories')
- %li
- = s_('AdminUsers|Access the API')
- %li
- = s_('AdminUsers|Be added to groups and projects')
diff --git a/app/views/admin/users/_user_detail_note.html.haml b/app/views/admin/users/_user_detail_note.html.haml
index 4f2a682c5ca..cc4827327c9 100644
--- a/app/views/admin/users/_user_detail_note.html.haml
+++ b/app/views/admin/users/_user_detail_note.html.haml
@@ -1,7 +1,7 @@
- if @user.note.present?
- text = @user.note
- .card.border-info
- .card-header.bg-info.text-white
+ .card
+ .card-header
= _('Admin Note')
.card-body
%p= text
diff --git a/app/views/admin/users/_user_reject_effects.html.haml b/app/views/admin/users/_user_reject_effects.html.haml
deleted file mode 100644
index 17b6862b0cc..00000000000
--- a/app/views/admin/users/_user_reject_effects.html.haml
+++ /dev/null
@@ -1,10 +0,0 @@
-%p
- = s_('AdminUsers|Rejected users:')
-%ul
- %li
- = s_('AdminUsers|Cannot sign in or access instance information')
- %li
- = s_('AdminUsers|Will be deleted')
-%p
- - link_start = '<a href="%{url}">'.html_safe % { url: help_page_path("user/profile/account/delete_account", anchor: "associated-records") }
- = s_('AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}').html_safe % { link_start: link_start, link_end: '</a>'.html_safe }
diff --git a/app/views/admin/users/keys.html.haml b/app/views/admin/users/keys.html.haml
index 5f9d11af7c1..28024ae084f 100644
--- a/app/views/admin/users/keys.html.haml
+++ b/app/views/admin/users/keys.html.haml
@@ -3,3 +3,4 @@
- page_title _("SSH Keys"), @user.name, _("Users")
= render 'admin/users/head'
= render 'profiles/keys/key_table', admin: true
+= render partial: 'admin/users/modals'
diff --git a/app/views/admin/users/projects.html.haml b/app/views/admin/users/projects.html.haml
index 3ff726e1945..8c56e888dcc 100644
--- a/app/views/admin/users/projects.html.haml
+++ b/app/views/admin/users/projects.html.haml
@@ -48,3 +48,5 @@
- if member.respond_to? :project
= link_to project_project_member_path(project, member), data: { confirm: remove_member_message(member) }, remote: true, method: :delete, class: "btn btn-sm btn-danger gl-button btn-icon gl-ml-3", title: _('Remove user from project') do
= sprite_icon('close', size: 16, css_class: 'gl-icon')
+
+= render partial: 'admin/users/modals'
diff --git a/app/views/admin/users/show.html.haml b/app/views/admin/users/show.html.haml
index 5477ece7439..ad8d9d1f04f 100644
--- a/app/views/admin/users/show.html.haml
+++ b/app/views/admin/users/show.html.haml
@@ -16,8 +16,10 @@
%strong
= link_to user_path(@user) do
= @user.username
- = render 'admin/users/profile', user: @user
-
+ -# Rendered on mobile only so order of cards can be different on desktop vs mobile
+ .gl-md-display-none
+ = render 'admin/users/profile', user: @user
+ = render 'admin/users/user_detail_note'
.card
.card-header
= _('Account:')
@@ -139,112 +141,8 @@
= render 'shared/custom_attributes', custom_attributes: @user.custom_attributes
- .col-md-6
- - unless @user == current_user
- = render 'admin/users/user_detail_note'
-
- - unless @user.internal?
- - if @user.deactivated?
- .gl-card.border-info.gl-mb-5
- .gl-card-header.bg-info.text-white
- = _('Reactivate this user')
- .gl-card-body
- = render partial: 'admin/users/user_activation_effects'
- %br
- %button.btn.gl-button.btn-info.js-confirm-modal-button{ data: user_activation_data(@user) }
- = s_('AdminUsers|Activate user')
- - elsif @user.can_be_deactivated?
- .gl-card.border-warning.gl-mb-5
- .gl-card-header.bg-warning.text-white
- = _('Deactivate this user')
- .gl-card-body
- = user_deactivation_effects
- %br
- %button.btn.gl-button.btn-warning.js-confirm-modal-button{ data: user_deactivation_data(@user, s_('AdminUsers|You can always re-activate their account, their data will remain intact.')) }
- = s_('AdminUsers|Deactivate user')
- - if @user.blocked?
- - if @user.blocked_pending_approval?
- = render 'admin/users/approve_user', user: @user
- = render 'admin/users/reject_pending_user', user: @user
- - elsif @user.banned?
- .gl-card.border-info.gl-mb-5
- .gl-card-header.gl-bg-blue-500.gl-text-white
- = _('This user is banned')
- .gl-card-body
- %p= _('A banned user cannot:')
- %ul
- %li= _('Log in')
- %li= _('Access Git repositories')
- - link_start = '<a href="%{url}" target="_blank">'.html_safe % { url: help_page_path("user/admin_area/moderate_users", anchor: "ban-a-user") }
- = s_('AdminUsers|Learn more about %{link_start}banned users.%{link_end}').html_safe % { link_start: link_start, link_end: '</a>'.html_safe }
- %p
- %button.btn.gl-button.btn-info.js-confirm-modal-button{ data: user_unban_data(@user) }
- = s_('AdminUsers|Unban user')
- - else
- .gl-card.border-info.gl-mb-5
- .gl-card-header.gl-bg-blue-500.gl-text-white
- = _('This user is blocked')
- .gl-card-body
- %p= _('A blocked user cannot:')
- %ul
- %li= _('Log in')
- %li= _('Access Git repositories')
- %br
- %button.btn.gl-button.btn-info.js-confirm-modal-button{ data: user_unblock_data(@user) }
- = s_('AdminUsers|Unblock user')
- - elsif !@user.internal?
- = render 'admin/users/block_user', user: @user
- = render 'admin/users/ban_user', user: @user
-
- - if @user.access_locked?
- .card.border-info.gl-mb-5
- .card-header.bg-info.text-white
- = _('This account has been locked')
- .card-body
- %p= _('This user has been temporarily locked due to excessive number of failed logins. You may manually unlock the account.')
- %br
- = link_to _('Unlock user'), unlock_admin_user_path(@user), method: :put, class: "btn gl-button btn-info", data: { confirm: _('Are you sure?') }
- - if !@user.blocked_pending_approval?
- .gl-card.border-danger.gl-mb-5
- .gl-card-header.bg-danger.text-white
- = s_('AdminUsers|Delete user')
- .gl-card-body
- - if @user.can_be_removed? && can?(current_user, :destroy_user, @user)
- %p= _('Deleting a user has the following effects:')
- = render 'users/deletion_guidance', user: @user
- %br
- %button.js-delete-user-modal-button.btn.gl-button.btn-danger{ data: { 'gl-modal-action': 'delete',
- delete_user_url: admin_user_path(@user),
- block_user_url: block_admin_user_path(@user),
- username: sanitize_name(@user.name) } }
- = s_('AdminUsers|Delete user')
- - else
- - if @user.solo_owned_groups.present?
- %p
- = _('This user is currently an owner in these groups:')
- %strong= @user.solo_owned_groups.map(&:name).join(', ')
- %p
- = _('You must transfer ownership or delete these groups before you can delete this user.')
- - else
- %p
- = _("You don't have access to delete this user.")
-
- .gl-card.border-danger
- .gl-card-header.bg-danger.text-white
- = s_('AdminUsers|Delete user and contributions')
- .gl-card-body
- - if can?(current_user, :destroy_user, @user)
- %p
- - link_to_ghost_user = link_to(_("system ghost user"), help_page_path("user/profile/account/delete_account"))
- = _("This option deletes the user and any contributions that would usually be moved to the %{link_to_ghost_user}. As well as the user's personal projects, groups owned solely by the user, and projects in them, will also be removed. Commits to other projects are unaffected.").html_safe % { link_to_ghost_user: link_to_ghost_user }
- %br
- %button.js-delete-user-modal-button.btn.gl-button.btn-danger{ data: { 'gl-modal-action': 'delete-with-contributions',
- delete_user_url: admin_user_path(@user, hard_delete: true),
- block_user_url: block_admin_user_path(@user),
- username: @user.name } }
- = s_('AdminUsers|Delete user and contributions')
- - else
- %p
- = _("You don't have access to delete this user.")
-
+ -# Rendered on desktop only so order of cards can be different on desktop vs mobile
+ .col-md-6.gl-display-none.gl-md-display-block
+ = render 'admin/users/profile', user: @user
+ = render 'admin/users/user_detail_note'
= render partial: 'admin/users/modals'
diff --git a/config/initializers/active_record_build_select.rb b/config/initializers/active_record_build_select.rb
index ab5a872cac6..48f1b1ee407 100644
--- a/config/initializers/active_record_build_select.rb
+++ b/config/initializers/active_record_build_select.rb
@@ -9,6 +9,10 @@
# statement cache. If a different migration is then run and one of these columns is
# removed in the meantime, the query is invalid.
+ActiveRecord::Base.class_eval do
+ class_attribute :enumerate_columns_in_select_statements
+end
+
module ActiveRecord
module QueryMethods
private
@@ -16,6 +20,8 @@ module ActiveRecord
def build_select(arel)
if select_values.any?
arel.project(*arel_columns(select_values.uniq))
+ elsif klass.enumerate_columns_in_select_statements
+ arel.project(*klass.column_names.map { |field| table[field] })
else
arel.project(@klass.arel_table[Arel.star])
end
diff --git a/db/migrate/20210707171554_create_vulnerability_flags.rb b/db/migrate/20210707171554_create_vulnerability_flags.rb
new file mode 100644
index 00000000000..bf33963b08f
--- /dev/null
+++ b/db/migrate/20210707171554_create_vulnerability_flags.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+class CreateVulnerabilityFlags < ActiveRecord::Migration[6.1]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+ FALSE_POSITIVE_ENUM_VALUE = 0
+
+ disable_ddl_transaction!
+
+ def up
+ create_table_with_constraints :vulnerability_flags do |t|
+ t.timestamps_with_timezone null: false
+
+ t.references :vulnerability_occurrence, null: false, foreign_key: { on_delete: :cascade }
+
+ t.integer :flag_type, limit: 2, null: false, default: FALSE_POSITIVE_ENUM_VALUE
+
+ t.text :origin, null: false
+ t.text :description, null: false
+
+ t.text_limit :origin, 255
+ t.text_limit :description, 1024
+ end
+ end
+
+ def down
+ drop_table :vulnerability_flags
+ end
+end
diff --git a/db/schema_migrations/20210707171554 b/db/schema_migrations/20210707171554
new file mode 100644
index 00000000000..ef6f174f734
--- /dev/null
+++ b/db/schema_migrations/20210707171554
@@ -0,0 +1 @@
+5f2acbd5ed9132ad6c11cf4be34061decde2f3c602ef319331454b424e6b4344 \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 45b1d0bc835..da2d4c50068 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -19331,6 +19331,27 @@ CREATE SEQUENCE vulnerability_findings_remediations_id_seq
ALTER SEQUENCE vulnerability_findings_remediations_id_seq OWNED BY vulnerability_findings_remediations.id;
+CREATE TABLE vulnerability_flags (
+ id bigint NOT NULL,
+ created_at timestamp with time zone NOT NULL,
+ updated_at timestamp with time zone NOT NULL,
+ vulnerability_occurrence_id bigint NOT NULL,
+ flag_type smallint DEFAULT 0 NOT NULL,
+ origin text NOT NULL,
+ description text NOT NULL,
+ CONSTRAINT check_45e743349f CHECK ((char_length(description) <= 1024)),
+ CONSTRAINT check_49c1d00032 CHECK ((char_length(origin) <= 255))
+);
+
+CREATE SEQUENCE vulnerability_flags_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE vulnerability_flags_id_seq OWNED BY vulnerability_flags.id;
+
CREATE TABLE vulnerability_historical_statistics (
id bigint NOT NULL,
created_at timestamp with time zone NOT NULL,
@@ -20612,6 +20633,8 @@ ALTER TABLE ONLY vulnerability_finding_signatures ALTER COLUMN id SET DEFAULT ne
ALTER TABLE ONLY vulnerability_findings_remediations ALTER COLUMN id SET DEFAULT nextval('vulnerability_findings_remediations_id_seq'::regclass);
+ALTER TABLE ONLY vulnerability_flags ALTER COLUMN id SET DEFAULT nextval('vulnerability_flags_id_seq'::regclass);
+
ALTER TABLE ONLY vulnerability_historical_statistics ALTER COLUMN id SET DEFAULT nextval('vulnerability_historical_statistics_id_seq'::regclass);
ALTER TABLE ONLY vulnerability_identifiers ALTER COLUMN id SET DEFAULT nextval('vulnerability_identifiers_id_seq'::regclass);
@@ -22342,6 +22365,9 @@ ALTER TABLE ONLY vulnerability_finding_signatures
ALTER TABLE ONLY vulnerability_findings_remediations
ADD CONSTRAINT vulnerability_findings_remediations_pkey PRIMARY KEY (id);
+ALTER TABLE ONLY vulnerability_flags
+ ADD CONSTRAINT vulnerability_flags_pkey PRIMARY KEY (id);
+
ALTER TABLE ONLY vulnerability_historical_statistics
ADD CONSTRAINT vulnerability_historical_statistics_pkey PRIMARY KEY (id);
@@ -25251,6 +25277,8 @@ CREATE INDEX index_vulnerability_findings_remediations_on_remediation_id ON vuln
CREATE UNIQUE INDEX index_vulnerability_findings_remediations_on_unique_keys ON vulnerability_findings_remediations USING btree (vulnerability_occurrence_id, vulnerability_remediation_id);
+CREATE INDEX index_vulnerability_flags_on_vulnerability_occurrence_id ON vulnerability_flags USING btree (vulnerability_occurrence_id);
+
CREATE INDEX index_vulnerability_historical_statistics_on_date_and_id ON vulnerability_historical_statistics USING btree (date, id);
CREATE UNIQUE INDEX index_vulnerability_identifiers_on_project_id_and_fingerprint ON vulnerability_identifiers USING btree (project_id, fingerprint);
@@ -27901,6 +27929,9 @@ ALTER TABLE ONLY clusters_integration_prometheus
ALTER TABLE ONLY vulnerability_occurrence_identifiers
ADD CONSTRAINT fk_rails_e4ef6d027c FOREIGN KEY (occurrence_id) REFERENCES vulnerability_occurrences(id) ON DELETE CASCADE;
+ALTER TABLE ONLY vulnerability_flags
+ ADD CONSTRAINT fk_rails_e59393b48b FOREIGN KEY (vulnerability_occurrence_id) REFERENCES vulnerability_occurrences(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY serverless_domain_cluster
ADD CONSTRAINT fk_rails_e59e868733 FOREIGN KEY (clusters_applications_knative_id) REFERENCES clusters_applications_knative(id) ON DELETE CASCADE;
diff --git a/doc/administration/geo/replication/docker_registry.md b/doc/administration/geo/replication/docker_registry.md
index 83007767215..5cc4f66017b 100644
--- a/doc/administration/geo/replication/docker_registry.md
+++ b/doc/administration/geo/replication/docker_registry.md
@@ -53,7 +53,7 @@ We need to make Docker Registry send notification events to the
registry['notifications'] = [
{
'name' => 'geo_event',
- 'url' => 'https://example.com/api/v4/container_registry_event/events',
+ 'url' => 'https://<example.com>/api/v4/container_registry_event/events',
'timeout' => '500ms',
'threshold' => 5,
'backoff' => '1s',
@@ -65,7 +65,8 @@ We need to make Docker Registry send notification events to the
```
NOTE:
- Replace `<replace_with_a_secret_token>` with a case sensitive alphanumeric string
+ Replace `<example.com>` with the `external_url` defined in your primary site's `/etc/gitlab/gitlab.rb` file, and
+ replace `<replace_with_a_secret_token>` with a case sensitive alphanumeric string
that starts with a letter. You can generate one with `< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c 32 | sed "s/^[0-9]*//"; echo`
NOTE:
diff --git a/doc/api/projects.md b/doc/api/projects.md
index ebf1c70c887..dc9e59f0cad 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -60,7 +60,7 @@ GET /projects
| `simple` | boolean | **{dotted-circle}** No | Return only limited fields for each project. This is a no-op without authentication as then _only_ simple fields are returned. |
| `sort` | string | **{dotted-circle}** No | Return projects sorted in `asc` or `desc` order. Default is `desc`. |
| `starred` | boolean | **{dotted-circle}** No | Limit by projects starred by the current user. |
-| `statistics` | boolean | **{dotted-circle}** No | Include project statistics. |
+| `statistics` | boolean | **{dotted-circle}** No | Include project statistics. Only available to Reporter or higher level role members. |
| `topic` | string | **{dotted-circle}** No | Comma-separated topic names. Limit results to projects that match all of given topics. See `topics` attribute. |
| `visibility` | string | **{dotted-circle}** No | Limit by visibility `public`, `internal`, or `private`. |
| `wiki_checksum_failed` **(PREMIUM)** | boolean | **{dotted-circle}** No | Limit projects where the wiki checksum calculation has failed ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/6137) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.2). |
@@ -379,7 +379,7 @@ GET /users/:user_id/projects
| `simple` | boolean | **{dotted-circle}** No | Return only limited fields for each project. This is a no-op without authentication as then _only_ simple fields are returned. |
| `sort` | string | **{dotted-circle}** No | Return projects sorted in `asc` or `desc` order. Default is `desc`. |
| `starred` | boolean | **{dotted-circle}** No | Limit by projects starred by the current user. |
-| `statistics` | boolean | **{dotted-circle}** No | Include project statistics. |
+| `statistics` | boolean | **{dotted-circle}** No | Include project statistics. Only available to Reporter or higher level role members. |
| `user_id` | string | **{check-circle}** Yes | The ID or username of the user. |
| `visibility` | string | **{dotted-circle}** No | Limit by visibility `public`, `internal`, or `private`. |
| `with_custom_attributes` | boolean | **{dotted-circle}** No | Include [custom attributes](custom_attributes.md) in response. _(admins only)_ |
@@ -612,7 +612,7 @@ GET /users/:user_id/starred_projects
| `simple` | boolean | **{dotted-circle}** No | Return only limited fields for each project. This is a no-op without authentication as then _only_ simple fields are returned.. |
| `sort` | string | **{dotted-circle}** No | Return projects sorted in `asc` or `desc` order. Default is `desc`. |
| `starred` | boolean | **{dotted-circle}** No | Limit by projects starred by the current user. |
-| `statistics` | boolean | **{dotted-circle}** No | Include project statistics. |
+| `statistics` | boolean | **{dotted-circle}** No | Include project statistics. Only available to Reporter or higher level role members. |
| `user_id` | string | **{check-circle}** Yes | The ID or username of the user. |
| `visibility` | string | **{dotted-circle}** No | Limit by visibility `public`, `internal`, or `private`. |
| `with_custom_attributes` | boolean | **{dotted-circle}** No | Include [custom attributes](custom_attributes.md) in response. _(admins only)_ |
@@ -831,7 +831,7 @@ GET /projects/:id
|--------------------------|----------------|------------------------|-------------|
| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding). |
| `license` | boolean | **{dotted-circle}** No | Include project license data. |
-| `statistics` | boolean | **{dotted-circle}** No | Include project statistics. |
+| `statistics` | boolean | **{dotted-circle}** No | Include project statistics. Only available to Reporter or higher level role members. |
| `with_custom_attributes` | boolean | **{dotted-circle}** No | Include [custom attributes](custom_attributes.md) in response. _(admins only)_ |
```json
@@ -1425,7 +1425,7 @@ GET /projects/:id/forks
| `simple` | boolean | **{dotted-circle}** No | Return only limited fields for each project. This is a no-op without authentication as then _only_ simple fields are returned. |
| `sort` | string | **{dotted-circle}** No | Return projects sorted in `asc` or `desc` order. Default is `desc`. |
| `starred` | boolean | **{dotted-circle}** No | Limit by projects starred by the current user. |
-| `statistics` | boolean | **{dotted-circle}** No | Include project statistics. |
+| `statistics` | boolean | **{dotted-circle}** No | Include project statistics. Only available to Reporter or higher level role members. |
| `visibility` | string | **{dotted-circle}** No | Limit by visibility `public`, `internal`, or `private`. |
| `with_custom_attributes` | boolean | **{dotted-circle}** No | Include [custom attributes](custom_attributes.md) in response. _(admins only)_ |
| `with_issues_enabled` | boolean | **{dotted-circle}** No | Limit by enabled issues feature. |
diff --git a/doc/development/code_review.md b/doc/development/code_review.md
index a24a4ecc266..929e75e7774 100644
--- a/doc/development/code_review.md
+++ b/doc/development/code_review.md
@@ -384,7 +384,7 @@ Before taking the decision to merge:
- Set the milestone.
- Consider warnings and errors from danger bot, code quality, and other reports.
Unless a strong case can be made for the violation, these should be resolved
- before merging. A comment must to be posted if the MR is merged with any failed job.
+ before merging. A comment must be posted if the MR is merged with any failed job.
- If the MR contains both Quality and non-Quality-related changes, the MR should be merged by the relevant maintainer for user-facing changes (backend, frontend, or database) after the Quality related changes are approved by a Software Engineer in Test.
If a merge request is fundamentally ready, but needs only trivial fixes (such as
diff --git a/doc/development/fe_guide/style/html.md b/doc/development/fe_guide/style/html.md
index 18f72a9655c..72492d56ee4 100644
--- a/doc/development/fe_guide/style/html.md
+++ b/doc/development/fe_guide/style/html.md
@@ -62,7 +62,7 @@ Avoid forcing links to open in a new window as this reduces the control the user
However, it might be a good idea to use a blank target when replacing the current page with
the link makes the user lose content or progress.
-Use `rel="noopener noreferrer"` whenever your links open in a new window, i.e. `target="_blank"`. This prevents a security vulnerability [documented by JitBit](https://www.jitbit.com/alexblog/256-targetblank---the-most-underestimated-vulnerability-ever/).
+Use `rel="noopener noreferrer"` whenever your links open in a new window, that is, `target="_blank"`. This prevents a security vulnerability [documented by JitBit](https://www.jitbit.com/alexblog/256-targetblank---the-most-underestimated-vulnerability-ever/).
When using `gl-link`, using `target="_blank"` is sufficient as it automatically adds `rel="noopener noreferrer"` to the link.
diff --git a/doc/development/fe_guide/style/scss.md b/doc/development/fe_guide/style/scss.md
index e05ef7c9f5d..4a9446f2949 100644
--- a/doc/development/fe_guide/style/scss.md
+++ b/doc/development/fe_guide/style/scss.md
@@ -51,7 +51,7 @@ We recommend a "utility-first" approach.
1. Start with utility classes.
1. If composing utility classes into a component class removes code duplication and encapsulates a clear responsibility, do it.
-This encourages an organic growth of component classes and prevents the creation of one-off non-reusable classes. Also, the kind of classes that emerge from "utility-first" tend to be design-centered (e.g. `.button`, `.alert`, `.card`) rather than domain-centered (e.g. `.security-report-widget`, `.commit-header-icon`).
+This encourages an organic growth of component classes and prevents the creation of one-off non-reusable classes. Also, the kind of classes that emerge from "utility-first" tend to be design-centered (for example, `.button`, `.alert`, `.card`) rather than domain-centered (for example, `.security-report-widget`, `.commit-header-icon`).
Inspiration:
diff --git a/doc/development/feature_flags/controls.md b/doc/development/feature_flags/controls.md
index 4ad3136408b..c69a698149e 100644
--- a/doc/development/feature_flags/controls.md
+++ b/doc/development/feature_flags/controls.md
@@ -221,7 +221,7 @@ you should fully roll out the feature by enabling the flag **globally** by runni
```
This changes the feature flag state to be **enabled** always, which overrides the
-existing gates (e.g. `--group=gitlab-org`) in the above processes.
+existing gates (for example, `--group=gitlab-org`) in the above processes.
Note, that if an actor based feature gate is present, switching the
`default_enabled` attribute of the YAML definition from `false` to `true`
diff --git a/doc/development/feature_flags/index.md b/doc/development/feature_flags/index.md
index ca830ca54b9..1962d5262ce 100644
--- a/doc/development/feature_flags/index.md
+++ b/doc/development/feature_flags/index.md
@@ -61,7 +61,7 @@ When the feature implementation is delivered among multiple merge requests:
One might be tempted to think that feature flags will delay the release of a
feature by at least one month (= one release). This is not the case. A feature
flag does not have to stick around for a specific amount of time
-(e.g. at least one release), instead they should stick around until the feature
+(for example, at least one release), instead they should stick around until the feature
is deemed stable. Stable means it works on GitLab.com without causing any
problems, such as outages.
diff --git a/doc/development/foreign_keys.md b/doc/development/foreign_keys.md
index e234cf9a177..a9edbc68a2e 100644
--- a/doc/development/foreign_keys.md
+++ b/doc/development/foreign_keys.md
@@ -17,7 +17,7 @@ end
Here you will need to add a foreign key on column `posts.user_id`. This ensures
that data consistency is enforced on database level. Foreign keys also mean that
-the database can very quickly remove associated data (e.g. when removing a
+the database can very quickly remove associated data (for example, when removing a
user), instead of Rails having to do this.
## Adding Foreign Keys In Migrations
diff --git a/doc/development/geo.md b/doc/development/geo.md
index 0ec394dc764..38245e5f4e5 100644
--- a/doc/development/geo.md
+++ b/doc/development/geo.md
@@ -56,7 +56,7 @@ Geo uses [streaming replication](#streaming-replication) to replicate
the database from the **primary** to the **secondary** nodes. This
replication gives the **secondary** nodes access to all the data saved
in the database. So users can log in on the **secondary** and read all
-the issues, merge requests, etc. on the **secondary** node.
+the issues, merge requests, and so on, on the **secondary** node.
### Repository replication
@@ -127,7 +127,7 @@ periodically to sync all uploads that aren't synced to the Geo
Files are copied via HTTP(s) and initiated via the
`/api/v4/geo/transfers/:type/:id` endpoint,
-e.g. `/api/v4/geo/transfers/lfs/123`.
+for example, `/api/v4/geo/transfers/lfs/123`.
## Authentication
@@ -219,7 +219,7 @@ bundle exec rake geo:db:migrate
Geo uses [Finders](https://gitlab.com/gitlab-org/gitlab/-/tree/master/app/finders),
which are classes take care of the heavy lifting of looking up
-projects/attachments/etc. in the tracking database and main database.
+projects/attachments/ and so on, in the tracking database and main database.
## Redis
@@ -228,7 +228,7 @@ node. It is used for caching, storing sessions, and other persistent
data.
Redis data replication between **primary** and **secondary** node is
-not used, so sessions etc. aren't shared between nodes.
+not used, so sessions and so on, aren't shared between nodes.
## Object Storage
diff --git a/doc/development/git_object_deduplication.md b/doc/development/git_object_deduplication.md
index 1607f3e7a12..3ac24b19fc2 100644
--- a/doc/development/git_object_deduplication.md
+++ b/doc/development/git_object_deduplication.md
@@ -163,7 +163,7 @@ repository and a pool.
### Pool existence
-If GitLab thinks a pool repository exists (i.e. it exists according to
+If GitLab thinks a pool repository exists (that is, it exists according to
SQL), but it does not on the Gitaly server, then it is created on
the fly by Gitaly.
diff --git a/doc/development/github_importer.md b/doc/development/github_importer.md
index 3e70585499d..a733d6881fa 100644
--- a/doc/development/github_importer.md
+++ b/doc/development/github_importer.md
@@ -145,7 +145,7 @@ long we're still performing work.
GitHub has a rate limit of 5,000 API calls per hour. The number of requests
necessary to import a project is largely dominated by the number of unique users
-involved in a project (e.g. issue authors). Other data such as issue pages
+involved in a project (for example, issue authors). Other data such as issue pages
and comments typically only requires a few dozen requests to import. This is
because we need the Email address of users in order to map them to GitLab users.
diff --git a/doc/development/go_guide/index.md b/doc/development/go_guide/index.md
index 72435e8f503..224d8a0a0f5 100644
--- a/doc/development/go_guide/index.md
+++ b/doc/development/go_guide/index.md
@@ -14,7 +14,7 @@ projects using the [Go language](https://golang.org).
GitLab is built on top of [Ruby on Rails](https://rubyonrails.org/), but we're
also using Go for projects where it makes sense. Go is a very powerful
language, with many advantages, and is best suited for projects with a lot of
-IO (disk/network access), HTTP requests, parallel processing, etc. Since we
+IO (disk/network access), HTTP requests, parallel processing, and so on. Since we
have both Ruby on Rails and Go at GitLab, we should evaluate carefully which of
the two is best for the job.
@@ -390,7 +390,7 @@ consistent across Workhorse, Gitaly, and, in future, other Go servers. For
example, in the case of `gitlab.com/gitlab-org/labkit/tracing` we can switch
from using `Opentracing` directly to using `Zipkin` or Gokit's own tracing wrapper
without changes to the application code, while still keeping the same
-consistent configuration mechanism (i.e. the `GITLAB_TRACING` environment
+consistent configuration mechanism (that is, the `GITLAB_TRACING` environment
variable).
### Context
@@ -437,7 +437,7 @@ and the version being used for [CNG](https://gitlab.com/gitlab-org/build/cng/blo
### Updating Go version
We should always use a [supported version](https://golang.org/doc/devel/release#policy)
-of Go, i.e., one of the three most recent minor releases, and should always use
+of Go, that is, one of the three most recent minor releases, and should always use
the most recent patch-level for that version, as it may contain security fixes.
Changing the version affects every project being compiled, so it's important to
diff --git a/doc/development/jh_features_review.md b/doc/development/jh_features_review.md
index b139a380344..cb0f8ddbd13 100644
--- a/doc/development/jh_features_review.md
+++ b/doc/development/jh_features_review.md
@@ -29,11 +29,14 @@ See the [merge request process](https://about.gitlab.com/handbook/ceo/chief-of-s
on the JiHu Support handbook.
This page is the single source of truth for JiHu-related processes.
-## Act as EE when `jh/` does not exist
+## Act as EE when `jh/` does not exist or when `EE_ONLY=1`
- In the case of EE repository, `jh/` does not exist so it should just act like EE (or CE when the license is absent)
- In the case of JH repository, `jh/` does exist but `EE_ONLY` environment variable can be set to force it run under EE mode.
-- In the case of JH repository, `jh/` does exist but `FOSS_ONLY` environment variable can be set to force it run under CE mode.
+
+## Act as FOSS when `FOSS_ONLY=1`
+
+- In the case of JH repository, `jh/` does exist but `FOSS_ONLY` environment variable can be set to force it run under FOSS (CE) mode.
## CI pipelines in a JH context
diff --git a/doc/development/logging.md b/doc/development/logging.md
index 45f5b672365..cb1070b49cc 100644
--- a/doc/development/logging.md
+++ b/doc/development/logging.md
@@ -58,12 +58,12 @@ Structured logging solves these problems. Consider the example from an API reque
In a single line, we've included all the information that a user needs
to understand what happened: the timestamp, HTTP method and path, user
-ID, etc.
+ID, and so on.
### How to use JSON logging
Suppose you want to log the events that happen in a project
-importer. You want to log issues created, merge requests, etc. as the
+importer. You want to log issues created, merge requests, and so on, as the
importer progresses. Here's what to do:
1. Look at [the list of GitLab Logs](../administration/logs.md) to see
@@ -174,7 +174,7 @@ Resources:
Similar to timezones, choosing the right time unit to log can impose avoidable overhead. So, whenever
challenged to choose between seconds, milliseconds or any other unit, lean towards _seconds_ as float
-(with microseconds precision, i.e. `Gitlab::InstrumentationHelper::DURATION_PRECISION`).
+(with microseconds precision, that is, `Gitlab::InstrumentationHelper::DURATION_PRECISION`).
In order to make it easier to track timings in the logs, make sure the log key has `_s` as
suffix and `duration` within its name (for example, `view_duration_s`).
diff --git a/doc/development/merge_request_performance_guidelines.md b/doc/development/merge_request_performance_guidelines.md
index 90976f04214..d87b7bcb5af 100644
--- a/doc/development/merge_request_performance_guidelines.md
+++ b/doc/development/merge_request_performance_guidelines.md
@@ -179,9 +179,9 @@ As a counterpart of the `without_sticky_writes` utility,
replicas regardless of the current primary stickiness.
This utility is reserved for cases where queries can tolerate replication lag.
-Internally, our database load balancer classifies the queries based on their main statement (`select`, `update`, `delete`, etc.). When in doubt, it redirects the queries to the primary database. Hence, there are some common cases the load balancer sends the queries to the primary unnecessarily:
+Internally, our database load balancer classifies the queries based on their main statement (`select`, `update`, `delete`, and so on). When in doubt, it redirects the queries to the primary database. Hence, there are some common cases the load balancer sends the queries to the primary unnecessarily:
-- Custom queries (via `exec_query`, `execute_statement`, `execute`, etc.)
+- Custom queries (via `exec_query`, `execute_statement`, `execute`, and so on)
- Read-only transactions
- In-flight connection configuration set
- Sidekiq background jobs
@@ -197,7 +197,7 @@ costly, time-consuming query to the replicas.
Read about [complex queries on the relation object](iterating_tables_in_batches.md#complex-queries-on-the-relation-object) for considerations on how to use CTEs. We have found in some situations that CTEs can become problematic in use (similar to the n+1 problem above). In particular, hierarchical recursive CTE queries such as the CTE in [AuthorizedProjectsWorker](https://gitlab.com/gitlab-org/gitlab/-/issues/325688) are very difficult to optimize and don't scale. We should avoid them when implementing new features that require any kind of hierarchical structure.
-CTEs have been effectively used as an optimization fence in many simpler cases,
+CTEs have been effectively used as an optimization fence in many simpler cases,
such as this [example](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/43242#note_61416277).
Beginning in PostgreSQL 12, CTEs are inlined then [optimized by default](https://paquier.xyz/postgresql-2/postgres-12-with-materialize/).
Keeping the old behavior requires marking CTEs with the keyword `MATERIALIZED`.
@@ -567,7 +567,7 @@ to work with you to possibly discover a better solution.
The usage of local storage is a desired solution to use,
especially since we work on deploying applications to Kubernetes clusters.
When you would like to use `Dir.mktmpdir`? In a case when you want for example
-to extract/create archives, perform extensive manipulation of existing data, etc.
+to extract/create archives, perform extensive manipulation of existing data, and so on.
```ruby
Dir.mktmpdir('designs') do |path|
diff --git a/doc/development/migration_style_guide.md b/doc/development/migration_style_guide.md
index 62e797276c1..f76b053c2bd 100644
--- a/doc/development/migration_style_guide.md
+++ b/doc/development/migration_style_guide.md
@@ -299,6 +299,8 @@ end
**Creating a new table when we have two foreign keys:**
+Only one foreign key should be created per migration. This is because [the addition of a foreign key constraint requires a `SHARE ROW EXCLUSIVE` lock on the referenced table](https://www.postgresql.org/docs/12/sql-createtable.html#:~:text=The%20addition%20of%20a%20foreign%20key%20constraint%20requires%20a%20SHARE%20ROW%20EXCLUSIVE%20lock%20on%20the%20referenced%20table), and locking multiple tables in the same transaction should be avoided.
+
For this, we need three migrations:
1. Creating the table without foreign keys (with the indices).
@@ -605,7 +607,7 @@ perform existence checks internally.
When adding a foreign-key constraint to either an existing or a new column also
remember to add an index on the column.
-This is **required** for all foreign-keys, e.g., to support efficient cascading
+This is **required** for all foreign-keys, for example, to support efficient cascading
deleting: when a lot of rows in a table get deleted, the referenced records need
to be deleted too. The database has to look for corresponding records in the
referenced table. Without an index, this results in a sequential scan on the
diff --git a/doc/development/module_with_instance_variables.md b/doc/development/module_with_instance_variables.md
index f298b603429..0f910f20534 100644
--- a/doc/development/module_with_instance_variables.md
+++ b/doc/development/module_with_instance_variables.md
@@ -49,9 +49,9 @@ instance variables in the final giant object, and that's where the problem is.
## Solutions
We should split the giant object into multiple objects, and they communicate
-with each other with the API, i.e. public methods. In short, composition over
+with each other with the API, that is, public methods. In short, composition over
inheritance. This way, each smaller objects would have their own respective
-limited states, i.e. instance variables. If one instance variable goes wrong,
+limited states, that is, instance variables. If one instance variable goes wrong,
we would be very clear that it's from that single small object, because
no one else could be touching it.
diff --git a/doc/development/multi_version_compatibility.md b/doc/development/multi_version_compatibility.md
index a12c01c734a..3314b5e7ddc 100644
--- a/doc/development/multi_version_compatibility.md
+++ b/doc/development/multi_version_compatibility.md
@@ -304,7 +304,7 @@ variable `CI_NODE_TOTAL` being an integer failed. This was caused because after
1. As a result, the [new code](https://gitlab.com/gitlab-org/gitlab/-/blob/42b82a9a3ac5a96f9152aad6cbc583c42b9fb082/app/models/concerns/ci/contextable.rb#L104)
was not run on the API server. The runner's request failed because the
older API server tried return the `CI_NODE_TOTAL` CI/CD variable, but
-instead of sending an integer value (e.g. 9), it sent a serialized
+instead of sending an integer value (for example, 9), it sent a serialized
`Hash` value (`{:number=>9, :total=>9}`).
If you look at the [deployment pipeline](https://ops.gitlab.net/gitlab-com/gl-infra/deployer/-/pipelines/202212),
diff --git a/doc/development/omnibus.md b/doc/development/omnibus.md
index 5b97221bd29..dc83b0ea257 100644
--- a/doc/development/omnibus.md
+++ b/doc/development/omnibus.md
@@ -12,7 +12,7 @@ when you are coding.
## Files are owned by root by default
-All the files in the Rails tree (`app/`, `config/` etc.) are owned by `root` in
+All the files in the Rails tree (`app/`, `config/`, and so on) are owned by `root` in
Omnibus installations. This makes the installation simpler and it provides
extra security. The Omnibus reconfigure script contains commands that give
write access to the `git` user only where needed.
diff --git a/doc/development/packages.md b/doc/development/packages.md
index fa0cdbef92e..94882cefc30 100644
--- a/doc/development/packages.md
+++ b/doc/development/packages.md
@@ -191,7 +191,7 @@ against the project or group before continuing.
The current database model allows you to store a name and a version for each package.
Every time you upload a new package, you can either create a new record of `Package`
or add files to existing record. `PackageFile` should be able to store all file-related
-information like the file `name`, `side`, `sha1`, etc.
+information like the file `name`, `side`, `sha1`, and so on.
If there is specific data necessary to be stored for only one package system support,
consider creating a separate metadata model. See `packages_maven_metadata` table
diff --git a/doc/development/performance.md b/doc/development/performance.md
index 84b3a8f1092..e59f7fb154b 100644
--- a/doc/development/performance.md
+++ b/doc/development/performance.md
@@ -120,7 +120,7 @@ allowing you to profile which code is running on CPU in detail.
It's important to note that profiling an application *alters its performance*.
Different profiling strategies have different overheads. Stackprof is a sampling
profiler. It samples stack traces from running threads at a configurable
-frequency (e.g. 100hz, that is 100 stacks per second). This type of profiling
+frequency (for example, 100hz, that is 100 stacks per second). This type of profiling
has quite a low (albeit non-zero) overhead and is generally considered to be
safe for production.
diff --git a/doc/development/pipelines.md b/doc/development/pipelines.md
index b58e644d639..0fe48fe8b9e 100644
--- a/doc/development/pipelines.md
+++ b/doc/development/pipelines.md
@@ -51,7 +51,7 @@ depending on the changes made in the MR:
We use the [`rules:`](../ci/yaml/index.md#rules) and [`needs:`](../ci/yaml/index.md#needs) keywords extensively
to determine the jobs that need to be run in a pipeline. Note that an MR that includes multiple types of changes would
-have a pipelines that include jobs from multiple types (e.g. a combination of docs-only and code-only pipelines).
+have a pipelines that include jobs from multiple types (for example, a combination of docs-only and code-only pipelines).
#### Documentation only MR pipeline
@@ -557,7 +557,7 @@ request, be sure to start the `dont-interrupt-me` job before pushing.
- `.yarn-cache`
- `.assets-compile-cache` (the key includes `${NODE_ENV}` so it's actually two different caches).
1. These cache definitions are composed of [multiple atomic caches](../ci/caching/index.md#use-multiple-caches).
-1. Only the following jobs, running in 2-hourly scheduled pipelines, are pushing (i.e. updating) to the caches:
+1. Only the following jobs, running in 2-hourly scheduled pipelines, are pushing (that is, updating) to the caches:
- `update-setup-test-env-cache`, defined in [`.gitlab/ci/rails.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/rails.gitlab-ci.yml).
- `update-gitaly-binaries-cache`, defined in [`.gitlab/ci/rails.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/rails.gitlab-ci.yml).
- `update-static-analysis-cache`, defined in [`.gitlab/ci/rails.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/rails.gitlab-ci.yml).
@@ -653,7 +653,7 @@ The current stages are:
- `fixtures`: This stage includes jobs that prepare fixtures needed by frontend tests.
- `test`: This stage includes most of the tests, DB/migration jobs, and static analysis jobs.
- `post-test`: This stage includes jobs that build reports or gather data from
- the `test` stage's jobs (e.g. coverage, Knapsack metadata etc.).
+ the `test` stage's jobs (for example, coverage, Knapsack metadata, and so on).
- `review-prepare`: This stage includes a job that build the CNG images that are
later used by the (Helm) Review App deployment (see
[Review Apps](testing_guide/review_apps.md) for details).
@@ -663,9 +663,9 @@ that is deployed in stage `review`.
- `qa`: This stage includes jobs that perform QA tasks against the Review App
that is deployed in stage `review`.
- `post-qa`: This stage includes jobs that build reports or gather data from
- the `qa` stage's jobs (e.g. Review App performance report).
+ the `qa` stage's jobs (for example, Review App performance report).
- `pages`: This stage includes a job that deploys the various reports as
- GitLab Pages (e.g. [`coverage-ruby`](https://gitlab-org.gitlab.io/gitlab/coverage-ruby/),
+ GitLab Pages (for example, [`coverage-ruby`](https://gitlab-org.gitlab.io/gitlab/coverage-ruby/),
[`coverage-javascript`](https://gitlab-org.gitlab.io/gitlab/coverage-javascript/),
and `webpack-report` (found at `https://gitlab-org.gitlab.io/gitlab/webpack-report/`, but there is
[an issue with the deployment](https://gitlab.com/gitlab-org/gitlab/-/issues/233458)).
@@ -721,7 +721,7 @@ that are scoped to a single [configuration keyword](../ci/yaml/index.md#job-keyw
| Job definitions | Description |
|------------------|-------------|
| `.default-retry` | Allows a job to [retry](../ci/yaml/index.md#retry) upon `unknown_failure`, `api_failure`, `runner_system_failure`, `job_execution_timeout`, or `stuck_or_timeout_failure`. |
-| `.default-before_script` | Allows a job to use a default `before_script` definition suitable for Ruby/Rails tasks that may need a database running (e.g. tests). |
+| `.default-before_script` | Allows a job to use a default `before_script` definition suitable for Ruby/Rails tasks that may need a database running (for example, tests). |
| `.setup-test-env-cache` | Allows a job to use a default `cache` definition suitable for setting up test environment for subsequent Ruby/Rails tasks. |
| `.rails-cache` | Allows a job to use a default `cache` definition suitable for Ruby/Rails tasks. |
| `.static-analysis-cache` | Allows a job to use a default `cache` definition suitable for static analysis tasks. |
@@ -757,8 +757,8 @@ and included in `rules` definitions via [YAML anchors](../ci/yaml/index.md#ancho
| `if:` conditions | Description | Notes |
|------------------|-------------|-------|
| `if-not-canonical-namespace` | Matches if the project isn't in the canonical (`gitlab-org/`) or security (`gitlab-org/security`) namespace. | Use to create a job for forks (by using `when: on_success|manual`), or **not** create a job for forks (by using `when: never`). |
-| `if-not-ee` | Matches if the project isn't EE (i.e. project name isn't `gitlab` or `gitlab-ee`). | Use to create a job only in the FOSS project (by using `when: on_success|manual`), or **not** create a job if the project is EE (by using `when: never`). |
-| `if-not-foss` | Matches if the project isn't FOSS (i.e. project name isn't `gitlab-foss`, `gitlab-ce`, or `gitlabhq`). | Use to create a job only in the EE project (by using `when: on_success|manual`), or **not** create a job if the project is FOSS (by using `when: never`). |
+| `if-not-ee` | Matches if the project isn't EE (that is, project name isn't `gitlab` or `gitlab-ee`). | Use to create a job only in the FOSS project (by using `when: on_success|manual`), or **not** create a job if the project is EE (by using `when: never`). |
+| `if-not-foss` | Matches if the project isn't FOSS (that is, project name isn't `gitlab-foss`, `gitlab-ce`, or `gitlabhq`). | Use to create a job only in the EE project (by using `when: on_success|manual`), or **not** create a job if the project is FOSS (by using `when: never`). |
| `if-default-refs` | Matches if the pipeline is for `master`, `main`, `/^[\d-]+-stable(-ee)?$/` (stable branches), `/^\d+-\d+-auto-deploy-\d+$/` (auto-deploy branches), `/^security\//` (security branches), merge requests, and tags. | Note that jobs aren't created for branches with this default configuration. |
| `if-master-refs` | Matches if the current branch is `master` or `main`. | |
| `if-master-push` | Matches if the current branch is `master` or `main` and pipeline source is `push`. | |
@@ -797,11 +797,11 @@ and included in `rules` definitions via [YAML anchors](../ci/yaml/index.md#ancho
| `ci-qa-patterns` | Only create job for CI configuration-related changes related to the `qa` stage. |
| `yaml-lint-patterns` | Only create job for YAML-related changes. |
| `docs-patterns` | Only create job for docs-related changes. |
-| `frontend-dependency-patterns` | Only create job when frontend dependencies are updated (i.e. `package.json`, and `yarn.lock`). changes. |
+| `frontend-dependency-patterns` | Only create job when frontend dependencies are updated (that is, `package.json`, and `yarn.lock`). changes. |
| `frontend-patterns` | Only create job for frontend-related changes. |
| `backend-patterns` | Only create job for backend-related changes. |
| `db-patterns` | Only create job for DB-related changes. |
-| `backstage-patterns` | Only create job for backstage-related changes (i.e. Danger, fixtures, RuboCop, specs). |
+| `backstage-patterns` | Only create job for backstage-related changes (that is, Danger, fixtures, RuboCop, specs). |
| `code-patterns` | Only create job for code-related changes. |
| `qa-patterns` | Only create job for QA-related changes. |
| `code-backstage-patterns` | Combination of `code-patterns` and `backstage-patterns`. |
diff --git a/doc/development/policies.md b/doc/development/policies.md
index 315878e19d9..8ad3d3f42ba 100644
--- a/doc/development/policies.md
+++ b/doc/development/policies.md
@@ -106,7 +106,7 @@ Each line represents a rule that was evaluated. There are a few things to note:
1. The `-` or `+` symbol indicates whether the rule block was evaluated to be
`false` or `true`, respectively.
1. The number inside the brackets indicates the score.
-1. The last part of the line (e.g. `@john : Issue/1`) shows the username
+1. The last part of the line (for example, `@john : Issue/1`) shows the username
and subject for that rule.
Here you can see that the first four rules were evaluated `false` for
@@ -150,7 +150,7 @@ then the result of the condition is cached globally only based on the subject -
**DANGER**: If you use a `:scope` option when the condition actually uses data from
both user and subject (including a simple anonymous check!) your result is cached at too global of a scope and results in cache bugs.
-Sometimes we are checking permissions for a lot of users for one subject, or a lot of subjects for one user. In this case, we want to set a *preferred scope* - i.e. tell the system that we prefer rules that can be cached on the repeated parameter. For example, in `Ability.users_that_can_read_project`:
+Sometimes we are checking permissions for a lot of users for one subject, or a lot of subjects for one user. In this case, we want to set a *preferred scope* - that is, tell the system that we prefer rules that can be cached on the repeated parameter. For example, in `Ability.users_that_can_read_project`:
```ruby
def users_that_can_read_project(users, project)
diff --git a/doc/development/polymorphic_associations.md b/doc/development/polymorphic_associations.md
index f341255a7e1..bbeaab40a90 100644
--- a/doc/development/polymorphic_associations.md
+++ b/doc/development/polymorphic_associations.md
@@ -146,7 +146,7 @@ filter rows using the `IS NULL` condition.
To summarize: using separate tables allows us to use foreign keys effectively,
create indexes only where necessary, conserve space, query data more
-efficiently, and scale these tables more easily (e.g. by storing them on
+efficiently, and scale these tables more easily (for example, by storing them on
separate disks). A nice side effect of this is that code can also become easier,
as a single model isn't responsible for handling different kinds of
data.
diff --git a/doc/development/pry_debugging.md b/doc/development/pry_debugging.md
index 402029164a7..5481da348e8 100644
--- a/doc/development/pry_debugging.md
+++ b/doc/development/pry_debugging.md
@@ -129,7 +129,7 @@ end
## Repeat last command
You can repeat the last command by just hitting the <kbd>Enter</kbd>
-key (e.g., with `step` or`next`), if you place the following snippet
+key (for example, with `step` or`next`), if you place the following snippet
in your `~/.pryrc`:
```ruby
diff --git a/doc/development/query_recorder.md b/doc/development/query_recorder.md
index 46866f67f68..8759bd09538 100644
--- a/doc/development/query_recorder.md
+++ b/doc/development/query_recorder.md
@@ -48,7 +48,7 @@ end
Use a [request spec](https://gitlab.com/gitlab-org/gitlab-foss/tree/master/spec/requests) when writing a N+1 test on the controller level.
Controller specs should not be used to write N+1 tests as the controller is only initialized once per example.
-This could lead to false successes where subsequent "requests" could have queries reduced (e.g. because of memoization).
+This could lead to false successes where subsequent "requests" could have queries reduced (for example, because of memoization).
## Finding the source of the query
diff --git a/doc/development/rails_initializers.md b/doc/development/rails_initializers.md
index 89902c81cd5..ee73dac2b72 100644
--- a/doc/development/rails_initializers.md
+++ b/doc/development/rails_initializers.md
@@ -19,4 +19,4 @@ Ruby files in this folder are loaded in alphabetical order just like the default
Some examples where you would need to do this are:
1. Modifying Rails' `config.autoload_paths`
-1. Changing configuration that Zeitwerk uses, e.g. inflections
+1. Changing configuration that Zeitwerk uses, for example, inflections
diff --git a/doc/development/reactive_caching.md b/doc/development/reactive_caching.md
index 4d01078868b..3c0a1419604 100644
--- a/doc/development/reactive_caching.md
+++ b/doc/development/reactive_caching.md
@@ -258,7 +258,7 @@ self.reactive_cache_hard_limit = 5.megabytes
- This is the type of work performed by the `calculate_reactive_cache` method. Based on this attribute,
it's able to pick the right worker to process the caching job. Make sure to
set it as `:external_dependency` if the work performs any external request
-(e.g. Kubernetes, Sentry); otherwise set it to `:no_dependency`.
+(for example, Kubernetes, Sentry); otherwise set it to `:no_dependency`.
#### `self.reactive_cache_worker_finder`
diff --git a/doc/development/refactoring_guide/index.md b/doc/development/refactoring_guide/index.md
index a25000589c0..a6ed83258f3 100644
--- a/doc/development/refactoring_guide/index.md
+++ b/doc/development/refactoring_guide/index.md
@@ -14,7 +14,7 @@ Pinning tests help you ensure that you don't unintentionally change the output o
### Example steps
-1. Identify all the possible inputs to the refactor subject (e.g. anything that's injected into the template or used in a conditional).
+1. Identify all the possible inputs to the refactor subject (for example, anything that's injected into the template or used in a conditional).
1. For each possible input, identify the significant possible values.
1. Create a test to save a full detailed snapshot for each helpful combination values per input. This should guarantee that we have "pinned down" the current behavior. The snapshot could be literally a screenshot, a dump of HTML, or even an ordered list of debugging statements.
1. Run all the pinning tests against the code, before you start refactoring (Oracle)
diff --git a/doc/development/renaming_features.md b/doc/development/renaming_features.md
index f7fc1c11e37..bd25fa1377e 100644
--- a/doc/development/renaming_features.md
+++ b/doc/development/renaming_features.md
@@ -12,7 +12,7 @@ Sometimes the business asks to change the name of a feature. Broadly speaking, t
- Pros: does not increase code complexity.
- Cons: more work to execute, and higher risk of immediate bugs.
- Façade, rename as little as possible; only the user-facing content like interfaces,
- documentation, error messages, etc.
+ documentation, error messages, and so on.
- Pros: less work to execute.
- Cons: increases code complexity, creating higher risk of future bugs.
diff --git a/doc/development/reusing_abstractions.md b/doc/development/reusing_abstractions.md
index 1e200e1f520..ded6b074324 100644
--- a/doc/development/reusing_abstractions.md
+++ b/doc/development/reusing_abstractions.md
@@ -215,7 +215,7 @@ provided by Active Record are not included, except for the following methods:
### Active Record
The API provided by Active Record itself, such as the `where` method, `save`,
-`delete_all`, etc.
+`delete_all`, and so on.
### Worker
diff --git a/doc/development/scalability.md b/doc/development/scalability.md
index 675f0968ec5..824c98b4b03 100644
--- a/doc/development/scalability.md
+++ b/doc/development/scalability.md
@@ -23,7 +23,7 @@ users. We discuss each component below.
### PostgreSQL
The PostgreSQL database holds all metadata for projects, issues, merge
-requests, users, etc. The schema is managed by the Rails application
+requests, users, and so on. The schema is managed by the Rails application
[db/structure.sql](https://gitlab.com/gitlab-org/gitlab/-/blob/master/db/structure.sql).
GitLab Web/API servers and Sidekiq nodes talk directly to the database by using a
@@ -91,7 +91,7 @@ ownership. It shares a lot of challenges with traditional, data-oriented
sharding, however. For instance, joining data has to happen in the
application itself rather than on the query layer (although additional
layers like GraphQL might mitigate that) and it requires true
-parallelism to run efficiently (i.e. a scatter-gather model to collect,
+parallelism to run efficiently (that is, a scatter-gather model to collect,
then zip up data records), which is a challenge in itself in Ruby based
systems.
diff --git a/doc/development/secure_coding_guidelines.md b/doc/development/secure_coding_guidelines.md
index 74f65034383..fc60c1d7d7f 100644
--- a/doc/development/secure_coding_guidelines.md
+++ b/doc/development/secure_coding_guidelines.md
@@ -49,7 +49,7 @@ Each time you implement a new feature/endpoint, whether it is at UI, API or Grap
- Do not forget **abuse cases**: write specs that **make sure certain things can't happen**
- A lot of specs are making sure things do happen and coverage percentage doesn't take into account permissions as same piece of code is used.
- Make assertions that certain actors cannot perform actions
-- Naming convention to ease auditability: to be defined, e.g. a subfolder containing those specific permission tests or a `#permissions` block
+- Naming convention to ease auditability: to be defined, for example, a subfolder containing those specific permission tests or a `#permissions` block
Be careful to **also test [visibility levels](https://gitlab.com/gitlab-org/gitlab-foss/-/blob/master/doc/development/permissions.md#feature-specific-permissions)** and not only project access rights.
@@ -59,13 +59,13 @@ Some example of well implemented access controls and tests:
1. [example2](https://dev.gitlab.org/gitlab/gitlabhq/-/merge_requests/2511/diffs#ed3aaab1510f43b032ce345909a887e5b167e196_142_155)
1. [example3](https://dev.gitlab.org/gitlab/gitlabhq/-/merge_requests/3170/diffs?diff_id=17494)
-**NB:** any input from development team is welcome, e.g. about Rubocop rules.
+**NB:** any input from development team is welcome, for example, about Rubocop rules.
## Regular Expressions guidelines
### Anchors / Multi line
-Unlike other programming languages (e.g. Perl or Python) Regular Expressions are matching multi-line by default in Ruby. Consider the following example in Python:
+Unlike other programming languages (for example, Perl or Python) Regular Expressions are matching multi-line by default in Ruby. Consider the following example in Python:
```python
import re
diff --git a/doc/development/serializing_data.md b/doc/development/serializing_data.md
index f5924a44753..48e756d015b 100644
--- a/doc/development/serializing_data.md
+++ b/doc/development/serializing_data.md
@@ -35,7 +35,7 @@ turn there's no way to query the data at all.
## Waste Of Space
Storing serialized data such as JSON or YAML will end up wasting a lot of space.
-This is because these formats often include additional characters (e.g. double
+This is because these formats often include additional characters (for example, double
quotes or newlines) besides the data that you are storing.
## Difficult To Manage
@@ -69,9 +69,9 @@ can easily take hours or even days to complete.
## Relational Databases Are Not Document Stores
When storing data as JSON or YAML you're essentially using your database as if
-it were a document store (e.g. MongoDB), except you're not using any of the
+it were a document store (for example, MongoDB), except you're not using any of the
powerful features provided by a typical RDBMS _nor_ are you using any of the
-features provided by a typical document store (e.g. the ability to index fields
+features provided by a typical document store (for example, the ability to index fields
of documents with variable fields). In other words, it's a waste.
## Consistent Fields
diff --git a/doc/subscriptions/gitlab_com/index.md b/doc/subscriptions/gitlab_com/index.md
index 32f587d900e..fad415807bd 100644
--- a/doc/subscriptions/gitlab_com/index.md
+++ b/doc/subscriptions/gitlab_com/index.md
@@ -274,13 +274,31 @@ If you add a member to a group by using the [share a group with another group](.
CI pipeline minutes are the execution time for your [pipelines](../../ci/pipelines/index.md)
on GitLab shared runners. Each [GitLab SaaS tier](https://about.gitlab.com/pricing/)
-includes a monthly quota of CI pipeline minutes for private and public projects:
-
-| Plan | Private projects | Public projects |
-|----------|------------------|-----------------|
-| Free | 400 | 50,000 |
-| Premium | 10,000 | 1,250,000 |
-| Ultimate | 50,000 | 6,250,000 |
+includes a monthly quota of CI pipeline minutes for private and public projects in
+the namespace:
+
+| Plan | CI pipeline minutes |
+|----------|---------------------|
+| Free | 400 |
+| Premium | 10,000 |
+| Ultimate | 50,000 |
+
+The consumption rate for CI pipeline minutes is based on the visibility of the projects:
+
+- Private projects in the namespace consume pipeline minutes at a rate of 1 CI pipeline minute
+ per 1 minute of execution time on GitLab shared runners.
+- Public projects in:
+ - Namespaces [created on or after 2021-07-17](https://gitlab.com/gitlab-org/gitlab/-/issues/332708)
+ consume pipeline minutes at a slower rate, 1 CI pipeline minute per 125 minutes
+ of execution time on GitLab shared runners. The per-minute rate for public projects
+ is 0.008 CI pipeline minutes per 1 minute of execution time on GitLab shared runners.
+ - Namespaces created before 2021-07-17 do not consume CI pipeline minutes.
+
+| Plan | CI pipeline minutes | Maximum **private** project execution time (all namespaces) | Maximum **public** project execution time (namespaces created 2021-07-17 and later) |
+|----------|---------------------|-------------------------------------------------------------|-------------------------------------------------------------------------------------|
+| Free | 400 | 400 minutes | 50,000 minutes |
+| Premium | 10,000 | 10,000 minutes | 1,250,000 minutes |
+| Ultimate | 50,000 | 50,000 minutes | 6,250,000 minutes |
Quotas apply to:
diff --git a/doc/user/admin_area/approving_users.md b/doc/user/admin_area/approving_users.md
index 3d9722035d5..2852f73ffc8 100644
--- a/doc/user/admin_area/approving_users.md
+++ b/doc/user/admin_area/approving_users.md
@@ -47,7 +47,8 @@ To approve or reject a user sign up:
1. On the top bar, select **Menu >** **{admin}** **Admin**.
1. On the left sidebar, select **Overview > Users**.
1. Select the **Pending approval** tab.
-1. In the user's row, select settings (**{settings}**).
+1. (Optional) Select a user.
+1. Select the **{settings}** **User administration** dropdown.
1. Select **Approve** or **Reject**.
Approving a user:
diff --git a/doc/user/admin_area/moderate_users.md b/doc/user/admin_area/moderate_users.md
index e2b4e97d824..3889dd93d59 100644
--- a/doc/user/admin_area/moderate_users.md
+++ b/doc/user/admin_area/moderate_users.md
@@ -23,8 +23,9 @@ or directly from the Admin Area. To do this:
1. On the top bar, select **Menu >** **{admin}** **Admin**.
1. On the left sidebar, select **Overview > Users**.
-1. Select a user.
-1. Under the **Account** tab, select **Block user**.
+1. (Optional) Select a user.
+1. Select the **{settings}** **User administration** dropdown.
+1. Select **Block**.
A blocked user:
@@ -47,8 +48,9 @@ A blocked user can be unblocked from the Admin Area. To do this:
1. On the top bar, select **Menu >** **{admin}** **Admin**.
1. On the left sidebar, select **Overview > Users**.
1. Select on the **Blocked** tab.
-1. Select a user.
-1. Under the **Account** tab, select **Unblock user**.
+1. (Optional) Select a user.
+1. Select the **{settings}** **User administration** dropdown.
+1. Select **Unblock**.
Users can also be unblocked using the [GitLab API](../../api/users.md#unblock-user).
@@ -85,8 +87,9 @@ A user can be deactivated from the Admin Area. To do this:
1. On the top bar, select **Menu >** **{admin}** **Admin**.
1. On the left sidebar, select **Overview > Users**.
-1. Select a user.
-1. Under the **Account** tab, select **Deactivate user**.
+1. (Optional) Select a user.
+1. Select the **{settings}** **User administration** dropdown.
+1. Select **Deactivate**.
Please note that for the deactivation option to be visible to an admin, the user:
@@ -126,8 +129,9 @@ To do this:
1. On the top bar, select **Menu >** **{admin}** **Admin**.
1. On the left sidebar, select **Overview > Users**.
1. Select the **Deactivated** tab.
-1. Select a user.
-1. Under the **Account** tab, select **Activate user**.
+1. (Optional) Select a user.
+1. Select the **{settings}** **User administration** dropdown.
+1. Select **Activate**.
Users can also be activated using the [GitLab API](../../api/users.md#activate-user).
@@ -157,8 +161,9 @@ Users can be banned using the Admin Area. To do this:
1. On the top bar, select **Menu >** **{admin}** **Admin**.
1. On the left sidebar, select **Overview > Users**.
-1. Select a user.
-1. Under the **Account** tab, select **Ban user**.
+1. (Optional) Select a user.
+1. Select the **{settings}** **User administration** dropdown.
+1. Select **Ban user**.
NOTE:
This feature is a work in progress. Currently, banning a user
@@ -172,8 +177,9 @@ A banned user can be unbanned using the Admin Area. To do this:
1. On the top bar, select **Menu >** **{admin}** **Admin**.
1. On the left sidebar, select **Overview > Users**.
1. Select the **Banned** tab.
-1. Select a user.
-1. Under the **Account** tab, select **Unban user**.
+1. (Optional) Select a user.
+1. Select the **{settings}** **User administration** dropdown.
+1. Select **Unban user**.
NOTE:
Unbanning a user changes the user's state to active and consumes a
diff --git a/doc/user/project/import/index.md b/doc/user/project/import/index.md
index 2dcd4a23870..dcc41c6c85e 100644
--- a/doc/user/project/import/index.md
+++ b/doc/user/project/import/index.md
@@ -74,6 +74,10 @@ best to [back up](../../../raketasks/backup_restore.md)
the existing instance and restore it on the new instance. For example, this is useful when migrating
a self-managed instance from an old server to a new server.
+The backups produced don't depend on the operating system running GitLab. You can therefore use
+the restore method to switch between different operating system distributions or versions, as long
+as the same GitLab version [is available for installation](https://docs.gitlab.com/omnibus/package-information/deprecated_os.md).
+
To instead merge two self-managed GitLab instances together, use the instructions in
[Migrate from self-managed GitLab to GitLab.com](#migrate-from-self-managed-gitlab-to-gitlabcom).
This method is useful when both self-managed instances have existing data that must be preserved.
diff --git a/doc/user/project/quick_actions.md b/doc/user/project/quick_actions.md
index 23f72d823c4..d8d464ce6d8 100644
--- a/doc/user/project/quick_actions.md
+++ b/doc/user/project/quick_actions.md
@@ -69,7 +69,7 @@ threads. Some quick actions might not be available to all subscription tiers.
| `/duplicate <#issue>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Close this issue and mark as a duplicate of another issue. **(FREE)** Also, mark both as related. |
| `/epic <epic>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Add to epic `<epic>`. The `<epic>` value should be in the format of `&epic`, `group&epic`, or a URL to an epic. |
| `/estimate <time>` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Set time estimate. For example, `/estimate 1mo 2w 3d 4h 5m`. Learn more about [time tracking](time_tracking.md). |
-| `/invite_email email1 email2` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Add up to six email participants. This action is behind feature flag `issue_email_participants`. |
+| `/invite_email email1 email2` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Add up to six email participants. This action is behind feature flag `issue_email_participants` and is not yet supported in issue templates. |
| `/iteration *iteration:"iteration name"` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Set iteration. For example, to set the `Late in July` iteration: `/iteration *iteration:"Late in July"` ([introduced in GitLab 13.1](https://gitlab.com/gitlab-org/gitlab/-/issues/196795)). |
| `/label ~label1 ~label2` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Add one or more labels. Label names can also start without a tilde (`~`), but mixed syntax is not supported. |
| `/lock` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Lock the discussions. |
diff --git a/doc/user/project/repository/branches/default.md b/doc/user/project/repository/branches/default.md
index 0f4c831216a..2f1171a7d4f 100644
--- a/doc/user/project/repository/branches/default.md
+++ b/doc/user/project/repository/branches/default.md
@@ -171,3 +171,26 @@ current default branch, instead of displaying the "not found" page.
- [Discussion of default branch renaming](https://lore.kernel.org/git/pull.656.v4.git.1593009996.gitgitgadget@gmail.com/)
on the Git mailing list
- [March 2021 blog post: The new Git default branch name](https://about.gitlab.com/blog/2021/03/10/new-git-default-branch-name/)
+
+## Troubleshooting
+
+### Unable to change default branch: resets to current branch
+
+We are tracking this problem in [issue 20474](https://gitlab.com/gitlab-org/gitlab/-/issues/20474).
+This issue often occurs when a branch named `HEAD` is present in the repository.
+To fix the problem:
+
+1. In your local repository, create a new, temporary branch and push it:
+
+ ```shell
+ git checkout -b tmp_default && git push -u origin tmp_default
+ ```
+
+1. In GitLab, proceed to [change the default branch](#change-the-default-branch-name-for-a-project) to that temporary branch.
+1. From your local repository, delete the `HEAD` branch:
+
+ ```shell
+ git push -d origin HEAD
+ ```
+
+1. In GitLab, [change the default branch](#change-the-default-branch-name-for-a-project) to the one you intend to use.
diff --git a/lib/gitlab/ci/reports/security/scanner.rb b/lib/gitlab/ci/reports/security/scanner.rb
index de5c354b5ab..c1de03cea44 100644
--- a/lib/gitlab/ci/reports/security/scanner.rb
+++ b/lib/gitlab/ci/reports/security/scanner.rb
@@ -39,7 +39,7 @@ module Gitlab
end
def <=>(other)
- sort_keys <=> other.sort_keys
+ sort_keys.compact <=> other.sort_keys.compact
end
protected
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index b46bb1d50ae..5a8c95a0ebf 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -1405,18 +1405,12 @@ msgstr ""
msgid "A Let's Encrypt account will be configured for this GitLab installation using your email address. You will receive emails to warn of expiring certificates."
msgstr ""
-msgid "A banned user cannot:"
-msgstr ""
-
msgid "A basic page and serverless function that uses AWS Lambda, AWS API Gateway, and GitLab Pages"
msgstr ""
msgid "A basic template for developing Linux programs using Kotlin Native"
msgstr ""
-msgid "A blocked user cannot:"
-msgstr ""
-
msgid "A complete DevOps platform"
msgstr ""
@@ -1702,9 +1696,6 @@ msgstr ""
msgid "Acceptable for use in this project"
msgstr ""
-msgid "Access Git repositories"
-msgstr ""
-
msgid "Access Git repositories or the API."
msgstr ""
@@ -2518,9 +2509,6 @@ msgstr ""
msgid "AdminUsers|Activate"
msgstr ""
-msgid "AdminUsers|Activate user"
-msgstr ""
-
msgid "AdminUsers|Activate user %{username}?"
msgstr ""
@@ -2542,24 +2530,15 @@ msgstr ""
msgid "AdminUsers|Approve"
msgstr ""
-msgid "AdminUsers|Approve user"
-msgstr ""
-
msgid "AdminUsers|Approve user %{username}?"
msgstr ""
msgid "AdminUsers|Approved users can:"
msgstr ""
-msgid "AdminUsers|Are you sure?"
-msgstr ""
-
msgid "AdminUsers|Automatically marked as default internal user"
msgstr ""
-msgid "AdminUsers|Ban"
-msgstr ""
-
msgid "AdminUsers|Ban user"
msgstr ""
@@ -2569,18 +2548,12 @@ msgstr ""
msgid "AdminUsers|Banned"
msgstr ""
-msgid "AdminUsers|Banning the user has the following effects:"
-msgstr ""
-
msgid "AdminUsers|Be added to groups and projects"
msgstr ""
msgid "AdminUsers|Block"
msgstr ""
-msgid "AdminUsers|Block this user"
-msgstr ""
-
msgid "AdminUsers|Block user"
msgstr ""
@@ -2620,9 +2593,6 @@ msgstr ""
msgid "AdminUsers|Deactivate"
msgstr ""
-msgid "AdminUsers|Deactivate user"
-msgstr ""
-
msgid "AdminUsers|Deactivate user %{username}?"
msgstr ""
@@ -2710,9 +2680,6 @@ msgstr ""
msgid "AdminUsers|Reject"
msgstr ""
-msgid "AdminUsers|Reject request"
-msgstr ""
-
msgid "AdminUsers|Reject user %{username}?"
msgstr ""
@@ -2749,21 +2716,12 @@ msgstr ""
msgid "AdminUsers|The user will not receive any notifications"
msgstr ""
-msgid "AdminUsers|This user has requested access"
-msgstr ""
-
msgid "AdminUsers|To confirm, type %{projectName}"
msgstr ""
msgid "AdminUsers|To confirm, type %{username}"
msgstr ""
-msgid "AdminUsers|Unban"
-msgstr ""
-
-msgid "AdminUsers|Unban %{username}?"
-msgstr ""
-
msgid "AdminUsers|Unban user"
msgstr ""
@@ -2773,19 +2731,16 @@ msgstr ""
msgid "AdminUsers|Unblock"
msgstr ""
-msgid "AdminUsers|Unblock user"
-msgstr ""
-
msgid "AdminUsers|Unblock user %{username}?"
msgstr ""
msgid "AdminUsers|Unlock user %{username}?"
msgstr ""
-msgid "AdminUsers|User is validated and can use free CI minutes on shared runners."
+msgid "AdminUsers|User administration"
msgstr ""
-msgid "AdminUsers|User will be blocked"
+msgid "AdminUsers|User is validated and can use free CI minutes on shared runners."
msgstr ""
msgid "AdminUsers|User will not be able to access git repositories"
@@ -2830,9 +2785,6 @@ msgstr ""
msgid "AdminUsers|You are about to permanently delete the user %{username}. This will delete all of the issues, merge requests, and groups linked to them. To avoid data loss, consider using the %{strongStart}block user%{strongEnd} feature instead. Once you %{strongStart}Delete user%{strongEnd}, it cannot be undone or recovered."
msgstr ""
-msgid "AdminUsers|You ban their account in the future if necessary."
-msgstr ""
-
msgid "AdminUsers|You can always block their account again if needed."
msgstr ""
@@ -10449,9 +10401,6 @@ msgstr ""
msgid "Deactivate dormant users after 90 days of inactivity. Users can return to active status by signing in to their account. While inactive, a user is not counted as an active user in the instance."
msgstr ""
-msgid "Deactivate this user"
-msgstr ""
-
msgid "Dear Administrator,"
msgstr ""
@@ -10701,9 +10650,6 @@ msgstr ""
msgid "Deleting a project places it into a read-only state until %{date}, at which point the project will be permanently deleted. Are you ABSOLUTELY sure?"
msgstr ""
-msgid "Deleting a user has the following effects:"
-msgstr ""
-
msgid "Deleting the project will delete its repository and all related resources, including issues and merge requests."
msgstr ""
@@ -19868,9 +19814,6 @@ msgstr ""
msgid "Locks the discussion."
msgstr ""
-msgid "Log in"
-msgstr ""
-
msgid "Login with smartcard"
msgstr ""
@@ -26921,9 +26864,6 @@ msgstr ""
msgid "Re-verification interval"
msgstr ""
-msgid "Reactivate this user"
-msgstr ""
-
msgid "Read documentation"
msgstr ""
@@ -33360,9 +33300,6 @@ msgstr ""
msgid "This URL is already used for another link; duplicate URLs are not allowed"
msgstr ""
-msgid "This account has been locked"
-msgstr ""
-
msgid "This action can lead to data loss. To prevent accidental actions we ask you to confirm your intention."
msgstr ""
@@ -33708,9 +33645,6 @@ msgstr ""
msgid "This only applies to repository indexing operations."
msgstr ""
-msgid "This option deletes the user and any contributions that would usually be moved to the %{link_to_ghost_user}. As well as the user's personal projects, groups owned solely by the user, and projects in them, will also be removed. Commits to other projects are unaffected."
-msgstr ""
-
msgid "This option is only available on GitLab.com"
msgstr ""
@@ -33813,9 +33747,6 @@ msgstr ""
msgid "This user has an unconfirmed email address. You may force a confirmation."
msgstr ""
-msgid "This user has been temporarily locked due to excessive number of failed logins. You may manually unlock the account."
-msgstr ""
-
msgid "This user has no active %{type}."
msgstr ""
@@ -33831,15 +33762,6 @@ msgstr ""
msgid "This user has the %{access} role in the %{name} project."
msgstr ""
-msgid "This user is banned"
-msgstr ""
-
-msgid "This user is blocked"
-msgstr ""
-
-msgid "This user is currently an owner in these groups:"
-msgstr ""
-
msgid "This user is the author of this %{noteable}."
msgstr ""
@@ -35100,9 +35022,6 @@ msgstr ""
msgid "Unlock this %{issuableDisplayName}? %{strongStart}Everyone%{strongEnd} will be able to comment."
msgstr ""
-msgid "Unlock user"
-msgstr ""
-
msgid "Unlocked"
msgstr ""
@@ -37623,9 +37542,6 @@ msgstr ""
msgid "You do not have permissions to run the import."
msgstr ""
-msgid "You don't have access to delete this user."
-msgstr ""
-
msgid "You don't have any U2F devices registered yet."
msgstr ""
@@ -37773,9 +37689,6 @@ msgstr ""
msgid "You must solve the CAPTCHA in order to submit"
msgstr ""
-msgid "You must transfer ownership or delete these groups before you can delete this user."
-msgstr ""
-
msgid "You must upload a file with the same file name when dropping onto an existing design."
msgstr ""
@@ -39748,9 +39661,6 @@ msgstr ""
msgid "suggestPipeline|We’re adding a GitLab CI configuration file to add a pipeline to the project. You could create it manually, but we recommend that you start with a GitLab template that works out of the box."
msgstr ""
-msgid "system ghost user"
-msgstr ""
-
msgid "tag name"
msgstr ""
diff --git a/qa/qa/page/admin/overview/users/show.rb b/qa/qa/page/admin/overview/users/show.rb
index 64c31b0b534..be73f3d80bf 100644
--- a/qa/qa/page/admin/overview/users/show.rb
+++ b/qa/qa/page/admin/overview/users/show.rb
@@ -14,8 +14,13 @@ module QA
element :user_id_content
end
- view 'app/views/admin/users/_approve_user.html.haml' do
+ view 'app/assets/javascripts/admin/users/components/actions/approve.vue' do
element :approve_user_button
+ element :approve_user_confirm_button
+ end
+
+ view 'app/assets/javascripts/admin/users/components/user_actions.vue' do
+ element :user_actions_dropdown_toggle
end
view 'app/helpers/users_helper.rb' do
@@ -23,6 +28,10 @@ module QA
element :confirm_user_confirm_button
end
+ def open_user_actions_dropdown(user)
+ click_element(:user_actions_dropdown_toggle, username: user.username)
+ end
+
def click_impersonate_user
click_element(:impersonate_user_link)
end
@@ -36,10 +45,10 @@ module QA
click_element :confirm_user_confirm_button
end
- def approve_user
- accept_confirm do
- click_element :approve_user_button
- end
+ def approve_user(user)
+ open_user_actions_dropdown(user)
+ click_element :approve_user_button
+ click_element :approve_user_confirm_button
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/1_manage/login/register_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/login/register_spec.rb
index 23a21d70cc1..696bbc2a7b7 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/login/register_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/login/register_spec.rb
@@ -143,7 +143,7 @@ module QA
Page::Admin::Overview::Users::Show.perform do |show|
user.id = show.user_id.to_i
- show.approve_user
+ show.approve_user(user)
end
expect(page).to have_text('Successfully approved')
diff --git a/spec/controllers/groups/group_links_controller_spec.rb b/spec/controllers/groups/group_links_controller_spec.rb
index 94d3c1ffa0f..fafe9715946 100644
--- a/spec/controllers/groups/group_links_controller_spec.rb
+++ b/spec/controllers/groups/group_links_controller_spec.rb
@@ -88,7 +88,7 @@ RSpec.describe Groups::GroupLinksController do
end
end
- it 'updates project permissions' do
+ it 'updates project permissions', :sidekiq_inline do
expect { subject }.to change { group_member.can?(:read_project, project) }.from(false).to(true)
end
@@ -207,7 +207,7 @@ RSpec.describe Groups::GroupLinksController do
end
end
- it 'updates project permissions' do
+ it 'updates project permissions', :sidekiq_inline do
expect { subject }.to change { group_member.can?(:create_release, project) }.from(true).to(false)
end
end
@@ -244,7 +244,7 @@ RSpec.describe Groups::GroupLinksController do
expect { subject }.to change(GroupGroupLink, :count).by(-1)
end
- it 'updates project permissions' do
+ it 'updates project permissions', :sidekiq_inline do
expect { subject }.to change { group_member.can?(:create_release, project) }.from(true).to(false)
end
end
diff --git a/spec/features/admin/admin_mode/workers_spec.rb b/spec/features/admin/admin_mode/workers_spec.rb
index fbbcf19063b..0caa883fb5b 100644
--- a/spec/features/admin/admin_mode/workers_spec.rb
+++ b/spec/features/admin/admin_mode/workers_spec.rb
@@ -4,6 +4,8 @@ require 'spec_helper'
# Test an operation that triggers background jobs requiring administrative rights
RSpec.describe 'Admin mode for workers', :request_store do
+ include Spec::Support::Helpers::Features::AdminUsersHelpers
+
let(:user) { create(:user) }
let(:user_to_delete) { create(:user) }
@@ -37,7 +39,8 @@ RSpec.describe 'Admin mode for workers', :request_store do
it 'can delete user', :js do
visit admin_user_path(user_to_delete)
- click_button 'Delete user'
+
+ click_action_in_user_dropdown(user_to_delete.id, 'Delete user')
page.within '.modal-dialog' do
find("input[name='username']").send_keys(user_to_delete.name)
diff --git a/spec/features/admin/users/user_spec.rb b/spec/features/admin/users/user_spec.rb
index 9766bd48a8f..e6eb76b13eb 100644
--- a/spec/features/admin/users/user_spec.rb
+++ b/spec/features/admin/users/user_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe 'Admin::Users::User' do
+ include Spec::Support::Helpers::Features::AdminUsersHelpers
+
let_it_be(:user) { create(:omniauth_user, provider: 'twitter', extern_uid: '123456') }
let_it_be(:current_user) { create(:admin) }
@@ -12,15 +14,18 @@ RSpec.describe 'Admin::Users::User' do
end
describe 'GET /admin/users/:id' do
- it 'has user info', :aggregate_failures do
+ it 'has user info', :js, :aggregate_failures do
visit admin_user_path(user)
expect(page).to have_content(user.email)
expect(page).to have_content(user.name)
expect(page).to have_content("ID: #{user.id}")
expect(page).to have_content("Namespace ID: #{user.namespace_id}")
- expect(page).to have_button('Deactivate user')
- expect(page).to have_button('Block user')
+
+ click_user_dropdown_toggle(user.id)
+
+ expect(page).to have_button('Block')
+ expect(page).to have_button('Deactivate')
expect(page).to have_button('Delete user')
expect(page).to have_button('Delete user and contributions')
end
@@ -29,9 +34,7 @@ RSpec.describe 'Admin::Users::User' do
it 'shows confirmation and allows blocking and unblocking', :js do
visit admin_user_path(user)
- find('button', text: 'Block user').click
-
- wait_for_requests
+ click_action_in_user_dropdown(user.id, 'Block')
expect(page).to have_content('Block user')
expect(page).to have_content('You can always unblock their account, their data will remain intact.')
@@ -41,21 +44,18 @@ RSpec.describe 'Admin::Users::User' do
wait_for_requests
expect(page).to have_content('Successfully blocked')
- expect(page).to have_content('This user is blocked')
- find('button', text: 'Unblock user').click
-
- wait_for_requests
+ click_action_in_user_dropdown(user.id, 'Unblock')
expect(page).to have_content('Unblock user')
expect(page).to have_content('You can always block their account again if needed.')
find('.modal-footer button', text: 'Unblock').click
- wait_for_requests
-
expect(page).to have_content('Successfully unblocked')
- expect(page).to have_content('Block this user')
+
+ click_user_dropdown_toggle(user.id)
+ expect(page).to have_content('Block')
end
end
@@ -63,9 +63,7 @@ RSpec.describe 'Admin::Users::User' do
it 'shows confirmation and allows deactivating/re-activating', :js do
visit admin_user_path(user)
- find('button', text: 'Deactivate user').click
-
- wait_for_requests
+ click_action_in_user_dropdown(user.id, 'Deactivate')
expect(page).to have_content('Deactivate user')
expect(page).to have_content('You can always re-activate their account, their data will remain intact.')
@@ -75,11 +73,8 @@ RSpec.describe 'Admin::Users::User' do
wait_for_requests
expect(page).to have_content('Successfully deactivated')
- expect(page).to have_content('Reactivate this user')
-
- find('button', text: 'Activate user').click
- wait_for_requests
+ click_action_in_user_dropdown(user.id, 'Activate')
expect(page).to have_content('Activate user')
expect(page).to have_content('You can always deactivate their account again if needed.')
@@ -89,7 +84,9 @@ RSpec.describe 'Admin::Users::User' do
wait_for_requests
expect(page).to have_content('Successfully activated')
- expect(page).to have_content('Deactivate this user')
+
+ click_user_dropdown_toggle(user.id)
+ expect(page).to have_content('Deactivate')
end
end
@@ -367,8 +364,11 @@ RSpec.describe 'Admin::Users::User' do
expect(page).to have_content(user.name)
expect(page).to have_content('Pending approval')
- expect(page).to have_link('Approve user')
- expect(page).to have_link('Reject request')
+
+ click_user_dropdown_toggle(user.id)
+
+ expect(page).to have_button('Approve')
+ expect(page).to have_button('Reject')
end
end
end
diff --git a/spec/features/admin/users/users_spec.rb b/spec/features/admin/users/users_spec.rb
index 187fa6fc2a4..119b01ff552 100644
--- a/spec/features/admin/users/users_spec.rb
+++ b/spec/features/admin/users/users_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe 'Admin::Users' do
+ include Spec::Support::Helpers::Features::AdminUsersHelpers
+
let_it_be(:user, reload: true) { create(:omniauth_user, provider: 'twitter', extern_uid: '123456') }
let_it_be(:current_user) { create(:admin) }
@@ -572,12 +574,6 @@ RSpec.describe 'Admin::Users' do
end
end
- def click_user_dropdown_toggle(user_id)
- page.within("[data-testid='user-actions-#{user_id}']") do
- find("[data-testid='dropdown-toggle']").click
- end
- end
-
def first_row
page.all('[role="row"]')[1]
end
@@ -592,14 +588,4 @@ RSpec.describe 'Admin::Users' do
click_link option
end
end
-
- def click_action_in_user_dropdown(user_id, action)
- click_user_dropdown_toggle(user_id)
-
- within find("[data-testid='user-actions-#{user_id}']") do
- find('li button', text: action).click
- end
-
- wait_for_requests
- end
end
diff --git a/spec/frontend/admin/users/components/user_actions_spec.js b/spec/frontend/admin/users/components/user_actions_spec.js
index 50471551268..43313424553 100644
--- a/spec/frontend/admin/users/components/user_actions_spec.js
+++ b/spec/frontend/admin/users/components/user_actions_spec.js
@@ -1,4 +1,5 @@
import { GlDropdownDivider } from '@gitlab/ui';
+import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import Actions from '~/admin/users/components/actions';
import AdminUserActions from '~/admin/users/components/user_actions.vue';
@@ -20,7 +21,7 @@ describe('AdminUserActions component', () => {
findUserActions(id).find('[data-testid="dropdown-toggle"]');
const findDropdownDivider = () => wrapper.findComponent(GlDropdownDivider);
- const initComponent = ({ actions = [] } = {}) => {
+ const initComponent = ({ actions = [], showButtonLabels } = {}) => {
wrapper = shallowMountExtended(AdminUserActions, {
propsData: {
user: {
@@ -28,6 +29,10 @@ describe('AdminUserActions component', () => {
actions,
},
paths,
+ showButtonLabels,
+ },
+ directives: {
+ GlTooltip: createMockDirective(),
},
});
};
@@ -144,4 +149,42 @@ describe('AdminUserActions component', () => {
});
});
});
+
+ describe('when `showButtonLabels` prop is `false`', () => {
+ beforeEach(() => {
+ initComponent({ actions: [EDIT, ...CONFIRMATION_ACTIONS] });
+ });
+
+ it('does not render "Edit" button label', () => {
+ const tooltip = getBinding(findEditButton().element, 'gl-tooltip');
+
+ expect(findEditButton().text()).toBe('');
+ expect(findEditButton().attributes('aria-label')).toBe(I18N_USER_ACTIONS.edit);
+ expect(tooltip).toBeDefined();
+ expect(tooltip.value).toBe(I18N_USER_ACTIONS.edit);
+ });
+
+ it('does not render "User administration" dropdown button label', () => {
+ expect(findActionsDropdown().props('text')).toBe(I18N_USER_ACTIONS.userAdministration);
+ expect(findActionsDropdown().props('textSrOnly')).toBe(true);
+ });
+ });
+
+ describe('when `showButtonLabels` prop is `true`', () => {
+ beforeEach(() => {
+ initComponent({ actions: [EDIT, ...CONFIRMATION_ACTIONS], showButtonLabels: true });
+ });
+
+ it('renders "Edit" button label', () => {
+ const tooltip = getBinding(findEditButton().element, 'gl-tooltip');
+
+ expect(findEditButton().text()).toBe(I18N_USER_ACTIONS.edit);
+ expect(tooltip).not.toBeDefined();
+ });
+
+ it('renders "User administration" dropdown button label', () => {
+ expect(findActionsDropdown().props('text')).toBe(I18N_USER_ACTIONS.userAdministration);
+ expect(findActionsDropdown().props('textSrOnly')).toBe(false);
+ });
+ });
});
diff --git a/spec/frontend/admin/users/index_spec.js b/spec/frontend/admin/users/index_spec.js
index 20b60bd8640..06dbadd6d3d 100644
--- a/spec/frontend/admin/users/index_spec.js
+++ b/spec/frontend/admin/users/index_spec.js
@@ -1,7 +1,8 @@
import { createWrapper } from '@vue/test-utils';
-import { initAdminUsersApp } from '~/admin/users';
+import { initAdminUsersApp, initAdminUserActions } from '~/admin/users';
import AdminUsersApp from '~/admin/users/components/app.vue';
-import { users, paths } from './mock_data';
+import UserActions from '~/admin/users/components/user_actions.vue';
+import { users, user, paths } from './mock_data';
describe('initAdminUsersApp', () => {
let wrapper;
@@ -14,15 +15,12 @@ describe('initAdminUsersApp', () => {
el.setAttribute('data-users', JSON.stringify(users));
el.setAttribute('data-paths', JSON.stringify(paths));
- document.body.appendChild(el);
-
wrapper = createWrapper(initAdminUsersApp(el));
});
afterEach(() => {
wrapper.destroy();
wrapper = null;
- el.remove();
el = null;
});
@@ -33,3 +31,31 @@ describe('initAdminUsersApp', () => {
});
});
});
+
+describe('initAdminUserActions', () => {
+ let wrapper;
+ let el;
+
+ const findUserActions = () => wrapper.find(UserActions);
+
+ beforeEach(() => {
+ el = document.createElement('div');
+ el.setAttribute('data-user', JSON.stringify(user));
+ el.setAttribute('data-paths', JSON.stringify(paths));
+
+ wrapper = createWrapper(initAdminUserActions(el));
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ el = null;
+ });
+
+ it('parses and passes props', () => {
+ expect(findUserActions().props()).toMatchObject({
+ user,
+ paths,
+ });
+ });
+});
diff --git a/spec/frontend/admin/users/mock_data.js b/spec/frontend/admin/users/mock_data.js
index e7cb4088bf1..ded3e6f7edf 100644
--- a/spec/frontend/admin/users/mock_data.js
+++ b/spec/frontend/admin/users/mock_data.js
@@ -18,6 +18,8 @@ export const users = [
},
];
+export const user = users[0];
+
export const paths = {
edit: '/admin/users/id/edit',
approve: '/admin/users/id/approve',
diff --git a/spec/helpers/users_helper_spec.rb b/spec/helpers/users_helper_spec.rb
index 7fc811c1ac6..480b1e2a0de 100644
--- a/spec/helpers/users_helper_spec.rb
+++ b/spec/helpers/users_helper_spec.rb
@@ -396,4 +396,22 @@ RSpec.describe UsersHelper do
end
end
end
+
+ describe '#admin_user_actions_data_attributes' do
+ subject(:data) { helper.admin_user_actions_data_attributes(user) }
+
+ before do
+ allow(helper).to receive(:current_user).and_return(user)
+ allow(Admin::UserEntity).to receive(:represent).and_call_original
+ end
+
+ it 'user matches the serialized json' do
+ expect(data[:user]).to be_valid_json
+ expect(Admin::UserEntity).to have_received(:represent).with(user, hash_including({ current_user: user }))
+ end
+
+ it 'paths matches the schema' do
+ expect(data[:paths]).to match_schema('entities/admin_users_data_attributes_paths')
+ end
+ end
end
diff --git a/spec/lib/gitlab/ci/reports/security/scanner_spec.rb b/spec/lib/gitlab/ci/reports/security/scanner_spec.rb
index 02bb72e2c67..99f5d4723d3 100644
--- a/spec/lib/gitlab/ci/reports/security/scanner_spec.rb
+++ b/spec/lib/gitlab/ci/reports/security/scanner_spec.rb
@@ -110,6 +110,7 @@ RSpec.describe Gitlab::Ci::Reports::Security::Scanner do
{ external_id: 'gemnasium-python', name: 'foo', vendor: 'bar' } | { external_id: 'bandit', name: 'foo', vendor: 'bar' } | 1
{ external_id: 'bandit', name: 'foo', vendor: 'bar' } | { external_id: 'semgrep', name: 'foo', vendor: 'bar' } | -1
{ external_id: 'semgrep', name: 'foo', vendor: 'bar' } | { external_id: 'unknown', name: 'foo', vendor: 'bar' } | -1
+ { external_id: 'gemnasium', name: 'foo', vendor: 'bar' } | { external_id: 'gemnasium', name: 'foo', vendor: nil } | 1
end
with_them do
diff --git a/spec/services/groups/group_links/destroy_service_spec.rb b/spec/services/groups/group_links/destroy_service_spec.rb
index 97fe23e9147..e63adc07313 100644
--- a/spec/services/groups/group_links/destroy_service_spec.rb
+++ b/spec/services/groups/group_links/destroy_service_spec.rb
@@ -24,7 +24,7 @@ RSpec.describe Groups::GroupLinks::DestroyService, '#execute' do
expect { subject.execute(link) }.to change { shared_group.shared_with_group_links.count }.from(1).to(0)
end
- it 'revokes project authorization' do
+ it 'revokes project authorization', :sidekiq_inline do
group.add_developer(user)
expect { subject.execute(link) }.to(
@@ -47,8 +47,8 @@ RSpec.describe Groups::GroupLinks::DestroyService, '#execute' do
it 'updates project authorization once per group' do
expect(GroupGroupLink).to receive(:delete).and_call_original
- expect(group).to receive(:refresh_members_authorized_projects).with(direct_members_only: true).once
- expect(another_group).to receive(:refresh_members_authorized_projects).with(direct_members_only: true).once
+ expect(group).to receive(:refresh_members_authorized_projects).with(direct_members_only: true, blocking: false).once
+ expect(another_group).to receive(:refresh_members_authorized_projects).with(direct_members_only: true, blocking: false).once
subject.execute(links)
end
diff --git a/spec/services/groups/group_links/update_service_spec.rb b/spec/services/groups/group_links/update_service_spec.rb
index 82c4a10f15a..31446c8e4bf 100644
--- a/spec/services/groups/group_links/update_service_spec.rb
+++ b/spec/services/groups/group_links/update_service_spec.rb
@@ -36,7 +36,7 @@ RSpec.describe Groups::GroupLinks::UpdateService, '#execute' do
expect(link.expires_at).to eq(expiry_date)
end
- it 'updates project permissions' do
+ it 'updates project permissions', :sidekiq_inline do
expect { subject }.to change { group_member_user.can?(:create_release, project) }.from(true).to(false)
end
diff --git a/spec/support/helpers/features/admin_users_helpers.rb b/spec/support/helpers/features/admin_users_helpers.rb
new file mode 100644
index 00000000000..99b19eedcff
--- /dev/null
+++ b/spec/support/helpers/features/admin_users_helpers.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module Spec
+ module Support
+ module Helpers
+ module Features
+ module AdminUsersHelpers
+ def click_user_dropdown_toggle(user_id)
+ page.within("[data-testid='user-actions-#{user_id}']") do
+ find("[data-testid='dropdown-toggle']").click
+ end
+ end
+
+ def click_action_in_user_dropdown(user_id, action)
+ click_user_dropdown_toggle(user_id)
+
+ within find("[data-testid='user-actions-#{user_id}']") do
+ find('li button', exact_text: action).click
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/workers/remove_expired_group_links_worker_spec.rb b/spec/workers/remove_expired_group_links_worker_spec.rb
index ff5f7b9db27..151bbb75226 100644
--- a/spec/workers/remove_expired_group_links_worker_spec.rb
+++ b/spec/workers/remove_expired_group_links_worker_spec.rb
@@ -51,7 +51,7 @@ RSpec.describe RemoveExpiredGroupLinksWorker do
subject.perform
end
- it 'removes project authorization' do
+ it 'removes project authorization', :sidekiq_inline do
shared_group = group_group_link.shared_group
shared_with_group = group_group_link.shared_with_group
project = create(:project, group: shared_group)