summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.rubocop_todo/layout/argument_alignment.yml25
-rw-r--r--GITLAB_KAS_VERSION2
-rw-r--r--app/assets/javascripts/ci/ci_variable_list/components/ci_variable_shared.vue15
-rw-r--r--app/assets/javascripts/ci/ci_variable_list/constants.js15
-rw-r--r--app/assets/javascripts/projects/commits/index.js16
-rw-r--r--app/assets/javascripts/super_sidebar/components/user_bar.vue12
-rw-r--r--app/helpers/sidebars_helper.rb21
-rw-r--r--app/workers/database/batched_background_migration/execution_worker.rb3
-rw-r--r--db/migrate/20230403023440_add_database_max_running_batched_background_migrations_to_application_settings.rb8
-rw-r--r--db/migrate/20230403023441_set_max_running_batched_background_migrations_for_gitlab_com.rb17
-rw-r--r--db/schema_migrations/202304030234401
-rw-r--r--db/schema_migrations/202304030234411
-rw-r--r--db/structure.sql1
-rw-r--r--doc/architecture/blueprints/cells/index.md14
-rw-r--r--doc/user/admin_area/reporting/git_abuse_rate_limit.md2
-rw-r--r--doc/user/group/reporting/git_abuse_rate_limit.md2
-rw-r--r--locale/gitlab.pot13
-rw-r--r--spec/features/commits_spec.rb22
-rw-r--r--spec/frontend/ci/ci_variable_list/components/ci_variable_shared_spec.js20
-rw-r--r--spec/frontend/super_sidebar/components/merge_request_menu_spec.js18
-rw-r--r--spec/frontend/super_sidebar/components/user_bar_spec.js31
-rw-r--r--spec/frontend/super_sidebar/mock_data.js10
-rw-r--r--spec/helpers/sidebars_helper_spec.rb45
-rw-r--r--spec/support/shared_examples/workers/batched_background_migration_execution_worker_shared_example.rb14
24 files changed, 239 insertions, 89 deletions
diff --git a/.rubocop_todo/layout/argument_alignment.yml b/.rubocop_todo/layout/argument_alignment.yml
index 880283839fe..af8f5a62980 100644
--- a/.rubocop_todo/layout/argument_alignment.yml
+++ b/.rubocop_todo/layout/argument_alignment.yml
@@ -1256,31 +1256,6 @@ Layout/ArgumentAlignment:
- 'ee/lib/slack/block_kit/app_home_opened.rb'
- 'ee/spec/components/billing/plan_component_spec.rb'
- 'ee/spec/components/namespaces/storage/pre_enforcement_alert_component_spec.rb'
- - 'ee/spec/controllers/ee/admin/sessions_controller_spec.rb'
- - 'ee/spec/controllers/ee/search_controller_spec.rb'
- - 'ee/spec/controllers/groups/analytics/cycle_analytics/stages_controller_spec.rb'
- - 'ee/spec/controllers/groups/epic_boards_controller_spec.rb'
- - 'ee/spec/controllers/groups/group_members_controller_spec.rb'
- - 'ee/spec/controllers/groups/omniauth_callbacks_controller_spec.rb'
- - 'ee/spec/controllers/groups/sso_controller_spec.rb'
- - 'ee/spec/controllers/operations_controller_spec.rb'
- - 'ee/spec/controllers/projects/approver_groups_controller_spec.rb'
- - 'ee/spec/controllers/projects/approvers_controller_spec.rb'
- - 'ee/spec/controllers/projects/branches_controller_spec.rb'
- - 'ee/spec/controllers/projects/environments_controller_spec.rb'
- - 'ee/spec/controllers/projects/imports_controller_spec.rb'
- - 'ee/spec/controllers/projects/iterations_controller_spec.rb'
- - 'ee/spec/controllers/projects/merge_requests/creations_controller_spec.rb'
- - 'ee/spec/controllers/projects/protected_environments_controller_spec.rb'
- - 'ee/spec/controllers/projects/repositories_controller_spec.rb'
- - 'ee/spec/controllers/projects/security/sast_configuration_controller_spec.rb'
- - 'ee/spec/controllers/projects/security/scanned_resources_controller_spec.rb'
- - 'ee/spec/controllers/projects/settings/ci_cd_controller_spec.rb'
- - 'ee/spec/controllers/projects/settings/merge_requests_controller_spec.rb'
- - 'ee/spec/controllers/projects/settings/operations_controller_spec.rb'
- - 'ee/spec/controllers/projects/vulnerability_feedback_controller_spec.rb'
- - 'ee/spec/controllers/projects_controller_spec.rb'
- - 'ee/spec/controllers/trials_controller_spec.rb'
- 'ee/spec/elastic/migrate/20220119120500_populate_commit_permissions_in_main_index_spec.rb'
- 'ee/spec/elastic/migrate/20221124090600_add_namespace_ancestry_ids_to_original_index_mapping_spec.rb'
- 'ee/spec/elastic/migrate/20221221110300_backfill_traversal_ids_to_blobs_and_wiki_blobs_spec.rb'
diff --git a/GITLAB_KAS_VERSION b/GITLAB_KAS_VERSION
index 332da5068c6..85ad38c582c 100644
--- a/GITLAB_KAS_VERSION
+++ b/GITLAB_KAS_VERSION
@@ -1 +1 @@
-v15.11.0-rc1
+v15.11.0-rc2
diff --git a/app/assets/javascripts/ci/ci_variable_list/components/ci_variable_shared.vue b/app/assets/javascripts/ci/ci_variable_list/components/ci_variable_shared.vue
index 418a22af406..ee2c0a771cf 100644
--- a/app/assets/javascripts/ci/ci_variable_list/components/ci_variable_shared.vue
+++ b/app/assets/javascripts/ci/ci_variable_list/components/ci_variable_shared.vue
@@ -9,6 +9,7 @@ import {
ENVIRONMENT_QUERY_LIMIT,
SORT_DIRECTIONS,
UPDATE_MUTATION_ACTION,
+ mapMutationActionToToast,
environmentFetchErrorText,
genericMutationErrorText,
variableFetchErrorText,
@@ -268,11 +269,15 @@ export default {
if (data.ciVariableMutation?.errors?.length) {
const { errors } = data.ciVariableMutation;
createAlert({ message: errors[0] });
- } else if (this.refetchAfterMutation) {
- // The writing to cache for admin variable is not working
- // because there is no ID in the cache at the top level.
- // We therefore need to manually refetch.
- this.$apollo.queries.ciVariables.refetch();
+ } else {
+ this.$toast.show(mapMutationActionToToast[mutationAction](variable.key));
+
+ if (this.refetchAfterMutation) {
+ // The writing to cache for admin variable is not working
+ // because there is no ID in the cache at the top level.
+ // We therefore need to manually refetch.
+ this.$apollo.queries.ciVariables.refetch();
+ }
}
} catch (e) {
createAlert({ message: genericMutationErrorText });
diff --git a/app/assets/javascripts/ci/ci_variable_list/constants.js b/app/assets/javascripts/ci/ci_variable_list/constants.js
index 03374162b79..c8f67bd3436 100644
--- a/app/assets/javascripts/ci/ci_variable_list/constants.js
+++ b/app/assets/javascripts/ci/ci_variable_list/constants.js
@@ -1,4 +1,4 @@
-import { __, s__ } from '~/locale';
+import { __, s__, sprintf } from '~/locale';
export const ADD_CI_VARIABLE_MODAL_ID = 'add-ci-variable';
export const ENVIRONMENT_QUERY_LIMIT = 30;
@@ -98,6 +98,19 @@ export const ADD_MUTATION_ACTION = 'add';
export const UPDATE_MUTATION_ACTION = 'update';
export const DELETE_MUTATION_ACTION = 'delete';
+export const ADD_VARIABLE_TOAST = (key) =>
+ sprintf(s__('CiVariable|Variable %{key} has been successfully added.'), { key });
+export const UPDATE_VARIABLE_TOAST = (key) =>
+ sprintf(s__('CiVariable|Variable %{key} has been updated.'), { key });
+export const DELETE_VARIABLE_TOAST = (key) =>
+ sprintf(s__('CiVariable|Variable %{key} has been deleted.'), { key });
+
+export const mapMutationActionToToast = {
+ [ADD_MUTATION_ACTION]: ADD_VARIABLE_TOAST,
+ [UPDATE_MUTATION_ACTION]: UPDATE_VARIABLE_TOAST,
+ [DELETE_MUTATION_ACTION]: DELETE_VARIABLE_TOAST,
+};
+
export const EXPANDED_VARIABLES_NOTE = __(
'%{codeStart}$%{codeEnd} will be treated as the start of a reference to another variable.',
);
diff --git a/app/assets/javascripts/projects/commits/index.js b/app/assets/javascripts/projects/commits/index.js
index f56884f605f..3179fcb14fd 100644
--- a/app/assets/javascripts/projects/commits/index.js
+++ b/app/assets/javascripts/projects/commits/index.js
@@ -35,6 +35,10 @@ export const initCommitsRefSwitcher = () => {
const { projectId, ref, commitsPath, refType } = el.dataset;
const commitsPathPrefix = commitsPath.match(COMMITS_PATH_REGEX)?.[0];
+ const generateRefDestinationUrl = (selectedRef, selectedRefType) => {
+ const commitsPathSuffix = selectedRefType ? `?ref_type=${selectedRefType}` : '';
+ return `${commitsPathPrefix}/${encodeURIComponent(selectedRef)}${commitsPathSuffix}`;
+ };
const useSymbolicRefNames = Boolean(refType);
return new Vue({
el,
@@ -48,15 +52,11 @@ export const initCommitsRefSwitcher = () => {
},
on: {
input(selected) {
- if (useSymbolicRefNames) {
- const matches = selected.match(/refs\/(heads|tags)\/(.+)/);
- if (matches) {
- visitUrl(`${commitsPathPrefix}/${matches[2]}?ref_type=${matches[1]}`);
- } else {
- visitUrl(`${commitsPathPrefix}/${selected}`);
- }
+ const matches = selected.match(/refs\/(heads|tags)\/(.+)/);
+ if (useSymbolicRefNames && matches) {
+ visitUrl(generateRefDestinationUrl(matches[2], matches[1]));
} else {
- visitUrl(`${commitsPathPrefix}/${selected}`);
+ visitUrl(generateRefDestinationUrl(selected));
}
},
},
diff --git a/app/assets/javascripts/super_sidebar/components/user_bar.vue b/app/assets/javascripts/super_sidebar/components/user_bar.vue
index 62161f2846a..750ec0aa0c6 100644
--- a/app/assets/javascripts/super_sidebar/components/user_bar.vue
+++ b/app/assets/javascripts/super_sidebar/components/user_bar.vue
@@ -70,6 +70,9 @@ export default {
v-gl-tooltip:super-sidebar.hover.bottom="$options.i18n.homepage"
:href="rootPath"
:title="$options.i18n.homepage"
+ data-track-action="click_link"
+ data-track-label="gitlab_logo_link"
+ data-track-property="nav_core_menu"
>
<img
v-if="sidebarData.logo_url"
@@ -120,6 +123,9 @@ export default {
:count="sidebarData.assigned_open_issues_count"
:href="sidebarData.issues_dashboard_path"
:label="$options.i18n.issues"
+ data-track-action="click_link"
+ data-track-label="issues_link"
+ data-track-property="nav_core_menu"
/>
<merge-request-menu
class="gl-flex-basis-third gl-display-block!"
@@ -131,6 +137,9 @@ export default {
icon="merge-request-open"
:count="sidebarData.total_merge_requests_count"
:label="$options.i18n.mergeRequests"
+ data-track-action="click_dropdown"
+ data-track-label="merge_requests_menu"
+ data-track-property="nav_core_menu"
/>
</merge-request-menu>
<counter
@@ -141,6 +150,9 @@ export default {
href="/dashboard/todos"
:label="$options.i18n.todoList"
data-qa-selector="todos_shortcut_button"
+ data-track-action="click_link"
+ data-track-label="todos_link"
+ data-track-property="nav_core_menu"
/>
</div>
</div>
diff --git a/app/helpers/sidebars_helper.rb b/app/helpers/sidebars_helper.rb
index 3769f03feb0..6a19943afa6 100644
--- a/app/helpers/sidebars_helper.rb
+++ b/app/helpers/sidebars_helper.rb
@@ -149,7 +149,12 @@ module SidebarsHelper
items: section[:menu_items].map do |item|
{
text: item[:title],
- href: item[:href]
+ href: item[:href],
+ extraAttrs: {
+ 'data-track-label': item[:id],
+ 'data-track-action': 'click_link',
+ 'data-track-property': 'nav_create_menu'
+ }
}
end
}
@@ -164,12 +169,22 @@ module SidebarsHelper
{
text: _('Assigned'),
href: merge_requests_dashboard_path(assignee_username: user.username),
- count: user_merge_requests_counts[:assigned]
+ count: user_merge_requests_counts[:assigned],
+ extraAttrs: {
+ 'data-track-action': 'click_link',
+ 'data-track-label': 'merge_requests_assigned',
+ 'data-track-property': 'nav_core_menu'
+ }
},
{
text: _('Review requests'),
href: merge_requests_dashboard_path(reviewer_username: user.username),
- count: user_merge_requests_counts[:review_requested]
+ count: user_merge_requests_counts[:review_requested],
+ extraAttrs: {
+ 'data-track-action': 'click_link',
+ 'data-track-label': 'merge_requests_to_review',
+ 'data-track-property': 'nav_core_menu'
+ }
}
]
}
diff --git a/app/workers/database/batched_background_migration/execution_worker.rb b/app/workers/database/batched_background_migration/execution_worker.rb
index 37b40c73ca6..53c92ab8969 100644
--- a/app/workers/database/batched_background_migration/execution_worker.rb
+++ b/app/workers/database/batched_background_migration/execution_worker.rb
@@ -11,7 +11,6 @@ module Database
INTERVAL_VARIANCE = 5.seconds.freeze
LEASE_TIMEOUT_MULTIPLIER = 3
- MAX_RUNNING_MIGRATIONS = 4
included do
data_consistency :always
@@ -21,7 +20,7 @@ module Database
class_methods do
def max_running_jobs
- MAX_RUNNING_MIGRATIONS
+ Gitlab::CurrentSettings.database_max_running_batched_background_migrations
end
# We have to overirde this one, as we want
diff --git a/db/migrate/20230403023440_add_database_max_running_batched_background_migrations_to_application_settings.rb b/db/migrate/20230403023440_add_database_max_running_batched_background_migrations_to_application_settings.rb
new file mode 100644
index 00000000000..136855403f9
--- /dev/null
+++ b/db/migrate/20230403023440_add_database_max_running_batched_background_migrations_to_application_settings.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+class AddDatabaseMaxRunningBatchedBackgroundMigrationsToApplicationSettings < Gitlab::Database::Migration[2.1]
+ def change
+ add_column :application_settings, :database_max_running_batched_background_migrations,
+ :integer, null: false, default: 2
+ end
+end
diff --git a/db/migrate/20230403023441_set_max_running_batched_background_migrations_for_gitlab_com.rb b/db/migrate/20230403023441_set_max_running_batched_background_migrations_for_gitlab_com.rb
new file mode 100644
index 00000000000..6e8d423e679
--- /dev/null
+++ b/db/migrate/20230403023441_set_max_running_batched_background_migrations_for_gitlab_com.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class SetMaxRunningBatchedBackgroundMigrationsForGitlabCom < Gitlab::Database::Migration[2.1]
+ restrict_gitlab_migration gitlab_schema: :gitlab_main
+
+ def up
+ return unless Gitlab.com? && !Gitlab.jh?
+
+ execute 'UPDATE application_settings SET database_max_running_batched_background_migrations = 4'
+ end
+
+ def down
+ return unless Gitlab.com? && !Gitlab.jh?
+
+ execute 'UPDATE application_settings SET database_max_running_batched_background_migrations = 2'
+ end
+end
diff --git a/db/schema_migrations/20230403023440 b/db/schema_migrations/20230403023440
new file mode 100644
index 00000000000..2705965c70c
--- /dev/null
+++ b/db/schema_migrations/20230403023440
@@ -0,0 +1 @@
+0cd1bcb2a2a02c09e7fd9271f815522adc1c20a1b39953349cfe2fff4541ed66 \ No newline at end of file
diff --git a/db/schema_migrations/20230403023441 b/db/schema_migrations/20230403023441
new file mode 100644
index 00000000000..6e259200d0e
--- /dev/null
+++ b/db/schema_migrations/20230403023441
@@ -0,0 +1 @@
+e9684618df40260a7bae8b3dc217f3dd662e1cbc5db8b8a6d0334716e034c77b \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 98b006c0ac3..89de87610b7 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -11756,6 +11756,7 @@ CREATE TABLE application_settings (
database_apdex_settings jsonb,
encrypted_openai_api_key bytea,
encrypted_openai_api_key_iv bytea,
+ database_max_running_batched_background_migrations integer DEFAULT 2 NOT NULL,
CONSTRAINT app_settings_container_reg_cleanup_tags_max_list_size_positive CHECK ((container_registry_cleanup_tags_service_max_list_size >= 0)),
CONSTRAINT app_settings_container_registry_pre_import_tags_rate_positive CHECK ((container_registry_pre_import_tags_rate >= (0)::numeric)),
CONSTRAINT app_settings_dep_proxy_ttl_policies_worker_capacity_positive CHECK ((dependency_proxy_ttl_group_policy_worker_capacity >= 0)),
diff --git a/doc/architecture/blueprints/cells/index.md b/doc/architecture/blueprints/cells/index.md
index 1fa53222827..516860cd6d6 100644
--- a/doc/architecture/blueprints/cells/index.md
+++ b/doc/architecture/blueprints/cells/index.md
@@ -75,7 +75,6 @@ Organizations work under the following assumptions:
#### Organization properties
- Top-level namespaces belong to organizations
-- Users can be members of different organizations
- Organizations are isolated from each other by default meaning that cross-namespace features will only work for namespaces that exist within a single organization
- User namespaces must not belong to an organization
@@ -83,9 +82,7 @@ Discouraged synonyms: Billable entities, customers
### Top-Level namespace
-A top-level namespace is the logical object container in the code that represents all groups, subgroups and projects that belong to an organization.
-
-A top-level namespace is the root of nested collection namespaces and projects. The namespace and its related entities form a tree-like hierarchy: Namespaces are the nodes of the tree, projects are the leaves.
+Top-level namespace is the name given to the top most group of all other groups. Groups and projects are nested underneath the top-level namespace.
Example:
@@ -94,7 +91,9 @@ Example:
- `gitlab-org` is a `top-level namespace`; the root for all groups and projects of an organization
- `gitlab` is a `project`; a project of the organization.
-Top-level namespaces may [be replaced by organizations](https://gitlab.com/gitlab-org/gitlab/-/issues/368237#high-level-goals). This proposal only uses the term top-level namespaces as the organization definition is ongoing.
+The top-level namespace has served as the defacto Organization entity. With the creation of Organization, top-level namespaces will be [nested underneath Organizations](https://gitlab.com/gitlab-org/gitlab/-/issues/394796).
+
+Over time there won't be a distinction between a top level namespace and a group. All features that make Top-level namespaces different from groups will move to Organization.
Discouraged synonyms: Root-level namespace
@@ -107,14 +106,15 @@ Discouraged synonyms: Root-level namespace
### Users
-Users are available globally and not restricted to a single Cell. Users can be members of many different organizations with varying permissions. Inside organizations, users can create multiple top-level namespaces. User activity is not limited to a single organization but their contributions (for example TODOs) are only aggregated within an organization. This avoids the need for aggregating across cells.
+Users are available globally and not restricted to a single Cell. Users belong to a single organization, but can participate in many organizations through group and project membership with varying permissions. Inside organizations, users can create multiple top-level namespaces. User activity is not limited to a single organization but their contributions (for example TODOs) are only aggregated within an organization. This avoids the need for aggregating across cells.
#### User properties
- Users are shared globally across all Cells
- Users can create multiple top-level namespaces
- Users can be a member of multiple top-level namespaces
-- Users can be a member of multiple organizations
+- Users belong to one organization. See [!395736](https://gitlab.com/gitlab-org/gitlab/-/issues/395736)
+- Users can be members of groups and projects in different organizations
- Users can administer organizations
- User activity is aggregated in an organization
- Every user has one personal namespace
diff --git a/doc/user/admin_area/reporting/git_abuse_rate_limit.md b/doc/user/admin_area/reporting/git_abuse_rate_limit.md
index a6c3ff3ae32..83b28404714 100644
--- a/doc/user/admin_area/reporting/git_abuse_rate_limit.md
+++ b/doc/user/admin_area/reporting/git_abuse_rate_limit.md
@@ -11,7 +11,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
This is the administration documentation. For information about Git abuse rate limiting at the group level, see the [group-level documentation](../../group/reporting/git_abuse_rate_limit.md).
-Git abuse rate limiting is a feature to automatically [ban users](../moderate_users.md#ban-and-unban-users) who download or clone more than a specified number of repositories in any project in the instance in a given time frame. Banned users cannot sign in to the instance and cannot access any non-public group via HTTP or SSH. The rate limit also applies to users who authenticate with a [personal](../../../user/profile/personal_access_tokens.md) or [group access token](../../../user/group/settings/group_access_tokens.md).
+Git abuse rate limiting is a feature to automatically [ban users](../moderate_users.md#ban-and-unban-users) who download, clone, or fork more than a specified number of repositories in any project in the instance in a given time frame. Banned users cannot sign in to the instance and cannot access any non-public group via HTTP or SSH. The rate limit also applies to users who authenticate with a [personal](../../../user/profile/personal_access_tokens.md) or [group access token](../../../user/group/settings/group_access_tokens.md).
Git abuse rate limiting does not apply to instance administrators, [deploy tokens](../../../user/project/deploy_tokens/index.md), or [deploy keys](../../../user/project/deploy_keys/index.md).
diff --git a/doc/user/group/reporting/git_abuse_rate_limit.md b/doc/user/group/reporting/git_abuse_rate_limit.md
index 8ccaea2b614..d5c44f4e245 100644
--- a/doc/user/group/reporting/git_abuse_rate_limit.md
+++ b/doc/user/group/reporting/git_abuse_rate_limit.md
@@ -13,7 +13,7 @@ On self-managed GitLab, by default this feature is not available. To make it ava
This is the group-level documentation. For self-managed instances, see the [administration documentation](../../admin_area/reporting/git_abuse_rate_limit.md).
-Git abuse rate limiting is a feature to automatically ban users who download or clone more than a specified number of repositories of a group in a given time frame. Banned users cannot access the top-level group or any of its non-public subgroups via HTTP or SSH. The rate limit also applies to users who authenticate with a [personal](../../../user/profile/personal_access_tokens.md) or [group access token](../../../user/group/settings/group_access_tokens.md). Access to unrelated groups is unaffected.
+Git abuse rate limiting is a feature to automatically ban users who download, clone, or fork more than a specified number of repositories of a group in a given time frame. Banned users cannot access the top-level group or any of its non-public subgroups via HTTP or SSH. The rate limit also applies to users who authenticate with a [personal](../../../user/profile/personal_access_tokens.md) or [group access token](../../../user/group/settings/group_access_tokens.md). Access to unrelated groups is unaffected.
Git abuse rate limiting does not apply to top-level group owners, [deploy tokens](../../../user/project/deploy_tokens/index.md), or [deploy keys](../../../user/project/deploy_keys/index.md).
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index f3efaded685..1aaaf6c9ab5 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -2886,7 +2886,7 @@ msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
-msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgid "AdminSettings|Configure limits on the number of repositories users can download, clone, or fork in a given time."
msgstr ""
msgid "AdminSettings|Configure product analytics to track events within your project applications."
@@ -9203,6 +9203,15 @@ msgstr ""
msgid "CiVariable|Search environments"
msgstr ""
+msgid "CiVariable|Variable %{key} has been deleted."
+msgstr ""
+
+msgid "CiVariable|Variable %{key} has been successfully added."
+msgstr ""
+
+msgid "CiVariable|Variable %{key} has been updated."
+msgstr ""
+
msgid "Classification Label (optional)"
msgstr ""
@@ -20643,7 +20652,7 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}What are compliance frameworks?%{linkEnd}"
msgstr ""
-msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgid "GroupSettings|Configure limits on the number of repositories users can download, clone, or fork in a given time."
msgstr ""
msgid "GroupSettings|Custom project templates"
diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb
index eafe74f4b0b..aacba6d2af8 100644
--- a/spec/features/commits_spec.rb
+++ b/spec/features/commits_spec.rb
@@ -165,10 +165,24 @@ RSpec.describe 'Commits', feature_category: :source_code_management do
context 'viewing commits for a branch' do
let(:branch_name) { 'master' }
+ let(:ref_selector) { '.ref-selector' }
+ let(:ref_with_hash) { 'ref-#-hash' }
+
+ def switch_ref_to(ref_name)
+ first(ref_selector).click
+ wait_for_requests
+
+ page.within ref_selector do
+ fill_in 'Search by Git revision', with: ref_name
+ wait_for_requests
+ find('li', text: ref_name, match: :prefer_exact).click
+ end
+ end
before do
project.add_maintainer(user)
sign_in(user)
+ project.repository.create_branch(ref_with_hash, branch_name)
visit project_commits_path(project, branch_name)
end
@@ -180,11 +194,17 @@ RSpec.describe 'Commits', feature_category: :source_code_management do
end
end
+ it 'switches ref to ref containing a hash', :js do
+ switch_ref_to(ref_with_hash)
+
+ expect(page).to have_selector ref_selector, text: ref_with_hash
+ end
+
it 'shows the ref switcher with the multi-file editor enabled', :js do
set_cookie('new_repo', 'true')
visit project_commits_path(project, branch_name)
- expect(find('.ref-selector')).to have_content branch_name
+ expect(find(ref_selector)).to have_content branch_name
end
end
diff --git a/spec/frontend/ci/ci_variable_list/components/ci_variable_shared_spec.js b/spec/frontend/ci/ci_variable_list/components/ci_variable_shared_spec.js
index da67bcf786d..66b41223b33 100644
--- a/spec/frontend/ci/ci_variable_list/components/ci_variable_shared_spec.js
+++ b/spec/frontend/ci/ci_variable_list/components/ci_variable_shared_spec.js
@@ -25,6 +25,7 @@ import {
environmentFetchErrorText,
genericMutationErrorText,
variableFetchErrorText,
+ mapMutationActionToToast,
} from '~/ci/ci_variable_list/constants';
import {
@@ -66,6 +67,8 @@ describe('Ci Variable Shared Component', () => {
let mockEnvironments;
let mockVariables;
+ const mockToastShow = jest.fn();
+
const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
const findCiTable = () => wrapper.findComponent(GlTable);
const findCiSettings = () => wrapper.findComponent(ciVariableSettings);
@@ -95,6 +98,11 @@ describe('Ci Variable Shared Component', () => {
},
apolloProvider: mockApollo,
stubs: { ciVariableSettings, ciVariableTable },
+ mocks: {
+ $toast: {
+ show: mockToastShow,
+ },
+ },
});
if (!isLoading) {
@@ -295,9 +303,9 @@ describe('Ci Variable Shared Component', () => {
${'update'} | ${groupProps.mutationData[UPDATE_MUTATION_ACTION]} | ${'update-variable'}
${'delete'} | ${groupProps.mutationData[DELETE_MUTATION_ACTION]} | ${'delete-variable'}
`(
- 'calls the right mutation from propsData when user performs $actionName variable',
- async ({ event, mutation }) => {
- jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue();
+ 'calls the mutation from propsData and shows a toast when user performs $actionName variable',
+ async ({ event, mutation, actionName }) => {
+ jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue({ data: {} });
await findCiSettings().vm.$emit(event, newVariable);
@@ -310,6 +318,12 @@ describe('Ci Variable Shared Component', () => {
variable: newVariable,
},
});
+
+ await nextTick();
+
+ expect(mockToastShow).toHaveBeenCalledWith(
+ mapMutationActionToToast[actionName](newVariable.key),
+ );
},
);
diff --git a/spec/frontend/super_sidebar/components/merge_request_menu_spec.js b/spec/frontend/super_sidebar/components/merge_request_menu_spec.js
index fe87c4be9c3..75998ee6c55 100644
--- a/spec/frontend/super_sidebar/components/merge_request_menu_spec.js
+++ b/spec/frontend/super_sidebar/components/merge_request_menu_spec.js
@@ -8,7 +8,7 @@ describe('MergeRequestMenu component', () => {
const findGlBadge = (at) => wrapper.findAllComponents(GlBadge).at(at);
const findGlDisclosureDropdown = () => wrapper.findComponent(GlDisclosureDropdown);
- const findLink = () => wrapper.findByRole('link');
+ const findLink = (name) => wrapper.findByRole('link', { name });
const createWrapper = () => {
wrapper = mountExtended(MergeRequestMenu, {
@@ -27,11 +27,17 @@ describe('MergeRequestMenu component', () => {
expect(findGlDisclosureDropdown().props('items')).toBe(mergeRequestMenuGroup);
});
- it('renders item text and count in link', () => {
- const { text, href, count } = mergeRequestMenuGroup[0].items[0];
- expect(findLink().text()).toContain(text);
- expect(findLink().text()).toContain(String(count));
- expect(findLink().attributes('href')).toBe(href);
+ it.each(mergeRequestMenuGroup[0].items)('renders item text and count in link', (item) => {
+ const index = mergeRequestMenuGroup[0].items.indexOf(item);
+ const { text, href, count, extraAttrs } = mergeRequestMenuGroup[0].items[index];
+ const link = findLink(new RegExp(text));
+
+ expect(link.text()).toContain(text);
+ expect(link.text()).toContain(String(count));
+ expect(link.attributes('href')).toBe(href);
+ expect(link.attributes('data-track-action')).toBe(extraAttrs['data-track-action']);
+ expect(link.attributes('data-track-label')).toBe(extraAttrs['data-track-label']);
+ expect(link.attributes('data-track-property')).toBe(extraAttrs['data-track-property']);
});
it('renders item count string in badge', () => {
diff --git a/spec/frontend/super_sidebar/components/user_bar_spec.js b/spec/frontend/super_sidebar/components/user_bar_spec.js
index 48e62c3564d..e078d5a943c 100644
--- a/spec/frontend/super_sidebar/components/user_bar_spec.js
+++ b/spec/frontend/super_sidebar/components/user_bar_spec.js
@@ -18,6 +18,9 @@ describe('UserBar component', () => {
const findCreateMenu = () => wrapper.findComponent(CreateMenu);
const findCounter = (at) => wrapper.findAllComponents(Counter).at(at);
+ const findIssuesCounter = () => findCounter(0);
+ const findMRsCounter = () => findCounter(1);
+ const findTodosCounter = () => findCounter(2);
const findMergeRequestMenu = () => wrapper.findComponent(MergeRequestMenu);
const findBrandLogo = () => wrapper.findByTestId('brand-header-custom-logo');
const findSearchButton = () => wrapper.findByTestId('super-sidebar-search-button');
@@ -60,20 +63,32 @@ describe('UserBar component', () => {
});
it('renders issues counter', () => {
- expect(findCounter(0).props('count')).toBe(sidebarData.assigned_open_issues_count);
- expect(findCounter(0).props('href')).toBe(sidebarData.issues_dashboard_path);
- expect(findCounter(0).props('label')).toBe(__('Issues'));
+ const isuesCounter = findIssuesCounter();
+ expect(isuesCounter.props('count')).toBe(sidebarData.assigned_open_issues_count);
+ expect(isuesCounter.props('href')).toBe(sidebarData.issues_dashboard_path);
+ expect(isuesCounter.props('label')).toBe(__('Issues'));
+ expect(isuesCounter.attributes('data-track-action')).toBe('click_link');
+ expect(isuesCounter.attributes('data-track-label')).toBe('issues_link');
+ expect(isuesCounter.attributes('data-track-property')).toBe('nav_core_menu');
});
it('renders merge requests counter', () => {
- expect(findCounter(1).props('count')).toBe(sidebarData.total_merge_requests_count);
- expect(findCounter(1).props('label')).toBe(__('Merge requests'));
+ const mrsCounter = findMRsCounter();
+ expect(mrsCounter.props('count')).toBe(sidebarData.total_merge_requests_count);
+ expect(mrsCounter.props('label')).toBe(__('Merge requests'));
+ expect(mrsCounter.attributes('data-track-action')).toBe('click_dropdown');
+ expect(mrsCounter.attributes('data-track-label')).toBe('merge_requests_menu');
+ expect(mrsCounter.attributes('data-track-property')).toBe('nav_core_menu');
});
it('renders todos counter', () => {
- expect(findCounter(2).props('count')).toBe(sidebarData.todos_pending_count);
- expect(findCounter(2).props('href')).toBe('/dashboard/todos');
- expect(findCounter(2).props('label')).toBe(__('To-Do list'));
+ const todosCounter = findTodosCounter();
+ expect(todosCounter.props('count')).toBe(sidebarData.todos_pending_count);
+ expect(todosCounter.props('href')).toBe('/dashboard/todos');
+ expect(todosCounter.props('label')).toBe(__('To-Do list'));
+ expect(todosCounter.attributes('data-track-action')).toBe('click_link');
+ expect(todosCounter.attributes('data-track-label')).toBe('todos_link');
+ expect(todosCounter.attributes('data-track-property')).toBe('nav_core_menu');
});
it('renders branding logo', () => {
diff --git a/spec/frontend/super_sidebar/mock_data.js b/spec/frontend/super_sidebar/mock_data.js
index 0a2dc5216d6..fdf88e910e3 100644
--- a/spec/frontend/super_sidebar/mock_data.js
+++ b/spec/frontend/super_sidebar/mock_data.js
@@ -49,11 +49,21 @@ export const mergeRequestMenuGroup = [
text: 'Assigned',
href: '/dashboard/merge_requests?assignee_username=root',
count: 4,
+ extraAttrs: {
+ 'data-track-action': 'click_link',
+ 'data-track-label': 'merge_requests_assigned',
+ 'data-track-property': 'nav_core_menu',
+ },
},
{
text: 'Review requests',
href: '/dashboard/merge_requests?reviewer_username=root',
count: 0,
+ extraAttrs: {
+ 'data-track-action': 'click_link',
+ 'data-track-label': 'merge_requests_to_review',
+ 'data-track-property': 'nav_core_menu',
+ },
},
],
},
diff --git a/spec/helpers/sidebars_helper_spec.rb b/spec/helpers/sidebars_helper_spec.rb
index f7260f14927..76e87667e25 100644
--- a/spec/helpers/sidebars_helper_spec.rb
+++ b/spec/helpers/sidebars_helper_spec.rb
@@ -151,12 +151,22 @@ RSpec.describe SidebarsHelper, feature_category: :navigation do
{
text: _('Assigned'),
href: merge_requests_dashboard_path(assignee_username: user.username),
- count: 4
+ count: 4,
+ extraAttrs: {
+ 'data-track-action': 'click_link',
+ 'data-track-label': 'merge_requests_assigned',
+ 'data-track-property': 'nav_core_menu'
+ }
},
{
text: _('Review requests'),
href: merge_requests_dashboard_path(reviewer_username: user.username),
- count: 0
+ count: 0,
+ extraAttrs: {
+ 'data-track-action': 'click_link',
+ 'data-track-label': 'merge_requests_to_review',
+ 'data-track-property': 'nav_core_menu'
+ }
}
]
}
@@ -164,19 +174,26 @@ RSpec.describe SidebarsHelper, feature_category: :navigation do
end
it 'returns "Create new" menu groups without headers', :use_clean_rails_memory_store_caching do
+ extra_attrs = { "data-track-action": "click_link", "data-track-property": "nav_create_menu" }
+
expect(subject[:create_new_menu_groups]).to eq([
{
name: "",
items: [
- { href: "/projects/new", text: "New project/repository" },
- { href: "/groups/new", text: "New group" },
- { href: "/-/snippets/new", text: "New snippet" }
+ { href: "/projects/new", text: "New project/repository",
+ extraAttrs: extra_attrs.merge("data-track-label": "general_new_project") },
+ { href: "/groups/new", text: "New group",
+ extraAttrs: extra_attrs.merge("data-track-label": "general_new_group") },
+ { href: "/-/snippets/new", text: "New snippet",
+ extraAttrs: extra_attrs.merge("data-track-label": "general_new_snippet") }
]
}
])
end
it 'returns "Create new" menu groups with headers', :use_clean_rails_memory_store_caching do
+ extra_attrs = { "data-track-action": "click_link", "data-track-property": "nav_create_menu" }
+
allow(group).to receive(:persisted?).and_return(true)
allow(helper).to receive(:can?).and_return(true)
@@ -184,17 +201,23 @@ RSpec.describe SidebarsHelper, feature_category: :navigation do
a_hash_including(
name: "In this group",
items: array_including(
- { href: "/projects/new", text: "New project/repository" },
- { href: "/groups/new#create-group-pane", text: "New subgroup" },
- { href: '', text: "Invite members" }
+ { href: "/projects/new", text: "New project/repository",
+ extraAttrs: extra_attrs.merge("data-track-label": "new_project") },
+ { href: "/groups/new#create-group-pane", text: "New subgroup",
+ extraAttrs: extra_attrs.merge("data-track-label": "new_subgroup") },
+ { href: "", text: "Invite members",
+ extraAttrs: extra_attrs.merge("data-track-label": "invite") }
)
),
a_hash_including(
name: "In GitLab",
items: array_including(
- { href: "/projects/new", text: "New project/repository" },
- { href: "/groups/new", text: "New group" },
- { href: "/-/snippets/new", text: "New snippet" }
+ { href: "/projects/new", text: "New project/repository",
+ extraAttrs: extra_attrs.merge("data-track-label": "general_new_project") },
+ { href: "/groups/new", text: "New group",
+ extraAttrs: extra_attrs.merge("data-track-label": "general_new_group") },
+ { href: "/-/snippets/new", text: "New snippet",
+ extraAttrs: extra_attrs.merge("data-track-label": "general_new_snippet") }
)
)
)
diff --git a/spec/support/shared_examples/workers/batched_background_migration_execution_worker_shared_example.rb b/spec/support/shared_examples/workers/batched_background_migration_execution_worker_shared_example.rb
index e224b71da91..095c32c3136 100644
--- a/spec/support/shared_examples/workers/batched_background_migration_execution_worker_shared_example.rb
+++ b/spec/support/shared_examples/workers/batched_background_migration_execution_worker_shared_example.rb
@@ -50,14 +50,20 @@ RSpec.shared_examples 'batched background migrations execution worker' do
end
describe '.max_running_jobs' do
- it 'returns MAX_RUNNING_MIGRATIONS' do
- expect(described_class.max_running_jobs).to eq(described_class::MAX_RUNNING_MIGRATIONS)
+ it 'returns database_max_running_batched_background_migrations application setting' do
+ stub_application_setting(database_max_running_batched_background_migrations: 3)
+
+ expect(described_class.max_running_jobs)
+ .to eq(Gitlab::CurrentSettings.database_max_running_batched_background_migrations)
end
end
describe '#max_running_jobs' do
- it 'returns MAX_RUNNING_MIGRATIONS' do
- expect(described_class.new.max_running_jobs).to eq(described_class::MAX_RUNNING_MIGRATIONS)
+ it 'returns database_max_running_batched_background_migrations application setting' do
+ stub_application_setting(database_max_running_batched_background_migrations: 3)
+
+ expect(described_class.new.max_running_jobs)
+ .to eq(Gitlab::CurrentSettings.database_max_running_batched_background_migrations)
end
end