Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2024-05-14 03:18:10 +00:00
parent 50d0abd57d
commit cb7f283a39
81 changed files with 523 additions and 233 deletions

View File

@ -35,23 +35,6 @@ Layout/ArgumentAlignment:
- 'app/graphql/mutations/work_items/delete.rb'
- 'app/graphql/mutations/work_items/update.rb'
- 'app/graphql/resolvers/admin/analytics/usage_trends/measurements_resolver.rb'
- 'app/graphql/resolvers/ci/runner_projects_resolver.rb'
- 'app/graphql/resolvers/ci/runner_resolver.rb'
- 'app/graphql/resolvers/ci/runner_setup_resolver.rb'
- 'app/graphql/resolvers/ci/runners_resolver.rb'
- 'app/graphql/resolvers/ci/template_resolver.rb'
- 'app/graphql/resolvers/ci/variables_resolver.rb'
- 'app/graphql/resolvers/clusters/agent_tokens_resolver.rb'
- 'app/graphql/resolvers/clusters/agents_resolver.rb'
- 'app/graphql/resolvers/concerns/board_item_filterable.rb'
- 'app/graphql/resolvers/concerns/group_issuable_resolver.rb'
- 'app/graphql/resolvers/concerns/issues/sort_arguments.rb'
- 'app/graphql/resolvers/concerns/project_search_arguments.rb'
- 'app/graphql/resolvers/concerns/resolves_pipelines.rb'
- 'app/graphql/resolvers/concerns/resolves_snippets.rb'
- 'app/graphql/resolvers/concerns/search_arguments.rb'
- 'app/graphql/resolvers/concerns/time_frame_arguments.rb'
- 'app/graphql/resolvers/container_repositories_resolver.rb'
- 'app/graphql/resolvers/work_items/types_resolver.rb'
- 'app/graphql/resolvers/work_items_resolver.rb'
- 'app/graphql/subscriptions/issuable_updated.rb'

View File

@ -1 +1 @@
4b1de41ff2b02d54bf57b0c965d9b265a175a3ec
d3853184e7d735a89479582ceac1d525497e477b

View File

