Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
a21c232a6c
commit
4b788dd673
2
Gemfile
2
Gemfile
|
|
@ -635,7 +635,7 @@ gem 'spamcheck', '~> 1.3.0' # rubocop:todo Gemfile/MissingFeatureCategory
|
|||
gem 'gitaly', '~> 17.5.0.pre.rc1', feature_category: :gitaly
|
||||
|
||||
# KAS GRPC protocol definitions
|
||||
gem 'gitlab-kas-grpc', '~> 17.6.1', feature_category: :deployment_management
|
||||
gem 'gitlab-kas-grpc', '~> 17.7.0', feature_category: :deployment_management
|
||||
|
||||
# Lock the version before issues below are resolved:
|
||||
# https://gitlab.com/gitlab-org/gitlab/-/issues/473169#note_2028352939
|
||||
|
|
|
|||
|
|
@ -231,7 +231,7 @@
|
|||
{"name":"gitlab-glfm-markdown","version":"0.0.23","platform":"ruby","checksum":"89a12909c39aea326adb0b7194f7b89d61b4f9122308435fba0bcb84e4f4ff24"},
|
||||
{"name":"gitlab-glfm-markdown","version":"0.0.23","platform":"x86_64-darwin","checksum":"4b77a37358d98c3b2269f7dd19f6549555c5de00bf12a4eca25c34076f72f78d"},
|
||||
{"name":"gitlab-glfm-markdown","version":"0.0.23","platform":"x86_64-linux","checksum":"2b71ec5ae06a524114e2cf423ce6635fd1f5c6776c0c956188aa0b2f0fbfbead"},
|
||||
{"name":"gitlab-kas-grpc","version":"17.6.2","platform":"ruby","checksum":"cf057d6c9ac2cfdbd59de9af95adeeb74be3996d4babfb5fb67397dd8a976887"},
|
||||
{"name":"gitlab-kas-grpc","version":"17.7.0","platform":"ruby","checksum":"3960e514672c22e7efad533140255acd59b0a32e22cc270af124361173400600"},
|
||||
{"name":"gitlab-labkit","version":"0.37.0","platform":"ruby","checksum":"d2dd0a60db2149a9a8eebf2975dc23f54ac3ceb01bdba732eb1b26b86dfffa70"},
|
||||
{"name":"gitlab-license","version":"2.6.0","platform":"ruby","checksum":"2c1f8ae73835640ec77bf758c1d0c9730635043c01cf77902f7976e826d7d016"},
|
||||
{"name":"gitlab-mail_room","version":"0.0.25","platform":"ruby","checksum":"223ce7c3c0797b6015eaa37147884e6ddc7be9a7ee90a424358c96bc18613b1a"},
|
||||
|
|
|
|||
|
|
@ -753,7 +753,7 @@ GEM
|
|||
nokogiri (~> 1, >= 1.10.8)
|
||||
gitlab-glfm-markdown (0.0.23)
|
||||
rb_sys (= 0.9.94)
|
||||
gitlab-kas-grpc (17.6.2)
|
||||
gitlab-kas-grpc (17.7.0)
|
||||
grpc (~> 1.0)
|
||||
gitlab-labkit (0.37.0)
|
||||
actionpack (>= 5.0.0, < 8.1.0)
|
||||
|
|
@ -2083,7 +2083,7 @@ DEPENDENCIES
|
|||
gitlab-glfm-markdown (~> 0.0.21)
|
||||
gitlab-housekeeper!
|
||||
gitlab-http!
|
||||
gitlab-kas-grpc (~> 17.6.1)
|
||||
gitlab-kas-grpc (~> 17.7.0)
|
||||
gitlab-labkit (~> 0.37.0)
|
||||
gitlab-license (~> 2.6)
|
||||
gitlab-mail_room (~> 0.0.24)
|
||||
|
|
|
|||
|
|
@ -232,7 +232,7 @@
|
|||
{"name":"gitlab-glfm-markdown","version":"0.0.23","platform":"ruby","checksum":"89a12909c39aea326adb0b7194f7b89d61b4f9122308435fba0bcb84e4f4ff24"},
|
||||
{"name":"gitlab-glfm-markdown","version":"0.0.23","platform":"x86_64-darwin","checksum":"4b77a37358d98c3b2269f7dd19f6549555c5de00bf12a4eca25c34076f72f78d"},
|
||||
{"name":"gitlab-glfm-markdown","version":"0.0.23","platform":"x86_64-linux","checksum":"2b71ec5ae06a524114e2cf423ce6635fd1f5c6776c0c956188aa0b2f0fbfbead"},
|
||||
{"name":"gitlab-kas-grpc","version":"17.6.2","platform":"ruby","checksum":"cf057d6c9ac2cfdbd59de9af95adeeb74be3996d4babfb5fb67397dd8a976887"},
|
||||
{"name":"gitlab-kas-grpc","version":"17.7.0","platform":"ruby","checksum":"3960e514672c22e7efad533140255acd59b0a32e22cc270af124361173400600"},
|
||||
{"name":"gitlab-labkit","version":"0.37.0","platform":"ruby","checksum":"d2dd0a60db2149a9a8eebf2975dc23f54ac3ceb01bdba732eb1b26b86dfffa70"},
|
||||
{"name":"gitlab-license","version":"2.6.0","platform":"ruby","checksum":"2c1f8ae73835640ec77bf758c1d0c9730635043c01cf77902f7976e826d7d016"},
|
||||
{"name":"gitlab-mail_room","version":"0.0.25","platform":"ruby","checksum":"223ce7c3c0797b6015eaa37147884e6ddc7be9a7ee90a424358c96bc18613b1a"},
|
||||
|
|
|
|||
|
|
@ -763,7 +763,7 @@ GEM
|
|||
nokogiri (~> 1, >= 1.10.8)
|
||||
gitlab-glfm-markdown (0.0.23)
|
||||
rb_sys (= 0.9.94)
|
||||
gitlab-kas-grpc (17.6.2)
|
||||
gitlab-kas-grpc (17.7.0)
|
||||
grpc (~> 1.0)
|
||||
gitlab-labkit (0.37.0)
|
||||
actionpack (>= 5.0.0, < 8.1.0)
|
||||
|
|
@ -2111,7 +2111,7 @@ DEPENDENCIES
|
|||
gitlab-glfm-markdown (~> 0.0.21)
|
||||
gitlab-housekeeper!
|
||||
gitlab-http!
|
||||
gitlab-kas-grpc (~> 17.6.1)
|
||||
gitlab-kas-grpc (~> 17.7.0)
|
||||
gitlab-labkit (~> 0.37.0)
|
||||
gitlab-license (~> 2.6)
|
||||
gitlab-mail_room (~> 0.0.24)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@ import { GlBreakpointInstance as bp } from '@gitlab/ui/dist/utils';
|
|||
import ShowMore from '~/vue_shared/components/show_more.vue';
|
||||
import AssigneeAvatarLink from '~/sidebar/components/assignees/assignee_avatar_link.vue';
|
||||
import { __, s__ } from '~/locale';
|
||||
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
|
||||
import { InternalEvents } from '~/tracking';
|
||||
import { CLICK_PIPELINE_LINK_ON_DEPLOYMENT_PAGE } from '~/deployments/utils';
|
||||
import AsideItem from './aside_item.vue';
|
||||
|
||||
export default {
|
||||
|
|
@ -17,6 +20,7 @@ export default {
|
|||
directives: {
|
||||
GlTooltip: GlTooltipDirective,
|
||||
},
|
||||
mixins: [InternalEvents.mixin()],
|
||||
props: {
|
||||
deployment: {
|
||||
required: true,
|
||||
|
|
@ -72,6 +76,9 @@ export default {
|
|||
hasUrl() {
|
||||
return Boolean(this.environment.externalUrl);
|
||||
},
|
||||
pipelineId() {
|
||||
return getIdFromGraphQLId(this.deployment.job.pipeline.id);
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
window.addEventListener('resize', this.handleWindowResize);
|
||||
|
|
@ -87,11 +94,15 @@ export default {
|
|||
toggleSidebar() {
|
||||
this.isExpanded = !this.isExpanded;
|
||||
},
|
||||
trackPipelineLinkClick() {
|
||||
this.trackEvent(CLICK_PIPELINE_LINK_ON_DEPLOYMENT_PAGE);
|
||||
},
|
||||
},
|
||||
i18n: {
|
||||
openUrl: s__('Deployment|Open URL'),
|
||||
triggerer: s__('Deployment|Triggerer'),
|
||||
relatedTags: s__('Deployment|Related Tags'),
|
||||
pipeline: s__('Deployment|Pipeline'),
|
||||
job: s__('Deployment|Job'),
|
||||
branch: s__('Deployment|Branch'),
|
||||
tag: s__('Deployment|Tag'),
|
||||
|
|
@ -172,6 +183,17 @@ export default {
|
|||
</show-more>
|
||||
</aside-item>
|
||||
|
||||
<aside-item v-if="hasJob" class="gl-mb-3" data-testid="deployment-pipeline">
|
||||
<template #header>{{ $options.i18n.pipeline }}</template>
|
||||
<gl-link
|
||||
:href="deployment.job.pipeline.path"
|
||||
data-testid="deployment-pipeline-link"
|
||||
@click="trackPipelineLinkClick"
|
||||
>
|
||||
#{{ pipelineId }}
|
||||
</gl-link>
|
||||
</aside-item>
|
||||
|
||||
<aside-item v-if="hasJob" class="gl-mb-3">
|
||||
<template #header>{{ $options.i18n.job }}</template>
|
||||
<gl-link :href="deployment.job.webPath">
|
||||
|
|
|
|||
|
|
@ -21,6 +21,10 @@ query fetchDeployment($fullPath: ID!, $iid: ID!) {
|
|||
...DeploymentJob
|
||||
canPlayJob
|
||||
manualJob
|
||||
pipeline {
|
||||
id
|
||||
path
|
||||
}
|
||||
}
|
||||
commit {
|
||||
id
|
||||
|
|
|
|||
|
|
@ -3,3 +3,5 @@ export const FINISHED_STATUSES = ['SUCCESS', 'FAILED', 'CANCELED'];
|
|||
export const UPCOMING_STATUSES = ['RUNNING', 'BLOCKED'];
|
||||
|
||||
export const isFinished = ({ status }) => FINISHED_STATUSES.includes(status);
|
||||
|
||||
export const CLICK_PIPELINE_LINK_ON_DEPLOYMENT_PAGE = 'clicked_deployment_details_pipeline_link';
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { GlModal, GlFormGroup, GlFormSelect, GlAlert, GlButton } from '@gitlab/ui';
|
||||
import { GlModal, GlFormGroup, GlFormSelect, GlAlert } from '@gitlab/ui';
|
||||
import { differenceBy } from 'lodash';
|
||||
import * as Sentry from '~/sentry/sentry_browser_wrapper';
|
||||
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
||||
|
|
@ -26,16 +26,14 @@ import convertWorkItemMutation from '../graphql/work_item_convert.mutation.graph
|
|||
import getWorkItemDesignListQuery from './design_management/graphql/design_collection.query.graphql';
|
||||
|
||||
export default {
|
||||
i18n: {
|
||||
type: __('Type'),
|
||||
subText: s__('WorkItem|Select which type you would like to change this item to.'),
|
||||
},
|
||||
components: {
|
||||
GlModal,
|
||||
GlFormGroup,
|
||||
GlFormSelect,
|
||||
GlAlert,
|
||||
GlButton,
|
||||
},
|
||||
actionCancel: {
|
||||
text: __('Cancel'),
|
||||
},
|
||||
mixins: [glFeatureFlagMixin()],
|
||||
inject: ['hasOkrsFeature'],
|
||||
|
|
@ -216,6 +214,15 @@ export default {
|
|||
selectedWorkItemTypeValue() {
|
||||
return this.selectedWorkItemType?.value || null;
|
||||
},
|
||||
actionPrimary() {
|
||||
return {
|
||||
text: s__('WorkItem|Change type'),
|
||||
attributes: {
|
||||
variant: 'confirm',
|
||||
disabled: this.changeTypeDisabled,
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async changeType() {
|
||||
|
|
@ -329,6 +336,10 @@ export default {
|
|||
modal-id="work-item-change-type"
|
||||
:title="s__('WorkItem|Change type')"
|
||||
size="sm"
|
||||
:action-primary="actionPrimary"
|
||||
:action-cancel="$options.actionCancel"
|
||||
@primary="changeType"
|
||||
@canceled="hide"
|
||||
>
|
||||
<gl-alert
|
||||
v-if="errorMessage"
|
||||
|
|
@ -339,46 +350,31 @@ export default {
|
|||
>
|
||||
{{ errorMessage }}
|
||||
</gl-alert>
|
||||
<div>
|
||||
<div class="gl-mb-4">{{ $options.i18n.subText }}</div>
|
||||
<gl-form-group :label="$options.i18n.type" label-for="work-item-type-select">
|
||||
<gl-form-select
|
||||
id="work-item-type-select"
|
||||
:value="selectedWorkItemTypeValue"
|
||||
width="md"
|
||||
:options="allowedConversionWorkItemTypes"
|
||||
@change="validateWorkItemType"
|
||||
/>
|
||||
</gl-form-group>
|
||||
<gl-alert
|
||||
v-if="warningMessage"
|
||||
data-testid="change-type-warning-message"
|
||||
variant="warning"
|
||||
:dismissible="false"
|
||||
>
|
||||
{{ warningMessage }}
|
||||
<ul v-if="hasWidgetDifference" class="gl-mb-0">
|
||||
<li v-for="widget in widgetsWithExistingData" :key="widget.type">
|
||||
{{ widget.name }}
|
||||
</li>
|
||||
</ul>
|
||||
</gl-alert>
|
||||
<div class="gl-mb-4">
|
||||
{{ s__('WorkItem|Select which type you would like to change this item to.') }}
|
||||
</div>
|
||||
<template #modal-footer>
|
||||
<div class="gl-m-0 gl-flex gl-flex-row gl-flex-wrap gl-justify-end">
|
||||
<gl-button @click="hide">
|
||||
{{ __('Cancel') }}
|
||||
</gl-button>
|
||||
<div class="gl-mr-3"></div>
|
||||
<gl-button
|
||||
:disabled="changeTypeDisabled"
|
||||
category="primary"
|
||||
variant="confirm"
|
||||
data-testid="change-type-confirmation-button"
|
||||
@click="changeType"
|
||||
>{{ s__('WorkItem|Change type') }}</gl-button
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
<gl-form-group :label="__('Type')" label-for="work-item-type-select">
|
||||
<gl-form-select
|
||||
id="work-item-type-select"
|
||||
data-testid="work-item-change-type-select"
|
||||
:value="selectedWorkItemTypeValue"
|
||||
width="md"
|
||||
:options="allowedConversionWorkItemTypes"
|
||||
@change="validateWorkItemType"
|
||||
/>
|
||||
</gl-form-group>
|
||||
<gl-alert
|
||||
v-if="warningMessage"
|
||||
data-testid="change-type-warning-message"
|
||||
variant="warning"
|
||||
:dismissible="false"
|
||||
>
|
||||
{{ warningMessage }}
|
||||
<ul v-if="hasWidgetDifference" class="gl-mb-0">
|
||||
<li v-for="widget in widgetsWithExistingData" :key="widget.type">
|
||||
{{ widget.name }}
|
||||
</li>
|
||||
</ul>
|
||||
</gl-alert>
|
||||
</gl-modal>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ module Types
|
|||
|
||||
include Gitlab::Graphql::Authorize::AuthorizeResource
|
||||
|
||||
PROTECTION_RULE_EXISTS_BATCH_SIZE = 20
|
||||
|
||||
description 'A container repository'
|
||||
|
||||
authorize :read_container_image
|
||||
|
|
@ -71,10 +73,12 @@ module Types
|
|||
def protection_rule_exists
|
||||
return false if Feature.disabled?(:container_registry_protected_containers, object.project.root_ancestor)
|
||||
|
||||
BatchLoader::GraphQL.for(object.path).batch do |repository_paths, loader|
|
||||
::ContainerRegistry::Protection::Rule
|
||||
.for_push_exists_for_multiple_containers(repository_paths: repository_paths, project_id: object.project_id)
|
||||
.each { |row| loader.call(row['repository_path'], row['protected']) }
|
||||
BatchLoader::GraphQL.for([object.project_id, object.path]).batch do |tuples, loader|
|
||||
tuples.each_slice(PROTECTION_RULE_EXISTS_BATCH_SIZE) do |projects_and_repository_paths|
|
||||
::ContainerRegistry::Protection::Rule
|
||||
.for_push_exists_for_projects_and_repository_paths(projects_and_repository_paths)
|
||||
.each { |row| loader.call([row['project_id'], row['repository_path']], row['protected']) }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -616,7 +616,7 @@ module ApplicationSettingsHelper
|
|||
|
||||
def signup_form_data
|
||||
{
|
||||
host: new_user_session_url(host: Gitlab.config.gitlab.host),
|
||||
host: new_user_registration_url(host: Gitlab.config.gitlab.host),
|
||||
settings_path: general_admin_application_settings_path(anchor: 'js-signup-settings'),
|
||||
signup_enabled: @application_setting[:signup_enabled].to_s,
|
||||
require_admin_approval_after_user_signup: @application_setting[:require_admin_approval_after_user_signup].to_s,
|
||||
|
|
|
|||
|
|
@ -42,27 +42,56 @@ module ContainerRegistry
|
|||
.exists?
|
||||
end
|
||||
|
||||
def self.for_push_exists_for_multiple_containers(repository_paths:, project_id:)
|
||||
return none if repository_paths.blank? || project_id.blank?
|
||||
##
|
||||
# Accepts a list of projects and repository paths and returns a result set
|
||||
# indicating whether the repository path is protected.
|
||||
#
|
||||
# @param [Array<Array>] projects_repository_paths an array of arrays where each sub-array contains a project id
|
||||
# and a repository path.
|
||||
# @return [ActiveRecord::Result] a result set indicating whether each project and repository path is protected.
|
||||
#
|
||||
# Example:
|
||||
# ContainerRegistry::Protection::Rule.for_push_exists_for_projects_and_repository_paths([
|
||||
# [1, '/my_group/my_project_1/image_1'],
|
||||
# [1, '/my_group/my_project_1/image_2'],
|
||||
# [2, '/my_group/my_project_2/image_1'],
|
||||
# ...
|
||||
# ])
|
||||
#
|
||||
# [
|
||||
# {'project_id' => 1, 'repository_path_pattern' => '/my_group/my_project_1/image_1', 'protected' => true},
|
||||
# {'project_id' => 1, 'repository_path_pattern' => '/my_group/my_project_1/image_2', 'protected' => false},
|
||||
# {'project_id' => 2, 'repository_path_pattern' => '/my_group/my_project_2/image_1', 'protected' => true},
|
||||
# ...
|
||||
# ]
|
||||
#
|
||||
def self.for_push_exists_for_projects_and_repository_paths(projects_repository_paths)
|
||||
return none if projects_repository_paths.blank?
|
||||
|
||||
project_ids, repository_paths = projects_repository_paths.transpose
|
||||
|
||||
cte_query =
|
||||
select('*').from(
|
||||
sanitize_sql_array([
|
||||
"unnest(ARRAY[:repository_paths]) AS x(repository_path)", { repository_paths: repository_paths }
|
||||
'unnest(ARRAY[:project_ids]::bigint[], ARRAY[:repository_paths]::text[]) ' \
|
||||
'AS projects_repository_paths(project_id, repository_path)',
|
||||
{ project_ids: project_ids, repository_paths: repository_paths }
|
||||
])
|
||||
)
|
||||
|
||||
cte_name = :container_names_and_types_cte
|
||||
cte_name = :projects_repository_paths_cte
|
||||
cte = Gitlab::SQL::CTE.new(cte_name, cte_query)
|
||||
|
||||
rules_cte_project_id = "#{cte_name}.#{connection.quote_column_name('project_id')}"
|
||||
rules_cte_repository_path = "#{cte_name}.#{connection.quote_column_name('repository_path')}"
|
||||
|
||||
protection_rule_exsits_subquery =
|
||||
select(1)
|
||||
.where(project_id: project_id)
|
||||
.where("#{rules_cte_project_id} = project_id")
|
||||
.where("#{rules_cte_repository_path} ILIKE #{::Gitlab::SQL::Glob.to_like('repository_path_pattern')}")
|
||||
|
||||
query = select(
|
||||
rules_cte_project_id,
|
||||
rules_cte_repository_path,
|
||||
sanitize_sql_array(['EXISTS(?) AS protected', protection_rule_exsits_subquery])
|
||||
).from(Arel.sql(cte_name.to_s))
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
---
|
||||
api_type:
|
||||
attr: elasticsearch_pause_indexing
|
||||
attr: elasticsearch
|
||||
clusterwide: false
|
||||
column: elasticsearch_pause_indexing
|
||||
db_type: boolean
|
||||
default: 'false'
|
||||
column: elasticsearch
|
||||
db_type: jsonb
|
||||
default: "'{}'::jsonb"
|
||||
description:
|
||||
encrypted: false
|
||||
gitlab_com_different_than_default: false
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
---
|
||||
api_type:
|
||||
attr: elasticsearch_analyzers_kuromoji_enabled
|
||||
clusterwide: true
|
||||
column: elasticsearch_analyzers_kuromoji_enabled
|
||||
db_type: boolean
|
||||
default: 'false'
|
||||
description:
|
||||
encrypted: false
|
||||
gitlab_com_different_than_default: false
|
||||
jihu: false
|
||||
not_null: true
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
---
|
||||
api_type:
|
||||
attr: elasticsearch_analyzers_kuromoji_search
|
||||
clusterwide: true
|
||||
column: elasticsearch_analyzers_kuromoji_search
|
||||
db_type: boolean
|
||||
default: 'false'
|
||||
description:
|
||||
encrypted: false
|
||||
gitlab_com_different_than_default: false
|
||||
jihu: false
|
||||
not_null: true
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
---
|
||||
api_type:
|
||||
attr: elasticsearch_analyzers_smartcn_enabled
|
||||
clusterwide: true
|
||||
column: elasticsearch_analyzers_smartcn_enabled
|
||||
db_type: boolean
|
||||
default: 'false'
|
||||
description:
|
||||
encrypted: false
|
||||
gitlab_com_different_than_default: false
|
||||
jihu: false
|
||||
not_null: true
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
---
|
||||
api_type:
|
||||
attr: elasticsearch_analyzers_smartcn_search
|
||||
clusterwide: true
|
||||
column: elasticsearch_analyzers_smartcn_search
|
||||
db_type: boolean
|
||||
default: 'false'
|
||||
description:
|
||||
encrypted: false
|
||||
gitlab_com_different_than_default: false
|
||||
jihu: false
|
||||
not_null: true
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
---
|
||||
api_type: boolean
|
||||
attr: elasticsearch_aws
|
||||
clusterwide: false
|
||||
column: elasticsearch_aws
|
||||
db_type: boolean
|
||||
default: 'false'
|
||||
description: Enable the use of AWS hosted Elasticsearch. Premium and Ultimate only.
|
||||
encrypted: false
|
||||
gitlab_com_different_than_default: false
|
||||
jihu: false
|
||||
not_null: true
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
---
|
||||
api_type: string
|
||||
attr: elasticsearch_aws_access_key
|
||||
clusterwide: false
|
||||
column: elasticsearch_aws_access_key
|
||||
db_type: character
|
||||
default:
|
||||
description: AWS IAM access key. Premium and Ultimate only.
|
||||
encrypted: false
|
||||
gitlab_com_different_than_default: true
|
||||
jihu: false
|
||||
not_null: false
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
---
|
||||
api_type: string
|
||||
attr: elasticsearch_aws_region
|
||||
clusterwide: false
|
||||
column: elasticsearch_aws_region
|
||||
db_type: character
|
||||
default: "'us-east-1'::character"
|
||||
description: The AWS region the Elasticsearch domain is configured. Premium and Ultimate
|
||||
only.
|
||||
encrypted: false
|
||||
gitlab_com_different_than_default: false
|
||||
jihu: false
|
||||
not_null: false
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
---
|
||||
api_type:
|
||||
attr: elasticsearch_client_request_timeout
|
||||
clusterwide: false
|
||||
column: elasticsearch_client_request_timeout
|
||||
db_type: integer
|
||||
default: '0'
|
||||
description:
|
||||
encrypted: false
|
||||
gitlab_com_different_than_default: true
|
||||
jihu: false
|
||||
not_null: true
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
---
|
||||
api_type: integer
|
||||
attr: elasticsearch_indexed_field_length_limit
|
||||
clusterwide: false
|
||||
column: elasticsearch_indexed_field_length_limit
|
||||
db_type: integer
|
||||
default: '0'
|
||||
description: Maximum size of text fields to index by Elasticsearch. 0 value means
|
||||
no limit. This does not apply to repository and wiki indexing. Premium and Ultimate
|
||||
only.
|
||||
encrypted: false
|
||||
gitlab_com_different_than_default: true
|
||||
jihu: false
|
||||
not_null: true
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
---
|
||||
api_type: integer
|
||||
attr: elasticsearch_indexed_file_size_limit_kb
|
||||
clusterwide: false
|
||||
column: elasticsearch_indexed_file_size_limit_kb
|
||||
db_type: integer
|
||||
default: '1024'
|
||||
description: Maximum size of repository and wiki files that are indexed by Elasticsearch.
|
||||
Premium and Ultimate only.
|
||||
encrypted: false
|
||||
gitlab_com_different_than_default: false
|
||||
jihu: false
|
||||
not_null: true
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
---
|
||||
api_type: boolean
|
||||
attr: elasticsearch_indexing
|
||||
clusterwide: false
|
||||
column: elasticsearch_indexing
|
||||
db_type: boolean
|
||||
default: 'false'
|
||||
description: Enable Elasticsearch indexing. Premium and Ultimate only.
|
||||
encrypted: false
|
||||
gitlab_com_different_than_default: true
|
||||
jihu: false
|
||||
not_null: true
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
---
|
||||
api_type: boolean
|
||||
attr: elasticsearch_limit_indexing
|
||||
clusterwide: false
|
||||
column: elasticsearch_limit_indexing
|
||||
db_type: boolean
|
||||
default: 'false'
|
||||
description: Limit Elasticsearch to index certain namespaces and projects. Premium
|
||||
and Ultimate only.
|
||||
encrypted: false
|
||||
gitlab_com_different_than_default: true
|
||||
jihu: false
|
||||
not_null: true
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
---
|
||||
api_type: integer
|
||||
attr: elasticsearch_max_bulk_concurrency
|
||||
clusterwide: false
|
||||
column: elasticsearch_max_bulk_concurrency
|
||||
db_type: smallint
|
||||
default: '10'
|
||||
description: Maximum concurrency of Elasticsearch bulk requests per indexing operation.
|
||||
This only applies to repository indexing operations. Premium and Ultimate only.
|
||||
encrypted: false
|
||||
gitlab_com_different_than_default: false
|
||||
jihu: false
|
||||
not_null: true
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
---
|
||||
api_type: integer
|
||||
attr: elasticsearch_max_bulk_size_mb
|
||||
clusterwide: false
|
||||
column: elasticsearch_max_bulk_size_mb
|
||||
db_type: smallint
|
||||
default: '10'
|
||||
description: Maximum size of Elasticsearch bulk indexing requests in MB. This only
|
||||
applies to repository indexing operations. Premium and Ultimate only.
|
||||
encrypted: false
|
||||
gitlab_com_different_than_default: false
|
||||
jihu: false
|
||||
not_null: true
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
---
|
||||
api_type: integer
|
||||
attr: elasticsearch_max_code_indexing_concurrency
|
||||
clusterwide: false
|
||||
column: elasticsearch_max_code_indexing_concurrency
|
||||
db_type: integer
|
||||
default: '30'
|
||||
description: Maximum concurrency of Elasticsearch code indexing background jobs. This
|
||||
only applies to repository indexing operations. Premium and Ultimate only.
|
||||
encrypted: false
|
||||
gitlab_com_different_than_default: true
|
||||
jihu: false
|
||||
not_null: true
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
---
|
||||
api_type: boolean
|
||||
attr: elasticsearch_requeue_workers
|
||||
clusterwide: false
|
||||
column: elasticsearch_requeue_workers
|
||||
db_type: boolean
|
||||
default: 'false'
|
||||
description: Enable automatic requeuing of indexing workers. This improves non-code
|
||||
indexing throughput by enqueuing Sidekiq jobs until all documents are processed.
|
||||
Premium and Ultimate only.
|
||||
encrypted: false
|
||||
gitlab_com_different_than_default: true
|
||||
jihu: false
|
||||
not_null: true
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
---
|
||||
api_type: integer
|
||||
attr: elasticsearch_retry_on_failure
|
||||
clusterwide: false
|
||||
column: elasticsearch_retry_on_failure
|
||||
db_type: integer
|
||||
default: '0'
|
||||
description: Maximum number of possible retries for Elasticsearch search requests.
|
||||
Premium and Ultimate only.
|
||||
encrypted: false
|
||||
gitlab_com_different_than_default: false
|
||||
jihu: false
|
||||
not_null: true
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
---
|
||||
api_type: boolean
|
||||
attr: elasticsearch_search
|
||||
clusterwide: false
|
||||
column: elasticsearch_search
|
||||
db_type: boolean
|
||||
default: 'false'
|
||||
description: Enable Elasticsearch search. Premium and Ultimate only.
|
||||
encrypted: false
|
||||
gitlab_com_different_than_default: true
|
||||
jihu: false
|
||||
not_null: true
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
---
|
||||
api_type: string
|
||||
attr: elasticsearch_username
|
||||
clusterwide: false
|
||||
column: elasticsearch_username
|
||||
db_type: text
|
||||
default:
|
||||
description: The `username` of your Elasticsearch instance. Premium and Ultimate only.
|
||||
encrypted: false
|
||||
gitlab_com_different_than_default: true
|
||||
jihu: false
|
||||
not_null: false
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
---
|
||||
api_type: integer
|
||||
attr: elasticsearch_worker_number_of_shards
|
||||
clusterwide: false
|
||||
column: elasticsearch_worker_number_of_shards
|
||||
db_type: integer
|
||||
default: '2'
|
||||
description: Number of indexing worker shards. This improves non-code indexing throughput
|
||||
by enqueuing more parallel Sidekiq jobs. Default is `2`. Premium and Ultimate only.
|
||||
encrypted: false
|
||||
gitlab_com_different_than_default: true
|
||||
jihu: false
|
||||
not_null: true
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
---
|
||||
description: Tracks when the pipeline link is clicked in the sidebar
|
||||
internal_events: true
|
||||
action: clicked_deployment_details_pipeline_link
|
||||
product_group: environments
|
||||
product_categories:
|
||||
- deployment_management
|
||||
milestone: '17.8'
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/176004
|
||||
tiers:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
- name: New Planner user role
|
||||
description: |
|
||||
We've introduced the new Planner role to give you tailored access to Agile planning tools like epics, roadmaps, and Kanban boards without over-provisioning permissions. This change helps you collaborate more effectively while keeping your workflows secure and aligned with the principle of least privilege.
|
||||
stage: plan
|
||||
self-managed: true
|
||||
gitlab-com: true
|
||||
available_in: ["Free", "Premium", "Ultimate"]
|
||||
documentation_link: https://docs.gitlab.com/ee/user/permissions.html
|
||||
image_url: https://about.gitlab.com/images/17_7/new_planner_user_role.png
|
||||
published_at: 2024-12-19
|
||||
release: 17.7
|
||||
|
||||
- name: Instance administrators can control which integrations can be enabled
|
||||
description: |
|
||||
Instance administrators can now configure an allowlist to control which integrations can be enabled on a GitLab instance. If an empty allowlist is configured, no integrations are allowed on the instance. After an allowlist is configured, new GitLab integrations are not on the allowlist by default. Previously enabled integrations that are later blocked by the allowlist settings are disabled. If these integrations are allowed again, they are re-enabled with their existing configuration.
|
||||
stage: foundations
|
||||
self-managed: true
|
||||
gitlab-com: true
|
||||
available_in: ["Ultimate"]
|
||||
documentation_link: https://docs.gitlab.com/ee/administration/settings/project_integration_management.html#integration-allowlist
|
||||
image_url: https://about.gitlab.com/images/17_7/integrations_allowlist.png
|
||||
published_at: 2024-12-19
|
||||
release: 17.7
|
||||
|
||||
- name: New user contribution and membership mapping available in direct transfer
|
||||
description: |
|
||||
Reassign memberships and contributions to existing users on the destination instance after the import has completed. Any memberships and contributions you import are first mapped to placeholder users. All contributions appear associated with placeholders until you reassign them on the destination instance.
|
||||
stage: foundations
|
||||
self-managed: true
|
||||
gitlab-com: true
|
||||
available_in: ["Free", "Premium", "Ultimate"]
|
||||
documentation_link: https://docs.gitlab.com/ee/user/project/import/#user-contribution-and-membership-mapping
|
||||
image_url: https://about.gitlab.com/images/17_7/user_contributions_mapping.png
|
||||
published_at: 2024-12-19
|
||||
release: 17.7
|
||||
|
||||
- name: Auto-resolve vulnerabilities when not found in subsequent scans
|
||||
description: |
|
||||
We are introducing a new policy type _Vulnerability Management policy_ for users who want vulnerabilities automatically set to Resolved when no longer detected by automated scanning. Simply configure a new policy with the new Auto-resolve option and apply it to the appropriate project(s). You can even configure the policy to only Auto-resolve vulnerabilities of a certain severity or from specific security scanners. Once in place, the next time the project's default branch is scanned, any existing vulnerabilities that are no longer found will be marked as Resolved. The action updates the vulnerability record with an activity note, timestamp when the action occurred, and the pipeline the vulnerability was determined to be removed in.
|
||||
stage: security_risk_management
|
||||
self-managed: true
|
||||
gitlab-com: true
|
||||
available_in: ["Ultimate"]
|
||||
documentation_link: https://docs.gitlab.com/ee/user/application_security/policies/vulnerability_management_policy.html
|
||||
image_url: https://about.gitlab.com/images/17_7/auto-resolve-when-not-found-in-subsequent-scan.png
|
||||
published_at: 2024-12-19
|
||||
release: 17.7
|
||||
|
||||
- name: Rotate personal, project, and group access tokens in the UI
|
||||
description: |
|
||||
You can now use the UI to rotate personal, project, and group access tokens. Previously, you had to use the API to do this.
|
||||
stage: software_supply_chain_security
|
||||
self-managed: true
|
||||
gitlab-com: true
|
||||
available_in: ["Free", "Premium", "Ultimate"]
|
||||
documentation_link: https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html#revoke-or-rotate-a-personal-access-token
|
||||
image_url: https://img.youtube.com/vi/YqK2CF655OE/hqdefault.jpg
|
||||
published_at: 2024-12-19
|
||||
release: 17.7
|
||||
|
||||
- name: Track CI/CD component usage across projects
|
||||
description: |
|
||||
We've added a new GraphQL query that enables DevOps teams to view a list of projects where a component is used across their organization's pipelines. This capability empowers DevOps teams to enhance productivity and make better decisions by providing crucial insights.
|
||||
stage: verify
|
||||
self-managed: true
|
||||
gitlab-com: true
|
||||
available_in: ["Premium", "Ultimate"]
|
||||
documentation_link: https://docs.gitlab.com/ee/api/graphql/reference/index.html#queryprojectsusingcomponents
|
||||
image_url: https://about.gitlab.com/images/17_7/catalog.png
|
||||
published_at: 2024-12-19
|
||||
release: 17.7
|
||||
|
|
@ -8,6 +8,24 @@ description: For storing mentioned users, groups, projects referenced in a snipp
|
|||
description.
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/19009
|
||||
milestone: '12.6'
|
||||
gitlab_schema: gitlab_main
|
||||
sharding_key_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/490493
|
||||
table_size: small
|
||||
gitlab_schema: gitlab_main_cell
|
||||
allow_cross_foreign_keys:
|
||||
- gitlab_main_clusterwide
|
||||
desired_sharding_key:
|
||||
snippet_project_id:
|
||||
references: projects
|
||||
backfill_via:
|
||||
parent:
|
||||
foreign_key: snippet_id
|
||||
table: snippets
|
||||
sharding_key: project_id
|
||||
belongs_to: snippet
|
||||
snippet_organization_id:
|
||||
references: organizations
|
||||
backfill_via:
|
||||
parent:
|
||||
foreign_key: snippet_id
|
||||
table: snippets
|
||||
sharding_key: organization_id
|
||||
belongs_to: snippet
|
||||
|
|
|
|||
|
|
@ -7,6 +7,5 @@ feature_categories:
|
|||
description: Stores data about X.509 certificate
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/17773
|
||||
milestone: '12.8'
|
||||
gitlab_schema: gitlab_main
|
||||
sharding_key_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/490494
|
||||
gitlab_schema: gitlab_main_clusterwide
|
||||
table_size: small
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddElasticsearchApplicationSettings < Gitlab::Database::Migration[2.2]
|
||||
disable_ddl_transaction!
|
||||
milestone '17.8'
|
||||
|
||||
def up
|
||||
add_column :application_settings, :elasticsearch, :jsonb, default: {}, null: false, if_not_exists: true
|
||||
|
||||
add_check_constraint(
|
||||
:application_settings,
|
||||
"(jsonb_typeof(elasticsearch) = 'object')",
|
||||
'check_application_settings_elasticsearch_is_hash'
|
||||
)
|
||||
end
|
||||
|
||||
def down
|
||||
remove_column :application_settings, :elasticsearch, if_exists: true
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class MoveElasticsearchApplicationSettingsToElasticsearchColumn < Gitlab::Database::Migration[2.2]
|
||||
milestone '17.8'
|
||||
restrict_gitlab_migration gitlab_schema: :gitlab_main
|
||||
|
||||
ELASTIC_SEARCH_REGEX = /^elasticsearch_/
|
||||
|
||||
class ApplicationSetting < MigrationRecord
|
||||
self.table_name = 'application_settings'
|
||||
end
|
||||
|
||||
def up
|
||||
application_setting = ApplicationSetting.last
|
||||
return unless application_setting
|
||||
|
||||
elasticsearch_settings = {}
|
||||
|
||||
ApplicationSetting.column_names.grep(ELASTIC_SEARCH_REGEX).each do |column_name|
|
||||
# We skip elasticsearch_url because we have multiple errors moving this field.
|
||||
# https://gitlab.com/gitlab-org/gitlab/-/merge_requests/174529 tried to solve this,
|
||||
# but we still have CI errors that we cannot reproduce locally.
|
||||
next if column_name == 'elasticsearch_url'
|
||||
|
||||
elasticsearch_settings[column_name] = application_setting.attribute_in_database(column_name)
|
||||
end
|
||||
|
||||
application_setting.update_columns(elasticsearch: elasticsearch_settings, updated_at: Time.current)
|
||||
end
|
||||
|
||||
def down
|
||||
application_setting = ApplicationSetting.last
|
||||
return unless application_setting
|
||||
|
||||
application_setting.update_column(:elasticsearch, {})
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ElasticsearchSettingsFromApplicationSettings < Gitlab::Database::Migration[2.2]
|
||||
disable_ddl_transaction!
|
||||
milestone '17.8'
|
||||
|
||||
def up
|
||||
with_lock_retries do
|
||||
remove_column :application_settings, :elasticsearch_aws
|
||||
remove_column :application_settings, :elasticsearch_search
|
||||
remove_column :application_settings, :elasticsearch_indexing
|
||||
remove_column :application_settings, :elasticsearch_username
|
||||
remove_column :application_settings, :elasticsearch_aws_region
|
||||
remove_column :application_settings, :elasticsearch_aws_access_key
|
||||
remove_column :application_settings, :elasticsearch_limit_indexing
|
||||
remove_column :application_settings, :elasticsearch_pause_indexing
|
||||
remove_column :application_settings, :elasticsearch_requeue_workers
|
||||
remove_column :application_settings, :elasticsearch_max_bulk_size_mb
|
||||
remove_column :application_settings, :elasticsearch_retry_on_failure
|
||||
remove_column :application_settings, :elasticsearch_max_bulk_concurrency
|
||||
remove_column :application_settings, :elasticsearch_client_request_timeout
|
||||
remove_column :application_settings, :elasticsearch_worker_number_of_shards
|
||||
remove_column :application_settings, :elasticsearch_analyzers_smartcn_search
|
||||
remove_column :application_settings, :elasticsearch_analyzers_kuromoji_search
|
||||
remove_column :application_settings, :elasticsearch_analyzers_smartcn_enabled
|
||||
remove_column :application_settings, :elasticsearch_analyzers_kuromoji_enabled
|
||||
remove_column :application_settings, :elasticsearch_indexed_field_length_limit
|
||||
remove_column :application_settings, :elasticsearch_indexed_file_size_limit_kb
|
||||
remove_column :application_settings, :elasticsearch_max_code_indexing_concurrency
|
||||
end
|
||||
end
|
||||
|
||||
def down
|
||||
with_lock_retries do
|
||||
add_column :application_settings, :elasticsearch_aws, :boolean, default: false, null: false
|
||||
add_column :application_settings, :elasticsearch_search, :boolean, default: false, null: false
|
||||
add_column :application_settings, :elasticsearch_indexing, :boolean, default: false, null: false
|
||||
add_column :application_settings, :elasticsearch_username, :text
|
||||
add_column :application_settings, :elasticsearch_aws_region, 'character varying', default: 'us-east-1'
|
||||
add_column :application_settings, :elasticsearch_aws_access_key, 'character varying'
|
||||
add_column :application_settings, :elasticsearch_limit_indexing, :boolean, default: false, null: false
|
||||
add_column :application_settings, :elasticsearch_pause_indexing, :boolean, default: false, null: false
|
||||
add_column :application_settings, :elasticsearch_requeue_workers, :boolean, default: false, null: false
|
||||
add_column :application_settings, :elasticsearch_max_bulk_size_mb, :smallint, default: 10, null: false
|
||||
add_column :application_settings, :elasticsearch_retry_on_failure, :integer, default: 0, null: false
|
||||
add_column :application_settings, :elasticsearch_max_bulk_concurrency, :smallint, default: 10, null: false
|
||||
add_column :application_settings, :elasticsearch_client_request_timeout, :integer, default: 0, null: false
|
||||
add_column :application_settings, :elasticsearch_worker_number_of_shards, :integer, default: 2, null: false
|
||||
add_column :application_settings, :elasticsearch_analyzers_smartcn_search, :boolean, default: false, null: false
|
||||
add_column :application_settings, :elasticsearch_analyzers_kuromoji_search, :boolean, default: false, null: false
|
||||
add_column :application_settings, :elasticsearch_analyzers_smartcn_enabled, :boolean, default: false, null: false
|
||||
add_column :application_settings, :elasticsearch_analyzers_kuromoji_enabled, :boolean, default: false, null: false
|
||||
add_column :application_settings, :elasticsearch_indexed_field_length_limit, :integer, default: 0, null: false
|
||||
add_column :application_settings, :elasticsearch_indexed_file_size_limit_kb, :integer, default: 1024, null: false
|
||||
add_column :application_settings, :elasticsearch_max_code_indexing_concurrency, :integer, default: 30, null: false
|
||||
end
|
||||
|
||||
add_check_constraint(:application_settings, 'char_length(elasticsearch_username) <= 255', 'check_e5024c8801')
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1 @@
|
|||
b34c958333e8352a72d0dbababdd78240c5f02aeec113d74a89131a97027cc2a
|
||||
|
|
@ -0,0 +1 @@
|
|||
127e8ea6fe4e343c33b39cf507bee3ea879267bbd048b71e4796402aa7cdc5cc
|
||||
|
|
@ -0,0 +1 @@
|
|||
0fa814e285a0401e9d72ad7bb0a41bed2d5779f6e6613b9a9117d227740fe317
|
||||
|
|
@ -6939,8 +6939,6 @@ CREATE TABLE application_settings (
|
|||
container_registry_token_expire_delay integer DEFAULT 5,
|
||||
after_sign_up_text text,
|
||||
user_default_external boolean DEFAULT false NOT NULL,
|
||||
elasticsearch_indexing boolean DEFAULT false NOT NULL,
|
||||
elasticsearch_search boolean DEFAULT false NOT NULL,
|
||||
repository_storages character varying DEFAULT 'default'::character varying,
|
||||
enabled_git_access_protocol character varying,
|
||||
usage_ping_enabled boolean DEFAULT true NOT NULL,
|
||||
|
|
@ -6967,9 +6965,6 @@ CREATE TABLE application_settings (
|
|||
unique_ips_limit_enabled boolean DEFAULT false NOT NULL,
|
||||
default_artifacts_expire_in character varying DEFAULT '0'::character varying NOT NULL,
|
||||
elasticsearch_url character varying DEFAULT 'http://localhost:9200'::character varying,
|
||||
elasticsearch_aws boolean DEFAULT false NOT NULL,
|
||||
elasticsearch_aws_region character varying DEFAULT 'us-east-1'::character varying,
|
||||
elasticsearch_aws_access_key character varying,
|
||||
geo_status_timeout integer DEFAULT 10,
|
||||
uuid character varying,
|
||||
polling_interval_multiplier numeric DEFAULT 1.0 NOT NULL,
|
||||
|
|
@ -7035,7 +7030,6 @@ CREATE TABLE application_settings (
|
|||
runners_registration_token_encrypted character varying,
|
||||
local_markdown_version integer DEFAULT 0 NOT NULL,
|
||||
first_day_of_week integer DEFAULT 0 NOT NULL,
|
||||
elasticsearch_limit_indexing boolean DEFAULT false NOT NULL,
|
||||
default_project_creation integer DEFAULT 2 NOT NULL,
|
||||
lets_encrypt_notification_email character varying,
|
||||
lets_encrypt_terms_of_service_accepted boolean DEFAULT false NOT NULL,
|
||||
|
|
@ -7098,9 +7092,6 @@ CREATE TABLE application_settings (
|
|||
encrypted_slack_app_verification_token_iv character varying(255),
|
||||
force_pages_access_control boolean DEFAULT false NOT NULL,
|
||||
updating_name_disabled_for_users boolean DEFAULT false NOT NULL,
|
||||
elasticsearch_indexed_field_length_limit integer DEFAULT 0 NOT NULL,
|
||||
elasticsearch_max_bulk_size_mb smallint DEFAULT 10 NOT NULL,
|
||||
elasticsearch_max_bulk_concurrency smallint DEFAULT 10 NOT NULL,
|
||||
disable_overriding_approvers_per_merge_request boolean DEFAULT false NOT NULL,
|
||||
prevent_merge_requests_author_approval boolean DEFAULT false NOT NULL,
|
||||
prevent_merge_requests_committers_approval boolean DEFAULT false NOT NULL,
|
||||
|
|
@ -7116,7 +7107,6 @@ CREATE TABLE application_settings (
|
|||
container_registry_features text[] DEFAULT '{}'::text[] NOT NULL,
|
||||
spam_check_endpoint_url text,
|
||||
spam_check_endpoint_enabled boolean DEFAULT false NOT NULL,
|
||||
elasticsearch_pause_indexing boolean DEFAULT false NOT NULL,
|
||||
repository_storages_weighted jsonb DEFAULT '{}'::jsonb NOT NULL,
|
||||
max_import_size integer DEFAULT 0 NOT NULL,
|
||||
compliance_frameworks smallint[] DEFAULT '{}'::smallint[] NOT NULL,
|
||||
|
|
@ -7131,12 +7121,10 @@ CREATE TABLE application_settings (
|
|||
maintenance_mode boolean DEFAULT false NOT NULL,
|
||||
maintenance_mode_message text,
|
||||
wiki_page_max_content_bytes bigint DEFAULT 52428800 NOT NULL,
|
||||
elasticsearch_indexed_file_size_limit_kb integer DEFAULT 1024 NOT NULL,
|
||||
enforce_namespace_storage_limit boolean DEFAULT false NOT NULL,
|
||||
container_registry_delete_tags_service_timeout integer DEFAULT 250 NOT NULL,
|
||||
kroki_url character varying,
|
||||
kroki_enabled boolean,
|
||||
elasticsearch_client_request_timeout integer DEFAULT 0 NOT NULL,
|
||||
gitpod_enabled boolean DEFAULT false NOT NULL,
|
||||
gitpod_url text DEFAULT 'https://gitpod.io/'::text,
|
||||
abuse_notification_email character varying,
|
||||
|
|
@ -7146,10 +7134,6 @@ CREATE TABLE application_settings (
|
|||
encrypted_ci_jwt_signing_key text,
|
||||
encrypted_ci_jwt_signing_key_iv text,
|
||||
container_registry_expiration_policies_worker_capacity integer DEFAULT 4 NOT NULL,
|
||||
elasticsearch_analyzers_smartcn_enabled boolean DEFAULT false NOT NULL,
|
||||
elasticsearch_analyzers_smartcn_search boolean DEFAULT false NOT NULL,
|
||||
elasticsearch_analyzers_kuromoji_enabled boolean DEFAULT false NOT NULL,
|
||||
elasticsearch_analyzers_kuromoji_search boolean DEFAULT false NOT NULL,
|
||||
secret_detection_token_revocation_enabled boolean DEFAULT false NOT NULL,
|
||||
secret_detection_token_revocation_url text,
|
||||
encrypted_secret_detection_token_revocation_token text,
|
||||
|
|
@ -7188,7 +7172,6 @@ CREATE TABLE application_settings (
|
|||
encrypted_spam_check_api_key bytea,
|
||||
encrypted_spam_check_api_key_iv bytea,
|
||||
floc_enabled boolean DEFAULT false NOT NULL,
|
||||
elasticsearch_username text,
|
||||
encrypted_elasticsearch_password bytea,
|
||||
encrypted_elasticsearch_password_iv bytea,
|
||||
diff_max_lines integer DEFAULT 50000 NOT NULL,
|
||||
|
|
@ -7368,8 +7351,6 @@ CREATE TABLE application_settings (
|
|||
unconfirmed_users_delete_after_days integer DEFAULT 7 NOT NULL,
|
||||
default_branch_protection_defaults jsonb DEFAULT '{}'::jsonb NOT NULL,
|
||||
gitlab_shell_operation_limit integer DEFAULT 600,
|
||||
elasticsearch_requeue_workers boolean DEFAULT false NOT NULL,
|
||||
elasticsearch_worker_number_of_shards integer DEFAULT 2 NOT NULL,
|
||||
protected_paths_for_get_request text[] DEFAULT '{}'::text[] NOT NULL,
|
||||
namespace_storage_forks_cost_factor double precision DEFAULT 1.0 NOT NULL,
|
||||
package_registry_allow_anyone_to_pull_option boolean DEFAULT true NOT NULL,
|
||||
|
|
@ -7403,7 +7384,6 @@ CREATE TABLE application_settings (
|
|||
encrypted_arkose_labs_data_exchange_key bytea,
|
||||
encrypted_arkose_labs_data_exchange_key_iv bytea,
|
||||
rate_limits jsonb DEFAULT '{}'::jsonb NOT NULL,
|
||||
elasticsearch_max_code_indexing_concurrency integer DEFAULT 30 NOT NULL,
|
||||
enable_member_promotion_management boolean DEFAULT false NOT NULL,
|
||||
lock_math_rendering_limits_enabled boolean DEFAULT false NOT NULL,
|
||||
security_approval_policies_limit integer DEFAULT 5 NOT NULL,
|
||||
|
|
@ -7441,7 +7421,6 @@ CREATE TABLE application_settings (
|
|||
sign_in_restrictions jsonb DEFAULT '{}'::jsonb NOT NULL,
|
||||
transactional_emails jsonb DEFAULT '{}'::jsonb NOT NULL,
|
||||
identity_verification_settings jsonb DEFAULT '{}'::jsonb NOT NULL,
|
||||
elasticsearch_retry_on_failure integer DEFAULT 0 NOT NULL,
|
||||
integrations jsonb DEFAULT '{}'::jsonb NOT NULL,
|
||||
user_seat_management jsonb DEFAULT '{}'::jsonb NOT NULL,
|
||||
secret_detection_service_url text DEFAULT ''::text NOT NULL,
|
||||
|
|
@ -7451,6 +7430,7 @@ CREATE TABLE application_settings (
|
|||
show_migrate_from_jenkins_banner boolean DEFAULT true NOT NULL,
|
||||
encrypted_ci_job_token_signing_key bytea,
|
||||
encrypted_ci_job_token_signing_key_iv bytea,
|
||||
elasticsearch jsonb DEFAULT '{}'::jsonb NOT NULL,
|
||||
CONSTRAINT app_settings_container_reg_cleanup_tags_max_list_size_positive CHECK ((container_registry_cleanup_tags_service_max_list_size >= 0)),
|
||||
CONSTRAINT app_settings_dep_proxy_ttl_policies_worker_capacity_positive CHECK ((dependency_proxy_ttl_group_policy_worker_capacity >= 0)),
|
||||
CONSTRAINT app_settings_ext_pipeline_validation_service_url_text_limit CHECK ((char_length(external_pipeline_validation_service_url) <= 255)),
|
||||
|
|
@ -7503,6 +7483,7 @@ CREATE TABLE application_settings (
|
|||
CONSTRAINT check_application_settings_cluster_agents_is_hash CHECK ((jsonb_typeof(cluster_agents) = 'object'::text)),
|
||||
CONSTRAINT check_application_settings_code_creation_is_hash CHECK ((jsonb_typeof(code_creation) = 'object'::text)),
|
||||
CONSTRAINT check_application_settings_duo_workflow_is_hash CHECK ((jsonb_typeof(duo_workflow) = 'object'::text)),
|
||||
CONSTRAINT check_application_settings_elasticsearch_is_hash CHECK ((jsonb_typeof(elasticsearch) = 'object'::text)),
|
||||
CONSTRAINT check_application_settings_importers_is_hash CHECK ((jsonb_typeof(importers) = 'object'::text)),
|
||||
CONSTRAINT check_application_settings_integrations_is_hash CHECK ((jsonb_typeof(integrations) = 'object'::text)),
|
||||
CONSTRAINT check_application_settings_package_registry_is_hash CHECK ((jsonb_typeof(package_registry) = 'object'::text)),
|
||||
|
|
@ -7520,7 +7501,6 @@ CREATE TABLE application_settings (
|
|||
CONSTRAINT check_d820146492 CHECK ((char_length(spam_check_endpoint_url) <= 255)),
|
||||
CONSTRAINT check_e2692d7523 CHECK ((char_length(default_preferred_language) <= 32)),
|
||||
CONSTRAINT check_e2dd6e290a CHECK ((char_length(jira_connect_application_key) <= 255)),
|
||||
CONSTRAINT check_e5024c8801 CHECK ((char_length(elasticsearch_username) <= 255)),
|
||||
CONSTRAINT check_e5aba18f02 CHECK ((char_length(container_registry_version) <= 255)),
|
||||
CONSTRAINT check_ef6176834f CHECK ((char_length(encrypted_cloud_license_auth_token_iv) <= 255)),
|
||||
CONSTRAINT check_identity_verification_settings_is_hash CHECK ((jsonb_typeof(identity_verification_settings) = 'object'::text))
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@ DETAILS:
|
|||
**Tier:** Free, Premium, Ultimate
|
||||
**Offering:** GitLab.com, GitLab Dedicated
|
||||
|
||||
You can run your CI/CD jobs on GitLab.com and GitLab Dedicated using GitLab-hosted runners to seamlessly build, test and deploy
|
||||
your application on different environments.
|
||||
Use GitLab-hosted runners to run your CI/CD jobs on GitLab.com and GitLab Dedicated to build, test, and deploy
|
||||
applications on different environments.
|
||||
|
||||
## Hosted runners for GitLab.com
|
||||
|
||||
|
|
@ -49,7 +49,7 @@ Hosted runners for GitLab.com are configured as such:
|
|||
- Inbound communication from the public internet to the ephemeral VM is not allowed.
|
||||
- Firewall rules do not permit communication between VMs.
|
||||
- The only internal communication allowed to the ephemeral VMs is from the runner manager.
|
||||
- Ephemeral runner VMs will only serve a single job and will be deleted right after the job execution.
|
||||
- Ephemeral runner VMs serve a single job and are deleted right after the job execution.
|
||||
|
||||
#### Architecture diagram of hosted runners for GitLab.com
|
||||
|
||||
|
|
@ -71,9 +71,11 @@ The build job ran on `runner-ns46nmmj-project-43717858`, test job on `f131a6a2ru
|
|||
GitLab sends the command to remove the ephemeral runner VM to the Google Compute API immediately after the CI job completes. The [Google Compute Engine hypervisor](https://cloud.google.com/blog/products/gcp/7-ways-we-harden-our-kvm-hypervisor-at-google-cloud-security-in-plaintext)
|
||||
takes over the task of securely deleting the virtual machine and associated data.
|
||||
|
||||
For more information about the security of hosted runners for GitLab.com, see
|
||||
[Google Cloud Infrastructure Security Design Overview whitepaper](https://cloud.google.com/docs/security/infrastructure/design/resources/google_infrastructure_whitepaper_fa.pdf),
|
||||
[GitLab Trust Center](https://about.gitlab.com/security/), or [GitLab Security Compliance Controls](https://handbook.gitlab.com/handbook/security/security-assurance/security-compliance/sec-controls/).
|
||||
For more information about the security of hosted runners for GitLab.com, see:
|
||||
|
||||
- [Google Cloud Infrastructure Security Design Overview whitepaper](https://cloud.google.com/docs/security/infrastructure/design/resources/google_infrastructure_whitepaper_fa.pdf)
|
||||
- [GitLab Trust Center](https://about.gitlab.com/security/)
|
||||
- [GitLab Security Compliance Controls](https://handbook.gitlab.com/handbook/security/security-assurance/security-compliance/sec-controls/)
|
||||
|
||||
### Caching on hosted runners for GitLab.com
|
||||
|
||||
|
|
@ -103,7 +105,7 @@ You can find all GitLab Runner breaking changes under [Deprecations and removals
|
|||
DETAILS:
|
||||
**Offering:** GitLab.com
|
||||
|
||||
If you want to [contribute to GitLab](https://about.gitlab.com/community/contribute/), jobs will be picked up by the
|
||||
If you want to [contribute to GitLab](https://about.gitlab.com/community/contribute/), jobs are picked up by the
|
||||
`gitlab-shared-runners-manager-X.gitlab.com` fleet of runners, dedicated for GitLab projects and related community forks.
|
||||
|
||||
These runners are backed by the same machine type as our `small` Linux x86-64 runners.
|
||||
|
|
|
|||
|
|
@ -29,8 +29,8 @@ Consider that you're developing a content management system that uses database f
|
|||
You need a database to test all features in the application. Running a database
|
||||
container as a service image is a good use case in this scenario.
|
||||
|
||||
It's easier and faster to use an existing image and run it as an additional container
|
||||
than to install `mysql`, for example, every time the project is built.
|
||||
Use an existing image and run it as an additional container
|
||||
instead of installing `mysql` every time you build a project.
|
||||
|
||||
You're not limited to only database services. You can add as many
|
||||
services you need to `.gitlab-ci.yml` or manually modify the [`config.toml`](https://docs.gitlab.com/runner/configuration/advanced-configuration.html).
|
||||
|
|
@ -357,9 +357,9 @@ The syntax of `command` is similar to [Dockerfile `CMD`](https://docs.docker.com
|
|||
|
||||
Containers started with `docker run` can also connect to services provided by GitLab.
|
||||
|
||||
When booting the service is expensive or time consuming, you can use
|
||||
this technique to run tests from different client environments,
|
||||
while only booting up the tested service once.
|
||||
If booting a service is expensive or time consuming, you can
|
||||
run tests from different client environments,
|
||||
while booting up the tested service only once.
|
||||
|
||||
```yaml
|
||||
access-service:
|
||||
|
|
@ -405,8 +405,8 @@ time.
|
|||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab-runner/-/merge_requests/3680) in GitLab Runner 15.6.
|
||||
|
||||
Logs generated by applications running in service containers can be captured for subsequent examination and debugging.
|
||||
You might want to look at service container's logs when the service container has started successfully, but is not
|
||||
behaving as expected, leading to job failures. The logs can indicate missing or incorrect configuration of the service
|
||||
View service container logs when a service container starts successfully but causes job failures due to unexpected behavior.
|
||||
The logs can indicate missing or incorrect configuration of the service
|
||||
in the container.
|
||||
|
||||
`CI_DEBUG_SERVICES` should only be enabled when service containers are being actively debugged as there are both storage
|
||||
|
|
@ -427,24 +427,24 @@ Accepted values are:
|
|||
|
||||
Any other values result in an error message and effectively disable the feature.
|
||||
|
||||
When enabled, logs for _all_ service containers are captured and streamed into the jobs trace log concurrently with
|
||||
When enabled, logs for all service containers are captured and streamed into the jobs trace log concurrently with
|
||||
other logs. Logs from each container are prefixed with the container's aliases, and displayed in a different color.
|
||||
|
||||
NOTE:
|
||||
You may want to adjust the logging level in the service container for which you want to capture logs because the default
|
||||
logging level may not provide sufficient details to diagnose job failures.
|
||||
To diagnose job failures, you can adjust the logging level in your service container for which you want to capture logs.
|
||||
The default logging level might not provide sufficient troubleshooting information.
|
||||
|
||||
WARNING:
|
||||
Enabling `CI_DEBUG_SERVICES` _may_ result in masked variables being revealed. When `CI_DEBUG_SERVICES` is enabled,
|
||||
service container logs and the CI job's logs are streamed to the job's trace log _concurrently_, which makes it possible
|
||||
for a service container log to be inserted _inside_ a job's masked log. This would thwart the variable masking mechanism
|
||||
Enabling `CI_DEBUG_SERVICES` might reveal masked variables. When `CI_DEBUG_SERVICES` is enabled,
|
||||
service container logs and the CI job's logs are streamed to the job's trace log concurrently. This means that the
|
||||
service container logs might get inserted into a job's masked log. This would thwart the variable masking mechanism
|
||||
and result in the masked variable being revealed.
|
||||
|
||||
See [Mask a CI/CD Variable](../variables/index.md#mask-a-cicd-variable)
|
||||
|
||||
## Debug a job locally
|
||||
|
||||
The following commands are run without root privileges. You should be able to run Docker with your user account.
|
||||
The following commands are run without root privileges. Verify that you can run Docker commands with your user account.
|
||||
|
||||
First start by creating a file named `build_script`:
|
||||
|
||||
|
|
@ -478,7 +478,7 @@ The above command creates a container named `build` that is spawned from the `go
|
|||
linked to it. The `build_script` is piped using `stdin` to the bash interpreter which in turn executes the
|
||||
`build_script` in the `build` container.
|
||||
|
||||
When you finish testing and no longer need the containers, you can remove them with:
|
||||
Use the following command to remove containers after testing is complete:
|
||||
|
||||
```shell
|
||||
docker rm -f -v build service-redis
|
||||
|
|
@ -500,7 +500,7 @@ need to access project files or store artifacts. If so, wait for the directory
|
|||
to exist and for `$CI_COMMIT_SHA` to be checked out. Any changes made before
|
||||
the job finishes its checkout process are removed by the checkout process.
|
||||
|
||||
The service must be able to detect when the job directory is populated and
|
||||
The service must detect when the job directory is populated and
|
||||
ready for processing. For example, wait for a specific file to become available.
|
||||
|
||||
Services that start working immediately when launched are likely to fail, as the
|
||||
|
|
|
|||
|
|
@ -7,12 +7,12 @@ info: Analysis of Application Settings for Cells 1.0.
|
|||
|
||||
## Statistics
|
||||
|
||||
- Number of attributes: 510
|
||||
- Number of attributes: 490
|
||||
- Number of encrypted attributes: 45 (9.0%)
|
||||
- Number of attributes documented: 311 (61.0%)
|
||||
- Number of attributes on GitLab.com different from the defaults: 222 (44.0%)
|
||||
- Number of attributes with `clusterwide` set: 510 (100.0%)
|
||||
- Number of attributes with `clusterwide: true` set: 124 (24.0%)
|
||||
- Number of attributes documented: 296 (60.0%)
|
||||
- Number of attributes on GitLab.com different from the defaults: 212 (43.0%)
|
||||
- Number of attributes with `clusterwide` set: 490 (100.0%)
|
||||
- Number of attributes with `clusterwide: true` set: 120 (24.0%)
|
||||
|
||||
## Individual columns
|
||||
|
||||
|
|
@ -148,30 +148,10 @@ info: Analysis of Application Settings for Cells 1.0.
|
|||
| `eks_account_id` | `false` | `character` | `string` | `false` | `null` | `true` | `true`| `true` |
|
||||
| `eks_integration_enabled` | `false` | `boolean` | `boolean` | `true` | `false` | `true` | `true`| `true` |
|
||||
| `eks_secret_access_key` | `true` | `text` | `string` | `false` | `null` | `true` | `true`| `true` |
|
||||
| `elasticsearch_analyzers_kuromoji_enabled` | `false` | `boolean` | `` | `true` | `false` | `false` | `true`| `false` |
|
||||
| `elasticsearch_analyzers_kuromoji_search` | `false` | `boolean` | `` | `true` | `false` | `false` | `true`| `false` |
|
||||
| `elasticsearch_analyzers_smartcn_enabled` | `false` | `boolean` | `` | `true` | `false` | `false` | `true`| `false` |
|
||||
| `elasticsearch_analyzers_smartcn_search` | `false` | `boolean` | `` | `true` | `false` | `false` | `true`| `false` |
|
||||
| `elasticsearch_aws` | `false` | `boolean` | `boolean` | `true` | `false` | `false` | `false`| `true` |
|
||||
| `elasticsearch_aws_access_key` | `false` | `character` | `string` | `false` | `null` | `true` | `false`| `true` |
|
||||
| `elasticsearch_aws_region` | `false` | `character` | `string` | `false` | `'us-east-1'::character` | `false` | `false`| `true` |
|
||||
| `elasticsearch` | `false` | `jsonb` | `` | `true` | `'{}'::jsonb` | `false` | `false`| `false` |
|
||||
| `elasticsearch_aws_secret_access_key` | `true` | `text` | `string` | `false` | `null` | `true` | `false`| `true` |
|
||||
| `elasticsearch_client_request_timeout` | `false` | `integer` | `` | `true` | `0` | `true` | `false`| `false` |
|
||||
| `elasticsearch_indexed_field_length_limit` | `false` | `integer` | `integer` | `true` | `0` | `true` | `false`| `true` |
|
||||
| `elasticsearch_indexed_file_size_limit_kb` | `false` | `integer` | `integer` | `true` | `1024` | `false` | `false`| `true` |
|
||||
| `elasticsearch_indexing` | `false` | `boolean` | `boolean` | `true` | `false` | `true` | `false`| `true` |
|
||||
| `elasticsearch_limit_indexing` | `false` | `boolean` | `boolean` | `true` | `false` | `true` | `false`| `true` |
|
||||
| `elasticsearch_max_bulk_concurrency` | `false` | `smallint` | `integer` | `true` | `10` | `false` | `false`| `true` |
|
||||
| `elasticsearch_max_bulk_size_mb` | `false` | `smallint` | `integer` | `true` | `10` | `false` | `false`| `true` |
|
||||
| `elasticsearch_max_code_indexing_concurrency` | `false` | `integer` | `integer` | `true` | `30` | `true` | `false`| `true` |
|
||||
| `elasticsearch_password` | `true` | `bytea` | `string` | `false` | `null` | `true` | `false`| `true` |
|
||||
| `elasticsearch_pause_indexing` | `false` | `boolean` | `` | `true` | `false` | `false` | `false`| `false` |
|
||||
| `elasticsearch_requeue_workers` | `false` | `boolean` | `boolean` | `true` | `false` | `true` | `false`| `true` |
|
||||
| `elasticsearch_retry_on_failure` | `false` | `integer` | `integer` | `true` | `0` | `false` | `false`| `true` |
|
||||
| `elasticsearch_search` | `false` | `boolean` | `boolean` | `true` | `false` | `true` | `false`| `true` |
|
||||
| `elasticsearch_url` | `false` | `character` | `string` | `false` | `'http://localhost:9200'::character` | `true` | `false`| `true` |
|
||||
| `elasticsearch_username` | `false` | `text` | `string` | `false` | `null` | `true` | `false`| `true` |
|
||||
| `elasticsearch_worker_number_of_shards` | `false` | `integer` | `integer` | `true` | `2` | `true` | `false`| `true` |
|
||||
| `email_additional_text` | `false` | `character` | `string` | `false` | `null` | `true` | `true`| `true` |
|
||||
| `email_author_in_body` | `false` | `boolean` | `boolean` | `false` | `false` | `false` | `true`| `true` |
|
||||
| `email_confirmation_setting` | `false` | `smallint` | `string` | `false` | `0` | `true` | `true`| `true` |
|
||||
|
|
|
|||
|
|
@ -414,6 +414,8 @@ Use **Chat** with a capital `c` for **Chat** or **GitLab Duo Chat**.
|
|||
On first use on a page, use **GitLab Duo Chat**.
|
||||
Thereafter, use **Chat** by itself.
|
||||
|
||||
Do not use **Duo Chat**.
|
||||
|
||||
## checkbox
|
||||
|
||||
Use one word for **checkbox**. Do not use **check box**.
|
||||
|
|
|
|||
|
|
@ -19,6 +19,9 @@ For more information, see the history.
|
|||
Use a vulnerability management policy to automatically resolve vulnerabilities that are no longer
|
||||
detected. This can help reduce the workload of triaging vulnerabilities.
|
||||
|
||||
The vulnerability management policy is available at the project level. Group level policies are
|
||||
being tracked in [this epic](https://gitlab.com/groups/gitlab-org/-/epics/15697).
|
||||
|
||||
When a scanner detects a vulnerability on the default branch, the scanner creates a vulnerability
|
||||
record with the status **Needs triage**. After the vulnerability has been remediated and the next
|
||||
security scan runs, the scan adds **No longer detected** to the record's activity log but the
|
||||
|
|
@ -43,6 +46,7 @@ detected are marked **Resolved**.
|
|||
|
||||
- You can assign a maximum of five rules to each policy.
|
||||
- You can assign a maximum of five vulnerability management policies to each security policy project.
|
||||
- Group level vulnerability management policies are not yet supported.
|
||||
|
||||
## Create a vulnerability management policy
|
||||
|
||||
|
|
|
|||
|
|
@ -7077,7 +7077,7 @@ msgstr ""
|
|||
msgid "ApplicationSettings|A Metrics Dashboard menu item appears in the Monitoring section of the Admin area."
|
||||
msgstr ""
|
||||
|
||||
msgid "ApplicationSettings|A user cap that exceeds the current licensed user count (%{licensedUserCount}) might result in %{linkStart}seat overages%{linkEnd}."
|
||||
msgid "ApplicationSettings|A user cap that exceeds the current licensed user count (%{licensedUserCount}) may result in a %{linkStart}true-up%{linkEnd}."
|
||||
msgstr ""
|
||||
|
||||
msgid "ApplicationSettings|Add a link to Grafana"
|
||||
|
|
@ -19450,6 +19450,9 @@ msgstr ""
|
|||
msgid "Deployment|Open URL"
|
||||
msgstr ""
|
||||
|
||||
msgid "Deployment|Pipeline"
|
||||
msgstr ""
|
||||
|
||||
msgid "Deployment|Ready to be deployed."
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -301,7 +301,7 @@
|
|||
"swagger-cli": "^4.0.4",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"timezone-mock": "^1.0.8",
|
||||
"vite": "^6.0.3",
|
||||
"vite": "^6.0.4",
|
||||
"vite-plugin-ruby": "^5.1.1",
|
||||
"vue-loader-vue3": "npm:vue-loader@17.4.2",
|
||||
"vue-test-utils-compat": "0.0.14",
|
||||
|
|
|
|||
|
|
@ -131,9 +131,6 @@ spec/frontend/branches/components/delete_branch_modal_spec.js
|
|||
spec/frontend/cascading_settings/components/cascading_lock_icon_spec.js
|
||||
spec/frontend/cascading_settings/components/lock_tooltip_spec.js
|
||||
spec/frontend/ci/artifacts/components/artifacts_bulk_delete_spec.js
|
||||
spec/frontend/ci/catalog/components/ci_catalog_home_spec.js
|
||||
spec/frontend/ci/catalog/components/list/ci_resources_list_item_spec.js
|
||||
spec/frontend/ci/catalog/components/pages/ci_resource_details_page_spec.js
|
||||
spec/frontend/ci/catalog/index_spec.js
|
||||
spec/frontend/ci/job_details/components/log/line_header_spec.js
|
||||
spec/frontend/ci/job_details/components/log/line_spec.js
|
||||
|
|
|
|||
|
|
@ -0,0 +1,103 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe 'Change type action', :js, feature_category: :portfolio_management do
|
||||
include ListboxHelpers, DesignManagementTestHelpers
|
||||
|
||||
let_it_be_with_reload(:user) { create(:user) }
|
||||
|
||||
let_it_be(:group) { create(:group, :nested) }
|
||||
let_it_be(:project) { create(:project, :public, namespace: group, developers: user) }
|
||||
let(:issue) { create(:work_item, :issue, project: project) }
|
||||
let(:task) { create(:work_item, :task, project: project) }
|
||||
|
||||
context 'for signed in user' do
|
||||
before do
|
||||
enable_design_management
|
||||
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
context 'when work item type is issue' do
|
||||
before do
|
||||
visit project_work_item_path(project, issue.iid)
|
||||
end
|
||||
|
||||
it_behaves_like 'work items change type', 'Task', '[data-testid="issue-type-task-icon"]'
|
||||
|
||||
context 'when issue has a child' do
|
||||
it 'does not allow changing the type' do
|
||||
within_testid 'work-item-tree' do
|
||||
click_button 'Add'
|
||||
click_button "Existing task"
|
||||
fill_in 'Search existing items', with: task.title
|
||||
click_button task.title
|
||||
send_keys :escape
|
||||
click_button "Add task"
|
||||
wait_for_all_requests
|
||||
end
|
||||
page.refresh
|
||||
wait_for_all_requests
|
||||
|
||||
trigger_change_type('Task')
|
||||
|
||||
expect(page).to have_button('Change type', disabled: true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when work item type is task' do
|
||||
before do
|
||||
visit project_work_item_path(project, task.iid)
|
||||
end
|
||||
|
||||
it_behaves_like 'work items change type', 'Issue', '[data-testid="issue-type-issue-icon"]'
|
||||
|
||||
context 'when task has a parent' do
|
||||
it 'does not allow changing the type' do
|
||||
within_testid 'work-item-parent' do
|
||||
click_button 'Edit'
|
||||
send_keys(issue.title)
|
||||
select_listbox_item(issue.title)
|
||||
end
|
||||
|
||||
trigger_change_type('Issue')
|
||||
|
||||
expect(page).to have_button('Change type', disabled: true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there is chance of data loss' do
|
||||
let_it_be(:issue_with_data) do
|
||||
create(:issue, project: project, title: "Issue with data")
|
||||
end
|
||||
|
||||
let_it_be(:design) { create(:design, :with_file, issue: issue_with_data) }
|
||||
|
||||
before do
|
||||
visit project_work_item_path(project, issue_with_data.iid)
|
||||
wait_for_all_requests
|
||||
end
|
||||
|
||||
it 'renders the warning about the data loss' do
|
||||
trigger_change_type('Task')
|
||||
|
||||
expect(page).to have_button('Change type', disabled: false)
|
||||
|
||||
within_testid('change-type-warning-message', wait: 20) do
|
||||
message = s_('Some fields are not present in %{type}. If you change type now, this information will be lost.')
|
||||
expect(page).to have_content(format(message, type: 'task'))
|
||||
expect(page).to have_content(s_('WorkItem|Designs'))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def trigger_change_type(type)
|
||||
click_button _('More actions'), match: :first
|
||||
click_button s_('WorkItem|Change type')
|
||||
find_by_testid('work-item-change-type-select').select(type)
|
||||
end
|
||||
end
|
||||
|
|
@ -75,6 +75,7 @@ RSpec.describe 'Work item detail', :js, feature_category: :team_planning do
|
|||
end
|
||||
|
||||
it_behaves_like 'work items parent', :issue
|
||||
it_behaves_like 'work items change type', 'Issue', '[data-testid="issue-type-issue-icon"]'
|
||||
end
|
||||
|
||||
context 'for signed in owner' do
|
||||
|
|
@ -123,5 +124,11 @@ RSpec.describe 'Work item detail', :js, feature_category: :team_planning do
|
|||
|
||||
expect(page).to have_content(note.note)
|
||||
end
|
||||
|
||||
it 'change type action is not displayed' do
|
||||
click_button _('More actions'), match: :first
|
||||
|
||||
expect(find_by_testid('work-item-actions-dropdown')).to have_button(s_('WorkItem|Change type'))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -3,44 +3,41 @@ import { createRouter } from '~/ci/catalog/router';
|
|||
import ciResourceDetailsPage from '~/ci/catalog/components/pages/ci_resource_details_page.vue';
|
||||
import CiCatalogHome from '~/ci/catalog/components/ci_catalog_home.vue';
|
||||
|
||||
const baseRoute = '/';
|
||||
const resourcesPageComponentStub = {
|
||||
name: 'page-component',
|
||||
template: '<div>Hello</div>',
|
||||
};
|
||||
|
||||
describe('CiCatalogHome', () => {
|
||||
const defaultProps = {};
|
||||
const baseRoute = '/';
|
||||
const resourcesPageComponentStub = {
|
||||
name: 'page-component',
|
||||
template: '<div>Hello</div>',
|
||||
};
|
||||
const router = createRouter(baseRoute, resourcesPageComponentStub);
|
||||
let router;
|
||||
|
||||
beforeEach(() => {
|
||||
router = createRouter(baseRoute, resourcesPageComponentStub);
|
||||
});
|
||||
|
||||
const createComponent = ({ props = {} } = {}) => {
|
||||
shallowMount(CiCatalogHome, {
|
||||
propsData: {
|
||||
...defaultProps,
|
||||
...props,
|
||||
},
|
||||
router,
|
||||
});
|
||||
};
|
||||
|
||||
describe('when mounted', () => {
|
||||
beforeEach(() => {
|
||||
describe('router', () => {
|
||||
it.each`
|
||||
path | component
|
||||
${baseRoute} | ${resourcesPageComponentStub}
|
||||
${'/1'} | ${ciResourceDetailsPage}
|
||||
`('when route is $path it renders the right component', async ({ path, component }) => {
|
||||
await router.push(path);
|
||||
|
||||
createComponent();
|
||||
});
|
||||
|
||||
describe('router', () => {
|
||||
it.each`
|
||||
path | component
|
||||
${baseRoute} | ${resourcesPageComponentStub}
|
||||
${'/1'} | ${ciResourceDetailsPage}
|
||||
`('when route is $path it renders the right component', async ({ path, component }) => {
|
||||
if (path !== '/') {
|
||||
await router.push(path);
|
||||
}
|
||||
const [root] = router.currentRoute.matched;
|
||||
|
||||
const [root] = router.currentRoute.matched;
|
||||
|
||||
expect(root.components.default).toBe(component);
|
||||
});
|
||||
expect(root.components.default).toBe(component);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
import Vue from 'vue';
|
||||
import VueRouter from 'vue-router';
|
||||
import { update, cloneDeep } from 'lodash';
|
||||
import { GlAvatar, GlBadge, GlSprintf, GlTruncate } from '@gitlab/ui';
|
||||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
|
|
@ -11,15 +9,18 @@ import ProjectVisibilityIcon from '~/ci/catalog/components/shared/project_visibi
|
|||
import Markdown from '~/vue_shared/components/markdown/non_gfm_markdown.vue';
|
||||
import { catalogSinglePageResponse } from '../../mock';
|
||||
|
||||
Vue.use(VueRouter);
|
||||
|
||||
const defaultEvent = { preventDefault: jest.fn, ctrlKey: false, metaKey: false };
|
||||
const baseRoute = '/';
|
||||
const resourcesPageComponentStub = {
|
||||
name: 'page-component',
|
||||
template: '<div>Hello</div>',
|
||||
};
|
||||
|
||||
describe('CiResourcesListItem', () => {
|
||||
let wrapper;
|
||||
let routerPush;
|
||||
let router;
|
||||
|
||||
const router = createRouter();
|
||||
const resource = catalogSinglePageResponse.data.ciCatalogResources.nodes[0];
|
||||
const release = {
|
||||
author: { id: 'author-id', name: 'author', username: 'author-username', webUrl: '/user/1' },
|
||||
|
|
@ -58,6 +59,7 @@ describe('CiResourcesListItem', () => {
|
|||
const findUserLink = () => wrapper.findByTestId('user-link');
|
||||
|
||||
beforeEach(() => {
|
||||
router = createRouter(baseRoute, resourcesPageComponentStub);
|
||||
routerPush = jest.spyOn(router, 'push').mockImplementation(() => {});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -22,13 +22,17 @@ import { catalogSharedDataMock } from '../../mock';
|
|||
Vue.use(VueApollo);
|
||||
Vue.use(VueRouter);
|
||||
|
||||
let router;
|
||||
|
||||
const defaultSharedData = { ...catalogSharedDataMock.data.ciCatalogResource };
|
||||
const baseRoute = '/';
|
||||
const resourcesPageComponentStub = {
|
||||
name: 'page-component',
|
||||
template: '<div>Hello</div>',
|
||||
};
|
||||
|
||||
describe('CiResourceDetailsPage', () => {
|
||||
let wrapper;
|
||||
let sharedDataResponse;
|
||||
let router;
|
||||
|
||||
const defaultProps = {};
|
||||
|
||||
|
|
@ -57,16 +61,14 @@ describe('CiResourceDetailsPage', () => {
|
|||
...defaultProps,
|
||||
...props,
|
||||
},
|
||||
stubs: {
|
||||
RouterView: true,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
sharedDataResponse = jest.fn();
|
||||
|
||||
router = createRouter();
|
||||
router = createRouter(baseRoute, resourcesPageComponentStub);
|
||||
|
||||
await router.push({
|
||||
name: CI_RESOURCE_DETAILS_PAGE_NAME,
|
||||
params: { id: defaultSharedData.webPath },
|
||||
|
|
|
|||
|
|
@ -5,6 +5,9 @@ import mockEnvironmentFixture from 'test_fixtures/graphql/deployments/graphql/qu
|
|||
import { mountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import ShowMore from '~/vue_shared/components/show_more.vue';
|
||||
import DeploymentAside from '~/deployments/components/deployment_aside.vue';
|
||||
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
|
||||
import { useMockInternalEventsTracking } from 'helpers/tracking_internal_events_helper';
|
||||
import { CLICK_PIPELINE_LINK_ON_DEPLOYMENT_PAGE } from '~/deployments/utils';
|
||||
|
||||
const {
|
||||
data: {
|
||||
|
|
@ -16,8 +19,9 @@ const {
|
|||
project: { environment },
|
||||
},
|
||||
} = mockEnvironmentFixture;
|
||||
const { bindInternalEventDocument } = useMockInternalEventsTracking();
|
||||
|
||||
describe('~/deployments/components/deployment_header.vue', () => {
|
||||
describe('~/deployments/components/deployment_aside.vue', () => {
|
||||
let wrapper;
|
||||
|
||||
const findSidebarToggleButton = () => wrapper.findByTestId('deployment-sidebar-toggle-button');
|
||||
|
|
@ -25,6 +29,8 @@ describe('~/deployments/components/deployment_header.vue', () => {
|
|||
const findSidebarItems = () => wrapper.findByTestId('deployment-sidebar-items');
|
||||
const findUrlButtonWrapper = () => wrapper.findByTestId('deployment-url-button-wrapper');
|
||||
const findTriggererItem = () => wrapper.findByTestId('deployment-triggerer-item');
|
||||
const findPipelineSection = () => wrapper.findByTestId('deployment-pipeline');
|
||||
const findPipelineLink = () => wrapper.findByTestId('deployment-pipeline-link');
|
||||
|
||||
const createComponent = ({ propsData = {} } = {}) => {
|
||||
wrapper = mountExtended(DeploymentAside, {
|
||||
|
|
@ -63,6 +69,28 @@ describe('~/deployments/components/deployment_header.vue', () => {
|
|||
expect(link.text()).toContain(deployment.triggerer.name);
|
||||
});
|
||||
|
||||
it('shows a section with a link to the Pipeline', () => {
|
||||
expect(findPipelineSection().exists()).toBe(true);
|
||||
expect(findPipelineSection().text()).toContain('Pipeline');
|
||||
|
||||
expect(findPipelineLink().exists()).toBe(true);
|
||||
expect(findPipelineLink().attributes('href')).toBe(deployment.job.pipeline.path);
|
||||
expect(findPipelineLink().text()).toBe(`#${getIdFromGraphQLId(deployment.job.pipeline.id)}`);
|
||||
});
|
||||
|
||||
it('should call trackEvent method when pipeline link is clicked', async () => {
|
||||
const { trackEventSpy } = bindInternalEventDocument(wrapper.element);
|
||||
|
||||
await findPipelineLink().vm.$emit('click');
|
||||
|
||||
expect(trackEventSpy).toHaveBeenCalledTimes(1);
|
||||
expect(trackEventSpy).toHaveBeenCalledWith(
|
||||
CLICK_PIPELINE_LINK_ON_DEPLOYMENT_PAGE,
|
||||
{},
|
||||
undefined,
|
||||
);
|
||||
});
|
||||
|
||||
it('shows a link to the tags of a deployment', () => {
|
||||
deployment.tags.forEach((tag) => {
|
||||
const link = wrapper.findByRole('link', { name: tag.name });
|
||||
|
|
|
|||
|
|
@ -99,7 +99,6 @@ describe('WorkItemChangeTypeModal component', () => {
|
|||
const findGlFormSelect = () => wrapper.findComponent(GlFormSelect);
|
||||
const findWarningAlert = () => wrapper.findByTestId('change-type-warning-message');
|
||||
const findErrorAlert = () => wrapper.findByTestId('change-type-error-message');
|
||||
const findConfirmationButton = () => wrapper.findByTestId('change-type-confirmation-button');
|
||||
|
||||
beforeEach(async () => {
|
||||
createComponent();
|
||||
|
|
@ -109,7 +108,13 @@ describe('WorkItemChangeTypeModal component', () => {
|
|||
it('renders change type modal with the select', () => {
|
||||
expect(findChangeTypeModal().exists()).toBe(true);
|
||||
expect(findGlFormSelect().exists()).toBe(true);
|
||||
expect(findConfirmationButton().props('disabled')).toBe(true);
|
||||
expect(findChangeTypeModal().props('actionPrimary')).toEqual({
|
||||
attributes: {
|
||||
disabled: true,
|
||||
variant: 'confirm',
|
||||
},
|
||||
text: 'Change type',
|
||||
});
|
||||
});
|
||||
|
||||
it('calls the `namespaceWorkItemTypesQuery` to get the work item types', () => {
|
||||
|
|
@ -137,7 +142,7 @@ describe('WorkItemChangeTypeModal component', () => {
|
|||
'Parent item type issue is not supported on key result. Remove the parent item to change type.',
|
||||
);
|
||||
|
||||
expect(findConfirmationButton().props('disabled')).toBe(true);
|
||||
expect(findChangeTypeModal().props('actionPrimary').attributes.disabled).toBe(true);
|
||||
});
|
||||
|
||||
it('does not allow to change type and disables `Change type` button when the work item has child items', async () => {
|
||||
|
|
@ -150,7 +155,7 @@ describe('WorkItemChangeTypeModal component', () => {
|
|||
expect(findWarningAlert().text()).toBe(
|
||||
'Key result does not support the task child item types. Remove child items to change type.',
|
||||
);
|
||||
expect(findConfirmationButton().props('disabled')).toBe(true);
|
||||
expect(findChangeTypeModal().props('actionPrimary').attributes.disabled).toBe(true);
|
||||
});
|
||||
|
||||
describe('when widget data has difference', () => {
|
||||
|
|
@ -167,7 +172,7 @@ describe('WorkItemChangeTypeModal component', () => {
|
|||
await nextTick();
|
||||
|
||||
expect(findWarningAlert().text()).toContain('Design');
|
||||
expect(findConfirmationButton().props('disabled')).toBe(false);
|
||||
expect(findChangeTypeModal().props('actionPrimary').attributes.disabled).toBe(false);
|
||||
});
|
||||
|
||||
// These are all possible use cases of conflicts among project level work items
|
||||
|
|
@ -193,7 +198,7 @@ describe('WorkItemChangeTypeModal component', () => {
|
|||
await nextTick();
|
||||
|
||||
expect(findWarningAlert().text()).toContain(expectedString);
|
||||
expect(findConfirmationButton().props('disabled')).toBe(false);
|
||||
expect(findChangeTypeModal().props('actionPrimary').attributes.disabled).toBe(false);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
|
@ -208,7 +213,7 @@ describe('WorkItemChangeTypeModal component', () => {
|
|||
|
||||
await nextTick();
|
||||
|
||||
findConfirmationButton().vm.$emit('click');
|
||||
findChangeTypeModal().vm.$emit('primary');
|
||||
|
||||
await waitForPromises();
|
||||
|
||||
|
|
@ -237,7 +242,7 @@ describe('WorkItemChangeTypeModal component', () => {
|
|||
|
||||
await nextTick();
|
||||
|
||||
findConfirmationButton().vm.$emit('click');
|
||||
findChangeTypeModal().vm.$emit('primary');
|
||||
|
||||
await waitForPromises();
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,101 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
require_migration!
|
||||
|
||||
RSpec.describe MoveElasticsearchApplicationSettingsToElasticsearchColumn, feature_category: :global_search do
|
||||
let(:application_settings) { table(:application_settings) }
|
||||
|
||||
describe '#up' do
|
||||
it 'updates setting' do
|
||||
application_settings.create!(
|
||||
elasticsearch_aws: true,
|
||||
elasticsearch_url: 'http://localhost:9200',
|
||||
elasticsearch_search: true,
|
||||
elasticsearch_indexing: true,
|
||||
elasticsearch_username: 'elastic',
|
||||
elasticsearch_aws_region: 'us-east-1',
|
||||
elasticsearch_aws_access_key: 'access_key',
|
||||
elasticsearch_limit_indexing: true,
|
||||
elasticsearch_pause_indexing: true,
|
||||
elasticsearch_requeue_workers: true,
|
||||
elasticsearch_max_bulk_size_mb: 10,
|
||||
elasticsearch_retry_on_failure: true,
|
||||
elasticsearch_max_bulk_concurrency: 10,
|
||||
elasticsearch_client_request_timeout: 0,
|
||||
elasticsearch_worker_number_of_shards: 4,
|
||||
elasticsearch_analyzers_smartcn_search: true,
|
||||
elasticsearch_analyzers_kuromoji_search: true,
|
||||
elasticsearch_analyzers_smartcn_enabled: true,
|
||||
elasticsearch_analyzers_kuromoji_enabled: true,
|
||||
elasticsearch_indexed_field_length_limit: 10,
|
||||
elasticsearch_indexed_file_size_limit_kb: 1024,
|
||||
elasticsearch_max_code_indexing_concurrency: 30
|
||||
)
|
||||
|
||||
migrate!
|
||||
|
||||
expect(application_settings.last.elasticsearch_url).to eq('http://localhost:9200')
|
||||
expect(application_settings.last.elasticsearch).to include(
|
||||
{
|
||||
'elasticsearch_aws' => true,
|
||||
'elasticsearch_search' => true,
|
||||
'elasticsearch_indexing' => true,
|
||||
'elasticsearch_username' => 'elastic',
|
||||
'elasticsearch_aws_region' => 'us-east-1',
|
||||
'elasticsearch_aws_access_key' => 'access_key',
|
||||
'elasticsearch_limit_indexing' => true,
|
||||
'elasticsearch_pause_indexing' => true,
|
||||
'elasticsearch_requeue_workers' => true,
|
||||
'elasticsearch_max_bulk_size_mb' => 10,
|
||||
'elasticsearch_retry_on_failure' => 1,
|
||||
'elasticsearch_max_bulk_concurrency' => 10,
|
||||
'elasticsearch_client_request_timeout' => 0,
|
||||
'elasticsearch_worker_number_of_shards' => 4,
|
||||
'elasticsearch_analyzers_smartcn_search' => true,
|
||||
'elasticsearch_analyzers_kuromoji_search' => true,
|
||||
'elasticsearch_analyzers_smartcn_enabled' => true,
|
||||
'elasticsearch_analyzers_kuromoji_enabled' => true,
|
||||
'elasticsearch_indexed_field_length_limit' => 10,
|
||||
'elasticsearch_indexed_file_size_limit_kb' => 1024,
|
||||
'elasticsearch_max_code_indexing_concurrency' => 30
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#down' do
|
||||
it 'updates setting' do
|
||||
application_settings.create!(
|
||||
elasticsearch: {
|
||||
elasticsearch_aws: true,
|
||||
elasticsearch_search: true,
|
||||
elasticsearch_indexing: true,
|
||||
elasticsearch_username: 'elastic',
|
||||
elasticsearch_aws_region: 'us-east-1',
|
||||
elasticsearch_aws_access_key: 'access_key',
|
||||
elasticsearch_limit_indexing: true,
|
||||
elasticsearch_pause_indexing: true,
|
||||
elasticsearch_requeue_workers: true,
|
||||
elasticsearch_max_bulk_size_mb: 10,
|
||||
elasticsearch_retry_on_failure: 1,
|
||||
elasticsearch_max_bulk_concurrency: 10,
|
||||
elasticsearch_client_request_timeout: 0,
|
||||
elasticsearch_worker_number_of_shards: 4,
|
||||
elasticsearch_analyzers_smartcn_search: true,
|
||||
elasticsearch_analyzers_kuromoji_search: true,
|
||||
elasticsearch_analyzers_smartcn_enabled: true,
|
||||
elasticsearch_analyzers_kuromoji_enabled: true,
|
||||
elasticsearch_indexed_field_length_limit: 10,
|
||||
elasticsearch_indexed_file_size_limit_kb: 1024,
|
||||
elasticsearch_max_code_indexing_concurrency: 30
|
||||
}
|
||||
)
|
||||
|
||||
migrate!
|
||||
schema_migrate_down!
|
||||
|
||||
expect(application_settings.last.elasticsearch).to eq({})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -334,45 +334,79 @@ RSpec.describe ContainerRegistry::Protection::Rule, type: :model, feature_catego
|
|||
end
|
||||
end
|
||||
|
||||
describe '.for_push_exists_for_multiple_containers' do
|
||||
let_it_be(:project) { create(:project) }
|
||||
describe '.for_push_exists_for_projects_and_repository_paths' do
|
||||
let_it_be(:project1) { create(:project) }
|
||||
let_it_be(:project1_crpr) { create(:container_registry_protection_rule, project: project1) }
|
||||
|
||||
let_it_be(:ppr_for_maintainer) do
|
||||
create(:container_registry_protection_rule,
|
||||
repository_path_pattern: "#{project.full_path}/my-container-prod*",
|
||||
project: project
|
||||
)
|
||||
end
|
||||
let_it_be(:project2) { create(:project) }
|
||||
let_it_be(:project2_crpr) { create(:container_registry_protection_rule, project: project2) }
|
||||
|
||||
let(:repository_paths) {
|
||||
let_it_be(:unprotected_project) { create(:project) }
|
||||
|
||||
let(:single_project_input) do
|
||||
[
|
||||
"#{project.full_path}/my-container-prod-1",
|
||||
"#{project.full_path}/unmatched-container-name"
|
||||
[project1.id, project1_crpr.repository_path_pattern],
|
||||
[project1.id, "#{project1_crpr.repository_path_pattern}/unprotected"]
|
||||
]
|
||||
}
|
||||
|
||||
subject do
|
||||
described_class
|
||||
.for_push_exists_for_multiple_containers(project_id: project.id, repository_paths: repository_paths)
|
||||
.to_a
|
||||
end
|
||||
|
||||
it do
|
||||
is_expected.to eq([
|
||||
{ "repository_path" => repository_paths.first, "protected" => true },
|
||||
{ "repository_path" => repository_paths.second, "protected" => false }
|
||||
])
|
||||
let(:single_project_expected_result) do
|
||||
[
|
||||
{ "project_id" => project1.id, "repository_path" => project1_crpr.repository_path_pattern,
|
||||
"protected" => true },
|
||||
{ "project_id" => project1.id, "repository_path" => "#{project1_crpr.repository_path_pattern}/unprotected",
|
||||
"protected" => false }
|
||||
]
|
||||
end
|
||||
|
||||
context 'when edge cases' do
|
||||
where(:repository_paths, :expected_result) do
|
||||
nil | []
|
||||
[] | []
|
||||
end
|
||||
let(:multi_projects_input) do
|
||||
[
|
||||
*single_project_input,
|
||||
[project2.id, project2_crpr.repository_path_pattern],
|
||||
[project2.id, "#{project2_crpr.repository_path_pattern}/unprotected"]
|
||||
]
|
||||
end
|
||||
|
||||
with_them do
|
||||
it { is_expected.to eq(expected_result) }
|
||||
end
|
||||
let(:multi_projects_expected_result) do
|
||||
[
|
||||
*single_project_expected_result,
|
||||
{ "project_id" => project2.id, "repository_path" => project2_crpr.repository_path_pattern,
|
||||
"protected" => true },
|
||||
{ "project_id" => project2.id, "repository_path" => "#{project2_crpr.repository_path_pattern}/unprotected",
|
||||
"protected" => false }
|
||||
]
|
||||
end
|
||||
|
||||
let(:unprotected_projects_input) do
|
||||
[
|
||||
*multi_projects_input,
|
||||
[unprotected_project.id, "#{unprotected_project.full_path}/unprotected1"],
|
||||
[unprotected_project.id, "#{unprotected_project.full_path}/unprotected2"]
|
||||
]
|
||||
end
|
||||
|
||||
let(:unprotected_projects_expected_result) do
|
||||
[
|
||||
*multi_projects_expected_result,
|
||||
{ "project_id" => unprotected_project.id, "repository_path" => "#{unprotected_project.full_path}/unprotected1",
|
||||
"protected" => false },
|
||||
{ "project_id" => unprotected_project.id, "repository_path" => "#{unprotected_project.full_path}/unprotected2",
|
||||
"protected" => false }
|
||||
]
|
||||
end
|
||||
|
||||
subject { described_class.for_push_exists_for_projects_and_repository_paths(projects_and_repository_paths).to_a }
|
||||
|
||||
where(:projects_and_repository_paths, :expected_result) do
|
||||
ref(:single_project_input) | ref(:single_project_expected_result)
|
||||
ref(:multi_projects_input) | ref(:multi_projects_expected_result)
|
||||
ref(:unprotected_projects_input) | ref(:unprotected_projects_expected_result)
|
||||
nil | []
|
||||
[] | []
|
||||
end
|
||||
|
||||
with_them do
|
||||
it { is_expected.to match_array expected_result }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -164,4 +164,84 @@ RSpec.describe 'getting container repositories in a group', feature_category: :s
|
|||
|
||||
expect(container_repositories_count_response).to eq(container_repositories.size)
|
||||
end
|
||||
|
||||
describe 'protectionRuleExists' do
|
||||
let_it_be(:container_registry_protection_rule) do
|
||||
create(:container_registry_protection_rule, project: project, repository_path_pattern: container_repository.path)
|
||||
end
|
||||
|
||||
let_it_be(:project_2) { create(:project, :private, group: group) }
|
||||
let_it_be(:container_repository_2) { create(:container_repository, project: project_2) }
|
||||
let_it_be(:container_registry_protection_rule_2) do
|
||||
create(:container_registry_protection_rule, project: project_2, repository_path_pattern: container_repository_2.path)
|
||||
end
|
||||
|
||||
let_it_be(:project_3) { create(:project, :private, group: group) }
|
||||
let_it_be(:container_repository_3) { create(:container_repository, project: project_3) }
|
||||
let_it_be(:container_registry_protection_rule_3) do
|
||||
create(:container_registry_protection_rule, project: project_3, repository_path_pattern: container_repository_3.path)
|
||||
end
|
||||
|
||||
before do
|
||||
stub_container_registry_tags(repository: container_repository_2.path, tags: %w[tag1 tag2 tag3], with_manifest: false)
|
||||
stub_container_registry_tags(repository: container_repository_3.path, tags: %w[tag1 tag2 tag3], with_manifest: false)
|
||||
end
|
||||
|
||||
it 'returns true for protected container respositories' do
|
||||
subject
|
||||
|
||||
expect(container_repositories_response.count).to eq 7
|
||||
|
||||
expect(find_container_repositories_response(container_repository.path).dig('node', 'protectionRuleExists')).to be true
|
||||
expect(find_container_repositories_response(container_repository_2.path).dig('node', 'protectionRuleExists')).to be true
|
||||
expect(find_container_repositories_response(container_repository_3.path).dig('node', 'protectionRuleExists')).to be true
|
||||
[*container_repositories_delete_scheduled, *container_repositories_delete_failed].each do |repository|
|
||||
expect(find_container_repositories_response(repository.path).dig('node', 'protectionRuleExists')).to be false
|
||||
end
|
||||
end
|
||||
|
||||
it 'executes only one database queries for all projects' do
|
||||
expect { subject }.to match_query_count(1).for_model(::ContainerRegistry::Protection::Rule)
|
||||
end
|
||||
|
||||
context 'when feature flag :container_registry_protected_containers disabled' do
|
||||
before do
|
||||
stub_feature_flags(container_registry_protected_containers: false)
|
||||
end
|
||||
|
||||
it 'returns false even for protected container respositories' do
|
||||
subject
|
||||
|
||||
expect(container_repositories_response.count).to eq 7
|
||||
expect(container_repositories_response).to all(include('node' => include('protectionRuleExists' => false)))
|
||||
end
|
||||
end
|
||||
|
||||
context 'when 25 container repositories belong to group' do
|
||||
let_it_be(:group) { create(:group) }
|
||||
let_it_be(:projects) { create_list(:project, 5, :private, group: group) }
|
||||
|
||||
before_all do
|
||||
projects.each do |project|
|
||||
container_repositories = create_list(:container_repository, 5, project: project)
|
||||
create(:container_registry_protection_rule, project: project,
|
||||
repository_path_pattern: container_repositories.first.path)
|
||||
end
|
||||
end
|
||||
|
||||
before do
|
||||
group.container_repositories.each do |container_repository|
|
||||
stub_container_registry_tags(repository: container_repository.path, tags: %w[tag1 tag2 tag3], with_manifest: false)
|
||||
end
|
||||
end
|
||||
|
||||
it 'executes only two database queries to check the protection rules for container repositories in batches of 20' do
|
||||
expect { subject }.to match_query_count(2).for_model(::ContainerRegistry::Protection::Rule)
|
||||
end
|
||||
end
|
||||
|
||||
def find_container_repositories_response(container_repository_path)
|
||||
container_repositories_response.find { |res| res.dig('node', 'path') == container_repository_path }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -980,3 +980,20 @@ RSpec.shared_examples 'work items linked items' do |is_group = false|
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
RSpec.shared_examples 'work items change type' do |selected_type, expected_selector|
|
||||
it 'change work item type to selected type', :aggregate_failures do
|
||||
click_button _('More actions'), match: :first
|
||||
click_button s_('WorkItem|Change type')
|
||||
|
||||
expect(find('#work-item-change-type')).to have_content(s_('WorkItem|Change type'))
|
||||
|
||||
find_by_testid('work-item-change-type-select').select(selected_type)
|
||||
|
||||
click_button s_('WorkItem|Change type')
|
||||
|
||||
wait_for_requests
|
||||
|
||||
expect(page).to have_selector(expected_selector)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -14836,10 +14836,10 @@ vite-plugin-ruby@^5.1.1:
|
|||
debug "^4.3.4"
|
||||
fast-glob "^3.3.2"
|
||||
|
||||
vite@^6.0.3:
|
||||
version "6.0.3"
|
||||
resolved "https://registry.yarnpkg.com/vite/-/vite-6.0.3.tgz#cc01f403e326a9fc1e064235df8a6de084c8a491"
|
||||
integrity sha512-Cmuo5P0ENTN6HxLSo6IHsjCLn/81Vgrp81oaiFFMRa8gGDj5xEjIcEpf2ZymZtZR8oU0P2JX5WuUp/rlXcHkAw==
|
||||
vite@^6.0.4:
|
||||
version "6.0.4"
|
||||
resolved "https://registry.yarnpkg.com/vite/-/vite-6.0.4.tgz#fe7cfaedff7c701d5582be5c4ed6a2150538ea9d"
|
||||
integrity sha512-zwlH6ar+6o6b4Wp+ydhtIKLrGM/LoqZzcdVmkGAFun0KHTzIzjh+h0kungEx7KJg/PYnC80I4TII9WkjciSR6Q==
|
||||
dependencies:
|
||||
esbuild "^0.24.0"
|
||||
postcss "^8.4.49"
|
||||
|
|
|
|||
Loading…
Reference in New Issue