Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2023-12-04 15:12:07 +00:00
parent 1570618396
commit a155ff5671
84 changed files with 916 additions and 349 deletions

View File

@ -1,4 +0,0 @@
---
# Grace period will be removed in https://gitlab.com/gitlab-org/gitlab/-/issues/428931
BackgroundMigration/DictionaryFile:
Details: grace period

View File

@ -160,7 +160,7 @@ export default {
labelSearchField() {
return this.isEmailSignupEnabled
? this.$options.labels.searchField
: s__('InviteMembersModal|Username');
: s__('InviteMembersModal|Username or name');
},
isEmptyInvites() {
return Boolean(this.newUsersToInvite.length);

View File

@ -183,6 +183,12 @@ export default {
this.$emit('clear');
},
handleTab(event) {
if (this.originalInput.length > 0) {
event.preventDefault();
this.$refs.tokenSelector.handleEnter();
}
},
hasError(token) {
return Object.keys(this.invalidMembers).includes(memberName(token));
},
@ -196,6 +202,7 @@ export default {
<template>
<gl-token-selector
ref="tokenSelector"
v-model="selectedTokens"
:state="exceptionState"
:dropdown-items="users"
@ -210,6 +217,7 @@ export default {
@input="handleInput"
@focus="handleFocus"
@token-remove="handleTokenRemove"
@keydown.tab="handleTab"
>
<template #token-content="{ token }">
<gl-icon

View File

@ -39,7 +39,7 @@ export const MEMBERS_TO_PROJECT_DEFAULT_INTRO_TEXT = s__(
export const MEMBERS_TO_PROJECT_CELEBRATE_INTRO_TEXT = s__(
"InviteMembersModal|Congratulations on creating your project, you're almost there!",
);
export const MEMBERS_SEARCH_FIELD = s__('InviteMembersModal|Username or email address');
export const MEMBERS_SEARCH_FIELD = s__('InviteMembersModal|Username, name or email address');
export const MEMBERS_PLACEHOLDER = s__('InviteMembersModal|Select members or type email addresses');
export const GROUP_MODAL_DEFAULT_TITLE = s__('InviteMembersModal|Invite a group');

View File

@ -184,10 +184,6 @@ $body.on('ajax:complete, ajax:beforeSend, submit', 'form', function ajaxComplete
}
});
$('.navbar-toggler').on('click', () => {
document.body.classList.toggle('top-nav-responsive-open');
});
/**
* Show suppressed commit diff
*

View File

@ -104,25 +104,6 @@
}
}
.navbar-toggler {
position: relative;
right: -10px;
border-radius: 0;
min-width: 45px;
padding: 0;
margin: $gl-padding-8 $gl-padding-8 $gl-padding-8 0;
font-size: 14px;
text-align: center;
color: currentColor;
&:hover,
&:focus,
&.active {
color: currentColor;
background-color: transparent;
}
}
.navbar-nav {
@include media-breakpoint-down(xs) {
display: flex;
@ -221,12 +202,6 @@
}
}
.top-nav-toggle,
> button {
background: transparent;
border: 0;
}
&.line-separator {
margin: 8px;
}
@ -519,12 +494,6 @@
}
}
.top-nav-toggle {
.dropdown-chevron {
top: 0;
}
}
.top-nav-menu-item {
&.active,
&:hover {
@ -536,39 +505,6 @@
}
}
.top-nav-responsive {
@include gl-display-none;
}
.top-nav-responsive-open {
.more-icon {
display: none;
}
.close-icon {
display: block;
margin: auto;
}
@include media-breakpoint-down(xs) {
.navbar-collapse {
display: flex;
}
.hide-when-top-nav-responsive-open {
display: none !important;
}
.top-nav-responsive {
@include gl-display-block;
}
.navbar-gitlab .header-content .title-container {
flex: 0;
}
}
}
header.navbar-gitlab.super-sidebar-logged-out {
background-color: $brand-charcoal !important;

View File

@ -0,0 +1,16 @@
# frozen_string_literal: true
module Mutations
module Organizations
class Base < BaseMutation
field :organization,
::Types::Organizations::OrganizationType,
null: true,
description: 'Organization after mutation.'
argument :description, GraphQL::Types::String,
required: false,
description: 'Description of the organization.'
end
end
end

View File

@ -2,16 +2,11 @@
module Mutations
module Organizations
class Create < BaseMutation
class Create < Base
graphql_name 'OrganizationCreate'
authorize :create_organization
field :organization,
::Types::Organizations::OrganizationType,
null: true,
description: 'Organization created.'
argument :name, GraphQL::Types::String,
required: true,
description: 'Name for the organization.'
@ -20,29 +15,16 @@ module Mutations
required: true,
description: 'Path for the organization.'
argument :description, GraphQL::Types::String,
required: false,
description: 'Description of the organization.'
def resolve(args)
authorize!(:global)
result = ::Organizations::CreateService.new(
current_user: current_user,
params: create_params(args)
params: args
).execute
{ organization: result.payload, errors: result.errors }
end
private
def create_params(params)
return params unless params.key?(:description)
params[:organization_detail_attributes] = { description: params.delete(:description) }
params
end
end
end
end

View File

@ -0,0 +1,36 @@
# frozen_string_literal: true
module Mutations
module Organizations
class Update < Base
graphql_name 'OrganizationUpdate'
authorize :admin_organization
argument :id,
Types::GlobalIDType[::Organizations::Organization],
required: true,
description: 'ID of the organization to mutate.'
argument :name, GraphQL::Types::String,
required: false,
description: 'Name for the organization.'
argument :path, GraphQL::Types::String,
required: false,
description: 'Path for the organization.'
def resolve(id:, **args)
organization = authorized_find!(id: id)
result = ::Organizations::UpdateService.new(
organization,
current_user: current_user,
params: args
).execute
{ organization: result.payload, errors: result.errors }
end
end
end
end

View File

@ -107,6 +107,7 @@ module Types
mount_mutation Mutations::Notes::RepositionImageDiffNote
mount_mutation Mutations::Notes::Destroy
mount_mutation Mutations::Organizations::Create, alpha: { milestone: '16.6' }
mount_mutation Mutations::Organizations::Update, alpha: { milestone: '16.7' }
mount_mutation Mutations::Projects::SyncFork, calls_gitaly: true, alpha: { milestone: '15.9' }
mount_mutation Mutations::Projects::Star, alpha: { milestone: '16.7' }
mount_mutation Mutations::Releases::Create

View File

@ -35,7 +35,7 @@ module Ci
return unless resource.present?
return unless resource.published?
return unless current_user.can?(:read_code, resource.project)
return unless Ability.allowed?(current_user, :read_code, resource.project)
resource
end

View File

@ -34,6 +34,8 @@ module Integrations
SNOWPLOW_EVENT_CATEGORY = name
RE2_SYNTAX_DOC_URL = 'https://github.com/google/re2/wiki/Syntax'
validates :url, public_url: true, presence: true, if: :activated?
validates :api_url, public_url: true, allow_blank: true
validates :username, presence: true, if: ->(object) {
@ -110,7 +112,15 @@ module Integrations
section: SECTION_TYPE_CONFIGURATION,
required: false,
title: -> { s_('JiraService|Jira issue regex') },
help: -> { s_('JiraService|Use regular expression to match Jira issue keys.') }
help: -> do
format(ERB::Util.html_escape(
s_("JiraService|Use regular expression to match Jira issue keys. The regular expression must follow the " \
"%{link_start}RE2 syntax%{link_end}. If empty, the default behavior is used.")),
link_start: format('<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe,
url: RE2_SYNTAX_DOC_URL),
link_end: '</a>'.html_safe
)
end
field :jira_issue_prefix,
section: SECTION_TYPE_CONFIGURATION,

View File

@ -18,6 +18,7 @@ module Organizations
end
rule { organization_user }.policy do
enable :admin_organization
enable :read_organization
enable :read_organization_user
end

View File

@ -9,6 +9,11 @@ module Organizations
def initialize(current_user: nil, params: {})
@current_user = current_user
@params = params.dup
return unless @params.key?(:description)
organization_detail_attributes = { description: @params.delete(:description) }
@params[:organization_detail_attributes] ||= {}
@params[:organization_detail_attributes].merge!(organization_detail_attributes)
end
end
end

View File

@ -0,0 +1,46 @@
# frozen_string_literal: true
module Organizations
class UpdateService < ::Organizations::BaseService
attr_reader :organization
def initialize(organization, current_user:, params: {})
@organization = organization
@current_user = current_user
@params = params.dup
return unless @params.key?(:description)
organization_detail_attributes = { description: @params.delete(:description) }
# TODO: Remove explicit passing of id once https://github.com/rails/rails/issues/48714 is resolved.
organization_detail_attributes[:id] = organization.id
@params[:organization_detail_attributes] ||= {}
@params[:organization_detail_attributes].merge!(organization_detail_attributes)
end
def execute
return error_no_permissions unless allowed?
if organization.update(params)
ServiceResponse.success(payload: organization)
else
error_updating
end
end
private
def allowed?
current_user&.can?(:admin_organization, organization)
end
def error_no_permissions
ServiceResponse.error(message: [_('You have insufficient permissions to update the organization')])
end
def error_updating
message = organization.errors.full_messages || _('Failed to update organization')
ServiceResponse.error(payload: organization, message: Array(message))
end
end
end

View File

@ -1,6 +1,6 @@
- if show_super_sidebar?
- @left_sidebar = true
.layout-page.hide-when-top-nav-responsive-open{ class: page_with_sidebar_class }
.layout-page{ class: page_with_sidebar_class }
- if show_super_sidebar?
-# Render the parent group sidebar while creating a new subgroup/project, see GroupsController#new.
- group = @parent_group || @group
@ -50,5 +50,3 @@
= yield :after_content
-# This is needed by [GitLab JH](https://gitlab.com/gitlab-jh/jh-team/gitlab-cn/-/issues/81)
= render_if_exists "shared/footer/global_footer"
= render "layouts/nav/top_nav_responsive", class: 'layout-page' if !show_super_sidebar? || !current_user

View File

@ -4,7 +4,7 @@
%body{ class: "#{user_tab_width} #{@body_class} fullscreen-layout", data: { page: body_data_page } }
= render 'peek/bar'
= header_message
.hide-when-top-nav-responsive-open.gl--flex-full.gl-h-full{ class: nav ? ["layout-page", page_with_sidebar_class, "gl-mt-0!"]: '' }
.gl--flex-full.gl-h-full{ class: nav ? ["layout-page", page_with_sidebar_class, "gl-mt-0!"]: '' }
- if defined?(nav) && nav
= render "layouts/nav/sidebar/#{nav}"
.gl--flex-full.gl-flex-direction-column.gl-w-full

View File

@ -5,19 +5,9 @@
%a.gl-sr-only.gl-accessibility{ href: "#content-body" } Skip to content
.container-fluid
.header-content.js-header-content
.title-container.hide-when-top-nav-responsive-open.gl-transition-medium.gl-display-flex.gl-align-items-stretch.gl-pt-0.gl-mr-3
.title-container.gl-transition-medium.gl-display-flex.gl-align-items-stretch.gl-pt-0.gl-mr-3
= render 'layouts/header/title'
- if current_user
.gl-display-none.gl-sm-display-block
= render "layouts/nav/top_nav"
- else
- if Gitlab.com?
= render 'layouts/header/marketing_links'
- else
.gl-display-none.gl-sm-display-block
= render "layouts/nav/top_nav"
.navbar-collapse.gl-transition-medium.collapse
%ul.nav.navbar-nav.gl-w-full.gl-align-items-center.gl-justify-content-end
- if current_user
@ -89,9 +79,6 @@
= sprite_icon('chevron-down', css_class: 'caret-down')
.dropdown-menu.dropdown-menu-right
= render 'layouts/header/help_dropdown'
- if !current_user && Gitlab.com?
%li.nav-item.gl-display-none.gl-sm-display-block
= render "layouts/nav/top_nav"
- if header_link?(:user_dropdown)
%li.nav-item.header-user.js-nav-user-dropdown.dropdown{ data: { testid: 'user-dropdown' }, class: ('mr-0' if has_impersonation_link) }
= link_to current_user, class: user_dropdown_class, data: { toggle: "dropdown", track_label: "profile_dropdown", track_action: "click_dropdown", track_property: "navigation_top" } do
@ -111,13 +98,6 @@
%li.nav-item{ class: 'gl-flex-grow-0! gl-flex-basis-half!' }
= link_to _('Sign in'), new_session_path(:user, redirect_to_referer: 'yes')
%button.navbar-toggler.d-block.d-sm-none{ type: 'button', class: 'gl-border-none!', data: { testid: 'mobile_navbar_button' } }
%span.sr-only= _('Toggle navigation')
%span.more-icon.gl-px-3.gl-font-sm.gl-font-weight-bold
%span.gl-pr-2= _('Menu')
= sprite_icon('hamburger', size: 16)
= sprite_icon('close', size: 12, css_class: 'close-icon')
- if display_whats_new?
#whats-new-app{ data: { version_digest: whats_new_version_digest } }

View File

@ -1,34 +0,0 @@
%ul.nav.navbar-sub-nav.gl-display-none.gl-lg-display-flex.gl-align-items-center
%li.dropdown.gl-mr-3
%button{ type: "button", data: { toggle: "dropdown" } }
= s_('LoggedOutMarketingHeader|About GitLab')
= sprite_icon('chevron-down', css_class: 'caret-down')
.dropdown-menu
%ul
%li
= link_to Gitlab::Utils.append_path(promo_url, 'stages-devops-lifecycle') do
= s_('LoggedOutMarketingHeader|GitLab: the DevOps platform')
%li
= link_to explore_root_path do
= s_('LoggedOutMarketingHeader|Explore GitLab')
%li
= link_to Gitlab::Utils.append_path(promo_url, 'install') do
= s_('LoggedOutMarketingHeader|Install GitLab')
%li
= link_to Gitlab::Utils.append_path(promo_url, 'is-it-any-good') do
= s_('LoggedOutMarketingHeader|How GitLab compares')
%li
= link_to Gitlab::Utils.append_path(promo_url, 'get-started') do
= s_('LoggedOutMarketingHeader|Get started')
%li
= link_to Gitlab::Saas::doc_url do
= s_('LoggedOutMarketingHeader|GitLab docs')
%li
= link_to Gitlab::Utils.append_path(promo_url, 'learn') do
= s_('LoggedOutMarketingHeader|GitLab Learn')
%li.gl-mr-3
= link_to Gitlab::Utils.append_path(promo_url, 'pricing') do
= s_('LoggedOutMarketingHeader|Pricing')
%li.gl-mr-3
= link_to Gitlab::Utils.append_path(promo_url, 'sales') do
= s_('LoggedOutMarketingHeader|Talk to an expert')

View File

@ -1,12 +0,0 @@
- view_model = top_nav_view_model(project: @project, group: @group)
%ul.list-unstyled.nav.navbar-sub-nav#js-top-nav{ data: { view_model: view_model.to_json } }
%li
%a.top-nav-toggle{ href: '#', type: 'button', data: { toggle: "dropdown" } }
= sprite_icon('hamburger')
- if view_model[:menuTitle]
.gl-ml-3= view_model[:menuTitle]
.hidden
- view_model[:shortcuts].each do |shortcut|
= link_to shortcut[:href], class: shortcut[:css_class] do
= shortcut[:title]

View File

@ -1,6 +0,0 @@
- top_class = local_assigns.fetch(:class, nil)
- view_model = top_nav_responsive_view_model(project: @project, group: @group)
.top-nav-responsive{ class: top_class }
.cloak-startup
#js-top-nav-responsive{ data: { view_model: view_model.to_json } }

View File

@ -181,6 +181,7 @@ options:
- p_ci_templates_terraform_module_base
- p_ci_templates_terraform_module
- p_ci_templates_pages_zola
- p_ci_templates_diffblue_cover
distribution:
- ce
- ee

View File

@ -0,0 +1,24 @@
---
key_path: redis_hll_counters.ci_templates.p_ci_templates_diffblue_cover_monthly
description: Count of pipelines using the Diffblue Cover template
product_section: ci
product_stage: pipeline_authoring
product_group: pipeline_authoring
value_type: number
status: active
milestone: "16.7"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/137047
time_frame: 28d
data_source: redis_hll
data_category: optional
instrumentation_class: RedisHLLMetric
distribution:
- ce
- ee
tier:
- free
- premium
- ultimate
options:
events:
- p_ci_templates_diffblue_cover

View File

@ -182,6 +182,7 @@ options:
- p_ci_templates_terraform_module_base
- p_ci_templates_terraform_module
- p_ci_templates_pages_zola
- p_ci_templates_diffblue_cover
distribution:
- ce
- ee

View File

@ -0,0 +1,24 @@
---
key_path: redis_hll_counters.ci_templates.p_ci_templates_diffblue_cover_weekly
description: Count of pipelines using the Diffblue Cover template
product_section: ci
product_stage: pipeline_authoring
product_group: pipeline_authoring
value_type: number
status: active
milestone: "16.7"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/137047
time_frame: 7d
data_source: redis_hll
data_category: optional
instrumentation_class: RedisHLLMetric
distribution:
- ce
- ee
tier:
- free
- premium
- ultimate
options:
events:
- p_ci_templates_diffblue_cover

View File

@ -3,6 +3,7 @@ migration_job_name: BackfillDismissalReasonInVulnerabilityReads
description: Backfill `dismissal_reason` for rows with `state` of `dismissed` in `vulnerability_reads`
table
feature_category: vulnerability_management
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/issues/412667
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/123518
queued_migration_version: 20230612232000
milestone: 16.1
finalized_by: '20231201144826'

View File

@ -2,5 +2,6 @@
migration_job_name: BackfillMissingCiCdSettings
description: Backfills ci_cd_settings for projects that do not have them
feature_category: source_code_management
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/issues/393502
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/124921
queued_migration_version: 20230628023103
milestone: 16.2

View File

@ -2,5 +2,6 @@
migration_job_name: CreateComplianceStandardsAdherence
description: This migration creates 'project_compliance_standards_adherence' table for existing projects
feature_category: compliance_management
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/issues/413235
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/129941
queued_migration_version: 20230818142801
milestone: 16.4

View File

@ -2,6 +2,6 @@
migration_job_name: DeleteInvalidProtectedBranchMergeAccessLevels
description: Remove rows from protected_branch_merge_access_levels for groups that do not have project_group_links to the project for the associated protected branch
feature_category: source_code_management
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/issues/427486
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/134337
milestone: 16.6
queued_migration_version: 20231016173129

View File

@ -2,6 +2,6 @@
migration_job_name: DeleteInvalidProtectedBranchPushAccessLevels
description: Remove rows from protected_branch_push_access_levels for groups that do not have project_group_links to the project for the associated protected branch
feature_category: source_code_management
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/issues/427486
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/134337
milestone: 16.6
queued_migration_version: 20231016194927

View File

@ -2,6 +2,6 @@
migration_job_name: DeleteInvalidProtectedTagCreateAccessLevels
description: Remove rows from protected_tag_create_access_levels for groups that do not have project_group_links to the project for the associated protected tag
feature_category: source_code_management
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/issues/427486
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/134337
milestone: 16.6
queued_migration_version: 20231016194943

View File

@ -4,5 +4,6 @@ Odescription: |
Deletes orphaned scan finding and license scanning approval rules
that could have been created with project import.
feature_category: security_policy_management
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/issues/415925
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/127212
queued_migration_version: 20230721095222
milestone: 16.5

View File

@ -4,5 +4,6 @@ Odescription: |
Deletes orphaned scan finding and license scanning approval rules
that could have been created with project import.
feature_category: security_policy_management
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/issues/415925
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/127212
queued_migration_version: 20230721095222
milestone: 16.5

View File

@ -2,5 +2,6 @@
migration_job_name: PopulateVulnerabilityDismissalFields
description: This populates missing dismissal info for vulnerabilities.
feature_category: vulnerability_management
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/issues/405032
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/117478
queued_migration_version: 20230412185837
milestone: 15.11

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true
# rubocop: disable BackgroundMigration/DictionaryFile
# rubocop:disable BackgroundMigration/DictionaryFile -- MigrateRemediationsForVulnerabilityFindings is rescheduled
class RescheduleMigrationForRemediation < Gitlab::Database::Migration[2.1]
MIGRATION = 'MigrateRemediationsForVulnerabilityFindings'
@ -29,4 +29,4 @@ class RescheduleMigrationForRemediation < Gitlab::Database::Migration[2.1]
delete_batched_background_migration(MIGRATION, :vulnerability_occurrences, :id, [])
end
end
# rubocop: enable BackgroundMigration/DictionaryFile
# rubocop:enable BackgroundMigration/DictionaryFile

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true
# rubocop:disable BackgroundMigration/DictionaryFile
# rubocop:disable BackgroundMigration/DictionaryFile -- BackfillProjectWikiRepositories is rescheduled
class RequeueBackfillProjectWikiRepositories < Gitlab::Database::Migration[2.1]
MIGRATION = "BackfillProjectWikiRepositories"
DELAY_INTERVAL = 2.minutes

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true
# rubocop:disable BackgroundMigration/DictionaryFile
# rubocop:disable BackgroundMigration/DictionaryFile -- MigrateEvidencesForVulnerabilityFindings is rescheduled
class RescheduleEvidencesHandlingUnicode < Gitlab::Database::Migration[2.1]
restrict_gitlab_migration gitlab_schema: :gitlab_main

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true
# rubocop: disable BackgroundMigration/DictionaryFile
# rubocop:disable BackgroundMigration/DictionaryFile -- MigrateLinksForVulnerabilityFindings is rescheduled
class RescheduleMigrationForLinksFromMetadata < Gitlab::Database::Migration[2.1]
MIGRATION = 'MigrateLinksForVulnerabilityFindings'
@ -29,4 +29,4 @@ class RescheduleMigrationForLinksFromMetadata < Gitlab::Database::Migration[2.1]
delete_batched_background_migration(MIGRATION, :vulnerability_occurrences, :id, [])
end
end
# rubocop: enable BackgroundMigration/DictionaryFile
# rubocop:enable BackgroundMigration/DictionaryFile

View File

@ -0,0 +1,45 @@
# frozen_string_literal: true
class RemoveRequirementsIgnoredColumns < Gitlab::Database::Migration[2.2]
milestone '16.7'
disable_ddl_transaction!
CONSTRAINT_NAME = 'check_785ae25b9d'
NAME_INDEX = 'index_requirements_on_title_trigram'
FOREIGN_KEY = 'fk_rails_33fed8aa4e'
def up
remove_column(:requirements, :created_at, if_exists: true)
remove_column(:requirements, :updated_at, if_exists: true)
remove_column(:requirements, :author_id, if_exists: true)
remove_column(:requirements, :cached_markdown_version, if_exists: true)
remove_column(:requirements, :state, if_exists: true)
remove_column(:requirements, :title, if_exists: true)
remove_column(:requirements, :title_html, if_exists: true)
remove_column(:requirements, :description, if_exists: true)
remove_column(:requirements, :description_html, if_exists: true)
end
def down
add_column(:requirements, :created_at, :datetime_with_timezone, if_not_exists: true)
add_column(:requirements, :updated_at, :datetime_with_timezone, if_not_exists: true)
add_column(:requirements, :author_id, :integer, if_not_exists: true)
add_column(:requirements, :cached_markdown_version, :integer, if_not_exists: true)
add_column(:requirements, :state, :smallint, default: 1, if_not_exists: true)
add_column(:requirements, :title, :string, limit: 255, if_not_exists: true)
add_column(:requirements, :title_html, :text, if_not_exists: true)
add_column(:requirements, :description, :text, if_not_exists: true)
add_column(:requirements, :description_html, :text, if_not_exists: true)
add_check_constraint(:requirements, "char_length(description) <= 10000", CONSTRAINT_NAME)
add_concurrent_foreign_key(:requirements, :users, column: :author_id, name: FOREIGN_KEY, on_delete: :nullify)
add_concurrent_index(:requirements, :created_at)
add_concurrent_index(:requirements, :updated_at)
add_concurrent_index(:requirements, :author_id)
add_concurrent_index(:requirements, :state)
add_concurrent_index(:requirements, :title, name: NAME_INDEX, using: :gin, opclass: { name: :gin_trgm_ops })
end
end

View File

@ -0,0 +1 @@
b0159c5ee766dff5d814311ec671909bbf14583f34d5a276b3e704940c7e2223

View File

@ -22784,19 +22784,9 @@ ALTER SEQUENCE required_code_owners_sections_id_seq OWNED BY required_code_owner
CREATE TABLE requirements (
id bigint NOT NULL,
created_at timestamp with time zone,
updated_at timestamp with time zone,
project_id integer NOT NULL,
author_id integer,
iid integer NOT NULL,
cached_markdown_version integer,
state smallint DEFAULT 1,
title character varying(255),
title_html text,
description text,
description_html text,
issue_id bigint,
CONSTRAINT check_785ae25b9d CHECK ((char_length(description) <= 10000)),
CONSTRAINT check_requirement_issue_not_null CHECK ((issue_id IS NOT NULL))
);
@ -34334,22 +34324,12 @@ CREATE INDEX index_requirements_management_test_reports_on_build_id ON requireme
CREATE INDEX index_requirements_management_test_reports_on_issue_id ON requirements_management_test_reports USING btree (issue_id);
CREATE INDEX index_requirements_on_author_id ON requirements USING btree (author_id);
CREATE INDEX index_requirements_on_created_at ON requirements USING btree (created_at);
CREATE UNIQUE INDEX index_requirements_on_issue_id ON requirements USING btree (issue_id);
CREATE INDEX index_requirements_on_project_id ON requirements USING btree (project_id);
CREATE UNIQUE INDEX index_requirements_on_project_id_and_iid ON requirements USING btree (project_id, iid) WHERE (project_id IS NOT NULL);
CREATE INDEX index_requirements_on_state ON requirements USING btree (state);
CREATE INDEX index_requirements_on_title_trigram ON requirements USING gin (title gin_trgm_ops);
CREATE INDEX index_requirements_on_updated_at ON requirements USING btree (updated_at);
CREATE INDEX index_requirements_project_id_user_id_id_and_target_type ON todos USING btree (project_id, user_id, id, target_type);
CREATE INDEX index_requirements_user_id_and_target_type ON todos USING btree (user_id, target_type);
@ -38637,9 +38617,6 @@ ALTER TABLE ONLY alert_management_alert_metric_images
ALTER TABLE ONLY suggestions
ADD CONSTRAINT fk_rails_33b03a535c FOREIGN KEY (note_id) REFERENCES notes(id) ON DELETE CASCADE;
ALTER TABLE ONLY requirements
ADD CONSTRAINT fk_rails_33fed8aa4e FOREIGN KEY (author_id) REFERENCES users(id) ON DELETE SET NULL;
ALTER TABLE ONLY metrics_dashboard_annotations
ADD CONSTRAINT fk_rails_345ab51043 FOREIGN KEY (cluster_id) REFERENCES clusters(id) ON DELETE CASCADE;

View File

@ -74,6 +74,7 @@ In the following table, you can see:
| [Сross-project pipelines with artifacts dependencies](../../ci/yaml/index.md#needsproject) | GitLab 16.7 and later |
| [Feature flag related issues](../../operations/feature_flags.md#feature-flag-related-issues) | GitLab 16.7 and later |
| [Merged results pipelines](../../ci/pipelines/merged_results_pipelines.md) | GitLab 16.7 and later |
| [CI/CD for external repositories](../../ci/ci_cd_for_external_repos/index.md) | GitLab 16.7 and later |
| [CI/CD for GitHub](../../ci/ci_cd_for_external_repos/github_integration.md) | GitLab 16.7 and later |
### Enable registration features

View File

@ -120,6 +120,16 @@ This field returns a [connection](#connections). It accepts the
four standard [pagination arguments](#connection-pagination-arguments):
`before: String`, `after: String`, `first: Int`, `last: Int`.
### `Query.auditEventsInstanceAmazonS3Configurations`
Instance-level Amazon S3 configurations for audit events.
Returns [`InstanceAmazonS3ConfigurationTypeConnection`](#instanceamazons3configurationtypeconnection).
This field returns a [connection](#connections). It accepts the
four standard [pagination arguments](#connection-pagination-arguments):
`before: String`, `after: String`, `first: Int`, `last: Int`.
### `Query.boardList`
Find an issue board list.
@ -5806,7 +5816,33 @@ Input type: `OrganizationCreateInput`
| ---- | ---- | ----------- |
| <a id="mutationorganizationcreateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationorganizationcreateerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
| <a id="mutationorganizationcreateorganization"></a>`organization` | [`Organization`](#organization) | Organization created. |
| <a id="mutationorganizationcreateorganization"></a>`organization` | [`Organization`](#organization) | Organization after mutation. |
### `Mutation.organizationUpdate`
WARNING:
**Introduced** in 16.7.
This feature is an Experiment. It can be changed or removed at any time.
Input type: `OrganizationUpdateInput`
#### Arguments
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationorganizationupdateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationorganizationupdatedescription"></a>`description` | [`String`](#string) | Description of the organization. |
| <a id="mutationorganizationupdateid"></a>`id` | [`OrganizationsOrganizationID!`](#organizationsorganizationid) | ID of the organization to mutate. |
| <a id="mutationorganizationupdatename"></a>`name` | [`String`](#string) | Name for the organization. |
| <a id="mutationorganizationupdatepath"></a>`path` | [`String`](#string) | Path for the organization. |
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationorganizationupdateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationorganizationupdateerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
| <a id="mutationorganizationupdateorganization"></a>`organization` | [`Organization`](#organization) | Organization after mutation. |
### `Mutation.pagesMarkOnboardingComplete`
@ -10997,6 +11033,29 @@ The edge type for [`InheritedCiVariable`](#inheritedcivariable).
| <a id="inheritedcivariableedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
| <a id="inheritedcivariableedgenode"></a>`node` | [`InheritedCiVariable`](#inheritedcivariable) | The item at the end of the edge. |
#### `InstanceAmazonS3ConfigurationTypeConnection`
The connection type for [`InstanceAmazonS3ConfigurationType`](#instanceamazons3configurationtype).
##### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="instanceamazons3configurationtypeconnectionedges"></a>`edges` | [`[InstanceAmazonS3ConfigurationTypeEdge]`](#instanceamazons3configurationtypeedge) | A list of edges. |
| <a id="instanceamazons3configurationtypeconnectionnodes"></a>`nodes` | [`[InstanceAmazonS3ConfigurationType]`](#instanceamazons3configurationtype) | A list of nodes. |
| <a id="instanceamazons3configurationtypeconnectionpageinfo"></a>`pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. |
#### `InstanceAmazonS3ConfigurationTypeEdge`
The edge type for [`InstanceAmazonS3ConfigurationType`](#instanceamazons3configurationtype).
##### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="instanceamazons3configurationtypeedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
| <a id="instanceamazons3configurationtypeedgenode"></a>`node` | [`InstanceAmazonS3ConfigurationType`](#instanceamazons3configurationtype) | The item at the end of the edge. |
#### `InstanceExternalAuditEventDestinationConnection`
The connection type for [`InstanceExternalAuditEventDestination`](#instanceexternalauditeventdestination).

View File

@ -53,6 +53,7 @@ Example response:
"web_url": "https://gitlab.example.com/user1",
"billable_members_count": 1,
"plan": "default",
"end_date": null,
"trial_ends_on": null,
"trial": false,
"root_repository_size": 100,
@ -70,6 +71,7 @@ Example response:
"members_count_with_descendants": 2,
"billable_members_count": 2,
"plan": "default",
"end_date": null,
"trial_ends_on": null,
"trial": false,
"root_repository_size": 100,
@ -87,6 +89,7 @@ Example response:
"members_count_with_descendants": 5,
"billable_members_count": 5,
"plan": "default",
"end_date": null,
"trial_ends_on": null,
"trial": false,
"root_repository_size": 100,
@ -168,6 +171,7 @@ Example response:
"max_seats_used": 0,
"seats_in_use": 0,
"plan": "default",
"end_date": null,
"trial_ends_on": null,
"trial": false,
"root_repository_size": 100,
@ -198,6 +202,7 @@ Example response:
"max_seats_used": 0,
"seats_in_use": 0,
"plan": "default",
"end_date": null,
"trial_ends_on": null,
"trial": false,
"root_repository_size": 100

View File

@ -191,14 +191,6 @@ This example uses [bound claims](https://developer.hashicorp.com/vault/api-docs/
Combined with [protected branches](../../../user/project/protected_branches.md), you can restrict who is able to authenticate and read the secrets.
To use the same policy for a list of projects, use `namespace_id`:
```json
"bound_claims": {
"namespace_id": ["12", "22", "37"]
}
```
Any of the claims [included in the JWT](#how-it-works) can be matched against a list of values
in the bound claims. For example:
@ -211,11 +203,18 @@ in the bound claims. For example:
"ref": ["main", "develop", "test"]
}
"bound_claims": {
"namespace_id": ["10", "20", "30"]
}
"bound_claims": {
"project_id": ["12", "22", "37"]
}
```
- If only `namespace_id` is used, all projects in the namespace are allowed.
- If both `namespace_id` and `project_id` are used, Vault first checks if the project's namespace is in `namespace_id`. If not, it then checks if the project is in `project_id`.
[`token_explicit_max_ttl`](https://developer.hashicorp.com/vault/api-docs/auth/jwt#token_explicit_max_ttl) specifies that the token issued by Vault, upon successful authentication, has a hard lifetime limit of 60 seconds.
[`user_claim`](https://developer.hashicorp.com/vault/api-docs/auth/jwt#user_claim) specifies the name for the Identity alias created by Vault upon a successful login.

View File

@ -101,7 +101,7 @@ export ANTHROPIC_API_KEY='<key>' # can use dev value of Gitlab::CurrentSettings
export VERTEX_AI_CREDENTIALS='<vertex-ai-credentials>' # can set as dev value of Gitlab::CurrentSettings.vertex_ai_credentials
export VERTEX_AI_PROJECT='<vertex-project-name>' # can use dev value of Gitlab::CurrentSettings.vertex_ai_project
REAL_AI_REQUEST=1 bundle exec rspec ee/spec/lib/gitlab/llm/chain/agents/zero_shot/executor_real_requests_spec.rb
REAL_AI_REQUEST=1 bundle exec rspec ee/spec/lib/gitlab/llm/completions/chat_real_requests_spec.rb
```
When you need to update the test questions that require documentation embeddings,
@ -112,7 +112,7 @@ make sure a new fixture is generated and committed together with the change.
The following CI jobs for GitLab project run the rspecs tagged with `real_ai_request`:
- `rspec-ee unit gitlab-duo-chat-zeroshot`:
the job runs `ee/spec/lib/gitlab/llm/chain/agents/zero_shot/executor_real_requests_spec.rb`.
the job runs `ee/spec/lib/gitlab/llm/completions/chat_real_requests_spec.rb`.
The job is optionally triggered and allowed to fail.
- `rspec-ee unit gitlab-duo-chat-qa`:

View File

@ -70,7 +70,7 @@ You can configure custom rules for how GitLab matches Jira issue keys by definin
- [A regex pattern](#use-regular-expression)
- [A prefix](#use-a-prefix)
When you don't configure custom rules, the [default behavior](https://gitlab.com/gitlab-org/gitlab/-/blob/710d83af298d8896f2b940faf48a46d2feb4cbaf/lib/gitlab/regex.rb#L552) is used. For more information, see the [RE2 wiki](https://github.com/google/re2/wiki/Syntax).
When you do not configure custom rules, the [default behavior](https://gitlab.com/gitlab-org/gitlab/-/blob/9b062706ac6203f0fa897a9baf5c8e9be1876c74/lib/gitlab/regex.rb#L245) is used.
### Use regular expression
@ -83,6 +83,8 @@ To define a regex pattern for Jira issue keys:
1. In the **Jira issue regex** text box, enter a regex pattern.
1. Select **Save changes**.
The regular expression must follow the [RE2 syntax](https://github.com/google/re2/wiki/Syntax).
For more information, see the [Atlassian documentation](https://confluence.atlassian.com/adminjiraserver073/changing-the-project-key-format-861253229.html).
### Use a prefix

View File

@ -100,6 +100,23 @@ To create a group:
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
For details about groups, watch [GitLab Namespaces (users, groups and subgroups)](https://youtu.be/r0sJgjR2f5A).
## Edit group name and description
You can edit your group details from the group general settings.
Prerequisites:
- You must have the Owner role for the group.
To edit group details:
1. On the left sidebar, select **Search or go to** and find your group.
1. Select **Settings > General**.
1. In the **Group name** text box, enter your group name. See the [limitations on group names](../../user/reserved_names.md).
1. Optional. In the **Group description (optional)** text box, enter your group description.
The description is limited to 250 characters.
1. Select **Save changes**.
## Remove a group
> Enabled delayed deletion by default and removed the option to delete immediately [on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/393622) and [on self-managed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/119606) in GitLab 16.0.

View File

@ -15,7 +15,7 @@ To create a blank project:
1. On the left sidebar, at the top, select **Create new** (**{plus}**) and **New project/repository**.
1. Select **Create blank project**.
1. Enter the project details:
- In the **Project name** field, enter the name of your project. The name must start with a lowercase or uppercase letter (`a-zA-Z`), digit (`0-9`), emoji, or underscore (`_`). It can also contain dots (`.`), pluses (`+`), dashes (`-`), or spaces.
- In the **Project name** field, enter the name of your project. See the [limitations on project names](../../user/reserved_names.md).
- In the **Project slug** field, enter the path to your project. The GitLab instance uses the
slug as the URL path to the project. To change the slug, first enter the project name,
then change the slug.
@ -52,7 +52,7 @@ To create a project from a built-in template:
- In the **Project slug** field, enter the path to your project. The GitLab instance uses the
slug as the URL path to the project. To change the slug, first enter the project name,
then change the slug.
- In the **Project description (optional)** field, enter the description of your project's dashboard.
- In the **Project description (optional)** field, enter the description of your project's dashboard. The description is limited to 250 characters.
- To modify the project's [viewing and access rights](../public_access.md) for users,
change the **Visibility Level**.
1. Select **Create project**.
@ -82,7 +82,7 @@ Custom project templates are available at:
- In the **Project slug** field, enter the path to your project. The GitLab instance uses the
slug as the URL path to the project. To change the slug, first enter the project name,
then change the slug.
- The description of your project's dashboard in the **Project description (optional)** field.
- The description of your project's dashboard in the **Project description (optional)** field. The description is limited to 250 characters.
- To modify the project's [viewing and access rights](../public_access.md) for users,
change the **Visibility Level**.
1. Select **Create project**.
@ -107,7 +107,7 @@ To create a project from the HIPAA Audit Protocol template:
- In the **Project slug** field, enter the path to your project. The GitLab instance uses the
slug as the URL path to the project. To change the slug, first enter the project name,
then change the slug.
- In the **Project description (optional)** field, enter the description of your project's dashboard.
- In the **Project description (optional)** field, enter the description of your project's dashboard. The description is limited to 250 characters.
- To modify the project's [viewing and access rights](../public_access.md) for users,
change the **Visibility Level**.
1. Select **Create project**.

View File

@ -60,8 +60,8 @@ Prerequisites:
1. On the left sidebar, select **Search or go to** and find your project.
1. Select **Settings > General**.
1. In the **Project name** text box, enter your project name.
1. In the **Project description** text box, enter your project description.
1. In the **Project name** text box, enter your project name. See the [limitations on project names](../../user/reserved_names.md).
1. In the **Project description** text box, enter your project description. The description is limited to 250 characters.
1. Under **Project avatar**, to change your project avatar, select **Choose file**.
## Star a project

View File

@ -0,0 +1,88 @@
# This template is provided and maintained by Diffblue.
# You can copy and paste this template into a new `.gitlab-ci.yml` file.
# This template is designed to be used with the Cover Pipeline for GitLab integration from Diffblue.
# It will download the latest version of Diffblue Cover, build the associated project, and
# automatically write Java unit tests for the project.
# Note that additional config is required:
# https://docs.diffblue.com/features/cover-pipeline/cover-pipeline-for-gitlab
# You should not add this template to an existing `.gitlab-ci.yml` file by using the `include:` keyword.
#
# To contribute improvements to CI/CD templates, please follow the Development guide at:
# https://docs.gitlab.com/ee/development/cicd/templates.html
# This specific template is located at:
# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Diffblue-Cover.gitlab-ci.yml
variables:
# Configure the following via the Diffblue Cover integration config for your project, or by
# using CI/CD masked Variables.
# For details, see https://docs.diffblue.com/features/cover-pipeline/cover-pipeline-for-gitlab
# Diffblue Cover license key: DIFFBLUE_LICENSE_KEY
# Refer to your welcome email or you can obtain a free trial key from
# https://www.diffblue.com/try-cover/gitlab
# GitLab access token: DIFFBLUE_ACCESS_TOKEN, DIFFBLUE_ACCESS_TOKEN_NAME
# The access token should have a role of Developer or better and should have
# api and write_repository permissions.
# Diffblue Cover requires a minimum of 4GB of memory.
JVM_ARGS: -Xmx4g
stages:
- build
diffblue-cover:
stage: build
# Select the Cover CLI docker image to use with your CI tool.
# Tag variations are produced for each supported JDK version.
# Go to https://hub.docker.com/r/diffblue/cover-cli for details.
# Note: To use the latest version of Diffblue Cover, use one of the latest-jdk<nn> tags.
# To use a specific release version, use one of the yyyy.mm.dd-jdk<nn> tags.
image: diffblue/cover-cli:latest-jdk17
# Diffblue Cover currently only supports running on merge_request_events.
rules:
- if: $CI_PIPELINE_SOURCE == 'merge_request_event'
# Diffblue Cover log files are saved to a .diffblue/ directory in the pipeline artifacts,
# and are available for download once the pipeline completes.
artifacts:
paths:
- "**/.diffblue/"
script:
# Diffblue Cover requires the project to be built before creating any tests.
# Either specify the build command here (one of the following), or provide
# prebuilt artifacts via a job dependency.
# Maven project example (comment out the Gradle version if used):
- mvn test-compile --batch-mode --no-transfer-progress
# Gradle project example (comment out the Maven version if used):
# - gradle testClasses
# Diffblue Cover commands and options to run.
# dcover the core Diffblue Cover command
# ci enable the GitLab CI/CD integration via environment variables
# activate - activate the license key
# validate - remove non-compiling and failing tests
# create - create new tests for your project
# --maven use the maven build tool
# For detailed information on Cover CLI commands and options, see
# https://docs.diffblue.com/features/cover-cli/commands-and-arguments
- dcover
ci
activate
validate --maven
create --maven
# Diffblue Cover will also respond to specific project labels:
# Diffblue Cover: Baseline
# Used to mark a merge request as requiring a full suite of tests to be written.
# This overrides the default behaviour where Cover will only write tests related
# to the code changes already in the merge request. This is useful when running Diffblue
# Cover for the first time on a project and when new product enhancements are released.
# Diffblue Cover: Skip
# Used to mark a merge request as requiring no tests to be written.

View File

@ -9,20 +9,19 @@ module Gitlab
#
# @param [String] Path of the group to find
# @param [Integer] Number of resources to create
def initialize(group_path:, seed_count:)
# @param[Boolean] If the created resources should be published or not, defaults to false
def initialize(group_path:, seed_count:, publish:)
@group = Group.find_by_full_path(group_path)
@seed_count = seed_count
@publish = publish
@current_user = @group&.first_owner
end
def seed
if @group.nil?
warn 'ERROR: Group was not found.'
return
end
return warn 'ERROR: Group was not found.' if @group.nil?
@seed_count.times do |i|
create_ci_catalog_resource(i)
seed_catalog_resource(i)
end
end
@ -58,9 +57,16 @@ module Gitlab
stage: $[[ inputs.stage ]]
YAML
project.repository.create_dir(
@current_user,
'templates',
message: 'Add template dir',
branch_name: project.default_branch_or_main
)
project.repository.create_file(
@current_user,
'template.yml',
'templates/component.yml',
template_content,
message: 'Add template.yml',
branch_name: project.default_branch_or_main
@ -77,21 +83,22 @@ module Gitlab
)
end
def create_ci_catalog(project)
def create_catalog_resource(project)
result = ::Ci::Catalog::Resources::CreateService.new(project, @current_user).execute
if result.success?
result.payload
else
warn "Project '#{project.name}' could not be converted to a Catalog resource"
warn "Catalog resource could not be created for Project '#{project.name}': #{result.errors.join}"
nil
end
end
def create_ci_catalog_resource(index)
def seed_catalog_resource(index)
name = "ci_seed_resource_#{index}"
existing_project = Project.find_by_name(name)
if Project.find_by_name(name).present?
warn "Project '#{name}' already exists!"
if existing_project.present? && existing_project.group.path == @group.path
warn "Project '#{@group.path}/#{name}' already exists!"
return
end
@ -102,9 +109,12 @@ module Gitlab
create_readme(project, index)
create_template_yml(project)
return unless create_ci_catalog(project)
new_catalog_resource = create_catalog_resource(project)
return unless new_catalog_resource
warn "Project '#{name}' was saved successfully!"
warn "Project '#{@group.path}/#{name}' was saved successfully!"
new_catalog_resource.publish! if @publish
end
end
end

View File

@ -3,19 +3,23 @@
# Seed CI/CD catalog resources
#
# @param group_path - Group name under which to create the projects
# @param seed_count - Total number of Catalog resources to create (default: 30)
# @param seed_count - Total number of Catalog resources to create
# @param publish - Whether or not created resources should be published in the catalog. Defaults to true.
#
# @example
# bundle exec rake "gitlab:seed:ci_catalog_resources[root, 50]"
# @example to create published resources
# bundle exec rake "gitlab:seed:ci_catalog_resources[Twitter, 50]"
# @example to create draft resources
# bundle exec rake "gitlab:seed:ci_catalog_resources[Flightjs, 2, false]"
#
namespace :gitlab do
namespace :seed do
desc 'Seed CI Catalog resources'
task :ci_catalog_resources,
[:group_path, :seed_count] => :gitlab_environment do |_t, args|
[:group_path, :seed_count, :publish] => :gitlab_environment do |_t, args|
Gitlab::Seeders::Ci::Catalog::ResourceSeeder.new(
group_path: args.group_path,
seed_count: args.seed_count&.to_i
seed_count: args.seed_count.to_i,
publish: Gitlab::Utils.to_boolean(args.publish, default: true)
).seed
puts "Task finished!"
end

View File

@ -15514,6 +15514,9 @@ msgstr ""
msgid "DastProfiles|Could not update the site profile. Please try again."
msgstr ""
msgid "DastProfiles|Crawl timeout"
msgstr ""
msgid "DastProfiles|DAST profile library"
msgstr ""
@ -15691,9 +15694,6 @@ msgstr ""
msgid "DastProfiles|Site type"
msgstr ""
msgid "DastProfiles|Spider timeout"
msgstr ""
msgid "DastProfiles|Submit button"
msgstr ""
@ -15706,7 +15706,7 @@ msgstr ""
msgid "DastProfiles|Target timeout"
msgstr ""
msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgid "DastProfiles|The maximum number of minutes allowed for the crawler to traverse the site."
msgstr ""
msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
@ -20400,6 +20400,9 @@ msgstr ""
msgid "Failed to update issue status"
msgstr ""
msgid "Failed to update organization"
msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
@ -26139,10 +26142,10 @@ msgstr ""
msgid "InviteMembersModal|To invite new users to this top-level group, you must remove existing users. You can still add existing users from the top-level group, including any subgroups and projects."
msgstr ""
msgid "InviteMembersModal|Username"
msgid "InviteMembersModal|Username or name"
msgstr ""
msgid "InviteMembersModal|Username or email address"
msgid "InviteMembersModal|Username, name or email address"
msgstr ""
msgid "InviteMembersModal|You only have space for %{count} more %{members} in %{name}"
@ -27233,7 +27236,7 @@ msgstr ""
msgid "JiraService|Use custom transitions"
msgstr ""
msgid "JiraService|Use regular expression to match Jira issue keys."
msgid "JiraService|Use regular expression to match Jira issue keys. The regular expression must follow the %{link_start}RE2 syntax%{link_end}. If empty, the default behavior is used."
msgstr ""
msgid "JiraService|Using Jira for issue tracking?"
@ -28822,39 +28825,15 @@ msgstr ""
msgid "Locks the discussion."
msgstr ""
msgid "LoggedOutMarketingHeader|About GitLab"
msgstr ""
msgid "LoggedOutMarketingHeader|Contact Sales"
msgstr ""
msgid "LoggedOutMarketingHeader|Explore GitLab"
msgstr ""
msgid "LoggedOutMarketingHeader|Get started"
msgstr ""
msgid "LoggedOutMarketingHeader|GitLab Learn"
msgstr ""
msgid "LoggedOutMarketingHeader|GitLab docs"
msgstr ""
msgid "LoggedOutMarketingHeader|GitLab: the DevOps platform"
msgstr ""
msgid "LoggedOutMarketingHeader|How GitLab compares"
msgstr ""
msgid "LoggedOutMarketingHeader|Install GitLab"
msgstr ""
msgid "LoggedOutMarketingHeader|Pricing"
msgstr ""
msgid "LoggedOutMarketingHeader|Talk to an expert"
msgstr ""
msgid "LoggedOutMarketingHeader|Why GitLab"
msgstr ""
@ -50707,9 +50686,6 @@ msgstr ""
msgid "Toggle keyboard shortcuts help dialog"
msgstr ""
msgid "Toggle navigation"
msgstr ""
msgid "Toggle project select"
msgstr ""
@ -55857,6 +55833,9 @@ msgstr ""
msgid "You have insufficient permissions to update an on-call schedule for this project"
msgstr ""
msgid "You have insufficient permissions to update the organization"
msgstr ""
msgid "You have insufficient permissions to update this HTTP integration"
msgstr ""

View File

@ -51,8 +51,8 @@
"@apollo/client": "^3.5.10",
"@babel/core": "^7.18.5",
"@babel/preset-env": "^7.18.2",
"@cubejs-client/core": "^0.34.24",
"@cubejs-client/vue": "^0.34.24",
"@cubejs-client/core": "^0.34.27",
"@cubejs-client/vue": "^0.34.27",
"@floating-ui/dom": "^1.2.9",
"@gitlab/application-sdk-browser": "^0.2.11",
"@gitlab/at.js": "1.5.7",

View File

@ -8,18 +8,19 @@ module RuboCop
class VersionedMigrationClass < RuboCop::Cop::Base
include MigrationHelpers
ENFORCED_SINCE = 2023_01_12_00_00_00
ENFORCED_SINCE = 2023_11_01_02_15_00
CURRENT_MIGRATION_VERSION = 2.2 # Should be the same value as Gitlab::Database::Migration.current_version
DOC_LINK = "https://docs.gitlab.com/ee/development/migration_style_guide.html#migration-helpers-and-versioning"
MSG_INHERIT = "Don't inherit from ActiveRecord::Migration or old versions of Gitlab::Database::Migration. " \
"Use Gitlab::Database::Migration[2.1] instead. See #{DOC_LINK}.".freeze
"Use Gitlab::Database::Migration[#{CURRENT_MIGRATION_VERSION}] instead. See #{DOC_LINK}.".freeze
MSG_INCLUDE = "Don't include migration helper modules directly. " \
"Inherit from Gitlab::Database::Migration[2.1] instead. See #{DOC_LINK}.".freeze
"Inherit from Gitlab::Database::Migration[#{CURRENT_MIGRATION_VERSION}] instead. See #{DOC_LINK}."
.freeze
GITLAB_MIGRATION_CLASS = 'Gitlab::Database::Migration'
ACTIVERECORD_MIGRATION_CLASS = 'ActiveRecord::Migration'
CURRENT_MIGRATION_VERSION = 2.1 # Should be the same value as Gitlab::Database::Migration.current_version
def_node_search :includes_helpers?, <<~PATTERN
(send nil? :include

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe 'Infrastructure Registry', feature_category: :groups_and_projects do
RSpec.describe 'Infrastructure Registry', feature_category: :package_registry do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project) }

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe 'Packages', feature_category: :groups_and_projects do
RSpec.describe 'Packages', feature_category: :package_registry do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project) }

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe 'Terraform', :js, feature_category: :groups_and_projects do
RSpec.describe 'Terraform', :js, feature_category: :package_registry do
let_it_be(:project) { create(:project) }
let_it_be(:terraform_state) { create(:terraform_state, :locked, :with_version, project: project) }

