Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-03-30 09:08:12 +00:00
parent 9e83c35c6a
commit df2358a5f7
83 changed files with 925 additions and 221 deletions

View File

@ -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

View File

@ -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|$))

View File

@ -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'

View File

@ -1,4 +1,4 @@
#import "~/graphql_shared/fragments/pageInfo.fragment.graphql"
#import "~/graphql_shared/fragments/page_info.fragment.graphql"
query accessTokensGetProjects(
$search: String = ""

View File

@ -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) {

View File

@ -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) {

View File

@ -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) {

View File

@ -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(

View File

@ -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(

View File

@ -1,4 +1,4 @@
#import "~/graphql_shared/fragments/pageInfo.fragment.graphql"
#import "~/graphql_shared/fragments/page_info.fragment.graphql"
query getProjects(
$search: String!

View File

@ -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>

View File

@ -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"

View File

@ -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>

View File

@ -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';

View File

@ -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);
});

View File

@ -190,7 +190,6 @@ export const autocompleteGroupedSearchOptions = (state) => {
results.push(groupedOptions[option.category]);
}
});
return results;
};

View File

@ -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(

View File

@ -1,4 +1,4 @@
#import "~/graphql_shared/fragments/pageInfo.fragment.graphql"
#import "~/graphql_shared/fragments/page_info.fragment.graphql"
query jiraGetProjects(
$search: String!

View File

@ -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(() => {});

View File

@ -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';

View File

@ -1,4 +1,4 @@
#import "~/graphql_shared/fragments/pageInfo.fragment.graphql"
#import "~/graphql_shared/fragments/page_info.fragment.graphql"
query getContainerRepositoryTags(
$id: ID!

View File

@ -1,4 +1,4 @@
#import "~/graphql_shared/fragments/pageInfo.fragment.graphql"
#import "~/graphql_shared/fragments/page_info.fragment.graphql"
query getDependencyProxyDetails(
$fullPath: ID!

View File

@ -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!

View File

@ -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) {

View File

@ -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) {

View File

@ -1,4 +1,4 @@
#import "~/graphql_shared/fragments/pageInfo.fragment.graphql"
#import "~/graphql_shared/fragments/page_info.fragment.graphql"
query getRunnerProjects(
$id: CiRunnerID!

View File

@ -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

View File

@ -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!

View File

@ -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 = {

View File

@ -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>

View File

@ -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>

View File

@ -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) {

View File

@ -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,

View File

@ -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

View File

@ -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')

View File

@ -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

View File

@ -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' }

View File

@ -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

View File

@ -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

View File

@ -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)**

View File

@ -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)**

View File

@ -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)**

View File

@ -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)**

View File

@ -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)**

View File

@ -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)**

View File

@ -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)**

View File

@ -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 - 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.
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.
- 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 GitLabs performance testing tool (and you can too)](https://about.gitlab.com/blog/2020/02/18/how-were-building-up-performance-testing-of-gitlab/).
### 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/).
### 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.
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 GitLabs 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

View File

@ -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
---

View File

@ -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
---

View File

@ -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).

View File

@ -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) {

View File

@ -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
---

View File

@ -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
---

View File

@ -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
---

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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))

View File

@ -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');
});

View File

@ -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', () => {

View File

@ -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);
});
});
});
});
});

View File

@ -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',
},
],
},
];

View File

@ -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,

View File

@ -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';

View File

@ -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';

View File

@ -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) }
describe '#valid?' do
subject { validator.valid? }
context 'when given data is invalid according to the schema' do
let(:report_data) { {} }
it { is_expected.to be_falsey }
end
context 'when given data is valid according to the schema' do
let(:report_data) { valid_data }
context 'and the report is valid' do
let(:report_data) do
{
'version' => report_version,
'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 is invalid' do
let(:report_data) do
{
'version' => report_version
}
end
it 'does not fail' do
expect { subject }.not_to raise_error
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 '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 '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
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
it { is_expected.to eq(expected_errors) }
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 { is_expected.to match_array(expected_errors) }
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_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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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) }

View File

@ -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