diff options
83 files changed, 919 insertions, 215 deletions
diff --git a/.gitlab/CODEOWNERS b/.gitlab/CODEOWNERS index 4a9aecc0225..2d8e4d82053 100644 --- a/.gitlab/CODEOWNERS +++ b/.gitlab/CODEOWNERS @@ -332,7 +332,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab /doc/api/graphql/sample_issue_boards.md @msedlakjakubowski /doc/api/group_access_tokens.md @eread /doc/api/group_activity_analytics.md @fneill -/doc/api/group_badges.md @eread +/doc/api/group_badges.md @fneill /doc/api/group_boards.md @msedlakjakubowski /doc/api/group_clusters.md @sselhorn /doc/api/group_import_export.md @ngaskill @@ -344,7 +344,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab /doc/api/group_relations_export.md @ngaskill /doc/api/group_repository_storage_moves.md @aqualls /doc/api/group_wikis.md @aqualls -/doc/api/groups.md @eread +/doc/api/groups.md @fneill /doc/api/import.md @ngaskill /doc/api/index.md @kpaizee /doc/api/instance_clusters.md @sselhorn @@ -705,7 +705,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab /doc/user/group/epics/index.md @msedlakjakubowski /doc/user/group/epics/linked_epics.md @msedlakjakubowski /doc/user/group/epics/manage_epics.md @msedlakjakubowski -/doc/user/group/index.md @eread +/doc/user/group/index.md @fneill /doc/user/group/import/index.md @ngaskill /doc/user/group/insights/index.md @fneill /doc/user/group/issues_analytics/index.md @msedlakjakubowski @@ -718,7 +718,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab /doc/user/group/saml_sso/scim_setup.md @eread /doc/user/group/settings/group_access_tokens.md @eread /doc/user/group/settings/import_export.md @ngaskill -/doc/user/group/subgroups/index.md @eread +/doc/user/group/subgroups/index.md @fneill /doc/user/group/value_stream_analytics/index.md @fneill /doc/user/infrastructure/clusters/ @sselhorn /doc/user/infrastructure/clusters/manage/management_project_applications/apparmor.md @ngaskill @@ -761,7 +761,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab /doc/user/project/issues/ @msedlakjakubowski /doc/user/project/issues/csv_import.md @ngaskill /doc/user/project/labels.md @msedlakjakubowski -/doc/user/project/members/index.md @eread +/doc/user/project/members/index.md @fneill /doc/user/project/members/share_project_with_groups.md @fneill /doc/user/project/merge_requests/ @aqualls /doc/user/project/merge_requests/accessibility_testing.md @eread diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 001b3e376bb..a51e9cc1f7c 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -113,17 +113,6 @@ Lint/RedundantCopDisableDirective: Lint/UselessMethodDefinition: Enabled: false -# Offense count: 11 -# Configuration parameters: EnforcedStyle. -# SupportedStyles: lowercase, uppercase -Naming/HeredocDelimiterCase: - Exclude: - - 'spec/lib/gitlab/diff/parser_spec.rb' - - 'spec/lib/json_web_token/rsa_token_spec.rb' - - 'spec/models/commit_spec.rb' - - 'spec/support/helpers/repo_helpers.rb' - - 'spec/support/helpers/seed_repo.rb' - # Offense count: 321 # Configuration parameters: ForbiddenDelimiters. # ForbiddenDelimiters: (?-mix:(^|\s)(EO[A-Z]{1}|END)(\s|$)) diff --git a/.rubocop_todo/naming/heredoc_delimiter_case.yml b/.rubocop_todo/naming/heredoc_delimiter_case.yml new file mode 100644 index 00000000000..8779831da21 --- /dev/null +++ b/.rubocop_todo/naming/heredoc_delimiter_case.yml @@ -0,0 +1,8 @@ +--- +Naming/HeredocDelimiterCase: + Exclude: + - 'spec/lib/gitlab/diff/parser_spec.rb' + - 'spec/lib/json_web_token/rsa_token_spec.rb' + - 'spec/models/commit_spec.rb' + - 'spec/support/helpers/repo_helpers.rb' + - 'spec/support/helpers/seed_repo.rb' diff --git a/app/assets/javascripts/access_tokens/graphql/queries/get_projects.query.graphql b/app/assets/javascripts/access_tokens/graphql/queries/get_projects.query.graphql index cdc8a952ead..a5fc70b9ca6 100644 --- a/app/assets/javascripts/access_tokens/graphql/queries/get_projects.query.graphql +++ b/app/assets/javascripts/access_tokens/graphql/queries/get_projects.query.graphql @@ -1,4 +1,4 @@ -#import "~/graphql_shared/fragments/pageInfo.fragment.graphql" +#import "~/graphql_shared/fragments/page_info.fragment.graphql" query accessTokensGetProjects( $search: String = "" diff --git a/app/assets/javascripts/analytics/usage_trends/graphql/queries/usage_count.query.graphql b/app/assets/javascripts/analytics/usage_trends/graphql/queries/usage_count.query.graphql index 2a5546efb68..f9311626cc3 100644 --- a/app/assets/javascripts/analytics/usage_trends/graphql/queries/usage_count.query.graphql +++ b/app/assets/javascripts/analytics/usage_trends/graphql/queries/usage_count.query.graphql @@ -1,4 +1,4 @@ -#import "~/graphql_shared/fragments/pageInfo.fragment.graphql" +#import "~/graphql_shared/fragments/page_info.fragment.graphql" #import "../fragments/count.fragment.graphql" query getCount($identifier: MeasurementIdentifier!, $first: Int, $after: String) { diff --git a/app/assets/javascripts/analytics/usage_trends/graphql/queries/users.query.graphql b/app/assets/javascripts/analytics/usage_trends/graphql/queries/users.query.graphql index 7c02ac49a42..d7638458b03 100644 --- a/app/assets/javascripts/analytics/usage_trends/graphql/queries/users.query.graphql +++ b/app/assets/javascripts/analytics/usage_trends/graphql/queries/users.query.graphql @@ -1,4 +1,4 @@ -#import "~/graphql_shared/fragments/pageInfo.fragment.graphql" +#import "~/graphql_shared/fragments/page_info.fragment.graphql" #import "../fragments/count.fragment.graphql" query getUsersCount($first: Int, $after: String) { diff --git a/app/assets/javascripts/boards/graphql/group_projects.query.graphql b/app/assets/javascripts/boards/graphql/group_projects.query.graphql index 0da14d0b872..e0a3cb0ee21 100644 --- a/app/assets/javascripts/boards/graphql/group_projects.query.graphql +++ b/app/assets/javascripts/boards/graphql/group_projects.query.graphql @@ -1,4 +1,4 @@ -#import "~/graphql_shared/fragments/pageInfo.fragment.graphql" +#import "~/graphql_shared/fragments/page_info.fragment.graphql" query boardsGetGroupProjects($fullPath: ID!, $search: String, $after: String) { group(fullPath: $fullPath) { diff --git a/app/assets/javascripts/clusters/agents/graphql/queries/get_cluster_agent.query.graphql b/app/assets/javascripts/clusters/agents/graphql/queries/get_cluster_agent.query.graphql index 3610662afc0..d7a8e447071 100644 --- a/app/assets/javascripts/clusters/agents/graphql/queries/get_cluster_agent.query.graphql +++ b/app/assets/javascripts/clusters/agents/graphql/queries/get_cluster_agent.query.graphql @@ -1,4 +1,4 @@ -#import "~/graphql_shared/fragments/pageInfo.fragment.graphql" +#import "~/graphql_shared/fragments/page_info.fragment.graphql" #import "../fragments/cluster_agent_token.fragment.graphql" query getClusterAgent( diff --git a/app/assets/javascripts/clusters_list/graphql/queries/get_agents.query.graphql b/app/assets/javascripts/clusters_list/graphql/queries/get_agents.query.graphql index 7743ffba5de..76920a0aef4 100644 --- a/app/assets/javascripts/clusters_list/graphql/queries/get_agents.query.graphql +++ b/app/assets/javascripts/clusters_list/graphql/queries/get_agents.query.graphql @@ -1,4 +1,4 @@ -#import "~/graphql_shared/fragments/pageInfo.fragment.graphql" +#import "~/graphql_shared/fragments/page_info.fragment.graphql" #import "../fragments/cluster_agent.fragment.graphql" query getAgents( diff --git a/app/assets/javascripts/graphql_shared/fragments/pageInfo.fragment.graphql b/app/assets/javascripts/graphql_shared/fragments/page_info.fragment.graphql index e6f5d7db11a..e6f5d7db11a 100644 --- a/app/assets/javascripts/graphql_shared/fragments/pageInfo.fragment.graphql +++ b/app/assets/javascripts/graphql_shared/fragments/page_info.fragment.graphql diff --git a/app/assets/javascripts/graphql_shared/fragments/pageInfoCursorsOnly.fragment.graphql b/app/assets/javascripts/graphql_shared/fragments/page_info_cursors_only.fragment.graphql index 22bcefbecd3..22bcefbecd3 100644 --- a/app/assets/javascripts/graphql_shared/fragments/pageInfoCursorsOnly.fragment.graphql +++ b/app/assets/javascripts/graphql_shared/fragments/page_info_cursors_only.fragment.graphql diff --git a/app/assets/javascripts/graphql_shared/queries/get_users_projects.query.graphql b/app/assets/javascripts/graphql_shared/queries/get_users_projects.query.graphql index 58b7b4c898d..b59bd781537 100644 --- a/app/assets/javascripts/graphql_shared/queries/get_users_projects.query.graphql +++ b/app/assets/javascripts/graphql_shared/queries/get_users_projects.query.graphql @@ -1,4 +1,4 @@ -#import "~/graphql_shared/fragments/pageInfo.fragment.graphql" +#import "~/graphql_shared/fragments/page_info.fragment.graphql" query getProjects( $search: String! diff --git a/app/assets/javascripts/header_search/components/app.vue b/app/assets/javascripts/header_search/components/app.vue index 36fc48a2ba8..4b9a87619aa 100644 --- a/app/assets/javascripts/header_search/components/app.vue +++ b/app/assets/javascripts/header_search/components/app.vue @@ -11,6 +11,7 @@ import { SEARCH_BOX_INDEX, SEARCH_INPUT_DESCRIPTION, SEARCH_RESULTS_DESCRIPTION, + SEARCH_SHORTCUTS_MIN_CHARACTERS, } from '../constants'; import HeaderSearchAutocompleteItems from './header_search_autocomplete_items.vue'; import HeaderSearchDefaultItems from './header_search_default_items.vue'; @@ -50,7 +51,7 @@ export default { }, computed: { ...mapState(['search', 'loading']), - ...mapGetters(['searchQuery', 'searchOptions']), + ...mapGetters(['searchQuery', 'searchOptions', 'autocompleteGroupedSearchOptions']), searchText: { get() { return this.search; @@ -66,14 +67,20 @@ export default { return this.currentFocusedOption?.html_id; }, isLoggedIn() { - return gon?.current_username; + return Boolean(gon?.current_username); }, showSearchDropdown() { - return this.showDropdown && this.isLoggedIn; + const hasResultsUnderMinCharacters = + this.searchText?.length === 1 ? this?.autocompleteGroupedSearchOptions?.length > 0 : true; + + return this.showDropdown && this.isLoggedIn && hasResultsUnderMinCharacters; }, showDefaultItems() { return !this.searchText; }, + showShortcuts() { + return this.searchText && this.searchText?.length >= SEARCH_SHORTCUTS_MIN_CHARACTERS; + }, defaultIndex() { if (this.showDefaultItems) { return SEARCH_BOX_INDEX; @@ -182,7 +189,10 @@ export default { :current-focused-option="currentFocusedOption" /> <template v-else> - <header-search-scoped-items :current-focused-option="currentFocusedOption" /> + <header-search-scoped-items + v-if="showShortcuts" + :current-focused-option="currentFocusedOption" + /> <header-search-autocomplete-items :current-focused-option="currentFocusedOption" /> </template> </div> diff --git a/app/assets/javascripts/header_search/components/header_search_autocomplete_items.vue b/app/assets/javascripts/header_search/components/header_search_autocomplete_items.vue index c0e2c18bece..11b4f681b63 100644 --- a/app/assets/javascripts/header_search/components/header_search_autocomplete_items.vue +++ b/app/assets/javascripts/header_search/components/header_search_autocomplete_items.vue @@ -72,8 +72,8 @@ export default { <template> <div> <template v-if="!loading"> - <div v-for="option in autocompleteGroupedSearchOptions" :key="option.category"> - <gl-dropdown-divider /> + <div v-for="(option, index) in autocompleteGroupedSearchOptions" :key="option.category"> + <gl-dropdown-divider v-if="index > 0" /> <gl-dropdown-section-header>{{ option.category }}</gl-dropdown-section-header> <gl-dropdown-item v-for="data in option.data" diff --git a/app/assets/javascripts/header_search/components/header_search_scoped_items.vue b/app/assets/javascripts/header_search/components/header_search_scoped_items.vue index 3aebee71509..34d1bd71399 100644 --- a/app/assets/javascripts/header_search/components/header_search_scoped_items.vue +++ b/app/assets/javascripts/header_search/components/header_search_scoped_items.vue @@ -1,5 +1,5 @@ <script> -import { GlDropdownItem } from '@gitlab/ui'; +import { GlDropdownItem, GlDropdownDivider } from '@gitlab/ui'; import { mapState, mapGetters } from 'vuex'; import { __, sprintf } from '~/locale'; @@ -7,6 +7,7 @@ export default { name: 'HeaderSearchScopedItems', components: { GlDropdownItem, + GlDropdownDivider, }, props: { currentFocusedOption: { @@ -17,7 +18,7 @@ export default { }, computed: { ...mapState(['search']), - ...mapGetters(['scopedSearchOptions']), + ...mapGetters(['scopedSearchOptions', 'autocompleteGroupedSearchOptions']), }, methods: { isOptionFocused(option) { @@ -53,5 +54,6 @@ export default { <span v-if="option.scope" class="gl-font-style-italic">{{ option.scope }}</span> </span> </gl-dropdown-item> + <gl-dropdown-divider v-if="autocompleteGroupedSearchOptions.length > 0" /> </div> </template> diff --git a/app/assets/javascripts/header_search/constants.js b/app/assets/javascripts/header_search/constants.js index b2e45fcd648..9cb08605444 100644 --- a/app/assets/javascripts/header_search/constants.js +++ b/app/assets/javascripts/header_search/constants.js @@ -28,6 +28,8 @@ export const FIRST_DROPDOWN_INDEX = 0; export const SEARCH_BOX_INDEX = -1; +export const SEARCH_SHORTCUTS_MIN_CHARACTERS = 2; + export const SEARCH_INPUT_DESCRIPTION = 'search-input-description'; export const SEARCH_RESULTS_DESCRIPTION = 'search-results-description'; diff --git a/app/assets/javascripts/header_search/store/actions.js b/app/assets/javascripts/header_search/store/actions.js index ee4c312fed0..3a86dcca409 100644 --- a/app/assets/javascripts/header_search/store/actions.js +++ b/app/assets/javascripts/header_search/store/actions.js @@ -5,7 +5,9 @@ export const fetchAutocompleteOptions = ({ commit, getters }) => { commit(types.REQUEST_AUTOCOMPLETE); return axios .get(getters.autocompleteQuery) - .then(({ data }) => commit(types.RECEIVE_AUTOCOMPLETE_SUCCESS, data)) + .then(({ data }) => { + commit(types.RECEIVE_AUTOCOMPLETE_SUCCESS, data); + }) .catch(() => { commit(types.RECEIVE_AUTOCOMPLETE_ERROR); }); diff --git a/app/assets/javascripts/header_search/store/getters.js b/app/assets/javascripts/header_search/store/getters.js index 87dec95153f..7d08aa859fb 100644 --- a/app/assets/javascripts/header_search/store/getters.js +++ b/app/assets/javascripts/header_search/store/getters.js @@ -190,7 +190,6 @@ export const autocompleteGroupedSearchOptions = (state) => { results.push(groupedOptions[option.category]); } }); - return results; }; diff --git a/app/assets/javascripts/issues/list/queries/get_issues.query.graphql b/app/assets/javascripts/issues/list/queries/get_issues.query.graphql index 529262d2162..ec24ea7c56a 100644 --- a/app/assets/javascripts/issues/list/queries/get_issues.query.graphql +++ b/app/assets/javascripts/issues/list/queries/get_issues.query.graphql @@ -1,4 +1,4 @@ -#import "~/graphql_shared/fragments/pageInfo.fragment.graphql" +#import "~/graphql_shared/fragments/page_info.fragment.graphql" #import "./issue.fragment.graphql" query getIssues( diff --git a/app/assets/javascripts/jira_connect/branches/graphql/queries/get_projects.query.graphql b/app/assets/javascripts/jira_connect/branches/graphql/queries/get_projects.query.graphql index 03e8e3e986b..d9fba40688d 100644 --- a/app/assets/javascripts/jira_connect/branches/graphql/queries/get_projects.query.graphql +++ b/app/assets/javascripts/jira_connect/branches/graphql/queries/get_projects.query.graphql @@ -1,4 +1,4 @@ -#import "~/graphql_shared/fragments/pageInfo.fragment.graphql" +#import "~/graphql_shared/fragments/page_info.fragment.graphql" query jiraGetProjects( $search: String! diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js index 4bfa39f2a20..8fc54be9c28 100644 --- a/app/assets/javascripts/main.js +++ b/app/assets/javascripts/main.js @@ -127,6 +127,7 @@ function deferredInitialisation() { // In case the user started searching before we bootstrapped, let's pass the search along. const initialSearchValue = searchInputBox.value; await initHeaderSearchApp(initialSearchValue); + // this is new #search input element. We need to re-find it. document.querySelector('#search').focus(); }) .catch(() => {}); diff --git a/app/assets/javascripts/monitoring/queries/getAnnotations.query.graphql b/app/assets/javascripts/monitoring/queries/get_annotations.query.graphql index 32b982ff195..32b982ff195 100644 --- a/app/assets/javascripts/monitoring/queries/getAnnotations.query.graphql +++ b/app/assets/javascripts/monitoring/queries/get_annotations.query.graphql diff --git a/app/assets/javascripts/monitoring/queries/getDashboardValidationWarnings.query.graphql b/app/assets/javascripts/monitoring/queries/get_dashboard_validation_warnings.query.graphql index a61d601cd34..a61d601cd34 100644 --- a/app/assets/javascripts/monitoring/queries/getDashboardValidationWarnings.query.graphql +++ b/app/assets/javascripts/monitoring/queries/get_dashboard_validation_warnings.query.graphql diff --git a/app/assets/javascripts/monitoring/queries/getEnvironments.query.graphql b/app/assets/javascripts/monitoring/queries/get_environments.query.graphql index 48d0a780fc7..48d0a780fc7 100644 --- a/app/assets/javascripts/monitoring/queries/getEnvironments.query.graphql +++ b/app/assets/javascripts/monitoring/queries/get_environments.query.graphql diff --git a/app/assets/javascripts/monitoring/stores/actions.js b/app/assets/javascripts/monitoring/stores/actions.js index 97b6bb5a973..7b9680aece1 100644 --- a/app/assets/javascripts/monitoring/stores/actions.js +++ b/app/assets/javascripts/monitoring/stores/actions.js @@ -6,9 +6,9 @@ import { convertObjectPropsToCamelCase } from '../../lib/utils/common_utils'; import { s__, sprintf } from '../../locale'; import { ENVIRONMENT_AVAILABLE_STATE, OVERVIEW_DASHBOARD_PATH, VARIABLE_TYPES } from '../constants'; import trackDashboardLoad from '../monitoring_tracking_helper'; -import getAnnotations from '../queries/getAnnotations.query.graphql'; -import getDashboardValidationWarnings from '../queries/getDashboardValidationWarnings.query.graphql'; -import getEnvironments from '../queries/getEnvironments.query.graphql'; +import getAnnotations from '../queries/get_annotations.query.graphql'; +import getDashboardValidationWarnings from '../queries/get_dashboard_validation_warnings.query.graphql'; +import getEnvironments from '../queries/get_environments.query.graphql'; import { getDashboard, getPrometheusQueryData } from '../requests'; import * as types from './mutation_types'; diff --git a/app/assets/javascripts/packages_and_registries/container_registry/explorer/graphql/queries/get_container_repository_tags.query.graphql b/app/assets/javascripts/packages_and_registries/container_registry/explorer/graphql/queries/get_container_repository_tags.query.graphql index d753d33a02c..8c577cc7b17 100644 --- a/app/assets/javascripts/packages_and_registries/container_registry/explorer/graphql/queries/get_container_repository_tags.query.graphql +++ b/app/assets/javascripts/packages_and_registries/container_registry/explorer/graphql/queries/get_container_repository_tags.query.graphql @@ -1,4 +1,4 @@ -#import "~/graphql_shared/fragments/pageInfo.fragment.graphql" +#import "~/graphql_shared/fragments/page_info.fragment.graphql" query getContainerRepositoryTags( $id: ID! diff --git a/app/assets/javascripts/packages_and_registries/dependency_proxy/graphql/queries/get_dependency_proxy_details.query.graphql b/app/assets/javascripts/packages_and_registries/dependency_proxy/graphql/queries/get_dependency_proxy_details.query.graphql index 9241dccb2d5..5c43b10a5e3 100644 --- a/app/assets/javascripts/packages_and_registries/dependency_proxy/graphql/queries/get_dependency_proxy_details.query.graphql +++ b/app/assets/javascripts/packages_and_registries/dependency_proxy/graphql/queries/get_dependency_proxy_details.query.graphql @@ -1,4 +1,4 @@ -#import "~/graphql_shared/fragments/pageInfo.fragment.graphql" +#import "~/graphql_shared/fragments/page_info.fragment.graphql" query getDependencyProxyDetails( $fullPath: ID! diff --git a/app/assets/javascripts/packages_and_registries/package_registry/graphql/queries/get_packages.query.graphql b/app/assets/javascripts/packages_and_registries/package_registry/graphql/queries/get_packages.query.graphql index 4b913590949..5bde5f08e56 100644 --- a/app/assets/javascripts/packages_and_registries/package_registry/graphql/queries/get_packages.query.graphql +++ b/app/assets/javascripts/packages_and_registries/package_registry/graphql/queries/get_packages.query.graphql @@ -1,5 +1,5 @@ #import "~/packages_and_registries/package_registry/graphql/fragments/package_data.fragment.graphql" -#import "~/graphql_shared/fragments/pageInfo.fragment.graphql" +#import "~/graphql_shared/fragments/page_info.fragment.graphql" query getPackages( $fullPath: ID! diff --git a/app/assets/javascripts/pipelines/graphql/queries/get_pipeline_jobs.query.graphql b/app/assets/javascripts/pipelines/graphql/queries/get_pipeline_jobs.query.graphql index 5fe47e09d9c..641ec7a3cf6 100644 --- a/app/assets/javascripts/pipelines/graphql/queries/get_pipeline_jobs.query.graphql +++ b/app/assets/javascripts/pipelines/graphql/queries/get_pipeline_jobs.query.graphql @@ -1,4 +1,4 @@ -#import "~/graphql_shared/fragments/pageInfo.fragment.graphql" +#import "~/graphql_shared/fragments/page_info.fragment.graphql" query getPipelineJobs($fullPath: ID!, $iid: ID!, $after: String) { project(fullPath: $fullPath) { diff --git a/app/assets/javascripts/runner/graphql/details/runner_jobs.query.graphql b/app/assets/javascripts/runner/graphql/details/runner_jobs.query.graphql index 2b1decd3ddd..14585e62bf2 100644 --- a/app/assets/javascripts/runner/graphql/details/runner_jobs.query.graphql +++ b/app/assets/javascripts/runner/graphql/details/runner_jobs.query.graphql @@ -1,4 +1,4 @@ -#import "~/graphql_shared/fragments/pageInfo.fragment.graphql" +#import "~/graphql_shared/fragments/page_info.fragment.graphql" query getRunnerJobs($id: CiRunnerID!, $first: Int, $last: Int, $before: String, $after: String) { runner(id: $id) { diff --git a/app/assets/javascripts/runner/graphql/details/runner_projects.query.graphql b/app/assets/javascripts/runner/graphql/details/runner_projects.query.graphql index f97237b8267..cb27de7c200 100644 --- a/app/assets/javascripts/runner/graphql/details/runner_projects.query.graphql +++ b/app/assets/javascripts/runner/graphql/details/runner_projects.query.graphql @@ -1,4 +1,4 @@ -#import "~/graphql_shared/fragments/pageInfo.fragment.graphql" +#import "~/graphql_shared/fragments/page_info.fragment.graphql" query getRunnerProjects( $id: CiRunnerID! diff --git a/app/assets/javascripts/runner/graphql/list/admin_runners.query.graphql b/app/assets/javascripts/runner/graphql/list/admin_runners.query.graphql index 8df4c2fc65c..2b19db705d9 100644 --- a/app/assets/javascripts/runner/graphql/list/admin_runners.query.graphql +++ b/app/assets/javascripts/runner/graphql/list/admin_runners.query.graphql @@ -1,5 +1,5 @@ #import "~/runner/graphql/list/list_item.fragment.graphql" -#import "~/graphql_shared/fragments/pageInfo.fragment.graphql" +#import "~/graphql_shared/fragments/page_info.fragment.graphql" query getRunners( $before: String diff --git a/app/assets/javascripts/runner/graphql/list/group_runners.query.graphql b/app/assets/javascripts/runner/graphql/list/group_runners.query.graphql index 7aac2c1d4f2..25c37b2fda0 100644 --- a/app/assets/javascripts/runner/graphql/list/group_runners.query.graphql +++ b/app/assets/javascripts/runner/graphql/list/group_runners.query.graphql @@ -1,5 +1,5 @@ #import "~/runner/graphql/list/list_item.fragment.graphql" -#import "~/graphql_shared/fragments/pageInfo.fragment.graphql" +#import "~/graphql_shared/fragments/page_info.fragment.graphql" query getGroupRunners( $groupFullPath: ID! diff --git a/app/assets/javascripts/sidebar/queries/sidebarDetails.query.graphql b/app/assets/javascripts/sidebar/queries/sidebar_details.query.graphql index 90d1a7794ea..90d1a7794ea 100644 --- a/app/assets/javascripts/sidebar/queries/sidebarDetails.query.graphql +++ b/app/assets/javascripts/sidebar/queries/sidebar_details.query.graphql diff --git a/app/assets/javascripts/sidebar/queries/sidebarDetailsMR.query.graphql b/app/assets/javascripts/sidebar/queries/sidebar_details_mr.query.graphql index 0505f88773d..0505f88773d 100644 --- a/app/assets/javascripts/sidebar/queries/sidebarDetailsMR.query.graphql +++ b/app/assets/javascripts/sidebar/queries/sidebar_details_mr.query.graphql diff --git a/app/assets/javascripts/sidebar/queries/updateStatus.mutation.graphql b/app/assets/javascripts/sidebar/queries/update_status.mutation.graphql index 2c6f379744e..2c6f379744e 100644 --- a/app/assets/javascripts/sidebar/queries/updateStatus.mutation.graphql +++ b/app/assets/javascripts/sidebar/queries/update_status.mutation.graphql diff --git a/app/assets/javascripts/sidebar/services/sidebar_service.js b/app/assets/javascripts/sidebar/services/sidebar_service.js index d8ab8f1c65b..90d8f2098bb 100644 --- a/app/assets/javascripts/sidebar/services/sidebar_service.js +++ b/app/assets/javascripts/sidebar/services/sidebar_service.js @@ -1,10 +1,10 @@ -import sidebarDetailsIssueQuery from 'ee_else_ce/sidebar/queries/sidebarDetails.query.graphql'; +import sidebarDetailsIssueQuery from 'ee_else_ce/sidebar/queries/sidebar_details.query.graphql'; import { TYPE_USER } from '~/graphql_shared/constants'; import { convertToGraphQLId } from '~/graphql_shared/utils'; import createGqClient, { fetchPolicies } from '~/lib/graphql'; import axios from '~/lib/utils/axios_utils'; import reviewerRereviewMutation from '../queries/reviewer_rereview.mutation.graphql'; -import sidebarDetailsMRQuery from '../queries/sidebarDetailsMR.query.graphql'; +import sidebarDetailsMRQuery from '../queries/sidebar_details_mr.query.graphql'; import toggleAttentionRequestedMutation from '../queries/toggle_attention_requested.mutation.graphql'; const queries = { diff --git a/app/assets/javascripts/snippets/components/edit.vue b/app/assets/javascripts/snippets/components/edit.vue index e3aa29d5f89..e4a97f08c8d 100644 --- a/app/assets/javascripts/snippets/components/edit.vue +++ b/app/assets/javascripts/snippets/components/edit.vue @@ -15,8 +15,8 @@ import TitleField from '~/vue_shared/components/form/title.vue'; import { SNIPPET_CREATE_MUTATION_ERROR, SNIPPET_UPDATE_MUTATION_ERROR } from '../constants'; import { getSnippetMixin } from '../mixins/snippets'; -import CreateSnippetMutation from '../mutations/createSnippet.mutation.graphql'; -import UpdateSnippetMutation from '../mutations/updateSnippet.mutation.graphql'; +import CreateSnippetMutation from '../mutations/create_snippet.mutation.graphql'; +import UpdateSnippetMutation from '../mutations/update_snippet.mutation.graphql'; import { markBlobPerformance } from '../utils/blob'; import { getErrorMessage } from '../utils/error'; @@ -238,9 +238,9 @@ export default { > </template> <template #append> - <gl-button type="cancel" data-testid="snippet-cancel-btn" :href="cancelButtonHref">{{ - __('Cancel') - }}</gl-button> + <gl-button type="cancel" data-testid="snippet-cancel-btn" :href="cancelButtonHref"> + {{ __('Cancel') }} + </gl-button> </template> </form-footer-actions> </template> diff --git a/app/assets/javascripts/snippets/components/snippet_header.vue b/app/assets/javascripts/snippets/components/snippet_header.vue index 9b24c8afe37..dd8f2897018 100644 --- a/app/assets/javascripts/snippets/components/snippet_header.vue +++ b/app/assets/javascripts/snippets/components/snippet_header.vue @@ -21,7 +21,7 @@ import { __, s__, sprintf } from '~/locale'; import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; import createFlash, { FLASH_TYPES } from '~/flash'; -import DeleteSnippetMutation from '../mutations/deleteSnippet.mutation.graphql'; +import DeleteSnippetMutation from '../mutations/delete_snippet.mutation.graphql'; export const i18n = { snippetSpamSuccess: sprintf( @@ -294,9 +294,9 @@ export default { <gl-modal ref="deleteModal" modal-id="delete-modal" title="Example title"> <template #modal-title>{{ __('Delete snippet?') }}</template> - <gl-alert v-if="errorMessage" variant="danger" class="mb-2" @dismiss="errorMessage = ''"> - {{ errorMessage }} - </gl-alert> + <gl-alert v-if="errorMessage" variant="danger" class="mb-2" @dismiss="errorMessage = ''">{{ + errorMessage + }}</gl-alert> <gl-sprintf :message="__('Are you sure you want to delete %{name}?')"> <template #name> diff --git a/app/assets/javascripts/snippets/fragments/snippetBase.fragment.graphql b/app/assets/javascripts/snippets/fragments/snippet_base.fragment.graphql index d75b4011d1c..d75b4011d1c 100644 --- a/app/assets/javascripts/snippets/fragments/snippetBase.fragment.graphql +++ b/app/assets/javascripts/snippets/fragments/snippet_base.fragment.graphql diff --git a/app/assets/javascripts/snippets/mutations/createSnippet.mutation.graphql b/app/assets/javascripts/snippets/mutations/create_snippet.mutation.graphql index 8640c4725f4..8640c4725f4 100644 --- a/app/assets/javascripts/snippets/mutations/createSnippet.mutation.graphql +++ b/app/assets/javascripts/snippets/mutations/create_snippet.mutation.graphql diff --git a/app/assets/javascripts/snippets/mutations/deleteSnippet.mutation.graphql b/app/assets/javascripts/snippets/mutations/delete_snippet.mutation.graphql index f43d53661f4..f43d53661f4 100644 --- a/app/assets/javascripts/snippets/mutations/deleteSnippet.mutation.graphql +++ b/app/assets/javascripts/snippets/mutations/delete_snippet.mutation.graphql diff --git a/app/assets/javascripts/snippets/mutations/updateSnippet.mutation.graphql b/app/assets/javascripts/snippets/mutations/update_snippet.mutation.graphql index 99242c5d500..99242c5d500 100644 --- a/app/assets/javascripts/snippets/mutations/updateSnippet.mutation.graphql +++ b/app/assets/javascripts/snippets/mutations/update_snippet.mutation.graphql diff --git a/app/assets/javascripts/terraform/graphql/queries/get_states.query.graphql b/app/assets/javascripts/terraform/graphql/queries/get_states.query.graphql index 4d26ea88ddf..2ae7b7d905e 100644 --- a/app/assets/javascripts/terraform/graphql/queries/get_states.query.graphql +++ b/app/assets/javascripts/terraform/graphql/queries/get_states.query.graphql @@ -1,5 +1,5 @@ #import "../fragments/state.fragment.graphql" -#import "~/graphql_shared/fragments/pageInfo.fragment.graphql" +#import "~/graphql_shared/fragments/page_info.fragment.graphql" query getStates($projectPath: ID!, $first: Int, $last: Int, $before: String, $after: String) { project(fullPath: $projectPath) { diff --git a/app/helpers/sorting_helper.rb b/app/helpers/sorting_helper.rb index 4db14d5cc4d..044404562af 100644 --- a/app/helpers/sorting_helper.rb +++ b/app/helpers/sorting_helper.rb @@ -186,6 +186,13 @@ module SortingHelper } end + def runners_sort_options_hash + { + sort_value_created_date => sort_title_created_date, + sort_value_contacted_date => sort_title_contacted_date + } + end + def starrers_sort_options_hash { sort_value_name => sort_title_name, diff --git a/app/helpers/workhorse_helper.rb b/app/helpers/workhorse_helper.rb index b808f013f4b..2460c956bb6 100644 --- a/app/helpers/workhorse_helper.rb +++ b/app/helpers/workhorse_helper.rb @@ -38,8 +38,6 @@ module WorkhorseHelper # Send an entry from artifacts through Workhorse def send_artifacts_entry(file, entry) headers.store(*Gitlab::Workhorse.send_artifacts_entry(file, entry)) - headers.store(*Gitlab::Workhorse.detect_content_type) - head :ok end diff --git a/app/models/namespace.rb b/app/models/namespace.rb index 873298ab9ba..5f7b8d1c8b1 100644 --- a/app/models/namespace.rb +++ b/app/models/namespace.rb @@ -661,10 +661,6 @@ class Namespace < ApplicationRecord # Use SHA2 of `traversal_ids` to account for moving a namespace within the same root ancestor hierarchy. "namespaces:{#{traversal_ids.first}}:first_auto_devops_config:#{group_id}:#{Digest::SHA2.hexdigest(traversal_ids.join(' '))}" end - - def allow_serialization?(options = nil) - Feature.disabled?(:block_namespace_serialization, self, default_enabled: :yaml) || super - end end Namespace.prepend_mod_with('Namespace') diff --git a/app/services/auth/container_registry_authentication_service.rb b/app/services/auth/container_registry_authentication_service.rb index bb6a52eb2f4..6d6d8641d9d 100644 --- a/app/services/auth/container_registry_authentication_service.rb +++ b/app/services/auth/container_registry_authentication_service.rb @@ -50,6 +50,12 @@ module Auth access_token(['pull'], names) end + def self.pull_nested_repositories_access_token(name) + name = name.chomp('/') if name.end_with?('/') + paths = [name, "#{name}/*"] + access_token(['pull'], paths) + end + def self.access_token(actions, names, type = 'repository') names = names.flatten registry = Gitlab.config.registry diff --git a/app/views/groups/runners/_sort_dropdown.html.haml b/app/views/groups/runners/_sort_dropdown.html.haml index e914bd00dac..b7b10cecee8 100644 --- a/app/views/groups/runners/_sort_dropdown.html.haml +++ b/app/views/groups/runners/_sort_dropdown.html.haml @@ -1,10 +1,3 @@ -- sorted_by = sort_options_hash[@sort] || sort_title_created_date +- runners_sort_options = runners_sort_options_hash.map { |value, text| { value: value, text: text, href: page_filter_path(sort: value) } } -.dropdown.inline.gl-ml-3 - %button.dropdown-menu-toggle{ type: 'button', data: { toggle: 'dropdown', display: 'static' } } - = sorted_by - = sprite_icon('chevron-down', css_class: 'dropdown-menu-toggle-icon gl-top-3') - %ul.dropdown-menu.dropdown-menu-right.dropdown-menu-selectable.dropdown-menu-sort - %li - = sortable_item(sort_title_created_date, page_filter_path(sort: sort_value_created_date), sorted_by) - = sortable_item(sort_title_contacted_date, page_filter_path(sort: sort_value_contacted_date), sorted_by) += gl_redirect_listbox_tag runners_sort_options, @sort, class: 'gl-ml-3', data: { display: 'static' } diff --git a/config/feature_flags/development/block_namespace_serialization.yml b/config/feature_flags/development/block_namespace_serialization.yml deleted file mode 100644 index 5152789c3fe..00000000000 --- a/config/feature_flags/development/block_namespace_serialization.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: block_namespace_serialization -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/82661 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/355553 -milestone: '14.9' -type: development -group: group::global search -default_enabled: false diff --git a/config/feature_flags/development/enforce_security_report_validation.yml b/config/feature_flags/development/enforce_security_report_validation.yml new file mode 100644 index 00000000000..0478033c55b --- /dev/null +++ b/config/feature_flags/development/enforce_security_report_validation.yml @@ -0,0 +1,8 @@ +--- +name: enforce_security_report_validation +introduced_by_url: +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/351000 +milestone: '14.9' +type: development +group: group::threat insights +default_enabled: false diff --git a/doc/administration/reference_architectures/10k_users.md b/doc/administration/reference_architectures/10k_users.md index 5d8de724664..a90c774f4df 100644 --- a/doc/administration/reference_architectures/10k_users.md +++ b/doc/administration/reference_architectures/10k_users.md @@ -14,7 +14,7 @@ full list of reference architectures, see > - **High Availability:** Yes ([Praefect](#configure-praefect-postgresql) needs a third-party PostgreSQL solution for HA) > - **Estimated Costs:** [See cost table](index.md#cost-to-run) > - **Cloud Native Hybrid Alternative:** [Yes](#cloud-native-hybrid-reference-architecture-with-helm-charts-alternative) -> - **Performance tested daily with the [GitLab Performance Tool](https://gitlab.com/gitlab-org/quality/performance)**: +> - **Validation and test results:** The Quality Engineering team does [regular smoke and performance tests](index.md#validation-and-test-results) to ensure the reference architectures remain compliant > - **Test requests per second (RPS) rates:** API: 200 RPS, Web: 20 RPS, Git (Pull): 20 RPS, Git (Push): 4 RPS > - **[Latest Results](https://gitlab.com/gitlab-org/quality/performance/-/wikis/Benchmarks/Latest/10k)** diff --git a/doc/administration/reference_architectures/1k_users.md b/doc/administration/reference_architectures/1k_users.md index 0d0e7681ffd..7213a1eb92b 100644 --- a/doc/administration/reference_architectures/1k_users.md +++ b/doc/administration/reference_architectures/1k_users.md @@ -21,7 +21,7 @@ many organizations. > - **Estimated Costs:** [See cost table](index.md#cost-to-run) > - **Cloud Native Hybrid:** No. For a cloud native hybrid environment, you > can follow a [modified hybrid reference architecture](#cloud-native-hybrid-reference-architecture-with-helm-charts). -> - **Performance tested daily with the [GitLab Performance Tool (GPT)](https://gitlab.com/gitlab-org/quality/performance)**: +> - **Validation and test results:** The Quality Engineering team does [regular smoke and performance tests](index.md#validation-and-test-results) to ensure the reference architectures remain compliant > - **Test requests per second (RPS) rates:** API: 20 RPS, Web: 2 RPS, Git (Pull): 2 RPS, Git (Push): 1 RPS > - **[Latest Results](https://gitlab.com/gitlab-org/quality/performance/-/wikis/Benchmarks/Latest/1k)** diff --git a/doc/administration/reference_architectures/25k_users.md b/doc/administration/reference_architectures/25k_users.md index 629bfa235be..1e130ee5dac 100644 --- a/doc/administration/reference_architectures/25k_users.md +++ b/doc/administration/reference_architectures/25k_users.md @@ -14,7 +14,7 @@ full list of reference architectures, see > - **High Availability:** Yes ([Praefect](#configure-praefect-postgresql) needs a third-party PostgreSQL solution for HA) > - **Estimated Costs:** [See cost table](index.md#cost-to-run) > - **Cloud Native Hybrid Alternative:** [Yes](#cloud-native-hybrid-reference-architecture-with-helm-charts-alternative) -> - **Performance tested weekly with the [GitLab Performance Tool (GPT)](https://gitlab.com/gitlab-org/quality/performance)**: +> - **Validation and test results:** The Quality Engineering team does [regular smoke and performance tests](index.md#validation-and-test-results) to ensure the reference architectures remain compliant > - **Test requests per second (RPS) rates:** API: 500 RPS, Web: 50 RPS, Git (Pull): 50 RPS, Git (Push): 10 RPS > - **[Latest Results](https://gitlab.com/gitlab-org/quality/performance/-/wikis/Benchmarks/Latest/25k)** diff --git a/doc/administration/reference_architectures/2k_users.md b/doc/administration/reference_architectures/2k_users.md index 26525074aa1..355316033c5 100644 --- a/doc/administration/reference_architectures/2k_users.md +++ b/doc/administration/reference_architectures/2k_users.md @@ -13,9 +13,9 @@ For a full list of reference architectures, see > - **Supported users (approximate):** 2,000 > - **High Availability:** No. For a highly-available environment, you can > follow a modified [3K reference architecture](3k_users.md#supported-modifications-for-lower-user-counts-ha). -> - **Estimated Costs:** [See cost table](index.md#cost-to-run) +> - **Estimated Costs:** [See cost table](index.md#cost-to-run) > - **Cloud Native Hybrid:** [Yes](#cloud-native-hybrid-reference-architecture-with-helm-charts-alternative) -> - **Performance tested daily with the [GitLab Performance Tool (GPT)](https://gitlab.com/gitlab-org/quality/performance)**: +> - **Validation and test results:** The Quality Engineering team does [regular smoke and performance tests](index.md#validation-and-test-results) to ensure the reference architectures remain compliant > - **Test requests per second (RPS) rates:** API: 40 RPS, Web: 4 RPS, Git (Pull): 4 RPS, Git (Push): 1 RPS > - **[Latest Results](https://gitlab.com/gitlab-org/quality/performance/-/wikis/Benchmarks/Latest/2k)** diff --git a/doc/administration/reference_architectures/3k_users.md b/doc/administration/reference_architectures/3k_users.md index 52d3bca3136..7b8438f0fd1 100644 --- a/doc/administration/reference_architectures/3k_users.md +++ b/doc/administration/reference_architectures/3k_users.md @@ -24,7 +24,7 @@ For a full list of reference architectures, see > - **High Availability:** Yes, although [Praefect](#configure-praefect-postgresql) needs a third-party PostgreSQL solution > - **Estimated Costs:** [See cost table](index.md#cost-to-run) > - **Cloud Native Hybrid Alternative:** [Yes](#cloud-native-hybrid-reference-architecture-with-helm-charts-alternative) -> - **Performance tested weekly with the [GitLab Performance Tool (GPT)](https://gitlab.com/gitlab-org/quality/performance)**: +> - **Validation and test results:** The Quality Engineering team does [regular smoke and performance tests](index.md#validation-and-test-results) to ensure the reference architectures remain compliant > - **Test requests per second (RPS) rates:** API: 60 RPS, Web: 6 RPS, Git (Pull): 6 RPS, Git (Push): 1 RPS > - **[Latest Results](https://gitlab.com/gitlab-org/quality/performance/-/wikis/Benchmarks/Latest/3k)** diff --git a/doc/administration/reference_architectures/50k_users.md b/doc/administration/reference_architectures/50k_users.md index a628e885b91..98bbfd5dc38 100644 --- a/doc/administration/reference_architectures/50k_users.md +++ b/doc/administration/reference_architectures/50k_users.md @@ -14,7 +14,7 @@ full list of reference architectures, see > - **High Availability:** Yes ([Praefect](#configure-praefect-postgresql) needs a third-party PostgreSQL solution for HA) > - **Estimated Costs:** [See cost table](index.md#cost-to-run) > - **Cloud Native Hybrid Alternative:** [Yes](#cloud-native-hybrid-reference-architecture-with-helm-charts-alternative) -> - **Performance tested weekly with the [GitLab Performance Tool (GPT)](https://gitlab.com/gitlab-org/quality/performance)**: +> - **Validation and test results:** The Quality Engineering team does [regular smoke and performance tests](index.md#validation-and-test-results) to ensure the reference architectures remain compliant > - **Test requests per second (RPS) rates:** API: 1000 RPS, Web: 100 RPS, Git (Pull): 100 RPS, Git (Push): 20 RPS > - **[Latest Results](https://gitlab.com/gitlab-org/quality/performance/-/wikis/Benchmarks/Latest/50k)** diff --git a/doc/administration/reference_architectures/5k_users.md b/doc/administration/reference_architectures/5k_users.md index c8d5aae35ad..4588b6cd535 100644 --- a/doc/administration/reference_architectures/5k_users.md +++ b/doc/administration/reference_architectures/5k_users.md @@ -21,7 +21,7 @@ costly-to-operate environment by using the > - **High Availability:** Yes ([Praefect](#configure-praefect-postgresql) needs a third-party PostgreSQL solution for HA) > - **Estimated Costs:** [See cost table](index.md#cost-to-run) > - **Cloud Native Hybrid Alternative:** [Yes](#cloud-native-hybrid-reference-architecture-with-helm-charts-alternative) -> - **Performance tested weekly with the [GitLab Performance Tool (GPT)](https://gitlab.com/gitlab-org/quality/performance)**: +> - **Validation and test results:** The Quality Engineering team does [regular smoke and performance tests](index.md#validation-and-test-results) to ensure the reference architectures remain compliant > - **Test requests per second (RPS) rates:** API: 100 RPS, Web: 10 RPS, Git (Pull): 10 RPS, Git (Push): 2 RPS > - **[Latest Results](https://gitlab.com/gitlab-org/quality/performance/-/wikis/Benchmarks/Latest/5k)** diff --git a/doc/administration/reference_architectures/index.md b/doc/administration/reference_architectures/index.md index 8355bdd4b21..2548a6443bd 100644 --- a/doc/administration/reference_architectures/index.md +++ b/doc/administration/reference_architectures/index.md @@ -18,20 +18,6 @@ you scale GitLab accordingly. ![Reference Architectures](img/reference-architectures.png) <!-- Internal link: https://docs.google.com/spreadsheets/d/1obYP4fLKkVVDOljaI3-ozhmCiPtEeMblbBKkf2OADKs/edit#gid=1403207183 --> -Testing on these reference architectures was performed with the -[GitLab Performance Tool](https://gitlab.com/gitlab-org/quality/performance) -at specific coded workloads, and the throughputs used for testing were -calculated based on sample customer data. Select the -[reference architecture](#available-reference-architectures) that matches your scale. - -Each endpoint type is tested with the following number of requests per second (RPS) -per 1,000 users: - -- API: 20 RPS -- Web: 2 RPS -- Git (Pull): 2 RPS -- Git (Push): 0.4 RPS (rounded to nearest integer) - For GitLab instances with less than 2,000 users, it's recommended that you use the [default setup](#automated-backups) by [installing GitLab](../../install/index.md) on a single machine to minimize @@ -48,7 +34,8 @@ When scaling GitLab, there are several factors to consider: - A load balancer is added in front to distribute traffic across the application nodes. - The application nodes connects to a shared file server and PostgreSQL and Redis services on the backend. -NOTE: +## Available reference architectures + Depending on your workflow, the following recommended reference architectures may need to be adapted accordingly. Your workload is influenced by factors including how active your users are, how much automation you use, mirroring, @@ -57,12 +44,10 @@ provided by [GCP machine types](https://cloud.google.com/compute/docs/machine-ty For different cloud vendors, attempt to select options that best match the provided architecture. -## Available reference architectures - -The following reference architectures are available. - ### GitLab package (Omnibus) +The following reference architectures, where the GitLab package is used, are available: + - [Up to 1,000 users](1k_users.md) - [Up to 2,000 users](2k_users.md) - [Up to 3,000 users](3k_users.md) @@ -87,17 +72,53 @@ to get assistance from Support with troubleshooting the [2,000 users](2k_users.m and higher reference architectures. [Read more about our definition of scaled architectures](https://about.gitlab.com/support/#definition-of-scaled-architecture). -### Validation and test results +## Validation and test results + +The [Quality Engineering team](https://about.gitlab.com/handbook/engineering/quality/quality-engineering/) +does regular smoke and performance tests for the reference architectures to ensure they +remain compliant. + +### Why we perform the tests + +The Quality Department has a focus on measuring and improving the performance +of GitLab, as well as creating and validating reference architectures that +self-managed customers can rely on as performant configurations. + +For more information, see our [handbook page](https://about.gitlab.com/handbook/engineering/quality/performance-and-scalability/). -The [Quality Engineering - Enablement team](https://about.gitlab.com/handbook/engineering/quality/quality-engineering/) does regular smoke and performance tests for the reference architectures to ensure they remain compliant. +### How we perform the tests -- Testing occurs against all reference architectures and cloud providers in an automated and ad-hoc fashion. This is done by two tools: - - The [GitLab Environment Toolkit](https://gitlab.com/gitlab-org/gitlab-environment-toolkit) for building the environments. - - The [GitLab Performance Tool](https://gitlab.com/gitlab-org/quality/performance) for performance testing. -- Network latency on the test environments between components on all Cloud Providers were measured at <5ms. Note that this is shared as an observation and not as an implicit recommendation. -- We aim to have a "test smart" approach where architectures tested have a good range that can also apply to others. Testing focuses on 10k Omnibus on GCP as the testing has shown this is a good bellwether for the other architectures and cloud providers as well as Cloud Native Hybrids. -- Testing is done publicly and all results are shared. -- For more information about performance testing at GitLab, read [how our QA team leverages GitLab’s performance testing tool (and you can too)](https://about.gitlab.com/blog/2020/02/18/how-were-building-up-performance-testing-of-gitlab/). +Testing occurs against all reference architectures and cloud providers in an automated and ad-hoc fashion. This is done by two tools: + +- The [GitLab Environment Toolkit](https://gitlab.com/gitlab-org/gitlab-environment-toolkit) for building the environments. +- The [GitLab Performance Tool](https://gitlab.com/gitlab-org/quality/performance) for performance testing. + +Network latency on the test environments between components on all Cloud Providers were measured at <5ms. Note that this is shared as an observation and not as an implicit recommendation. + +We aim to have a "test smart" approach where architectures tested have a good range that can also apply to others. Testing focuses on 10k Omnibus on GCP as the testing has shown this is a good bellwether for the other architectures and cloud providers as well as Cloud Native Hybrids. + +The Standard Reference Architectures are designed to be platform agnostic, with everything being run on VMs via [Omnibus GitLab](https://docs.gitlab.com/omnibus/). While testing occurs primarily on GCP, ad-hoc testing has shown that they perform similarly on equivalently specced hardware on other Cloud Providers or if run on premises (bare-metal). + +Testing on these reference architectures is performed with the +[GitLab Performance Tool](https://gitlab.com/gitlab-org/quality/performance) +at specific coded workloads, and the throughputs used for testing are +calculated based on sample customer data. Select the +[reference architecture](#available-reference-architectures) that matches your scale. + +Each endpoint type is tested with the following number of requests per second (RPS) +per 1,000 users: + +- API: 20 RPS +- Web: 2 RPS +- Git (Pull): 2 RPS +- Git (Push): 0.4 RPS (rounded to nearest integer) + +### How to interpret the results + +NOTE: +Read our blog post on [how our QA team leverages GitLab’s performance testing tool](https://about.gitlab.com/blog/2020/02/18/how-were-building-up-performance-testing-of-gitlab/). + +Testing is done publicly and all results are shared. The following table details the testing done against the reference architectures along with the frequency and results. Additional testing is continuously evaluated, and the table is updated accordingly. @@ -192,9 +213,7 @@ table.test-coverage th { </tr> </table> -The Standard Reference Architectures are designed to be platform agnostic, with everything being run on VMs via [Omnibus GitLab](https://docs.gitlab.com/omnibus/). While testing occurs primarily on GCP, ad-hoc testing has shown that they perform similarly on equivalently specced hardware on other Cloud Providers or if run on premises (bare-metal). - -### Cost to run +## Cost to run <table class="test-coverage"> <col> @@ -271,7 +290,7 @@ The Standard Reference Architectures are designed to be platform agnostic, with </tr> </table> -### Recommended cloud providers and services +## Recommended cloud providers and services NOTE: The following lists are non exhaustive. Generally, other cloud providers not listed diff --git a/doc/api/group_badges.md b/doc/api/group_badges.md index 360790daf8c..b1166ba5b54 100644 --- a/doc/api/group_badges.md +++ b/doc/api/group_badges.md @@ -1,6 +1,6 @@ --- stage: Manage -group: Authentication and Authorization +group: Workspace info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments --- diff --git a/doc/api/groups.md b/doc/api/groups.md index e381d679478..e04e5207c95 100644 --- a/doc/api/groups.md +++ b/doc/api/groups.md @@ -1,6 +1,6 @@ --- stage: Manage -group: Authentication and Authorization +group: Workspace info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments --- diff --git a/doc/ci/pipelines/merge_request_pipelines.md b/doc/ci/pipelines/merge_request_pipelines.md index b6ebc2bf283..26644819327 100644 --- a/doc/ci/pipelines/merge_request_pipelines.md +++ b/doc/ci/pipelines/merge_request_pipelines.md @@ -25,10 +25,11 @@ Branch pipelines: Merge request pipelines: - Run when you: - - Create a new merge request. + - Create a new merge request from a source branch with one or more commits. - Push a new commit to the source branch for a merge request. - Select **Run pipeline** from the **Pipelines** tab in a merge request. This option - is only available when merge request pipelines are configured for the pipeline. + is only available when merge request pipelines are configured for the pipeline + and the source branch has at least one commit. - Do not run by default. The jobs in the CI/CD configuration file [must be configured](#prerequisites) to run in merge request pipelines. - Have access to [more predefined variables](#available-predefined-variables). diff --git a/doc/development/fe_guide/graphql.md b/doc/development/fe_guide/graphql.md index e79a473df9e..8f98e50375b 100644 --- a/doc/development/fe_guide/graphql.md +++ b/doc/development/fe_guide/graphql.md @@ -583,7 +583,7 @@ we want to fetch after or before a given endpoint. For example, here we're fetching 10 designs after a cursor (let us call this `projectQuery`): ```javascript -#import "~/graphql_shared/fragments/pageInfo.fragment.graphql" +#import "~/graphql_shared/fragments/page_info.fragment.graphql" query { project(fullPath: "root/my-project") { @@ -606,7 +606,7 @@ query { } ``` -Note that we are using the [`pageInfo.fragment.graphql`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/assets/javascripts/graphql_shared/fragments/pageInfo.fragment.graphql) to populate the `pageInfo` information. +Note that we are using the [`page_info.fragment.graphql`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/assets/javascripts/graphql_shared/fragments/page_info.fragment.graphql) to populate the `pageInfo` information. #### Using `fetchMore` method in components @@ -869,7 +869,7 @@ You'd then be able to retrieve the data without providing any pagination-specifi Here's an example of a query using the `@connection` directive: ```graphql -#import "~/graphql_shared/fragments/pageInfo.fragment.graphql" +#import "~/graphql_shared/fragments/page_info.fragment.graphql" query DastSiteProfiles($fullPath: ID!, $after: String, $before: String, $first: Int, $last: Int) { project(fullPath: $fullPath) { diff --git a/doc/user/group/index.md b/doc/user/group/index.md index 6d0957d67f9..a93fd9927b0 100644 --- a/doc/user/group/index.md +++ b/doc/user/group/index.md @@ -1,7 +1,6 @@ --- -type: reference, howto stage: Manage -group: Authentication and Authorization +group: Workspace info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments --- diff --git a/doc/user/group/subgroups/index.md b/doc/user/group/subgroups/index.md index a98f213bb3f..76097e4f43a 100644 --- a/doc/user/group/subgroups/index.md +++ b/doc/user/group/subgroups/index.md @@ -1,6 +1,6 @@ --- stage: Manage -group: Authentication and Authorization +group: Workspace info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments --- diff --git a/doc/user/project/members/index.md b/doc/user/project/members/index.md index c3d5dca0675..799ff25a239 100644 --- a/doc/user/project/members/index.md +++ b/doc/user/project/members/index.md @@ -1,6 +1,6 @@ --- stage: Manage -group: Authentication and Authorization +group: Workspace info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments --- diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index fefadddc652..de9d42bdce7 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -707,7 +707,6 @@ module API def send_artifacts_entry(file, entry) header(*Gitlab::Workhorse.send_artifacts_entry(file, entry)) - header(*Gitlab::Workhorse.detect_content_type) body '' end diff --git a/lib/gitlab/ci/parsers/security/validators/schema_validator.rb b/lib/gitlab/ci/parsers/security/validators/schema_validator.rb index 0ab1a128052..88853ea4fb6 100644 --- a/lib/gitlab/ci/parsers/security/validators/schema_validator.rb +++ b/lib/gitlab/ci/parsers/security/validators/schema_validator.rb @@ -87,19 +87,80 @@ module Gitlab end def initialize(report_type, report_data, report_version = nil) - @report_type = report_type + @report_type = report_type&.to_sym @report_data = report_data @report_version = report_version + @errors = [] + @warnings = [] + + populate_errors + populate_warnings end def valid? errors.empty? end - def errors - @errors ||= schema.validate(report_data).map { |error| JSONSchemer::Errors.pretty(error) } + def populate_errors + if Feature.enabled?(:enforce_security_report_validation) + @errors += schema.validate(report_data).map { |error| JSONSchemer::Errors.pretty(error) } + else + @warnings += schema.validate(report_data).map { |error| JSONSchemer::Errors.pretty(error) } + end + end + + def populate_warnings + add_deprecated_report_version_message if report_uses_deprecated_schema_version? + add_unsupported_report_version_message if !report_uses_supported_schema_version? && !report_uses_deprecated_schema_version? + end + + def add_deprecated_report_version_message + message = "Version #{report_version} for report type #{report_type} has been deprecated, supported versions for this report type are: #{supported_schema_versions}" + add_message_as(level: :warning, message: message) end + def add_unsupported_report_version_message + if Feature.enabled?(:enforce_security_report_validation) + handle_unsupported_report_version(treat_as: :error) + else + handle_unsupported_report_version(treat_as: :warning) + end + end + + def report_uses_deprecated_schema_version? + DEPRECATED_VERSIONS[report_type].include?(report_version) + end + + def report_uses_supported_schema_version? + SUPPORTED_VERSIONS[report_type].include?(report_version) + end + + def handle_unsupported_report_version(treat_as:) + if report_version.nil? + message = "Report version not provided, #{report_type} report type supports versions: #{supported_schema_versions}" + add_message_as(level: treat_as, message: message) + else + message = "Version #{report_version} for report type #{report_type} is unsupported, supported versions for this report type are: #{supported_schema_versions}" + end + + add_message_as(level: treat_as, message: message) + end + + def supported_schema_versions + SUPPORTED_VERSIONS[report_type].join(", ") + end + + def add_message_as(level:, message:) + case level + when :error + @errors << message + when :warning + @warnings << message + end + end + + attr_reader :errors, :warnings + private attr_reader :report_type, :report_data, :report_version diff --git a/lib/gitlab/workhorse.rb b/lib/gitlab/workhorse.rb index d74efd458f6..19d30daa577 100644 --- a/lib/gitlab/workhorse.rb +++ b/lib/gitlab/workhorse.rb @@ -226,13 +226,6 @@ module Gitlab end end - def detect_content_type - [ - Gitlab::Workhorse::DETECT_HEADER, - 'true' - ] - end - protected # This is the outermost encoding of a senddata: header. It is safe for diff --git a/spec/controllers/projects/artifacts_controller_spec.rb b/spec/controllers/projects/artifacts_controller_spec.rb index d51880b282d..650ecf702cf 100644 --- a/spec/controllers/projects/artifacts_controller_spec.rb +++ b/spec/controllers/projects/artifacts_controller_spec.rb @@ -323,7 +323,6 @@ RSpec.describe Projects::ArtifactsController do subject expect(response).to have_gitlab_http_status(:ok) - expect(response.headers['Gitlab-Workhorse-Detect-Content-Type']).to eq('true') expect(send_data).to start_with('artifacts-entry:') expect(params.keys).to eq(%w(Archive Entry)) diff --git a/spec/frontend/header_search/components/app_spec.js b/spec/frontend/header_search/components/app_spec.js index dcbeeeffb2d..f0de5b083ae 100644 --- a/spec/frontend/header_search/components/app_spec.js +++ b/spec/frontend/header_search/components/app_spec.js @@ -16,6 +16,7 @@ import { MOCK_USERNAME, MOCK_DEFAULT_SEARCH_OPTIONS, MOCK_SCOPED_SEARCH_OPTIONS, + MOCK_SORTED_AUTOCOMPLETE_OPTIONS, } from '../mock_data'; Vue.use(Vuex); @@ -108,6 +109,11 @@ describe('HeaderSearchApp', () => { search | showDefault | showScoped | showAutocomplete | showDropdownNavigation ${null} | ${true} | ${false} | ${false} | ${true} ${''} | ${true} | ${false} | ${false} | ${true} + ${'1'} | ${false} | ${false} | ${false} | ${false} + ${')'} | ${false} | ${false} | ${false} | ${false} + ${'t'} | ${false} | ${false} | ${true} | ${true} + ${'te'} | ${false} | ${true} | ${true} | ${true} + ${'tes'} | ${false} | ${true} | ${true} | ${true} ${MOCK_SEARCH} | ${false} | ${true} | ${true} | ${true} `( 'Header Search Dropdown Items', @@ -115,7 +121,13 @@ describe('HeaderSearchApp', () => { describe(`when search is ${search}`, () => { beforeEach(() => { window.gon.current_username = MOCK_USERNAME; - createComponent({ search }); + createComponent( + { search }, + { + autocompleteGroupedSearchOptions: () => + search.match(/^[A-Za-z]+$/g) ? MOCK_SORTED_AUTOCOMPLETE_OPTIONS : [], + }, + ); findHeaderSearchInput().vm.$emit('click'); }); diff --git a/spec/frontend/header_search/components/header_search_autocomplete_items_spec.js b/spec/frontend/header_search/components/header_search_autocomplete_items_spec.js index f427482be46..b6be53fec99 100644 --- a/spec/frontend/header_search/components/header_search_autocomplete_items_spec.js +++ b/spec/frontend/header_search/components/header_search_autocomplete_items_spec.js @@ -1,4 +1,4 @@ -import { GlDropdownItem, GlLoadingIcon, GlAvatar, GlAlert } from '@gitlab/ui'; +import { GlDropdownItem, GlLoadingIcon, GlAvatar, GlAlert, GlDropdownDivider } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; import Vue, { nextTick } from 'vue'; import Vuex from 'vuex'; @@ -9,7 +9,14 @@ import { PROJECTS_CATEGORY, SMALL_AVATAR_PX, } from '~/header_search/constants'; -import { MOCK_GROUPED_AUTOCOMPLETE_OPTIONS, MOCK_SORTED_AUTOCOMPLETE_OPTIONS } from '../mock_data'; +import { + MOCK_GROUPED_AUTOCOMPLETE_OPTIONS, + MOCK_SORTED_AUTOCOMPLETE_OPTIONS, + MOCK_GROUPED_AUTOCOMPLETE_OPTIONS_SETTINGS_HELP, + MOCK_GROUPED_AUTOCOMPLETE_OPTIONS_HELP, + MOCK_SEARCH, + MOCK_GROUPED_AUTOCOMPLETE_OPTIONS_2, +} from '../mock_data'; Vue.use(Vuex); @@ -41,6 +48,7 @@ describe('HeaderSearchAutocompleteItems', () => { }); const findDropdownItems = () => wrapper.findAllComponents(GlDropdownItem); + const findGlDropdownDividers = () => wrapper.findAllComponents(GlDropdownDivider); const findFirstDropdownItem = () => findDropdownItems().at(0); const findDropdownItemTitles = () => findDropdownItems().wrappers.map((w) => w.text()); const findDropdownItemLinks = () => findDropdownItems().wrappers.map((w) => w.attributes('href')); @@ -140,6 +148,34 @@ describe('HeaderSearchAutocompleteItems', () => { }); }); }); + + describe.each` + search | items | dividerCount + ${null} | ${[]} | ${0} + ${''} | ${[]} | ${0} + ${'1'} | ${[]} | ${0} + ${')'} | ${[]} | ${0} + ${'t'} | ${MOCK_GROUPED_AUTOCOMPLETE_OPTIONS_SETTINGS_HELP} | ${1} + ${'te'} | ${MOCK_GROUPED_AUTOCOMPLETE_OPTIONS_HELP} | ${0} + ${'tes'} | ${MOCK_GROUPED_AUTOCOMPLETE_OPTIONS_2} | ${1} + ${MOCK_SEARCH} | ${MOCK_GROUPED_AUTOCOMPLETE_OPTIONS_2} | ${1} + `('Header Search Dropdown Dividers', ({ search, items, dividerCount }) => { + describe(`when search is ${search}`, () => { + beforeEach(() => { + createComponent( + { search }, + { + autocompleteGroupedSearchOptions: () => items, + }, + {}, + ); + }); + + it(`component should have ${dividerCount} dividers`, () => { + expect(findGlDropdownDividers()).toHaveLength(dividerCount); + }); + }); + }); }); describe('watchers', () => { diff --git a/spec/frontend/header_search/components/header_search_scoped_items_spec.js b/spec/frontend/header_search/components/header_search_scoped_items_spec.js index a65b4d8b813..8788fb23458 100644 --- a/spec/frontend/header_search/components/header_search_scoped_items_spec.js +++ b/spec/frontend/header_search/components/header_search_scoped_items_spec.js @@ -1,17 +1,21 @@ -import { GlDropdownItem } from '@gitlab/ui'; +import { GlDropdownItem, GlDropdownDivider } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; import Vue from 'vue'; import Vuex from 'vuex'; import { trimText } from 'helpers/text_helper'; import HeaderSearchScopedItems from '~/header_search/components/header_search_scoped_items.vue'; -import { MOCK_SEARCH, MOCK_SCOPED_SEARCH_OPTIONS } from '../mock_data'; +import { + MOCK_SEARCH, + MOCK_SCOPED_SEARCH_OPTIONS, + MOCK_GROUPED_AUTOCOMPLETE_OPTIONS, +} from '../mock_data'; Vue.use(Vuex); describe('HeaderSearchScopedItems', () => { let wrapper; - const createComponent = (initialState, props) => { + const createComponent = (initialState, mockGetters, props) => { const store = new Vuex.Store({ state: { search: MOCK_SEARCH, @@ -19,6 +23,8 @@ describe('HeaderSearchScopedItems', () => { }, getters: { scopedSearchOptions: () => MOCK_SCOPED_SEARCH_OPTIONS, + autocompleteGroupedSearchOptions: () => MOCK_GROUPED_AUTOCOMPLETE_OPTIONS, + ...mockGetters, }, }); @@ -35,6 +41,7 @@ describe('HeaderSearchScopedItems', () => { }); const findDropdownItems = () => wrapper.findAllComponents(GlDropdownItem); + const findGlDropdownDivider = () => wrapper.findComponent(GlDropdownDivider); const findFirstDropdownItem = () => findDropdownItems().at(0); const findDropdownItemTitles = () => findDropdownItems().wrappers.map((w) => trimText(w.text())); const findDropdownItemAriaLabels = () => @@ -79,7 +86,7 @@ describe('HeaderSearchScopedItems', () => { `('isOptionFocused', ({ currentFocusedOption, isFocused, ariaSelected }) => { describe(`when currentFocusedOption.html_id is ${currentFocusedOption?.html_id}`, () => { beforeEach(() => { - createComponent({}, { currentFocusedOption }); + createComponent({}, {}, { currentFocusedOption }); }); it(`should${isFocused ? '' : ' not'} have gl-bg-gray-50 applied`, () => { @@ -91,5 +98,21 @@ describe('HeaderSearchScopedItems', () => { }); }); }); + + describe.each` + autosuggestResults | showDivider + ${[]} | ${false} + ${MOCK_GROUPED_AUTOCOMPLETE_OPTIONS} | ${true} + `('scoped search items', ({ autosuggestResults, showDivider }) => { + describe(`when when we have ${autosuggestResults.length} auto-sugest results`, () => { + beforeEach(() => { + createComponent({}, { autocompleteGroupedSearchOptions: () => autosuggestResults }, {}); + }); + + it(`divider should${showDivider ? '' : ' not'} be shown`, () => { + expect(findGlDropdownDivider().exists()).toBe(showDivider); + }); + }); + }); }); }); diff --git a/spec/frontend/header_search/mock_data.js b/spec/frontend/header_search/mock_data.js index 1d980679547..358c224dfa6 100644 --- a/spec/frontend/header_search/mock_data.js +++ b/spec/frontend/header_search/mock_data.js @@ -226,3 +226,98 @@ export const MOCK_SORTED_AUTOCOMPLETE_OPTIONS = [ url: 'help/gitlab', }, ]; + +export const MOCK_GROUPED_AUTOCOMPLETE_OPTIONS_HELP = [ + { + category: 'Help', + data: [ + { + html_id: 'autocomplete-Help-1', + category: 'Help', + label: 'Rake Tasks Help', + url: '/help/raketasks/index', + }, + { + html_id: 'autocomplete-Help-2', + category: 'Help', + label: 'System Hooks Help', + url: '/help/system_hooks/system_hooks', + }, + ], + }, +]; + +export const MOCK_GROUPED_AUTOCOMPLETE_OPTIONS_SETTINGS_HELP = [ + { + category: 'Settings', + data: [ + { + html_id: 'autocomplete-Settings-0', + category: 'Settings', + label: 'User settings', + url: '/-/profile', + }, + { + html_id: 'autocomplete-Settings-3', + category: 'Settings', + label: 'Admin Section', + url: '/admin', + }, + ], + }, + { + category: 'Help', + data: [ + { + html_id: 'autocomplete-Help-1', + category: 'Help', + label: 'Rake Tasks Help', + url: '/help/raketasks/index', + }, + { + html_id: 'autocomplete-Help-2', + category: 'Help', + label: 'System Hooks Help', + url: '/help/system_hooks/system_hooks', + }, + ], + }, +]; + +export const MOCK_GROUPED_AUTOCOMPLETE_OPTIONS_2 = [ + { + category: 'Groups', + data: [ + { + html_id: 'autocomplete-Groups-0', + category: 'Groups', + id: 148, + label: 'Jashkenas / Test Subgroup / test-subgroup', + url: '/jashkenas/test-subgroup/test-subgroup', + avatar_url: '', + }, + { + html_id: 'autocomplete-Groups-1', + category: 'Groups', + id: 147, + label: 'Jashkenas / Test Subgroup', + url: '/jashkenas/test-subgroup', + avatar_url: '', + }, + ], + }, + { + category: 'Projects', + data: [ + { + html_id: 'autocomplete-Projects-2', + category: 'Projects', + id: 1, + value: 'Gitlab Test', + label: 'Gitlab Org / Gitlab Test', + url: '/gitlab-org/gitlab-test', + avatar_url: '/uploads/-/system/project/avatar/1/icons8-gitlab-512.png', + }, + ], + }, +]; diff --git a/spec/frontend/monitoring/store/actions_spec.js b/spec/frontend/monitoring/store/actions_spec.js index f60c531e3f6..b8734a13f08 100644 --- a/spec/frontend/monitoring/store/actions_spec.js +++ b/spec/frontend/monitoring/store/actions_spec.js @@ -7,9 +7,9 @@ import * as commonUtils from '~/lib/utils/common_utils'; import statusCodes from '~/lib/utils/http_status'; import { ENVIRONMENT_AVAILABLE_STATE } from '~/monitoring/constants'; -import getAnnotations from '~/monitoring/queries/getAnnotations.query.graphql'; -import getDashboardValidationWarnings from '~/monitoring/queries/getDashboardValidationWarnings.query.graphql'; -import getEnvironments from '~/monitoring/queries/getEnvironments.query.graphql'; +import getAnnotations from '~/monitoring/queries/get_annotations.query.graphql'; +import getDashboardValidationWarnings from '~/monitoring/queries/get_dashboard_validation_warnings.query.graphql'; +import getEnvironments from '~/monitoring/queries/get_environments.query.graphql'; import { createStore } from '~/monitoring/stores'; import { setGettingStartedEmptyState, diff --git a/spec/frontend/snippets/components/edit_spec.js b/spec/frontend/snippets/components/edit_spec.js index 61424fa1eb2..9cfe136129a 100644 --- a/spec/frontend/snippets/components/edit_spec.js +++ b/spec/frontend/snippets/components/edit_spec.js @@ -19,8 +19,8 @@ import { SNIPPET_VISIBILITY_INTERNAL, SNIPPET_VISIBILITY_PUBLIC, } from '~/snippets/constants'; -import CreateSnippetMutation from '~/snippets/mutations/createSnippet.mutation.graphql'; -import UpdateSnippetMutation from '~/snippets/mutations/updateSnippet.mutation.graphql'; +import CreateSnippetMutation from '~/snippets/mutations/create_snippet.mutation.graphql'; +import UpdateSnippetMutation from '~/snippets/mutations/update_snippet.mutation.graphql'; import FormFooterActions from '~/vue_shared/components/form/form_footer_actions.vue'; import TitleField from '~/vue_shared/components/form/title.vue'; import { testEntries, createGQLSnippetsQueryResponse, createGQLSnippet } from '../test_utils'; diff --git a/spec/frontend/snippets/components/snippet_header_spec.js b/spec/frontend/snippets/components/snippet_header_spec.js index 1b9d170556b..b750225a383 100644 --- a/spec/frontend/snippets/components/snippet_header_spec.js +++ b/spec/frontend/snippets/components/snippet_header_spec.js @@ -8,7 +8,7 @@ import waitForPromises from 'helpers/wait_for_promises'; import { Blob, BinaryBlob } from 'jest/blob/components/mock_data'; import { differenceInMilliseconds } from '~/lib/utils/datetime_utility'; import SnippetHeader, { i18n } from '~/snippets/components/snippet_header.vue'; -import DeleteSnippetMutation from '~/snippets/mutations/deleteSnippet.mutation.graphql'; +import DeleteSnippetMutation from '~/snippets/mutations/delete_snippet.mutation.graphql'; import axios from '~/lib/utils/axios_utils'; import createFlash, { FLASH_TYPES } from '~/flash'; diff --git a/spec/lib/gitlab/ci/parsers/security/validators/schema_validator_spec.rb b/spec/lib/gitlab/ci/parsers/security/validators/schema_validator_spec.rb index c83427b68ef..5f8f0d55186 100644 --- a/spec/lib/gitlab/ci/parsers/security/validators/schema_validator_spec.rb +++ b/spec/lib/gitlab/ci/parsers/security/validators/schema_validator_spec.rb @@ -3,6 +3,8 @@ require 'spec_helper' RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do + let(:validator) { described_class.new(report_type, report_data, report_version) } + describe 'SUPPORTED_VERSIONS' do schema_path = Rails.root.join("lib", "gitlab", "ci", "parsers", "security", "validators", "schemas") @@ -47,48 +49,484 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do end end - using RSpec::Parameterized::TableSyntax + describe '#valid?' do + subject { validator.valid? } - where(:report_type, :report_version, :expected_errors, :valid_data) do - 'sast' | '10.0.0' | ['root is missing required keys: vulnerabilities'] | { 'version' => '10.0.0', 'vulnerabilities' => [] } - :sast | '10.0.0' | ['root is missing required keys: vulnerabilities'] | { 'version' => '10.0.0', 'vulnerabilities' => [] } - :secret_detection | '10.0.0' | ['root is missing required keys: vulnerabilities'] | { 'version' => '10.0.0', 'vulnerabilities' => [] } - end + context 'when given a supported schema version' do + let(:report_type) { :dast } + let(:report_version) { described_class::SUPPORTED_VERSIONS[report_type].last } - with_them do - let(:validator) { described_class.new(report_type, report_data, report_version) } + context 'and the report is valid' do + let(:report_data) do + { + 'version' => report_version, + 'vulnerabilities' => [] + } + end - describe '#valid?' do - subject { validator.valid? } + it { is_expected.to be_truthy } + end - context 'when given data is invalid according to the schema' do - let(:report_data) { {} } + context 'and the report is invalid' do + let(:report_data) do + { + 'version' => report_version + } + end it { is_expected.to be_falsey } end + end + + context 'when given a deprecated schema version' do + let(:report_type) { :dast } + let(:report_version) { described_class::DEPRECATED_VERSIONS[report_type].last } - context 'when given data is valid according to the schema' do - let(:report_data) { valid_data } + context 'and the report passes schema validation' do + let(:report_data) do + { + 'version' => '10.0.0', + 'vulnerabilities' => [] + } + end it { is_expected.to be_truthy } end - context 'when no report_version is provided' do - let(:report_version) { nil } - let(:report_data) { valid_data } + context 'and the report does not pass schema validation' do + context 'and enforce_security_report_validation is enabled' do + before do + stub_feature_flags(enforce_security_report_validation: true) + end + + let(:report_data) do + { + 'version' => 'V2.7.0' + } + end + + it { is_expected.to be_falsey } + end + + context 'and enforce_security_report_validation is disabled' do + before do + stub_feature_flags(enforce_security_report_validation: false) + end + + let(:report_data) do + { + 'version' => 'V2.7.0' + } + end + + it { is_expected.to be_truthy } + end + end + end + + context 'when given an unsupported schema version' do + let(:report_type) { :dast } + let(:report_version) { "12.37.0" } + + context 'if enforce_security_report_validation is enabled' do + before do + stub_feature_flags(enforce_security_report_validation: true) + end + + context 'and the report is valid' do + let(:report_data) do + { + 'version' => report_version, + 'vulnerabilities' => [] + } + end + + it { is_expected.to be_falsey } + end + + context 'and the report is invalid' do + let(:report_data) do + { + 'version' => report_version + } + end + + it { is_expected.to be_falsey } + end + end + + context 'if enforce_security_report_validation is disabled' do + before do + stub_feature_flags(enforce_security_report_validation: false) + end + + context 'and the report is valid' do + let(:report_data) do + { + 'version' => report_version, + 'vulnerabilities' => [] + } + end + + it { is_expected.to be_truthy } + end + + context 'and the report is invalid' do + let(:report_data) do + { + 'version' => report_version + } + end + + it { is_expected.to be_truthy } + end + end + end + end + + describe '#errors' do + subject { validator.errors } + + context 'when given a supported schema version' do + let(:report_type) { :dast } + let(:report_version) { described_class::SUPPORTED_VERSIONS[report_type].last } + + context 'and the report is valid' do + let(:report_data) do + { + 'version' => report_version, + 'vulnerabilities' => [] + } + end + + let(:expected_errors) { [] } + + it { is_expected.to match_array(expected_errors) } + end + + context 'and the report is invalid' do + let(:report_data) do + { + 'version' => report_version + } + end + + let(:expected_errors) do + [ + 'root is missing required keys: vulnerabilities' + ] + end + + it { is_expected.to match_array(expected_errors) } + end + end + + context 'when given a deprecated schema version' do + let(:report_type) { :dast } + let(:report_version) { described_class::DEPRECATED_VERSIONS[report_type].last } + + context 'and the report passes schema validation' do + let(:report_data) do + { + 'version' => '10.0.0', + 'vulnerabilities' => [] + } + end + + let(:expected_errors) { [] } + + it { is_expected.to match_array(expected_errors) } + end + + context 'and the report does not pass schema validation' do + context 'and enforce_security_report_validation is enabled' do + before do + stub_feature_flags(enforce_security_report_validation: true) + end + + let(:report_data) do + { + 'version' => 'V2.7.0' + } + end + + let(:expected_errors) do + [ + "property '/version' does not match pattern: ^[0-9]+\\.[0-9]+\\.[0-9]+$", + "root is missing required keys: vulnerabilities" + ] + end + + it { is_expected.to match_array(expected_errors) } + end + + context 'and enforce_security_report_validation is disabled' do + before do + stub_feature_flags(enforce_security_report_validation: false) + end + + let(:report_data) do + { + 'version' => 'V2.7.0' + } + end + + let(:expected_errors) { [] } - it 'does not fail' do - expect { subject }.not_to raise_error + it { is_expected.to match_array(expected_errors) } end end end - describe '#errors' do - let(:report_data) { { 'version' => '10.0.0' } } + context 'when given an unsupported schema version' do + let(:report_type) { :dast } + let(:report_version) { "12.37.0" } - subject { validator.errors } + context 'if enforce_security_report_validation is enabled' do + before do + stub_feature_flags(enforce_security_report_validation: true) + end + + context 'and the report is valid' do + let(:report_data) do + { + 'version' => report_version, + 'vulnerabilities' => [] + } + end - it { is_expected.to eq(expected_errors) } + let(:expected_errors) do + [ + "Version 12.37.0 for report type dast is unsupported, supported versions for this report type are: 14.0.0, 14.0.1, 14.0.2, 14.0.3, 14.0.4, 14.0.5, 14.0.6, 14.1.0" + ] + end + + it { is_expected.to match_array(expected_errors) } + end + + context 'and the report is invalid' do + let(:report_data) do + { + 'version' => report_version + } + end + + let(:expected_errors) do + [ + "Version 12.37.0 for report type dast is unsupported, supported versions for this report type are: 14.0.0, 14.0.1, 14.0.2, 14.0.3, 14.0.4, 14.0.5, 14.0.6, 14.1.0", + "root is missing required keys: vulnerabilities" + ] + end + + it { is_expected.to match_array(expected_errors) } + end + end + + context 'if enforce_security_report_validation is disabled' do + before do + stub_feature_flags(enforce_security_report_validation: false) + end + + context 'and the report is valid' do + let(:report_data) do + { + 'version' => report_version, + 'vulnerabilities' => [] + } + end + + let(:expected_errors) { [] } + + it { is_expected.to match_array(expected_errors) } + end + + context 'and the report is invalid' do + let(:report_data) do + { + 'version' => report_version + } + end + + let(:expected_errors) { [] } + + it { is_expected.to match_array(expected_errors) } + end + end + end + end + + describe '#warnings' do + subject { validator.warnings } + + context 'when given a supported schema version' do + let(:report_type) { :dast } + let(:report_version) { described_class::SUPPORTED_VERSIONS[report_type].last } + + context 'and the report is valid' do + let(:report_data) do + { + 'version' => report_version, + 'vulnerabilities' => [] + } + end + + let(:expected_warnings) { [] } + + it { is_expected.to match_array(expected_warnings) } + end + + context 'and the report is invalid' do + let(:report_data) do + { + 'version' => report_version + } + end + + let(:expected_warnings) { [] } + + it { is_expected.to match_array(expected_warnings) } + end + end + + context 'when given a deprecated schema version' do + let(:report_type) { :dast } + let(:report_version) { described_class::DEPRECATED_VERSIONS[report_type].last } + + context 'and the report passes schema validation' do + let(:report_data) do + { + 'vulnerabilities' => [] + } + end + + let(:expected_warnings) do + [ + "Version V2.7.0 for report type dast has been deprecated, supported versions for this report type are: 14.0.0, 14.0.1, 14.0.2, 14.0.3, 14.0.4, 14.0.5, 14.0.6, 14.1.0" + ] + end + + it { is_expected.to match_array(expected_warnings) } + end + + context 'and the report does not pass schema validation' do + context 'and enforce_security_report_validation is enabled' do + before do + stub_feature_flags(enforce_security_report_validation: true) + end + + let(:report_data) do + { + 'version' => 'V2.7.0' + } + end + + let(:expected_warnings) do + [ + "Version V2.7.0 for report type dast has been deprecated, supported versions for this report type are: 14.0.0, 14.0.1, 14.0.2, 14.0.3, 14.0.4, 14.0.5, 14.0.6, 14.1.0" + ] + end + + it { is_expected.to match_array(expected_warnings) } + end + + context 'and enforce_security_report_validation is disabled' do + before do + stub_feature_flags(enforce_security_report_validation: false) + end + + let(:report_data) do + { + 'version' => 'V2.7.0' + } + end + + let(:expected_warnings) do + [ + "Version V2.7.0 for report type dast has been deprecated, supported versions for this report type are: 14.0.0, 14.0.1, 14.0.2, 14.0.3, 14.0.4, 14.0.5, 14.0.6, 14.1.0", + "property '/version' does not match pattern: ^[0-9]+\\.[0-9]+\\.[0-9]+$", + "root is missing required keys: vulnerabilities" + ] + end + + it { is_expected.to match_array(expected_warnings) } + end + end + end + + context 'when given an unsupported schema version' do + let(:report_type) { :dast } + let(:report_version) { "12.37.0" } + + context 'if enforce_security_report_validation is enabled' do + before do + stub_feature_flags(enforce_security_report_validation: true) + end + + context 'and the report is valid' do + let(:report_data) do + { + 'version' => report_version, + 'vulnerabilities' => [] + } + end + + let(:expected_warnings) { [] } + + it { is_expected.to match_array(expected_warnings) } + end + + context 'and the report is invalid' do + let(:report_data) do + { + 'version' => report_version + } + end + + let(:expected_warnings) { [] } + + it { is_expected.to match_array(expected_warnings) } + end + end + + context 'if enforce_security_report_validation is disabled' do + before do + stub_feature_flags(enforce_security_report_validation: false) + end + + context 'and the report is valid' do + let(:report_data) do + { + 'version' => report_version, + 'vulnerabilities' => [] + } + end + + let(:expected_warnings) do + [ + "Version 12.37.0 for report type dast is unsupported, supported versions for this report type are: 14.0.0, 14.0.1, 14.0.2, 14.0.3, 14.0.4, 14.0.5, 14.0.6, 14.1.0" + ] + end + + it { is_expected.to match_array(expected_warnings) } + end + + context 'and the report is invalid' do + let(:report_data) do + { + 'version' => report_version + } + end + + let(:expected_warnings) do + [ + "Version 12.37.0 for report type dast is unsupported, supported versions for this report type are: 14.0.0, 14.0.1, 14.0.2, 14.0.3, 14.0.4, 14.0.5, 14.0.6, 14.1.0", + "root is missing required keys: vulnerabilities" + ] + end + + it { is_expected.to match_array(expected_warnings) } + end + end end end end diff --git a/spec/lib/gitlab/workhorse_spec.rb b/spec/lib/gitlab/workhorse_spec.rb index 91ab0a53c6c..3bab9aec454 100644 --- a/spec/lib/gitlab/workhorse_spec.rb +++ b/spec/lib/gitlab/workhorse_spec.rb @@ -448,14 +448,6 @@ RSpec.describe Gitlab::Workhorse do end end - describe '.detect_content_type' do - subject { described_class.detect_content_type } - - it 'returns array setting detect content type in workhorse' do - expect(subject).to eq(%w[Gitlab-Workhorse-Detect-Content-Type true]) - end - end - describe '.send_git_blob' do include FakeBlobHelpers diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb index f2ea17556a0..09ac15429a5 100644 --- a/spec/models/namespace_spec.rb +++ b/spec/models/namespace_spec.rb @@ -2235,13 +2235,5 @@ RSpec.describe Namespace do let(:object) { build(:namespace) } it_behaves_like 'blocks unsafe serialization' - - context 'when feature flag block_namespace_serialization is disabled' do - before do - stub_feature_flags(block_namespace_serialization: false) - end - - it_behaves_like 'allows unsafe serialization' - end end end diff --git a/spec/requests/api/ci/job_artifacts_spec.rb b/spec/requests/api/ci/job_artifacts_spec.rb index 3a52f2ad30f..8146560bc26 100644 --- a/spec/requests/api/ci/job_artifacts_spec.rb +++ b/spec/requests/api/ci/job_artifacts_spec.rb @@ -556,8 +556,7 @@ RSpec.describe API::Ci::JobArtifacts do expect(response).to have_gitlab_http_status(:ok) expect(response.headers.to_h) .to include('Content-Type' => 'application/json', - 'Gitlab-Workhorse-Send-Data' => /artifacts-entry/, - 'Gitlab-Workhorse-Detect-Content-Type' => 'true') + 'Gitlab-Workhorse-Send-Data' => /artifacts-entry/) end end @@ -627,8 +626,7 @@ RSpec.describe API::Ci::JobArtifacts do expect(response).to have_gitlab_http_status(:ok) expect(response.headers.to_h) .to include('Content-Type' => 'application/json', - 'Gitlab-Workhorse-Send-Data' => /artifacts-entry/, - 'Gitlab-Workhorse-Detect-Content-Type' => 'true') + 'Gitlab-Workhorse-Send-Data' => /artifacts-entry/) expect(response.parsed_body).to be_empty end end @@ -646,8 +644,7 @@ RSpec.describe API::Ci::JobArtifacts do expect(response).to have_gitlab_http_status(:ok) expect(response.headers.to_h) .to include('Content-Type' => 'application/json', - 'Gitlab-Workhorse-Send-Data' => /artifacts-entry/, - 'Gitlab-Workhorse-Detect-Content-Type' => 'true') + 'Gitlab-Workhorse-Send-Data' => /artifacts-entry/) end end diff --git a/spec/support/shared_examples/services/container_registry_auth_service_shared_examples.rb b/spec/support/shared_examples/services/container_registry_auth_service_shared_examples.rb index a780952d51b..95471203983 100644 --- a/spec/support/shared_examples/services/container_registry_auth_service_shared_examples.rb +++ b/spec/support/shared_examples/services/container_registry_auth_service_shared_examples.rb @@ -204,6 +204,46 @@ RSpec.shared_examples 'a container registry auth service' do it_behaves_like 'not a container repository factory' end + describe '.pull_nested_repositories_access_token' do + let_it_be(:project) { create(:project) } + + let(:token) { described_class.pull_nested_repositories_access_token(project.full_path) } + let(:access) do + [ + { + 'type' => 'repository', + 'name' => project.full_path, + 'actions' => ['pull'] + }, + { + 'type' => 'repository', + 'name' => "#{project.full_path}/*", + 'actions' => ['pull'] + } + ] + end + + subject { { token: token } } + + it 'has the correct scope' do + expect(payload).to include('access' => access) + end + + it_behaves_like 'a valid token' + it_behaves_like 'not a container repository factory' + + context 'with path ending with a slash' do + let(:token) { described_class.pull_nested_repositories_access_token("#{project.full_path}/") } + + it 'has the correct scope' do + expect(payload).to include('access' => access) + end + + it_behaves_like 'a valid token' + it_behaves_like 'not a container repository factory' + end + end + context 'user authorization' do let_it_be(:current_user) { create(:user) } diff --git a/spec/views/groups/runners/_sort_dropdown.html.haml_spec.rb b/spec/views/groups/runners/_sort_dropdown.html.haml_spec.rb index 4b5027a5a56..5438fea85ee 100644 --- a/spec/views/groups/runners/_sort_dropdown.html.haml_spec.rb +++ b/spec/views/groups/runners/_sort_dropdown.html.haml_spec.rb @@ -4,27 +4,22 @@ require 'spec_helper' RSpec.describe 'groups/runners/sort_dropdown.html.haml' do describe 'render' do - let_it_be(:sort_options_hash) { { by_title: 'Title' } } - let_it_be(:sort_title_created_date) { 'Created date' } - - before do - allow(view).to receive(:sort).and_return('by_title') - end - describe 'when a sort option is not selected' do it 'renders a default sort option' do - render 'groups/runners/sort_dropdown', sort_options_hash: sort_options_hash, sort_title_created_date: sort_title_created_date + render 'groups/runners/sort_dropdown' - expect(rendered).to have_content 'Created date' + expect(rendered).to have_content _('Created date') end end describe 'when a sort option is selected' do - it 'renders the selected sort option' do - @sort = :by_title - render 'groups/runners/sort_dropdown', sort_options_hash: sort_options_hash, sort_title_created_date: sort_title_created_date + before do + assign(:sort, 'contacted_asc') + render 'groups/runners/sort_dropdown' + end - expect(rendered).to have_content 'Title' + it 'renders the selected sort option' do + expect(rendered).to have_content _('Last Contact') end end end |