View File

@ -12,6 +12,7 @@ const placeholder = 'Search for a member';
const user1 = { id: 1, name: 'John Smith', username: 'one_1', avatar_url: '' };
const user2 = { id: 2, name: 'Jane Doe', username: 'two_2', avatar_url: '' };
const allUsers = [user1, user2];
const handleEnterSpy = jest.fn();
const createComponent = (props) => {
return shallowMount(MembersTokenSelect, {
@ -22,7 +23,11 @@ const createComponent = (props) => {
...props,
},
stubs: {
GlTokenSelector: stubComponent(GlTokenSelector),
GlTokenSelector: stubComponent(GlTokenSelector, {
methods: {
handleEnter: handleEnterSpy,
},
}),
},
});
};
@ -173,6 +178,14 @@ describe('MembersTokenSelect', () => {
});
});
});
it('allows tab to function as enter', () => {
tokenSelector.vm.$emit('text-input', 'username');
tokenSelector.vm.$emit('keydown', new KeyboardEvent('keydown', { key: 'Tab' }));
expect(handleEnterSpy).toHaveBeenCalled();
});
});
describe('when user is selected', () => {

View File

@ -0,0 +1,26 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'Diffblue-Cover.gitlab-ci.yml', feature_category: :continuous_integration do
subject(:template) { Gitlab::Template::GitlabCiYmlTemplate.find('Diffblue-Cover') }
describe 'the created pipeline' do
let(:pipeline_branch) { 'patch-1' }
let_it_be(:project) { create(:project, :repository, create_branch: 'patch-1') }
let(:user) { project.first_owner }
let(:mr_service) { MergeRequests::CreatePipelineService.new(project: project, current_user: user) }
let(:merge_request) { create(:merge_request, :simple, source_project: project, source_branch: pipeline_branch) }
let(:mr_pipeline) { mr_service.execute(merge_request).payload }
let(:mr_build_names) { mr_pipeline.builds.pluck(:name) }
before do
stub_ci_pipeline_yaml_file(template.content)
end
it 'creates diffblue-cover jobs' do
expect(mr_build_names).to include('diffblue-cover')
end
end
end

View File

@ -20,6 +20,7 @@ RSpec.describe 'CI YML Templates' do
context 'that support autodevops' do
exceptions = [
'Diffblue-Cover.gitlab-ci.yml', # no auto-devops
'Security/DAST.gitlab-ci.yml', # DAST stage is defined inside AutoDevops yml
'Security/DAST-API.gitlab-ci.yml', # no auto-devops
'Security/API-Fuzzing.gitlab-ci.yml', # no auto-devops

View File

@ -34,6 +34,12 @@ RSpec.describe Gitlab::Database::Migration do
# untouched.
expect(described_class[described_class.current_version]).to be < ActiveRecord::Migration::Current
end
it 'matches the version used by Rubocop' do
require 'rubocop'
load 'rubocop/cop/migration/versioned_migration_class.rb'
expect(described_class.current_version).to eq(RuboCop::Cop::Migration::VersionedMigrationClass::CURRENT_MIGRATION_VERSION)
end
end
describe Gitlab::Database::Migration::LockRetriesConcern do

View File

@ -7,10 +7,11 @@ RSpec.describe ::Gitlab::Seeders::Ci::Catalog::ResourceSeeder, feature_category:
let_it_be_with_reload(:group) { create(:group) }
let_it_be(:seed_count) { 2 }
let_it_be(:last_resource_id) { seed_count - 1 }
let(:publish) { true }
let(:group_path) { group.path }
subject(:seeder) { described_class.new(group_path: group_path, seed_count: seed_count) }
subject(:seeder) { described_class.new(group_path: group_path, seed_count: seed_count, publish: publish) }
before_all do
group.add_owner(admin)
@ -28,12 +29,26 @@ RSpec.describe ::Gitlab::Seeders::Ci::Catalog::ResourceSeeder, feature_category:
end
context 'when project name already exists' do
before do
create(:project, namespace: group, name: "ci_seed_resource_0")
context 'in the same group' do
before do
create(:project, namespace: group, name: 'ci_seed_resource_0')
end
it 'skips that project creation and keeps seeding' do
expect { seed }.to change { Project.count }.by(seed_count - 1)
end
end
it 'skips that project creation and keeps seeding' do
expect { seed }.to change { Project.count }.by(seed_count - 1)
context 'in a different group' do
let(:new_group) { create(:group) }
before do
create(:project, namespace: new_group, name: 'ci_seed_resource_0')
end
it 'executes the project creation' do
expect { seed }.to change { Project.count }.by(seed_count)
end
end
end
@ -65,6 +80,26 @@ RSpec.describe ::Gitlab::Seeders::Ci::Catalog::ResourceSeeder, feature_category:
end
end
describe 'publish argument' do
context 'when false' do
let(:publish) { false }
it 'creates catalog resources in draft state' do
group.projects.each do |project|
expect(project.catalog_resource.state).to be('draft')
end
end
end
context 'when true' do
it 'creates catalog resources in published state' do
group.projects.each do |project|
expect(project.catalog_resource&.state).to be('published')
end
end
end
end
it 'skips seeding a project if the project name already exists' do
# We call the same command twice, as it means it would try to recreate
# projects that were already created!
@ -87,12 +122,11 @@ RSpec.describe ::Gitlab::Seeders::Ci::Catalog::ResourceSeeder, feature_category:
project = group.projects.last
default_branch = project.default_branch_or_main
expect(project.repository.blob_at(default_branch, "README.md")).not_to be_nil
expect(project.repository.blob_at(default_branch, "template.yml")).not_to be_nil
expect(project.repository.blob_at(default_branch, 'README.md')).not_to be_nil
expect(project.repository.blob_at(default_branch, 'templates/component.yml')).not_to be_nil
end
# This should be run again when fixing: https://gitlab.com/gitlab-org/gitlab/-/issues/429649
xit 'creates projects with CI catalog resources' do
it 'creates projects with CI catalog resources' do
expect { seed }.to change { Project.count }.by(seed_count)
expect(group.projects.all?(&:catalog_resource)).to eq true

View File

@ -185,11 +185,11 @@ RSpec.describe Ci::Catalog::Listing, feature_category: :pipeline_composition do
end
describe '#find_resource' do
let_it_be(:accessible_resource) { create(:ci_catalog_resource, :published, project: public_project) }
subject { list.find_resource(id: id) }
context 'when the resource is published and visible to the user' do
let_it_be(:accessible_resource) { create(:ci_catalog_resource, :published, project: public_project) }
let(:id) { accessible_resource.id }
it 'fetches the resource' do
@ -200,9 +200,7 @@ RSpec.describe Ci::Catalog::Listing, feature_category: :pipeline_composition do
context 'when the resource is not found' do
let(:id) { 'not-an-id' }
it 'returns nil' do
is_expected.to be_nil
end
it { is_expected.to be_nil }
end
context 'when the resource is not published' do
@ -210,9 +208,7 @@ RSpec.describe Ci::Catalog::Listing, feature_category: :pipeline_composition do
let(:id) { draft_resource.id }
it 'returns nil' do
is_expected.to be_nil
end
it { is_expected.to be_nil }
end
context "when the current user cannot read code on the resource's project" do
@ -220,8 +216,25 @@ RSpec.describe Ci::Catalog::Listing, feature_category: :pipeline_composition do
let(:id) { inaccessible_resource.id }
it 'returns nil' do
is_expected.to be_nil
it { is_expected.to be_nil }
end
context 'when the current user is anonymous' do
let(:user) { nil }
context 'when the resource is public' do
let(:id) { accessible_resource.id }
it 'fetches the public resource' do
is_expected.to eq(accessible_resource)
end
end
context 'when the resource is internal' do
let(:internal_resource) { create(:ci_catalog_resource, :published, project: internal_project) }
let(:id) { internal_resource.id }
it { is_expected.to be_nil }
end
end
end

View File

@ -12,6 +12,7 @@ RSpec.describe Organizations::OrganizationPolicy, feature_category: :cell do
let_it_be(:current_user) { nil }
it { is_expected.to be_allowed(:read_organization) }
it { is_expected.to be_disallowed(:admin_organization) }
end
context 'when the user is an admin' do
@ -34,11 +35,13 @@ RSpec.describe Organizations::OrganizationPolicy, feature_category: :cell do
create :organization_user, organization: organization, user: current_user
end
it { is_expected.to be_allowed(:read_organization_user) }
it { is_expected.to be_allowed(:admin_organization) }
it { is_expected.to be_allowed(:read_organization) }
it { is_expected.to be_allowed(:read_organization_user) }
end
context 'when the user is not part of the organization' do
it { is_expected.to be_disallowed(:admin_organization) }
it { is_expected.to be_disallowed(:read_organization_user) }
# All organizations are currently public, and hence they are allowed to be read
# even if the user is not a part of the organization.

View File

@ -0,0 +1,94 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Mutations::Organizations::Update, feature_category: :cell do
include GraphqlHelpers
let_it_be(:user) { create(:user) }
let_it_be_with_reload(:organization) do
create(:organization) { |org| create(:organization_user, organization: org, user: user) }
end
let(:mutation) { graphql_mutation(:organization_update, params) }
let(:name) { 'Name' }
let(:path) { 'path' }
let(:description) { 'org-description' }
let(:params) do
{
id: organization.to_global_id.to_s,
name: name,
path: path,
description: description
}
end
subject(:update_organization) { post_graphql_mutation(mutation, current_user: current_user) }
it { expect(described_class).to require_graphql_authorizations(:admin_organization) }
def mutation_response
graphql_mutation_response(:organization_update)
end
context 'when the user does not have permission' do
let(:current_user) { nil }
it_behaves_like 'a mutation that returns a top-level access error'
it 'does not update the organization' do
initial_name = organization.name
initial_path = organization.path
update_organization
organization.reset
expect(organization.name).to eq(initial_name)
expect(organization.path).to eq(initial_path)
end
end
context 'when the user has permission' do
let(:current_user) { user }
context 'when the params are invalid' do
let(:name) { '' }
it 'returns the validation error' do
update_organization
expect(mutation_response).to include('errors' => ["Name can't be blank"])
end
end
context 'when single attribute is update' do
using RSpec::Parameterized::TableSyntax
where(attribute: %w[name path description])
with_them do
let(:value) { "new-#{attribute}" }
let(:attribute_hash) { { attribute => value } }
let(:params) { { id: organization.to_global_id.to_s }.merge(attribute_hash) }
it 'updates the given field' do
update_organization
expect(graphql_data_at(:organization_update, :organization)).to match a_hash_including(attribute_hash)
expect(mutation_response['errors']).to be_empty
end
end
end
it 'returns the updated organization' do
update_organization
expect(graphql_data_at(:organization_update, :organization)).to match a_hash_including(
'name' => name,
'path' => path,
'description' => description
)
expect(mutation_response['errors']).to be_empty
end
end
end

View File

@ -46,7 +46,7 @@ RSpec.describe Organizations::SettingsController, feature_category: :cell do
create :organization_user, organization: organization, user: user
end
it_behaves_like 'organization - not found response'
it_behaves_like 'organization - successful response'
it_behaves_like 'organization - action disabled by `ui_for_organizations` feature flag'
end
end

View File

@ -49,7 +49,7 @@ RSpec.describe RuboCop::Cop::Migration::VersionedMigrationClass, feature_categor
it 'adds an offence if inheriting from ActiveRecord::Migration' do
expect_offense(<<~RUBY)
class MyMigration < ActiveRecord::Migration[6.1]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Don't inherit from ActiveRecord::Migration or old versions of Gitlab::Database::Migration. Use Gitlab::Database::Migration[2.1] instead. See https://docs.gitlab.com/ee/development/migration_style_guide.html#migration-helpers-and-versioning.
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Don't inherit from ActiveRecord::Migration or old versions of Gitlab::Database::Migration. Use Gitlab::Database::Migration[#{described_class::CURRENT_MIGRATION_VERSION}] instead. See https://docs.gitlab.com/ee/development/migration_style_guide.html#migration-helpers-and-versioning.
end
RUBY
end
@ -57,23 +57,23 @@ RSpec.describe RuboCop::Cop::Migration::VersionedMigrationClass, feature_categor
it 'adds an offence if inheriting from old version of Gitlab::Database::Migration' do
expect_offense(<<~RUBY)
class MyMigration < Gitlab::Database::Migration[2.0]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Don't inherit from ActiveRecord::Migration or old versions of Gitlab::Database::Migration. Use Gitlab::Database::Migration[2.1] instead. See https://docs.gitlab.com/ee/development/migration_style_guide.html#migration-helpers-and-versioning.
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Don't inherit from ActiveRecord::Migration or old versions of Gitlab::Database::Migration. Use Gitlab::Database::Migration[#{described_class::CURRENT_MIGRATION_VERSION}] instead. See https://docs.gitlab.com/ee/development/migration_style_guide.html#migration-helpers-and-versioning.
end
RUBY
end
it 'adds an offence if including Gitlab::Database::MigrationHelpers directly' do
expect_offense(<<~RUBY)
class MyMigration < Gitlab::Database::Migration[2.1]
class MyMigration < Gitlab::Database::Migration[#{described_class::CURRENT_MIGRATION_VERSION}]
include Gitlab::Database::MigrationHelpers
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Don't include migration helper modules directly. Inherit from Gitlab::Database::Migration[2.1] instead. See https://docs.gitlab.com/ee/development/migration_style_guide.html#migration-helpers-and-versioning.
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Don't include migration helper modules directly. Inherit from Gitlab::Database::Migration[#{described_class::CURRENT_MIGRATION_VERSION}] instead. See https://docs.gitlab.com/ee/development/migration_style_guide.html#migration-helpers-and-versioning.
end
RUBY
end
it 'excludes ActiveRecord classes defined inside the migration' do
expect_no_offenses(<<~RUBY)
class TestMigration < Gitlab::Database::Migration[2.1]
class TestMigration < Gitlab::Database::Migration[#{described_class::CURRENT_MIGRATION_VERSION}]
class TestModel < ApplicationRecord
end
@ -85,7 +85,7 @@ RSpec.describe RuboCop::Cop::Migration::VersionedMigrationClass, feature_categor
it 'excludes parentless classes defined inside the migration' do
expect_no_offenses(<<~RUBY)
class TestMigration < Gitlab::Database::Migration[2.1]
class TestMigration < Gitlab::Database::Migration[#{described_class::CURRENT_MIGRATION_VERSION}]
class TestClass
end
end

View File

@ -0,0 +1,70 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Organizations::UpdateService, feature_category: :cell do
describe '#execute' do
let_it_be(:user) { create(:user) }
let_it_be_with_reload(:organization) { create(:organization) }
let(:current_user) { user }
let(:name) { 'Name' }
let(:path) { 'path' }
let(:description) { nil }
let(:params) { { name: name, path: path } }
subject(:response) do
described_class.new(organization, current_user: current_user, params: params).execute
end
context 'when user does not have permission' do
let(:current_user) { nil }
it 'returns an error' do
expect(response).to be_error
expect(response.message).to match_array(['You have insufficient permissions to update the organization'])
end
end
context 'when user has permission' do
before do
create(:organization_user, organization: organization, user: current_user)
end
shared_examples 'updating an organization' do
it 'updates the organization' do
response
organization.reset
expect(response).to be_success
expect(organization.name).to eq(name)
expect(organization.path).to eq(path)
expect(organization.description).to eq(description)
end
end
context 'with description' do
let(:description) { 'Organization description' }
let(:params) do
{
name: name,
path: path,
description: description
}
end
it_behaves_like 'updating an organization'
end
include_examples 'updating an organization'
it 'returns an error when the organization is not updated' do
params[:name] = nil
expect(response).to be_error
expect(response.message).to match_array(["Name can't be blank"])
end
end
end
end

View File

@ -23,7 +23,7 @@ require (
github.com/smartystreets/goconvey v1.8.1
github.com/stretchr/testify v1.8.4
gitlab.com/gitlab-org/gitaly/v16 v16.4.1
gitlab.com/gitlab-org/labkit v1.20.0
gitlab.com/gitlab-org/labkit v1.21.0
gocloud.dev v0.34.0
golang.org/x/image v0.7.0
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616

View File

@ -452,8 +452,8 @@ github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPR
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
gitlab.com/gitlab-org/gitaly/v16 v16.4.1 h1:Qh5TFK+Jy/mBV8hCfNro2VCqRrhgt3M2iTrdYVF5N6o=
gitlab.com/gitlab-org/gitaly/v16 v16.4.1/go.mod h1:TdN/Q3OqxU75pcp8V5YWpnE8Gk6dagwlC/HefNnW1IE=
gitlab.com/gitlab-org/labkit v1.20.0 h1:DGIVAdzbCR8sq2TppBvAh35wWBYIOy5dBL5wqFK3Wa8=
gitlab.com/gitlab-org/labkit v1.20.0/go.mod h1:zeATDAaSBelPcPLbTTq8J3ZJEHyPTLVBM1q3nva+/W4=
gitlab.com/gitlab-org/labkit v1.21.0 h1:hLmdBDtXjD1yOmZ+uJOac3a5Tlo83QaezwhES4IYik4=
gitlab.com/gitlab-org/labkit v1.21.0/go.mod h1:zeATDAaSBelPcPLbTTq8J3ZJEHyPTLVBM1q3nva+/W4=
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=

View File

@ -60,3 +60,8 @@ func (b *blocker) WriteHeader(status int) {
func (b *blocker) flush() {
b.WriteHeader(http.StatusOK)
}
// Unwrap lets http.ResponseController get the underlying http.ResponseWriter.
func (b *blocker) Unwrap() http.ResponseWriter {
return b.rw
}

View File

@ -54,3 +54,13 @@ func TestBlocker(t *testing.T) {
})
}
}
func TestBlockerFlushable(t *testing.T) {
rw := httptest.NewRecorder()
b := blocker{rw: rw}
rc := http.NewResponseController(&b)
err := rc.Flush()
require.NoError(t, err, "the underlying response writer is not flushable")
require.True(t, rw.Flushed)
}

View File

@ -54,3 +54,8 @@ func (c *countingResponseWriter) Count() int64 {
func (c *countingResponseWriter) Status() int {
return c.status
}
// Unwrap lets http.ResponseController get the underlying http.ResponseWriter.
func (c *countingResponseWriter) Unwrap() http.ResponseWriter {
return c.rw
}

View File

@ -4,6 +4,7 @@ import (
"bytes"
"io"
"net/http"
"net/http/httptest"
"testing"
"testing/iotest"
@ -48,3 +49,13 @@ func TestCountingResponseWriterWrite(t *testing.T) {
require.Equal(t, string(testData), string(trw.data))
}
func TestCountingResponseWriterFlushable(t *testing.T) {
rw := httptest.NewRecorder()
crw := countingResponseWriter{rw: rw}
rc := http.NewResponseController(&crw)
err := rc.Flush()
require.NoError(t, err, "the underlying response writer is not flushable")
require.True(t, rw.Flushed)
}

View File

@ -2,7 +2,6 @@ package contentprocessor
import (
"bytes"
"io"
"net/http"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/headers"
@ -30,7 +29,7 @@ func SetContentHeaders(h http.Handler) http.Handler {
status: http.StatusOK,
}
defer cd.flush()
defer cd.Flush()
h.ServeHTTP(cd, r)
})
@ -71,7 +70,8 @@ func (cd *contentDisposition) flushBuffer() error {
if cd.buf.Len() > 0 {
cd.writeContentHeaders()
cd.WriteHeader(cd.status)
_, err := io.Copy(cd.rw, cd.buf)
_, err := cd.rw.Write(cd.buf.Bytes())
cd.buf.Reset()
return err
}
@ -121,6 +121,20 @@ func (cd *contentDisposition) isUnbuffered() bool {
return cd.flushed || !cd.active
}
func (cd *contentDisposition) flush() {
cd.flushBuffer()
func (cd *contentDisposition) Flush() {
cd.FlushError()
}
// FlushError lets http.ResponseController to be used to flush the underlying http.ResponseWriter.
func (cd *contentDisposition) FlushError() error {
err := cd.flushBuffer()
if err != nil {
return err
}
return http.NewResponseController(cd.rw).Flush()
}
// Unwrap lets http.ResponseController get the underlying http.ResponseWriter.
func (cd *contentDisposition) Unwrap() http.ResponseWriter {
return cd.rw
}

View File

@ -104,3 +104,8 @@ func (s *sendDataResponseWriter) tryInject() bool {
func (s *sendDataResponseWriter) flush() {
s.WriteHeader(http.StatusOK)
}
// Unwrap lets http.ResponseController get the underlying http.ResponseWriter.
func (s *sendDataResponseWriter) Unwrap() http.ResponseWriter {
return s.rw
}

View File

@ -103,6 +103,11 @@ func (s *sendFileResponseWriter) WriteHeader(status int) {
s.rw.WriteHeader(s.status)
}
// Unwrap lets http.ResponseController get the underlying http.ResponseWriter.
func (s *sendFileResponseWriter) Unwrap() http.ResponseWriter {
return s.rw
}
func sendFileFromDisk(w http.ResponseWriter, r *http.Request, file string) {
log.WithContextFields(r.Context(), log.Fields{
"file": file,

View File

@ -170,3 +170,13 @@ func makeRequest(t *testing.T, fixturePath string, httpHeaders map[string]string
return resp
}
func TestSendFileResponseWriterFlushable(t *testing.T) {
rw := httptest.NewRecorder()
sfrw := sendFileResponseWriter{rw: rw}
rc := http.NewResponseController(&sfrw)
err := rc.Flush()
require.NoError(t, err, "the underlying response writer is not flushable")
require.True(t, rw.Flushed)
}

View File

@ -158,7 +158,11 @@ func (e *entry) Inject(w http.ResponseWriter, r *http.Request, sendData string)
w.WriteHeader(resp.StatusCode)
defer resp.Body.Close()
n, err := io.Copy(w, resp.Body)
// Flushes the response right after it received.
// Important for streaming responses, where content delivered in chunks.
// Without flushing the body gets buffered by the HTTP server's internal buffer.
n, err := io.Copy(newFlushingResponseWriter(w), resp.Body)
sendURLBytes.Add(float64(n))
if err != nil {
@ -190,3 +194,25 @@ func newClient(params entryParams) *http.Client {
return client
}
func newFlushingResponseWriter(w http.ResponseWriter) *httpFlushingResponseWriter {
return &httpFlushingResponseWriter{
ResponseWriter: w,
controller: http.NewResponseController(w),
}
}
type httpFlushingResponseWriter struct {
http.ResponseWriter
controller *http.ResponseController
}
// Write flushes the response once its written
func (h *httpFlushingResponseWriter) Write(data []byte) (int, error) {
n, err := h.ResponseWriter.Write(data)
if err != nil {
return n, err
}
return n, h.controller.Flush()
}

View File

@ -11,14 +11,12 @@ import (
"github.com/prometheus/client_golang/prometheus/promauto"
)
var (
staticErrorResponses = promauto.NewCounterVec(
prometheus.CounterOpts{
Name: "gitlab_workhorse_static_error_responses",
Help: "How many HTTP responses have been changed to a static error page, by HTTP status code.",
},
[]string{"code"},
)
var staticErrorResponses = promauto.NewCounterVec(
prometheus.CounterOpts{
Name: "gitlab_workhorse_static_error_responses",
Help: "How many HTTP responses have been changed to a static error page, by HTTP status code.",
},
[]string{"code"},
)
type ErrorFormat int
@ -120,6 +118,11 @@ func (s *errorPageResponseWriter) flush() {
s.WriteHeader(http.StatusOK)
}
// Unwrap lets http.ResponseController get the underlying http.ResponseWriter.
func (s *errorPageResponseWriter) Unwrap() http.ResponseWriter {
return s.rw
}
func (st *Static) ErrorPagesUnless(disabled bool, format ErrorFormat, handler http.Handler) http.Handler {
if disabled {
return handler

View File

@ -17,7 +17,7 @@ func TestIfErrorPageIsPresented(t *testing.T) {
dir := t.TempDir()
errorPage := "ERROR"
os.WriteFile(filepath.Join(dir, "404.html"), []byte(errorPage), 0600)
os.WriteFile(filepath.Join(dir, "404.html"), []byte(errorPage), 0o600)
w := httptest.NewRecorder()
h := http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
@ -57,7 +57,7 @@ func TestIfErrorPageIsIgnoredInDevelopment(t *testing.T) {
dir := t.TempDir()
errorPage := "ERROR"
os.WriteFile(filepath.Join(dir, "500.html"), []byte(errorPage), 0600)
os.WriteFile(filepath.Join(dir, "500.html"), []byte(errorPage), 0o600)
w := httptest.NewRecorder()
serverError := "Interesting Server Error"
@ -76,7 +76,7 @@ func TestIfErrorPageIsIgnoredIfCustomError(t *testing.T) {
dir := t.TempDir()
errorPage := "ERROR"
os.WriteFile(filepath.Join(dir, "500.html"), []byte(errorPage), 0600)
os.WriteFile(filepath.Join(dir, "500.html"), []byte(errorPage), 0o600)
w := httptest.NewRecorder()
serverError := "Interesting Server Error"
@ -107,7 +107,7 @@ func TestErrorPageInterceptedByContentType(t *testing.T) {
dir := t.TempDir()
errorPage := "ERROR"
os.WriteFile(filepath.Join(dir, "500.html"), []byte(errorPage), 0600)
os.WriteFile(filepath.Join(dir, "500.html"), []byte(errorPage), 0o600)
w := httptest.NewRecorder()
serverError := "Interesting Server Error"
@ -168,3 +168,13 @@ func TestIfErrorPageIsPresentedText(t *testing.T) {
testhelper.RequireResponseBody(t, w, errorPage)
testhelper.RequireResponseHeader(t, w, "Content-Type", "text/plain; charset=utf-8")
}
func TestErrorPageResponseWriterFlushable(t *testing.T) {
rw := httptest.NewRecorder()
eprw := errorPageResponseWriter{rw: rw}
rc := http.NewResponseController(&eprw)
err := rc.Flush()
require.NoError(t, err, "the underlying response writer is not flushable")
require.True(t, rw.Flushed)
}

View File

@ -1025,10 +1025,10 @@
resolved "https://registry.yarnpkg.com/@csstools/selector-specificity/-/selector-specificity-3.0.0.tgz#798622546b63847e82389e473fd67f2707d82247"
integrity sha512-hBI9tfBtuPIi885ZsZ32IMEU/5nlZH/KOVYJCOh7gyMxaVLGmLedYqFN6Ui1LXkI8JlC8IsuC0rF0btcRZKd5g==
"@cubejs-client/core@^0.34.24":
version "0.34.24"
resolved "https://registry.yarnpkg.com/@cubejs-client/core/-/core-0.34.24.tgz#6bb1f8d223dc0b70b2f543f072c0a07ca58999c4"
integrity sha512-6dnRmvxlYYeWtPCuNB8jzkCdndHvZhWNM9RP6gZxtE3pMdVjO/sFeVXQg2Q14egY9854bOCyxVbSNk1qjeeCrw==
"@cubejs-client/core@^0.34.27":
version "0.34.27"
resolved "https://registry.yarnpkg.com/@cubejs-client/core/-/core-0.34.27.tgz#d108d1986dceb98581a2112129aecca6f3240d9c"
integrity sha512-SrM9mKRAF5UQ5JQa6nHlnmoUTWl1wfFq219hcGF9OofEqJpDoGobCDVR8dFRrNV+OLmT1GxLgJ2PgNpMejE41Q==
dependencies:
"@babel/runtime" "^7.1.2"
core-js "^3.6.5"
@ -1038,12 +1038,12 @@
url-search-params-polyfill "^7.0.0"
uuid "^8.3.2"
"@cubejs-client/vue@^0.34.24":
version "0.34.24"
resolved "https://registry.yarnpkg.com/@cubejs-client/vue/-/vue-0.34.24.tgz#0df238edef6cc09e80df8513cf4bc061271aba52"
integrity sha512-w9tXm9lDhat1FWAqklIbewXatrLYePFXPpd93tXsiWUzanarVBAeFlQjdPWsjaBU0541Ry4oQqn5y64r+Y996A==
"@cubejs-client/vue@^0.34.27":
version "0.34.27"
resolved "https://registry.yarnpkg.com/@cubejs-client/vue/-/vue-0.34.27.tgz#9d1e2fc7e62b885a207e8b20fd5af3d612b471d3"
integrity sha512-9k4ejQlI13awrV1ihB0ISck7L33qdZ/etSJUzVe1aoZvaoQhbfRPrJRuCzPeMh/a7mJJCDxkkffWBEmW9lVVdA==
dependencies:
"@cubejs-client/core" "^0.34.24"
"@cubejs-client/core" "^0.34.27"
core-js "^3.6.5"
ramda "^0.27.2"