@ -283,7 +283,7 @@ export default {
>
<span
:id="`ci-variable-key-${item.id}`"
class="gl-display-inline-block gl-max-w-full gl-word-break-word"
class="gl-display-inline-block gl-max-w-full gl-break-anywhere"
>{{ item.key }}</span
>
<gl-button
@ -343,7 +343,7 @@ export default {
>
<span
:id="`ci-variable-env-${item.id}`"
class="gl-display-inline-block gl-max-w-full gl-word-break-word"
class="gl-display-inline-block gl-max-w-full gl-break-anywhere"
>{{ convertEnvironmentScopeValue(item.environmentScope) }}</span
>
<gl-button
@ -365,7 +365,7 @@ export default {
<gl-link
:id="`ci-variable-group-${item.id}`"
data-testid="ci-variable-table-row-cicd-path"
class="gl-display-inline-block gl-max-w-full gl-word-break-word"
class="gl-display-inline-block gl-max-w-full gl-break-anywhere"
:href="item.groupCiCdSettingsPath"
>
{{ item.groupName }}

View File

@ -90,7 +90,7 @@ export default {
@close="closeDetailsDrawer"
>
<template #title>
<h4 class="gl-font-weight-bold gl-font-size-h2 gl-m-0 gl-word-break-word">
<h4 class="gl-font-weight-bold gl-font-size-h2 gl-m-0 gl-break-anywhere">
{{ selectedItem.name }}
</h4>
</template>

View File

@ -160,7 +160,7 @@ export default {
class="table-mobile-content gl-text-left gl-display-flex flex-column js-feature-flag-title gl-mr-5"
>
<div class="gl-display-flex gl-align-items-center">
<div class="feature-flag-name text-monospace text-wrap gl-word-break-word">
<div class="feature-flag-name text-monospace text-wrap gl-break-anywhere">
{{ featureFlag.name }}
</div>
<div class="feature-flag-description">

View File

@ -203,7 +203,7 @@ export default {
data-testid="group-name"
:href="group.relativePath"
:title="group.fullName"
class="no-expand gl-mr-3 gl-text-gray-900! gl-word-break-word"
class="no-expand gl-mr-3 gl-text-gray-900! gl-break-anywhere"
:itemprop="microdata.nameItemprop"
>
<!-- ending bracket must be by closing tag to prevent -->

View File

@ -102,14 +102,14 @@ export default {
<div v-for="(line, index) in allLines" :key="index">
<span
data-testid="highlights-text"
class="gl-white-space-pre-wrap gl-word-break-word"
class="gl-white-space-pre-wrap gl-break-anywhere"
v-text="line.text"
>
</span
><mark
v-show="line.highlightedText"
data-testid="highlights-mark"
class="gl-px-1 gl-py-0 gl-bg-orange-100 gl-text-transparent gl-white-space-pre-wrap gl-word-break-word"
class="gl-px-1 gl-py-0 gl-bg-orange-100 gl-text-transparent gl-white-space-pre-wrap gl-break-anywhere"
v-text="line.highlightedText"
>
</mark>

View File

@ -18,7 +18,7 @@ export default {
{
key: 'source_title',
label: __('Title'),
tdClass: 'gl-md-w-30 gl-word-break-word',
tdClass: 'gl-md-w-30 gl-break-anywhere',
},
{
key: 'error',

View File

@ -15,7 +15,7 @@ export default {
{
key: 'title',
label: __('Title'),
tdClass: 'gl-md-w-30 gl-word-break-word',
tdClass: 'gl-md-w-30 gl-break-anywhere',
},
{
key: 'provider_url',

View File

@ -70,7 +70,7 @@ export default {
<template>
<ul class="gl-list-none">
<workload-details-item :label="$options.i18n.name">
<span class="gl-word-break-word"> {{ item.name }}</span>
<span class="gl-break-anywhere"> {{ item.name }}</span>
</workload-details-item>
<workload-details-item :label="$options.i18n.kind">
{{ item.kind }}

View File

@ -85,7 +85,7 @@ export default {
@close="closeDetailsDrawer"
>
<template #title>
<h4 class="gl-font-weight-bold gl-font-size-h2 gl-m-0 gl-word-break-word">
<h4 class="gl-font-weight-bold gl-font-size-h2 gl-m-0 gl-break-anywhere">
{{ selectedItem.name }}
</h4>
</template>

View File

@ -34,7 +34,7 @@ export const DEFAULT_WORKLOAD_TABLE_FIELDS = [
{
key: 'name',
label: s__('KubernetesDashboard|Name'),
tdClass: 'gl-md-w-half gl-lg-w-40p gl-word-break-word',
tdClass: 'gl-md-w-half gl-lg-w-40p gl-break-anywhere',
},
{
key: 'status',
@ -44,7 +44,7 @@ export const DEFAULT_WORKLOAD_TABLE_FIELDS = [
{
key: 'namespace',
label: s__('KubernetesDashboard|Namespace'),
tdClass: 'gl-md-w-30p gl-lg-w-40p gl-word-break-word',
tdClass: 'gl-md-w-30p gl-lg-w-40p gl-break-anywhere',
},
{
key: 'age',

View File

@ -49,23 +49,6 @@ if (process.env.NODE_ENV !== 'production' && gon?.test_env) {
import(/* webpackMode: "eager" */ './test_utils');
}
if (gon?.user_color_mode === 'gl-system') {
const root = document.documentElement;
// eslint-disable-next-line @gitlab/require-i18n-strings
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
root.classList.add('gl-dark');
}
// eslint-disable-next-line @gitlab/require-i18n-strings
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
if (e.matches) {
root.classList.add('gl-dark');
} else {
root.classList.remove('gl-dark');
}
});
}
document.addEventListener('beforeunload', () => {
// Unbind scroll events
// eslint-disable-next-line @gitlab/no-global-event-off

View File

@ -86,7 +86,7 @@ export default {
<assignee-avatar-link
:user="user"
:issuable-type="issuableType"
class="gl-word-break-word"
class="gl-break-anywhere"
data-css-area="user"
>
<div

View File

@ -192,7 +192,7 @@ export default {
class="labels-fetch-loading gl-align-items-center gl-w-full gl-h-full"
size="lg"
/>
<ul v-else class="list-unstyled gl-mb-0 gl-word-break-word">
<ul v-else class="list-unstyled gl-mb-0 gl-break-anywhere">
<label-item
v-for="(label, index) in visibleLabels"
:key="label.id"

View File

@ -10,7 +10,7 @@ export default {
</script>
<template>
<div class="gl-display-flex gl-word-break-word">
<div class="gl-display-flex gl-break-anywhere">
<span
class="dropdown-label-box gl-flex-shrink-0 gl-top-0 gl-mr-3"
:style="{ 'background-color': label.color }"

View File

@ -162,7 +162,7 @@ export default {
:user="user"
:root-path="rootPath"
:issuable-type="issuableType"
class="gl-word-break-word gl-mr-2"
class="gl-break-anywhere gl-mr-2"
data-css-area="user"
>
<div class="gl-ml-3 gl-line-height-normal gl-display-grid gl-align-items-center">

View File

@ -144,7 +144,7 @@ export default {
<gl-link
:href="getUsageQuotasUrl(project.webUrl)"
class="gl-text-gray-900! js-project-link gl-word-break-word"
class="gl-text-gray-900! js-project-link gl-break-anywhere"
data-testid="project-link"
>
{{ getProjectRelativePath(project.nameWithNamespace) }}

View File

@ -255,7 +255,7 @@ export default {
</template>
</gl-avatar-labeled>
</div>
<div class="gl-mt-2 gl-w-full gl-word-break-word">
<div class="gl-mt-2 gl-w-full gl-break-anywhere">
<template v-if="userIsLoading">
<gl-skeleton-loader
:lines="$options.maxSkeletonLines"

View File

@ -132,7 +132,7 @@ export default {
:note-id="noteId"
:is-system-note="true"
>
<span ref="gfm-content" v-safe-html="actionTextHtml" class="gl-word-break-word"></span>
<span ref="gfm-content" v-safe-html="actionTextHtml" class="gl-break-anywhere"></span>
<template v-if="canSeeDescriptionVersion" #extra-controls>
&middot;
<gl-button

View File

@ -18,14 +18,13 @@ module WebHooks
end
def create
self.hook = relation.new(hook_params)
hook.save
result = WebHooks::CreateService.new(current_user).execute(hook_params, relation)
if hook.valid?
if result.success?
flash[:notice] = _('Webhook was created')
else
self.hooks = relation.select(&:persisted?)
flash[:alert] = hook.errors.full_messages.to_sentence.html_safe
flash[:alert] = result.message
end
redirect_to action: :index

View File

@ -26,8 +26,8 @@ module Resolvers
unique_project_ids = plucked_runner_and_project_ids.collect { |_runner_id, project_id| project_id }.uniq
projects = ProjectsFinder
.new(current_user: current_user,
params: project_finder_params(args),
project_ids_relation: unique_project_ids)
params: project_finder_params(args),
project_ids_relation: unique_project_ids)
.execute
projects = apply_lookahead(projects)
Preloaders::ProjectPolicyPreloader.new(projects, current_user).execute

View File

@ -9,26 +9,26 @@ module Resolvers
description 'Runner setup instructions.'
argument :platform,
type: GraphQL::Types::String,
required: true,
description: 'Platform to generate the instructions for.'
type: GraphQL::Types::String,
required: true,
description: 'Platform to generate the instructions for.'
argument :architecture,
type: GraphQL::Types::String,
required: true,
description: 'Architecture to generate the instructions for.'
type: GraphQL::Types::String,
required: true,
description: 'Architecture to generate the instructions for.'
argument :project_id,
type: ::Types::GlobalIDType[::Project],
required: false,
deprecated: { reason: 'No longer used', milestone: '13.11' },
description: 'Project to register the runner for.'
type: ::Types::GlobalIDType[::Project],
required: false,
deprecated: { reason: 'No longer used', milestone: '13.11' },
description: 'Project to register the runner for.'
argument :group_id,
type: ::Types::GlobalIDType[::Group],
required: false,
deprecated: { reason: 'No longer used', milestone: '13.11' },
description: 'Group to register the runner for.'
type: ::Types::GlobalIDType[::Group],
required: false,
deprecated: { reason: 'No longer used', milestone: '13.11' },
description: 'Group to register the runner for.'
def resolve(platform:, architecture:, **args)
instructions = Gitlab::Ci::RunnerInstructions.new(

View File

@ -9,53 +9,53 @@ module Resolvers
type Types::Ci::RunnerType.connection_type, null: true
argument :active, ::GraphQL::Types::Boolean,
required: false,
description: 'Filter runners by `active` (true) or `paused` (false) status.',
deprecated: { reason: :renamed, replacement: 'paused', milestone: '14.8' }
required: false,
description: 'Filter runners by `active` (true) or `paused` (false) status.',
deprecated: { reason: :renamed, replacement: 'paused', milestone: '14.8' }
argument :paused, ::GraphQL::Types::Boolean,
required: false,
description: 'Filter runners by `paused` (true) or `active` (false) status.'
required: false,
description: 'Filter runners by `paused` (true) or `active` (false) status.'
argument :status, ::Types::Ci::RunnerStatusEnum,
required: false,
description: 'Filter runners by status.'
required: false,
description: 'Filter runners by status.'
argument :type, ::Types::Ci::RunnerTypeEnum,
required: false,
description: 'Filter runners by type.'
required: false,
description: 'Filter runners by type.'
argument :tag_list, [GraphQL::Types::String],
required: false,
description: 'Filter by tags associated with the runner (comma-separated or array).'
required: false,
description: 'Filter by tags associated with the runner (comma-separated or array).'
argument :search, GraphQL::Types::String,
required: false,
description: 'Filter by full token or partial text in description field.'
required: false,
description: 'Filter by full token or partial text in description field.'
argument :sort, ::Types::Ci::RunnerSortEnum,
required: false,
description: 'Sort order of results.'
required: false,
description: 'Sort order of results.'
argument :upgrade_status, ::Types::Ci::RunnerUpgradeStatusEnum,
required: false,
description: 'Filter by upgrade status.'
required: false,
description: 'Filter by upgrade status.'
argument :creator_id, ::Types::GlobalIDType[::User].as('UserID'),
required: false,
description: 'Filter runners by creator ID.'
required: false,
description: 'Filter runners by creator ID.'
argument :creator_username, GraphQL::Types::String,
required: false,
description: 'Filter runners by creator username.',
alpha: { milestone: '16.7' }
required: false,
description: 'Filter runners by creator username.',
alpha: { milestone: '16.7' }
argument :version_prefix, GraphQL::Types::String,
required: false,
description: "Filter runners by version. Runners that contain runner managers with the version at " \
"the start of the search term are returned. For example, the search term '14.' returns " \
"runner managers with versions '14.11.1' and '14.2.3'.",
alpha: { milestone: '16.6' }
required: false,
description: "Filter runners by version. Runners that contain runner managers with the version at " \
"the start of the search term are returned. For example, the search term '14.' returns " \
"runner managers with versions '14.11.1' and '14.2.3'.",
alpha: { milestone: '16.6' }
def resolve_with_lookahead(**args)
apply_lookahead(

View File

@ -6,10 +6,10 @@ module Resolvers
type Types::Ci::TemplateType, null: true
argument :name,
GraphQL::Types::String,
required: true,
description: 'Name of the CI/CD template to search for. ' \
'Template must be formatted as `Name.gitlab-ci.yml`.'
GraphQL::Types::String,
required: true,
description: 'Name of the CI/CD template to search for. ' \
'Template must be formatted as `Name.gitlab-ci.yml`.'
alias_method :project, :object

View File

@ -6,8 +6,8 @@ module Resolvers
type Types::Ci::InstanceVariableType.connection_type, null: true
argument :sort, ::Types::Ci::VariableSortEnum,
required: false,
description: 'Sort order of results.'
required: false,
description: 'Sort order of results.'
def resolve(**args)
if parent.is_a?(Group) || parent.is_a?(Project)

View File

@ -11,8 +11,8 @@ module Resolvers
when_single do
argument :name, GraphQL::Types::String,
required: true,
description: 'Name of the cluster agent.'
required: true,
description: 'Name of the cluster agent.'
end
def resolve_with_lookahead(**args)

View File

@ -15,7 +15,7 @@ module BoardItemFilterable
if filters[:or]
if ::Feature.disabled?(:or_issuable_queries, resource_parent)
raise ::Gitlab::Graphql::Errors::ArgumentError,
"'or' arguments are only allowed when the `or_issuable_queries` feature flag is enabled."
"'or' arguments are only allowed when the `or_issuable_queries` feature flag is enabled."
end
rewrite_param_name(filters[:or], :author_usernames, :author_username)

View File

@ -5,14 +5,14 @@ module GroupIssuableResolver
included do
argument :include_subgroups, GraphQL::Types::Boolean,
required: false,
default_value: false,
description: "Include #{issuable_collection_name} belonging to subgroups"
required: false,
default_value: false,
description: "Include #{issuable_collection_name} belonging to subgroups"
argument :include_archived, GraphQL::Types::Boolean,
required: false,
default_value: false,
description: "Return #{issuable_collection_name} from archived projects"
required: false,
default_value: false,
description: "Return #{issuable_collection_name} from archived projects"
end
def resolve(**args)

View File

@ -12,9 +12,9 @@ module Issues
included do
argument :sort, Types::IssueSortEnum,
description: 'Sort issues by this criteria.',
required: false,
default_value: :created_desc
description: 'Sort issues by this criteria.',
required: false,
default_value: :created_desc
end
private

View File

@ -5,30 +5,30 @@ module ProjectSearchArguments
included do
argument :membership, GraphQL::Types::Boolean,
required: false,
description: 'Return only projects that the current user is a member of.'
required: false,
description: 'Return only projects that the current user is a member of.'
argument :search, GraphQL::Types::String,
required: false,
description: 'Search query, which can be for the project name, a path, or a description.'
required: false,
description: 'Search query, which can be for the project name, a path, or a description.'
argument :search_namespaces, GraphQL::Types::Boolean,
required: false,
description: 'Include namespace in project search.'
required: false,
description: 'Include namespace in project search.'
argument :topics, type: [GraphQL::Types::String],
required: false,
description: 'Filter projects by topics.'
required: false,
description: 'Filter projects by topics.'
argument :personal, GraphQL::Types::Boolean,
required: false,
description: 'Return only personal projects.'
required: false,
description: 'Return only personal projects.'
argument :sort, GraphQL::Types::String,
required: false,
default_value: 'id_desc',
description: "Sort order of results. Format: `<field_name>_<sort_direction>`, " \
"for example: `id_desc` or `name_asc`"
required: false,
default_value: 'id_desc',
description: "Sort order of results. Format: `<field_name>_<sort_direction>`, " \
"for example: `id_desc` or `name_asc`"
end
private

View File

@ -6,36 +6,36 @@ module ResolvesPipelines
included do
type Types::Ci::PipelineType.connection_type, null: false
argument :status,
Types::Ci::PipelineStatusEnum,
required: false,
description: "Filter pipelines by their status."
Types::Ci::PipelineStatusEnum,
required: false,
description: "Filter pipelines by their status."
argument :scope, ::Types::Ci::PipelineScopeEnum,
required: false,
description: 'Filter pipelines by scope.'
required: false,
description: 'Filter pipelines by scope.'
argument :ref,
GraphQL::Types::String,
required: false,
description: "Filter pipelines by the ref they are run for."
GraphQL::Types::String,
required: false,
description: "Filter pipelines by the ref they are run for."
argument :sha,
GraphQL::Types::String,
required: false,
description: "Filter pipelines by the sha of the commit they are run for."
GraphQL::Types::String,
required: false,
description: "Filter pipelines by the sha of the commit they are run for."
argument :source,
GraphQL::Types::String,
required: false,
description: "Filter pipelines by their source."
GraphQL::Types::String,
required: false,
description: "Filter pipelines by their source."
argument :updated_after, Types::TimeType,
required: false,
description: 'Pipelines updated after this date.'
required: false,
description: 'Pipelines updated after this date.'
argument :updated_before, Types::TimeType,
required: false,
description: 'Pipelines updated before this date.'
required: false,
description: 'Pipelines updated before this date.'
argument :username,
GraphQL::Types::String,
required: false,
description: "Filter pipelines by the user that triggered the pipeline."
GraphQL::Types::String,
required: false,
description: "Filter pipelines by the user that triggered the pipeline."
end
class_methods do

View File

@ -8,12 +8,12 @@ module ResolvesSnippets
type Types::SnippetType.connection_type, null: true
argument :ids, [::Types::GlobalIDType[::Snippet]],
required: false,
description: 'Array of global snippet IDs. For example, `gid://gitlab/ProjectSnippet/1`.'
required: false,
description: 'Array of global snippet IDs. For example, `gid://gitlab/ProjectSnippet/1`.'
argument :visibility, Types::Snippets::VisibilityScopesEnum,
required: false,
description: 'Visibility of the snippet.'
required: false,
description: 'Visibility of the snippet.'
end
def resolve(**args)

View File

@ -5,14 +5,14 @@ module SearchArguments
included do
argument :search, GraphQL::Types::String,
required: false,
description: 'Search query for title or description.'
required: false,
description: 'Search query for title or description.'
argument :in, [Types::IssuableSearchableFieldEnum],
required: false,
description: <<~DESC
required: false,
description: <<~DESC
Specify the fields to perform the search in.
Defaults to `[TITLE, DESCRIPTION]`. Requires the `search` argument.'
DESC
DESC
end
def ready?(**args)
@ -28,7 +28,7 @@ module SearchArguments
return unless args[:in].present? && args[:search].blank?
raise Gitlab::Graphql::Errors::ArgumentError,
'`search` should be present when including the `in` argument'
'`search` should be present when including the `in` argument'
end
def validate_search_rate_limit!(args)

View File

@ -5,8 +5,8 @@ module TimeFrameArguments
included do
argument :timeframe, Types::TimeframeInputType,
required: false,
description: 'List items overlapping the given timeframe.'
required: false,
description: 'List items overlapping the given timeframe.'
end
def transform_timeframe_parameters(args)

View File

@ -7,13 +7,13 @@ module Resolvers
type Types::ContainerRepositoryType, null: true
argument :name, GraphQL::Types::String,
required: false,
description: 'Filter the container repositories by their name.'
required: false,
description: 'Filter the container repositories by their name.'
argument :sort, Types::ContainerRepositorySortEnum,
description: 'Sort container repositories by this criteria.',
required: false,
default_value: :created_desc
description: 'Sort container repositories by this criteria.',
required: false,
default_value: :created_desc
def resolve(name: nil, sort: nil)
ContainerRepositoriesFinder.new(user: current_user, subject: object, params: { name: name, sort: sort })

View File

@ -26,7 +26,7 @@ module WebHooks
end
def can_access_web_hooks?(object)
Ability.allowed?(current_user, :admin_project, object)
Ability.allowed?(current_user, :admin_web_hook, object)
end
end
end

View File

@ -0,0 +1,28 @@
# frozen_string_literal: true
module WebHooks
class CreateService
include Services::ReturnServiceResponses
def initialize(current_user)
@current_user = current_user
end
def execute(hook_params, relation)
hook = relation.new(hook_params)
if hook.save
success({ hook: hook, async: false })
else
return error("Invalid url given", 422) if hook.errors[:url].present?
return error("Invalid branch filter given", 422) if hook.errors[:push_events_branch_filter].present?
error(hook.errors.full_messages.to_sentence, 422)
end
end
private
attr_reader :current_user
end
end

View File

@ -6,7 +6,7 @@
.gl-display-flex.gl-justify-content-space-between.gl-flex-wrap.gl-flex-direction-column.gl-sm-flex-direction-row.gl-gap-3.gl-my-5
.home-panel-title-row.gl-display-flex
= render Pajamas::AvatarComponent.new(@group, alt: @group.name, size: 48, class: 'float-none gl-align-self-start gl-flex-shrink-0 gl-mr-3', avatar_options: { itemprop: 'logo' })
%h1.home-panel-title.gl-heading-1.gl-mt-3.gl-display-flex.gl-align-items-center.gl-flex-wrap.gl-gap-3.gl-word-break-word{ class: 'gl-mb-0!', itemprop: 'name' }
%h1.home-panel-title.gl-heading-1.gl-mt-3.gl-display-flex.gl-align-items-center.gl-flex-wrap.gl-gap-3.gl-break-anywhere{ class: 'gl-mb-0!', itemprop: 'name' }
= @group.name
%span.visibility-icon.gl-text-secondary.has-tooltip{ data: { container: 'body' }, title: visibility_icon_description(@group) }
= visibility_level_icon(@group.visibility_level, options: { class: 'icon' })

View File

@ -0,0 +1,17 @@
- return unless user_application_system_mode?
= javascript_tag do
:plain
const root = document.documentElement;
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
root.classList.add('gl-dark');
}
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
if (e.matches) {
root.classList.add('gl-dark');
} else {
root.classList.remove('gl-dark');
}
});

View File

@ -14,6 +14,8 @@
= Gon::Base.render_data(nonce: content_security_policy_nonce)
= yield :project_javascripts
= render 'layouts/application_color_mode_js'
= render 'layouts/startup_js'
= yield :startup_js

View File

@ -4,7 +4,7 @@
.gl-display-flex.gl-justify-content-space-between.gl-flex-wrap.gl-flex-direction-column.gl-md-flex-direction-row.gl-gap-5
.home-panel-title-row.gl-display-flex.gl-align-items-center
= render Pajamas::AvatarComponent.new(@project, alt: @project.name, class: 'gl-align-self-start gl-flex-shrink-0 gl-mr-3', size: 48, avatar_options: { itemprop: 'image' })
%h1.home-panel-title.gl-heading-1.gl-mt-3.gl-display-flex.gl-align-items-center.gl-flex-wrap.gl-gap-3.gl-word-break-word{ class: 'gl-mb-0!', data: { testid: 'project-name-content' }, itemprop: 'name' }
%h1.home-panel-title.gl-heading-1.gl-mt-3.gl-display-flex.gl-align-items-center.gl-flex-wrap.gl-gap-3.gl-break-anywhere{ class: 'gl-mb-0!', data: { testid: 'project-name-content' }, itemprop: 'name' }
= @project.name
= visibility_level_content(@project, css_class: 'visibility-icon gl-display-inline-flex gl-text-secondary', icon_css_class: 'icon')
= render_if_exists 'compliance_management/compliance_framework/compliance_framework_badge', project: @project, additional_classes: 'gl-align-self-center'

View File

@ -14,7 +14,7 @@
.detail-page-header.border-bottom-0.gl-display-block.gl-pt-5{ class: "gl-sm-display-flex! #{'is-merge-request' if !fluid_layout}" }
.detail-page-header-body
%h1.title.page-title.gl-font-size-h-display.gl-my-0.gl-display-inline-block.gl-flex-grow-1.gl-word-break-word{ data: { testid: 'title-content' } }
%h1.title.page-title.gl-font-size-h-display.gl-my-0.gl-display-inline-block.gl-flex-grow-1.gl-break-anywhere{ data: { testid: 'title-content' } }
= markdown_field(@merge_request, :title)
- unless hide_gutter_toggle

View File

@ -24,7 +24,7 @@
- else
= render Pajamas::AvatarComponent.new(project, size: 48, alt: '')
.gl-w-full.gl-pt-2.gl-word-break-word
.gl-w-full.gl-pt-2.gl-break-anywhere
.gl-display-flex.gl-align-items-center.gl-flex-wrap
%h2.gl-font-base.gl-line-height-20.gl-my-0
= link_to project_path(project), class: 'text-plain gl-mr-3 js-prefetch-document', title: project.name do

View File

@ -7,7 +7,7 @@
= render 'devise/shared/tab_single', tab_title: _('Authorize identity provider link')
%h4.gl-mt-0= safe_format(s_('Allow %{strongOpen}%{provider}%{strongClose} to sign you in?'), tag_pair(tag.strong, :strongOpen, :strongClose), provider: provider)
%p= safe_format(s_('Authorizing allows you to sign in with %{strongOpen}%{provider}%{strongClose}.'), tag_pair(tag.strong, :strongOpen, :strongClose), provider: provider)
= render Pajamas::AlertComponent.new(variant: :warning, dismissible: false, alert_options: { class: 'gl-mb-5 gl-word-break-word' }) do |c|
= render Pajamas::AlertComponent.new(variant: :warning, dismissible: false, alert_options: { class: 'gl-mb-5 gl-break-anywhere' }) do |c|
- c.with_body do
= safe_format(_('To allow %{strongOpen}%{provider}%{strongClose} to manage your GitLab account %{strongOpen}%{username}%{strongClose} (%{email}) after you sign in successfully using single sign-on, select %{strongOpen}Authorize%{strongClose}.'), tag_pair(tag.strong, :strongOpen, :strongClose), provider: provider, username: current_user.username, email: current_user.email)

View File

@ -31,6 +31,10 @@ function gitLabUIUtilities({ addUtilities }) {
'var(--default-mono-font, "GitLab Mono"), "JetBrains Mono", "Menlo", "DejaVu Sans Mono", "Liberation Mono", "Consolas", "Ubuntu Mono", "Courier New", "andale mono", "lucida console", monospace',
'font-variant-ligatures': 'none',
},
'.break-anywhere': {
'overflow-wrap': 'anywhere',
'word-break': 'normal',
},
});
}

View File

@ -23,3 +23,4 @@ desired_sharding_key:
table: merge_requests
sharding_key: target_project_id
belongs_to: merge_request
desired_sharding_key_migration_job_name: BackfillApprovalMergeRequestRulesProjectId

View File

@ -0,0 +1,9 @@
---
migration_job_name: BackfillApprovalMergeRequestRulesProjectId
description: Backfills sharding key `approval_merge_request_rules.project_id` from `merge_requests`.
feature_category: code_review_workflow
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/151630
milestone: '17.0'
queued_migration_version: 20240501044351
finalize_after: '2024-06-22'
finalized_by: # version of the migration that finalized this BBM

View File

@ -1,9 +1,11 @@
---
migration_job_name: BackfillArchivedAndTraversalIdsToVulnerabilityReads
description: Backfill project.archived and project.namespace.traversal_ids values to the denormalized columns of the same name on vulnerability_reads. No-oped and requeued in job RequeueBackfillArchivedAndTraversalIdsToVulnerabilityReads.
description: Backfill project.archived and project.namespace.traversal_ids values
to the denormalized columns of the same name on vulnerability_reads. No-oped and
requeued in job RequeueBackfillArchivedAndTraversalIdsToVulnerabilityReads.
feature_category: vulnerability_management
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/144765
milestone: '16.10'
queued_migration_version: 20240214163238
finalize_after: '2024-03-15'
finalized_by:
finalized_by: '20240513231841'

View File

@ -8,3 +8,4 @@ description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/56344
milestone: '13.11'
gitlab_schema: gitlab_main
sharding_key_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/442659

View File

@ -8,3 +8,4 @@ description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55681
milestone: '13.12'
gitlab_schema: gitlab_main
sharding_key_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/442660

View File

@ -8,3 +8,4 @@ description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/48334
milestone: '13.8'
gitlab_schema: gitlab_main
sharding_key_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/442661

View File

@ -8,3 +8,4 @@ description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/34069
milestone: '13.2'
gitlab_schema: gitlab_main
sharding_key_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/442662

View File

@ -9,3 +9,4 @@ description: Represents an Advanced Search index
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/113612/
milestone: '15.11'
gitlab_schema: gitlab_main
sharding_key_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/442663

View File

@ -9,3 +9,4 @@ description: Describes a Zoekt server that will be used for indexing and search
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105049
milestone: '15.9'
gitlab_schema: gitlab_main
sharding_key_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/442657

View File

@ -0,0 +1,9 @@
# frozen_string_literal: true
class AddProjectIdToApprovalMergeRequestRules < Gitlab::Database::Migration[2.2]
milestone '17.0'
def change
add_column :approval_merge_request_rules, :project_id, :bigint
end
end

View File

@ -0,0 +1,16 @@
# frozen_string_literal: true
class IndexApprovalMergeRequestRulesOnProjectId < Gitlab::Database::Migration[2.2]
milestone '17.0'
disable_ddl_transaction!
INDEX_NAME = 'index_approval_merge_request_rules_on_project_id'
def up
add_concurrent_index :approval_merge_request_rules, :project_id, name: INDEX_NAME
end
def down
remove_concurrent_index_by_name :approval_merge_request_rules, INDEX_NAME
end
end

View File

@ -0,0 +1,16 @@
# frozen_string_literal: true
class AddApprovalMergeRequestRulesProjectIdFk < Gitlab::Database::Migration[2.2]
milestone '17.0'
disable_ddl_transaction!
def up
add_concurrent_foreign_key :approval_merge_request_rules, :projects, column: :project_id, on_delete: :cascade
end
def down
with_lock_retries do
remove_foreign_key :approval_merge_request_rules, column: :project_id
end
end
end

View File

@ -0,0 +1,25 @@
# frozen_string_literal: true
class AddApprovalMergeRequestRulesProjectIdTrigger < Gitlab::Database::Migration[2.2]
milestone '17.0'
def up
install_sharding_key_assignment_trigger(
table: :approval_merge_request_rules,
sharding_key: :project_id,
parent_table: :merge_requests,
parent_sharding_key: :target_project_id,
foreign_key: :merge_request_id
)
end
def down
remove_sharding_key_assignment_trigger(
table: :approval_merge_request_rules,
sharding_key: :project_id,
parent_table: :merge_requests,
parent_sharding_key: :target_project_id,
foreign_key: :merge_request_id
)
end
end

View File

@ -0,0 +1,40 @@
# frozen_string_literal: true
class QueueBackfillApprovalMergeRequestRulesProjectId < Gitlab::Database::Migration[2.2]
milestone '17.0'
restrict_gitlab_migration gitlab_schema: :gitlab_main_cell
MIGRATION = "BackfillApprovalMergeRequestRulesProjectId"
DELAY_INTERVAL = 2.minutes
BATCH_SIZE = 1000
SUB_BATCH_SIZE = 100
def up
queue_batched_background_migration(
MIGRATION,
:approval_merge_request_rules,
:id,
:project_id,
:merge_requests,
:target_project_id,
:merge_request_id,
job_interval: DELAY_INTERVAL,
batch_size: BATCH_SIZE,
sub_batch_size: SUB_BATCH_SIZE
)
end
def down
delete_batched_background_migration(
MIGRATION,
:approval_merge_request_rules,
:id,
[
:project_id,
:merge_requests,
:target_project_id,
:merge_request_id
]
)
end
end

View File

@ -0,0 +1,21 @@
# frozen_string_literal: true
class FinalizeBackfillArchivedAndTraversalIdsToVulnerabilityReads < Gitlab::Database::Migration[2.2]
milestone '17.0'
disable_ddl_transaction!
restrict_gitlab_migration gitlab_schema: :gitlab_main
def up
ensure_batched_background_migration_is_finished(
job_class_name: 'BackfillArchivedAndTraversalIdsToVulnerabilityReads',
table_name: :vulnerability_reads,
column_name: :id,
job_arguments: [],
finalize: true
)
end
def down; end
end

View File

@ -0,0 +1 @@
dfce55f9bc75cc5c2781a4facac3638979345cdf79132b7789fe812bbd75c2ab

View File

@ -0,0 +1 @@
d59f45b47fad1f968c4406afcf5a411339509ebb3c824c3d494e95d2b9f0ed28

View File

@ -0,0 +1 @@
a8ab824118902e1d4cfbd02c551ad67d541126d89c28cb6b06728483d4e9851a

View File

@ -0,0 +1 @@
a4f37df2585fb1234dbeb478bb2c59478b5c7b13749f84826d262f806f47c0db

View File

@ -0,0 +1 @@
baa0cadb88abee532373ca3800d135645bf2e8a42558f9f7ddb17a79024eab60

View File

@ -0,0 +1 @@
7ff0d546bcdd55adad6153210404eeb6e4d4db644b146570efc820344ae13883

View File

@ -704,6 +704,22 @@ $$;
COMMENT ON FUNCTION table_sync_function_3f39f64fc3() IS 'Partitioning migration: table sync for merge_request_diff_files table';
CREATE FUNCTION trigger_01b3fc052119() RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
IF NEW."project_id" IS NULL THEN
SELECT "target_project_id"
INTO NEW."project_id"
FROM "merge_requests"
WHERE "merge_requests"."id" = NEW."merge_request_id";
END IF;
RETURN NEW;
END
$$;
CREATE FUNCTION trigger_10ee1357e825() RETURNS trigger
LANGUAGE plpgsql
AS $$
@ -4753,6 +4769,7 @@ CREATE TABLE approval_merge_request_rules (
security_orchestration_policy_configuration_id bigint,
scan_result_policy_id bigint,
applicable_post_merge boolean,
project_id bigint,
CONSTRAINT check_6fca5928b2 CHECK ((char_length(section) <= 255))
);
@ -24497,6 +24514,8 @@ CREATE UNIQUE INDEX index_approval_merge_request_rules_groups_1 ON approval_merg
CREATE INDEX index_approval_merge_request_rules_groups_2 ON approval_merge_request_rules_groups USING btree (group_id);
CREATE INDEX index_approval_merge_request_rules_on_project_id ON approval_merge_request_rules USING btree (project_id);
CREATE UNIQUE INDEX index_approval_merge_request_rules_users_1 ON approval_merge_request_rules_users USING btree (approval_merge_request_rule_id, user_id);
CREATE INDEX index_approval_merge_request_rules_users_2 ON approval_merge_request_rules_users USING btree (user_id);
@ -29855,6 +29874,8 @@ CREATE TRIGGER table_sync_trigger_cd362c20e2 AFTER INSERT OR DELETE OR UPDATE ON
CREATE TRIGGER tags_loose_fk_trigger AFTER DELETE ON tags REFERENCING OLD TABLE AS old_table FOR EACH STATEMENT EXECUTE FUNCTION insert_into_loose_foreign_keys_deleted_records();
CREATE TRIGGER trigger_01b3fc052119 BEFORE INSERT OR UPDATE ON approval_merge_request_rules FOR EACH ROW EXECUTE FUNCTION trigger_01b3fc052119();
CREATE TRIGGER trigger_10ee1357e825 BEFORE INSERT OR UPDATE ON p_ci_builds FOR EACH ROW EXECUTE FUNCTION trigger_10ee1357e825();
CREATE TRIGGER trigger_174b23fa3dfb BEFORE INSERT OR UPDATE ON approval_project_rules_users FOR EACH ROW EXECUTE FUNCTION trigger_174b23fa3dfb();
@ -30985,6 +31006,9 @@ ALTER TABLE p_ci_builds_metadata
ALTER TABLE ONLY gitlab_subscriptions
ADD CONSTRAINT fk_e2595d00a1 FOREIGN KEY (namespace_id) REFERENCES namespaces(id) ON DELETE CASCADE;
ALTER TABLE ONLY approval_merge_request_rules
ADD CONSTRAINT fk_e33a9aaf67 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
ALTER TABLE ONLY abuse_events
ADD CONSTRAINT fk_e5ce49c215 FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL;

View File

@ -265,8 +265,9 @@ The default expiration and the expiration on GitLab.com is 15 minutes.
```ruby
gitlab_workhorse['env'] = {
"http_proxy" => "http://USERNAME:PASSWORD@example.com:8080",
"https_proxy" => "http://USERNAME:PASSWORD@example.com:8080"
"http_proxy" => "http://USERNAME:PASSWORD@example.com:8080",
"https_proxy" => "http://USERNAME:PASSWORD@example.com:8080"
}
```
1. Save the file and [reconfigure GitLab](../restart_gitlab.md#reconfigure-a-linux-package-installation) for the changes to take effect.

View File

@ -9,9 +9,9 @@ info: To determine the technical writer assigned to the Stage/Group associated w
DETAILS:
**Tier:** Free, Premium, Ultimate
**Offering:** GitLab.com, Self-managed, GitLab Dedicated
**Status:** Beta
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/391331) in GitLab 15.11 as a Beta feature.
> - [Made Generally Available](https://gitlab.com/gitlab-com/www-gitlab-com/-/merge_requests/134062) in GitLab 17.0.
Use inputs to increase the flexibility of CI/CD configuration files that are designed
to be reused.

View File

@ -37,13 +37,6 @@ You can create, edit, or delete a compliance framework from a compliance project
- [Edit a compliance framework](../../user/compliance/compliance_center/compliance_projects_report.md#edit-a-compliance-framework).
- [Delete a compliance framework](../../user/compliance/compliance_center/compliance_projects_report.md#delete-a-compliance-framework).
### From group settings
1. On the left sidebar, select **Search or go to** and find your group.
1. Select **Settings** > **General**.
1. Expand the **Compliance frameworks** section.
1. Create, edit, or delete compliance frameworks.
Subgroups and projects have access to all compliance frameworks created on their top-level group. However, compliance frameworks cannot be created, edited,
or deleted at the subgroup or project level. Project owners can choose a framework to apply to their projects.
@ -61,16 +54,6 @@ Frameworks cannot be added to projects in personal namespaces.
To assign a compliance framework to a project, apply the compliance framework through the
[Compliance projects report](../../user/compliance/compliance_center/compliance_projects_report.md#apply-a-compliance-framework-to-projects-in-a-group).
### From group settings
To assign a compliance framework to a project:
1. On the left sidebar, select **Search or go to** and find your project.
1. Select **Settings** > **General**.
1. Expand **Compliance frameworks**.
1. Select a compliance framework.
1. Select **Save changes**.
### GraphQL API
You can use the [GraphQL API](../../api/graphql/reference/index.md#mutationprojectsetcomplianceframework) to add a
@ -115,18 +98,6 @@ To set as default (or remove the default) from [compliance framework report](../
1. Select **Set as default**.
1. Select **Save changes**.
#### From group settings
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/375038) in GitLab 15.7.
To set as default (or remove the default) by using group settings:
1. On the left sidebar, select **Search or go to** and find your group.
1. Select **Settings > General**.
1. Expand the **Compliance frameworks** section and locate the compliance framework to set (or remove) as default.
1. Select the vertical ellipsis (**{ellipsis_v}**) for the compliance frame and then select **Set default** (or
**Remove default**).
#### Example GraphQL mutations for setting a default compliance framework
Creating a new compliance framework and setting it as the default framework for the group.
@ -178,13 +149,3 @@ Prerequisites:
To remove a compliance framework from one or multiple project in a group, remove the compliance framework through the
[Compliance projects report](../../user/compliance/compliance_center/compliance_projects_report.md#remove-a-compliance-framework-from-projects-in-a-group).
### From group settings
To remove a compliance framework from one project in a group:
1. On the left sidebar, select **Search or go to** and find your project.
1. Select **Settings** > **General**.
1. Expand **Compliance frameworks**.
1. Select **None**.
1. Select **Save changes**.

View File

@ -21,6 +21,10 @@ You can migrate GitLab groups:
- From one self-managed GitLab instance to another.
- Between groups in the same GitLab instance.
Migration by direct transfer creates a new copy of the group. If you want to move groups instead of copying groups, you
can [transfer groups](../manage.md#transfer-a-group) if the groups are in the same GitLab instance. Transferring groups
instead of migrating them is a faster and more complete option.
You can migrate groups in two ways:
- By direct transfer (recommended).
@ -55,9 +59,6 @@ ready for production use.
We invite you to leave your feedback about migrating by direct transfer in
[the feedback issue](https://gitlab.com/gitlab-org/gitlab/-/issues/284495).
If you want to move groups instead of copying groups, you can [transfer groups](../manage.md#transfer-a-group) if the
groups are in the same GitLab instance. Transferring groups is a faster and more complete option.
## Known issues
- Because of [issue 406685](https://gitlab.com/gitlab-org/gitlab/-/issues/406685), files with a filename longer than 255 characters are not migrated.

View File

@ -99,9 +99,14 @@ module API
end
post ":id/hooks" do
hook_params = create_hook_params
hook = user_project.hooks.new(hook_params)
save_hook(hook, Entities::ProjectHook)
result = WebHooks::CreateService.new(current_user).execute(hook_params, hook_scope)
if result[:status] == :success
present result[:hook], with: Entities::ProjectHook
else
error!(result.message, result.http_status || 422)
end
end
desc 'Edit project hook' do

View File

@ -82,9 +82,14 @@ module API
end
post do
hook_params = create_hook_params
hook = SystemHook.new(hook_params)
save_hook(hook, Entities::Hook)
result = WebHooks::CreateService.new(current_user).execute(hook_params, hook_scope)
if result[:status] == :success
present result[:hook], with: Entities::Hook
else
error!(result.message, result.http_status || 422)
end
end
desc 'Edit system hook' do

View File

@ -0,0 +1,12 @@
# frozen_string_literal: true
module Gitlab
module BackgroundMigration
# rubocop: disable Migration/BackgroundMigrationBaseClass -- BackfillDesiredShardingKeyJob inherits from BatchedMigrationJob.
class BackfillApprovalMergeRequestRulesProjectId < BackfillDesiredShardingKeyJob
operation_name :backfill_approval_merge_request_rules_project_id
feature_category :code_review_workflow
end
# rubocop: enable Migration/BackgroundMigrationBaseClass
end
end

View File

@ -69,6 +69,7 @@ module Gitlab
GEO_NODES_LOAD = 'SELECT 1 AS one FROM "geo_nodes" LIMIT 1'
LICENSES_LOAD = 'SELECT "licenses".* FROM "licenses" ORDER BY "licenses"."id"'
SCHEMA_INTROSPECTION = %r{SELECT.*(FROM|JOIN) (pg_attribute|pg_class)}m
SAVEPOINT = %r{(RELEASE )?SAVEPOINT}m
# queries can be safely ignored if they are amoritized in regular usage
# (i.e. only requested occasionally and otherwise cached).
@ -76,6 +77,7 @@ module Gitlab
return true if sql&.include?(GEO_NODES_LOAD)
return true if sql&.include?(LICENSES_LOAD)
return true if SCHEMA_INTROSPECTION.match?(sql)
return true if SAVEPOINT.match?(sql)
false
end

View File

@ -37,7 +37,7 @@ describe('Workload table component', () => {
key: 'name',
label: 'Name',
sortable: true,
tdClass: 'gl-md-w-half gl-lg-w-40p gl-word-break-word',
tdClass: 'gl-md-w-half gl-lg-w-40p gl-break-anywhere',
},
{
key: 'status',
@ -49,7 +49,7 @@ describe('Workload table component', () => {
key: 'namespace',
label: 'Namespace',
sortable: true,
tdClass: 'gl-md-w-30p gl-lg-w-40p gl-word-break-word',
tdClass: 'gl-md-w-30p gl-lg-w-40p gl-break-anywhere',
},
{
key: 'age',

View File

@ -0,0 +1,15 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::BackgroundMigration::BackfillApprovalMergeRequestRulesProjectId,
feature_category: :code_review_workflow,
schema: 20240501044347 do
include_examples 'desired sharding key backfill job' do
let(:batch_table) { :approval_merge_request_rules }
let(:backfill_column) { :project_id }
let(:backfill_via_table) { :merge_requests }
let(:backfill_via_column) { :target_project_id }
let(:backfill_via_foreign_key) { :merge_request_id }
end
end

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe Gitlab::QueryLimiting::Transaction do
RSpec.describe Gitlab::QueryLimiting::Transaction, feature_category: :database do
after do
Thread.current[described_class::THREAD_KEY] = nil
end
@ -87,6 +87,8 @@ RSpec.describe Gitlab::QueryLimiting::Transaction do
transaction.increment(described_class::LICENSES_LOAD)
transaction.increment('SELECT a.attname, a.other_column FROM pg_attribute a')
transaction.increment('SELECT x.foo, a.attname FROM some_table x JOIN pg_attribute a')
transaction.increment('SAVEPOINT active_record_2')
transaction.increment('RELEASE SAVEPOINT active_record_2')
transaction.increment(<<-SQL)
SELECT a.attname, a.other_column
FROM pg_attribute a

View File

@ -0,0 +1,33 @@
# frozen_string_literal: true
require 'spec_helper'
require_migration!
RSpec.describe QueueBackfillApprovalMergeRequestRulesProjectId, feature_category: :code_review_workflow do
let!(:batched_migration) { described_class::MIGRATION }
it 'schedules a new batched migration' do
reversible_migration do |migration|
migration.before -> {
expect(batched_migration).not_to have_scheduled_batched_migration
}
migration.after -> {
expect(batched_migration).to have_scheduled_batched_migration(
table_name: :approval_merge_request_rules,
column_name: :id,
interval: described_class::DELAY_INTERVAL,
batch_size: described_class::BATCH_SIZE,
sub_batch_size: described_class::SUB_BATCH_SIZE,
gitlab_schema: :gitlab_main_cell,
job_arguments: [
:project_id,
:merge_requests,
:target_project_id,
:merge_request_id
]
)
}
end
end
end

View File

@ -0,0 +1,62 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe WebHooks::CreateService, feature_category: :webhooks do
let_it_be(:current_user) { create(:user) }
describe '#execute' do
let_it_be(:project) { create(:project) }
let_it_be(:relation) { ProjectHook.none }
let(:hook_params) { { url: 'https://example.com/hook', project_id: project.id } }
subject(:webhook_created) { described_class.new(current_user) }
context 'when creating a new hook' do
it 'creates a new hook' do
expect do
response = webhook_created.execute(hook_params, relation)
expect(response).to be_success
expect(response[:async]).to eq(false)
end.to change { ProjectHook.count }.by(1)
end
end
context 'when the URL is invalid' do
it 'returns an error response' do
hook_params[:url] = 'invalid_url'
response = webhook_created.execute(hook_params, relation)
expect(response).not_to be_success
expect(response[:message]).to eq("Invalid url given")
expect(response[:http_status]).to eq(422)
end
end
context 'when the branch filter is invalid' do
let(:invalid_params) { hook_params.merge(push_events_branch_filter: 'bad branch name') }
it 'returns an error response' do
response = webhook_created.execute(invalid_params, relation)
expect(response).not_to be_success
expect(response[:message]).to eq("Invalid branch filter given")
expect(response[:http_status]).to eq(422)
end
end
context 'when the project is not provided' do
let(:invalid_params) { hook_params.merge(project_id: nil) }
it 'returns an error response for missing project' do
response = webhook_created.execute(invalid_params, relation)
expect(response).not_to be_success
expect(response[:message]).to eq("Project can't be blank")
expect(response[:http_status]).to eq(422)
end
end
end
end