Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2024-11-07 03:22:58 +00:00
parent 098c9a1d51
commit ef2c517527
38 changed files with 429 additions and 143 deletions

View File

@ -138,16 +138,6 @@ Layout/LineLength:
- 'app/helpers/import_helper.rb'
- 'app/helpers/issuables_helper.rb'
- 'app/helpers/labels_helper.rb'
- 'app/helpers/visibility_level_helper.rb'
- 'app/helpers/whats_new_helper.rb'
- 'app/helpers/wiki_helper.rb'
- 'app/mailers/emails/members.rb'
- 'app/mailers/emails/merge_requests.rb'
- 'app/mailers/emails/pages_domains.rb'
- 'app/mailers/emails/profile.rb'
- 'app/mailers/previews/notify_preview.rb'
- 'app/models/analytics/cycle_analytics/aggregation.rb'
- 'app/models/analytics/cycle_analytics/issue_stage_event.rb'
- 'app/models/analytics/cycle_analytics/merge_request_stage_event.rb'
- 'app/models/application_record.rb'
- 'app/models/application_setting.rb'

View File

@ -299,7 +299,6 @@ RSpec/ExampleWithoutDescription:
- 'spec/lib/bitbucket/representation/issue_spec.rb'
- 'spec/lib/bitbucket/representation/pull_request_spec.rb'
- 'spec/lib/bitbucket_server/representation/activity_spec.rb'
- 'spec/lib/bitbucket_server/representation/comment_spec.rb'
- 'spec/lib/bitbucket_server/representation/pull_request_comment_spec.rb'
- 'spec/lib/bitbucket_server/representation/pull_request_spec.rb'
- 'spec/lib/bulk_imports/groups/pipelines/group_attributes_pipeline_spec.rb'

View File

@ -1586,7 +1586,6 @@ RSpec/NamedSubject:
- 'spec/lib/bitbucket_server/collection_spec.rb'
- 'spec/lib/bitbucket_server/connection_spec.rb'
- 'spec/lib/bitbucket_server/representation/activity_spec.rb'
- 'spec/lib/bitbucket_server/representation/comment_spec.rb'
- 'spec/lib/bitbucket_server/representation/pull_request_comment_spec.rb'
- 'spec/lib/bitbucket_server/representation/pull_request_spec.rb'
- 'spec/lib/bitbucket_server/representation/repo_spec.rb'

View File

@ -27,7 +27,6 @@ RSpec/RepeatedExampleGroupBody:
- 'spec/lib/api/entities/application_setting_spec.rb'
- 'spec/lib/banzai/filter/references/commit_range_reference_filter_spec.rb'
- 'spec/lib/banzai/filter/references/commit_reference_filter_spec.rb'
- 'spec/lib/bitbucket_server/representation/comment_spec.rb'
- 'spec/lib/gitlab/blob_helper_spec.rb'
- 'spec/lib/gitlab/ci/config/entry/release_spec.rb'
- 'spec/lib/gitlab/ci/pipeline/seed/build_spec.rb'

View File

@ -146,7 +146,10 @@ module VisibilityLevelHelper
def project_visibility_level_description(level)
case level
when Gitlab::VisibilityLevel::PRIVATE
s_("VisibilityLevel|Project access must be granted explicitly to each user. If this project is part of a group, access is granted to members of the group.")
s_(
"VisibilityLevel|Project access must be granted explicitly to each user. " \
"If this project is part of a group, access is granted to members of the group."
)
when Gitlab::VisibilityLevel::INTERNAL
s_("VisibilityLevel|The project can be accessed by any logged in user except external users.")
when Gitlab::VisibilityLevel::PUBLIC
@ -155,7 +158,9 @@ module VisibilityLevelHelper
end
def show_updated_public_description_for_setting(group)
group && !group.new_record? && Gitlab::CurrentSettings.current_application_settings.try(:should_check_namespace_plan?)
group &&
!group.new_record? &&
Gitlab::CurrentSettings.current_application_settings.try(:should_check_namespace_plan?)
end
def group_visibility_level_description(level, group = nil)
@ -163,7 +168,9 @@ module VisibilityLevelHelper
when Gitlab::VisibilityLevel::PRIVATE
s_("VisibilityLevel|The group and its projects can only be viewed by members.")
when Gitlab::VisibilityLevel::INTERNAL
s_("VisibilityLevel|The group and any internal projects can be viewed by any logged in user except external users.")
s_(
"VisibilityLevel|The group and any internal projects can be viewed by any logged in user except external users."
)
when Gitlab::VisibilityLevel::PUBLIC
unless show_updated_public_description_for_setting(group)
return s_('VisibilityLevel|The group and any public projects can be viewed without any authentication.')
@ -171,12 +178,16 @@ module VisibilityLevelHelper
Kernel.format(
s_(
'VisibilityLevel|The group, any public projects, and any of their members, issues, and merge requests can be viewed without authentication. ' \
'VisibilityLevel|The group, any public projects, and any of their members, issues, ' \
'and merge requests can be viewed without authentication. ' \
'Public groups and projects will be indexed by search engines. ' \
'Read more about %{free_user_limit_doc_link_start}free user limits%{link_end}, ' \
'or %{group_billings_link_start}upgrade to a paid tier%{link_end}.'),
free_user_limit_doc_link_start: "<a href='#{help_page_path('user/free_user_limit.md')}' target='_blank' rel='noopener noreferrer'>".html_safe,
group_billings_link_start: "<a href='#{group_billings_path(group)}' target='_blank' rel='noopener noreferrer'>".html_safe,
'or %{group_billings_link_start}upgrade to a paid tier%{link_end}.'
),
free_user_limit_doc_link_start: "<a href='#{help_page_path('user/free_user_limit.md')}' target='_blank' " \
"rel='noopener noreferrer'>".html_safe,
group_billings_link_start: "<a href='#{group_billings_path(group)}' target='_blank' " \
"rel='noopener noreferrer'>".html_safe,
link_end: "</a>".html_safe
).html_safe
end

View File

@ -36,7 +36,9 @@ module WhatsNewHelper
when 'current_tier'
_("Only include features new to your current subscription tier.")
when 'disabled'
_("%{italic_start}What's new%{italic_end} is inactive and cannot be viewed.").html_safe % { italic_start: '<i>'.html_safe, italic_end: '</i>'.html_safe }
_("%{italic_start}What's new%{italic_end} is inactive and cannot be viewed.").html_safe % {
italic_start: '<i>'.html_safe, italic_end: '</i>'.html_safe
}
end
end
end

View File

@ -22,7 +22,10 @@ module WikiHelper
end
def wiki_sidebar_toggle_button
render Pajamas::ButtonComponent.new(icon: 'chevron-double-lg-left', button_options: { class: 'sidebar-toggle js-sidebar-wiki-toggle' })
render Pajamas::ButtonComponent.new(
icon: 'chevron-double-lg-left',
button_options: { class: 'sidebar-toggle js-sidebar-wiki-toggle' }
)
end
# Produces a pure text breadcrumb for a given page.
@ -68,14 +71,25 @@ module WikiHelper
link_options = { action: action, direction: reversed_direction }
render Pajamas::ButtonComponent.new(href: wiki_path(wiki, **link_options), icon: "sort-#{icon_class}", button_options: { class: link_class, title: title })
render Pajamas::ButtonComponent.new(
href: wiki_path(wiki, **link_options),
icon: "sort-#{icon_class}",
button_options: { class: link_class, title: title }
)
end
def wiki_empty_state_messages(wiki)
case wiki.container
when Project
writable_body = s_("WikiEmpty|Use GitLab Wiki to collaborate on documentation in a project or group. You can store wiki pages written in markup formats like Markdown or AsciiDoc in a separate Git repository, and access the wiki through Git, the GitLab web interface, or the API.")
writable_body += s_("WikiEmpty| Have a Confluence wiki already? Use that instead.") if show_enable_confluence_integration?(wiki.container)
writable_body = s_(
"WikiEmpty|Use GitLab Wiki to collaborate on documentation in a project or group. " \
"You can store wiki pages written in markup formats like Markdown or AsciiDoc in a " \
"separate Git repository, and access the wiki through Git, the GitLab web interface, or the API."
)
if show_enable_confluence_integration?(wiki.container)
writable_body += s_("WikiEmpty| Have a Confluence wiki already? Use that instead.")
end
{
writable: {
@ -84,18 +98,29 @@ module WikiHelper
},
readonly: {
title: s_('WikiEmpty|This wiki doesn\'t have any content yet'),
body: s_('WikiEmpty|You can use GitLab Wiki to collaborate on documentation in a project or group. You can store wiki pages written in markup formats like Markdown or AsciiDoc in a separate Git repository, and access the wiki through Git, the GitLab web interface, or the API.')
body: s_(
'WikiEmpty|You can use GitLab Wiki to collaborate on documentation in a project or group. ' \
'You can store wiki pages written in markup formats like Markdown or AsciiDoc in a ' \
'separate Git repository, and access the wiki through Git, the GitLab web interface, or the API.'
)
}
}
when Group
{
writable: {
title: s_('WikiEmpty|Get started with wikis'),
body: s_("WikiEmpty|Use GitLab Wiki to collaborate on documentation in a project or group. You can store wiki pages written in markup formats like Markdown or AsciiDoc in a separate Git repository, and access the wiki through Git, the GitLab web interface, or the API.")
body: s_(
"WikiEmpty|Use GitLab Wiki to collaborate on documentation in a project or group. " \
"You can store wiki pages written in markup formats like Markdown or AsciiDoc in a " \
"separate Git repository, and access the wiki through Git, the GitLab web interface, or the API."
)
},
readonly: {
title: s_('WikiEmpty|This wiki doesn\'t have any content yet'),
body: s_('WikiEmpty|You can use GitLab Wiki to collaborate on documentation in a project or group. You can store wiki pages written in markup formats like Markdown or AsciiDoc in a separate Git repository, and access the wiki through Git, the GitLab web interface, or the API.')
body: s_('WikiEmpty|You can use GitLab Wiki to collaborate on documentation in a project or group. ' \
'You can store wiki pages written in markup formats like Markdown or AsciiDoc in a ' \
'separate Git repository, and access the wiki through Git, the GitLab web interface, or the API.'
)
}
}
else

View File

@ -91,7 +91,12 @@ module Emails
email_with_layout(
to: member.user.notification_email_for(notification_group),
subject: subject(s_("Your membership will expire in %{days_to_expire} days") % { days_to_expire: @days_to_expire }))
subject: subject(
s_("Your membership will expire in %{days_to_expire} days") % {
days_to_expire: @days_to_expire
}
)
)
end
# rubocop: disable CodeReuse/ActiveRecord
@ -115,7 +120,12 @@ module Emails
private
def member_exists?
Gitlab::AppLogger.info("Tried to send an email invitation for a deleted group. Member id: #{@member_id}") if member.blank?
if member.blank?
Gitlab::AppLogger.info(
"Tried to send an email invitation for a deleted group. Member id: #{@member_id}"
)
end
member.present?
end

View File

@ -25,7 +25,16 @@ module Emails
end
# existing_commits - an array containing the first and last commits
def push_to_merge_request_email(recipient_id, merge_request_id, updated_by_user_id, reason = nil, new_commits:, total_new_commits_count:, existing_commits:, total_existing_commits_count:)
def push_to_merge_request_email(
recipient_id,
merge_request_id,
updated_by_user_id,
reason = nil,
new_commits:,
total_new_commits_count:,
existing_commits:,
total_existing_commits_count:
)
setup_merge_request_mail(merge_request_id, recipient_id)
@new_commits = new_commits
@ -49,7 +58,13 @@ module Emails
end
# rubocop: disable CodeReuse/ActiveRecord
def reassigned_merge_request_email(recipient_id, merge_request_id, previous_assignee_ids, updated_by_user_id, reason = nil)
def reassigned_merge_request_email(
recipient_id,
merge_request_id,
previous_assignee_ids,
updated_by_user_id,
reason = nil
)
setup_merge_request_mail(merge_request_id, recipient_id)
previous_assignees = []
@ -62,7 +77,13 @@ module Emails
# rubocop: enable CodeReuse/ActiveRecord
# rubocop: disable CodeReuse/ActiveRecord
def changed_reviewer_of_merge_request_email(recipient_id, merge_request_id, previous_reviewer_ids, updated_by_user_id, reason = nil)
def changed_reviewer_of_merge_request_email(
recipient_id,
merge_request_id,
previous_reviewer_ids,
updated_by_user_id,
reason = nil
)
setup_merge_request_mail(merge_request_id, recipient_id)
@previous_reviewers = []
@ -87,7 +108,13 @@ module Emails
mail_answer_thread(@merge_request, merge_request_thread_options(updated_by_user_id, reason))
end
def changed_milestone_merge_request_email(recipient_id, merge_request_id, milestone, updated_by_user_id, reason = nil)
def changed_milestone_merge_request_email(
recipient_id,
merge_request_id,
milestone,
updated_by_user_id,
reason = nil
)
setup_merge_request_mail(merge_request_id, recipient_id)
@milestone = milestone

View File

@ -46,7 +46,10 @@ module Emails
@domain = domain
@project = domain.project
subject_text = _("ACTION REQUIRED: Something went wrong while obtaining the Let's Encrypt certificate for GitLab Pages domain '%{domain}'") % { domain: domain.domain }
subject_text = _(
"ACTION REQUIRED: Something went wrong while obtaining the Let's Encrypt certificate for " \
"GitLab Pages domain '%{domain}'"
) % { domain: domain.domain }
mail_with_locale(
to: recipient.notification_email_for(@project.group),
subject: subject(subject_text)

View File

@ -93,7 +93,10 @@ module Emails
@target_url = user_settings_personal_access_tokens_url
@token_name = token_name
email_with_layout(to: @user.notification_email_or_default, subject: subject(_("A new personal access token has been created")))
email_with_layout(
to: @user.notification_email_or_default,
subject: subject(_("A new personal access token has been created"))
)
end
def access_token_about_to_expire_email(user, token_names, params = {})
@ -106,7 +109,14 @@ module Emails
@target_url = user_settings_personal_access_tokens_url
@days_to_expire = params.fetch(:days_to_expire, PersonalAccessToken::DAYS_TO_EXPIRE)
email_with_layout(to: @user.notification_email_or_default, subject: subject(_("Your personal access tokens will expire in %{days_to_expire} days or less") % { days_to_expire: @days_to_expire }))
email_with_layout(
to: @user.notification_email_or_default,
subject: subject(
_("Your personal access tokens will expire in %{days_to_expire} days or less") % {
days_to_expire: @days_to_expire
}
)
)
end
def access_token_expired_email(user, token_names = [])
@ -116,7 +126,10 @@ module Emails
@token_names = token_names
@target_url = user_settings_personal_access_tokens_url
email_with_layout(to: @user.notification_email_or_default, subject: subject(_("Your personal access tokens have expired")))
email_with_layout(
to: @user.notification_email_or_default,
subject: subject(_("Your personal access tokens have expired"))
)
end
def access_token_revoked_email(user, token_name, source = nil)
@ -127,7 +140,10 @@ module Emails
@target_url = user_settings_personal_access_tokens_url
@source = source
email_with_layout(to: @user.notification_email_or_default, subject: subject(_("Your personal access token has been revoked")))
email_with_layout(
to: @user.notification_email_or_default,
subject: subject(_("Your personal access token has been revoked"))
)
end
def ssh_key_expired_email(user, fingerprints)
@ -170,7 +186,12 @@ module Emails
email_with_layout(
to: @user.notification_email_or_default,
subject: subject(_("Attempted sign in to %{host} using an incorrect verification code") % { host: Gitlab.config.gitlab.host }))
subject: subject(
_("Attempted sign in to %{host} using an incorrect verification code") % {
host: Gitlab.config.gitlab.host
}
)
)
end
def disabled_two_factor_email(user)
@ -178,7 +199,10 @@ module Emails
@user = user
email_with_layout(to: @user.notification_email_or_default, subject: subject(_("Two-factor authentication disabled")))
email_with_layout(
to: @user.notification_email_or_default,
subject: subject(_("Two-factor authentication disabled"))
)
end
def new_email_address_added_email(user, email)
@ -198,7 +222,12 @@ module Emails
email_with_layout(
to: @user.notification_email_or_default,
subject: subject(s_("Achievements|%{namespace_full_path} awarded you the %{achievement_name} achievement") % { namespace_full_path: @achievement.namespace.full_path, achievement_name: @achievement.name }))
subject: subject(
s_("Achievements|%{namespace_full_path} awarded you the %{achievement_name} achievement") % {
namespace_full_path: @achievement.namespace.full_path, achievement_name: @achievement.name
}
)
)
end
end
end

View File

@ -60,7 +60,13 @@ class NotifyPreview < ActionMailer::Preview
diff_refs: merge_request.diff_refs
)
create_note(noteable_type: 'merge_request', noteable_id: merge_request.id, type: 'DiffNote', position: position, note: note)
create_note(
noteable_type: 'merge_request',
noteable_id: merge_request.id,
type: 'DiffNote',
position: position,
note: note
)
end
end
@ -119,7 +125,12 @@ class NotifyPreview < ActionMailer::Preview
end
def issues_csv_email
Notify.issues_csv_email(user, project, '1997,Ford,E350', { truncated: false, rows_expected: 3, rows_written: 3 }).message
Notify.issues_csv_email(
user,
project,
'1997,Ford,E350',
{ truncated: false, rows_expected: 3, rows_written: 3 }
).message
end
def new_issue_email
@ -190,7 +201,12 @@ class NotifyPreview < ActionMailer::Preview
def pages_domain_enabled_email
cleanup do
pages_domain = PagesDomain.new(domain: 'my.example.com', project: project, verified_at: Time.now, enabled_until: 1.week.from_now)
pages_domain = PagesDomain.new(
domain: 'my.example.com',
project: project,
verified_at: Time.now,
enabled_until: 1.week.from_now
)
Notify.pages_domain_enabled_email(pages_domain, user).message
end
@ -350,7 +366,10 @@ class NotifyPreview < ActionMailer::Preview
end
def github_gists_import_errors_email
Notify.github_gists_import_errors_email(user.id, { '12345' => 'Snippet maximum file count exceeded', '67890' => 'error message 2' }).message
Notify.github_gists_import_errors_email(
user.id,
{ '12345' => 'Snippet maximum file count exceeded', '67890' => 'error message 2' }
).message
end
def bulk_import_complete
@ -414,23 +433,25 @@ class NotifyPreview < ActionMailer::Preview
end
def custom_email_verification
@custom_email_verification ||= project.service_desk_custom_email_verification || ServiceDesk::CustomEmailVerification.create!(
project: project,
token: 'XXXXXXXXXXXX',
triggerer: user,
triggered_at: Time.current,
state: 'started'
)
@custom_email_verification ||= project.service_desk_custom_email_verification ||
ServiceDesk::CustomEmailVerification.create!(
project: project,
token: 'XXXXXXXXXXXX',
triggerer: user,
triggered_at: Time.current,
state: 'started'
)
end
def custom_email_credential
@custom_email_credential ||= project.service_desk_custom_email_credential || ServiceDesk::CustomEmailCredential.create!(
project: project,
smtp_address: 'smtp.gmail.com', # Use gmail, because Gitlab::HTTP_V2::UrlBlocker resolves DNS
smtp_port: 587,
smtp_username: 'user@gmail.com',
smtp_password: 'supersecret'
)
@custom_email_credential ||= project.service_desk_custom_email_credential ||
ServiceDesk::CustomEmailCredential.create!(
project: project,
smtp_address: 'smtp.gmail.com', # Use gmail, because Gitlab::HTTP_V2::UrlBlocker resolves DNS
smtp_port: 587,
smtp_username: 'user@gmail.com',
smtp_password: 'supersecret'
)
end
def service_desk_setting

View File

@ -4,9 +4,16 @@ class Analytics::CycleAnalytics::Aggregation < ApplicationRecord
include FromUnion
include Analytics::CycleAnalytics::Parentable
validates :incremental_runtimes_in_seconds, :incremental_processed_records, :full_runtimes_in_seconds, :full_processed_records, presence: true, length: { maximum: 10 }, allow_blank: true
validates :incremental_runtimes_in_seconds,
:incremental_processed_records,
:full_runtimes_in_seconds,
:full_processed_records,
presence: true,
length: { maximum: 10 },
allow_blank: true
scope :priority_order, ->(column_to_sort = :last_incremental_run_at) { order(arel_table[column_to_sort].asc.nulls_first) }
scope :priority_order,
->(column_to_sort = :last_incremental_run_at) { order(arel_table[column_to_sort].asc.nulls_first) }
scope :enabled, -> { where('enabled IS TRUE') }
def cursor_for(mode, model)

View File

@ -14,7 +14,8 @@ module Analytics
scope :assigned_to, ->(user) do
assignees_class = IssueAssignee
condition = assignees_class.where(user_id: user).where(arel_table[:issue_id].eq(assignees_class.arel_table[:issue_id]))
condition = assignees_class.where(user_id: user)
.where(arel_table[:issue_id].eq(assignees_class.arel_table[:issue_id]))
where(condition.arel.exists)
end

View File

@ -6,10 +6,6 @@ module Ci
storage_location :ci_secure_files
# TODO: Remove this line
# See https://gitlab.com/gitlab-org/gitlab/-/issues/232917
alias_method :upload, :model
# Use Lockbox to encrypt/decrypt the stored file (registers CarrierWave callbacks)
encrypt(key: :key)

View File

@ -6,10 +6,6 @@ module Terraform
storage_location :terraform_state
# TODO: Remove this line
# See https://gitlab.com/gitlab-org/gitlab/-/issues/232917
alias_method :upload, :model
delegate :terraform_state, :project_id, to: :model
# Use Lockbox to encrypt/decrypt the stored file (registers CarrierWave callbacks)

View File

@ -5,6 +5,11 @@
reporter: trizzi # (required) GitLab username of the person reporting the deprecation
stage: Package # (required) String value of the stage that the feature was created in. e.g., Growth
issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/414236 # (required) Link to the deprecation issue in GitLab
impact: low
scope: group
resolution_role: developer
manual_task: true
window: "3"
body: | # (required) Do not modify this line, instead modify the lines below.
You can use GraphQL to query the amount of storage used by the GitLab Dependency Proxy. However, the `dependencyProxyTotalSizeInBytes` field is limited to ~2Gb (in bytes), which is not always large enough for the Dependency Proxy. As a result, `dependencyProxyTotalSizeInBytes` is deprecated and will be removed in GitLab 17.0.

View File

@ -0,0 +1,16 @@
- title: "Removal of `migrationState` field in `ContainerRepository` GraphQL API"
announcement_milestone: "17.6"
removal_milestone: "18.0"
breaking_change: true
reporter: trizzi
stage: Package
issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/459869
impact: low
scope: project
resolution_role: developer
manual_task: true
window: "3"
body: |
The `migrationState` field in the `ContainerRepositoryType` of GitLab's GraphQL API will be removed in GitLab 18.0. This deprecation is part of our efforts to streamline and improve our API.
To prepare for this change, we recommend reviewing and updating your GraphQL queries that interact with the `ContainerRepositoryType`. Remove any references to the `migrationState` field and adjust your application logic accordingly.

View File

@ -0,0 +1,8 @@
---
migration_job_name: RequeueBackfillSecurityFindingsProjectId
description: Backfills sharding key `security_findings.project_id` from `vulnerability_scanners`.
feature_category: vulnerability_management
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/171503
milestone: '17.6'
queued_migration_version: 20241104065605
finalized_by: # version of the migration that finalized this BBM

View File

@ -0,0 +1,17 @@
# frozen_string_literal: true
class UpdateAdminBotUserConfirmed < Gitlab::Database::Migration[2.2]
restrict_gitlab_migration gitlab_schema: :gitlab_main
milestone '17.6'
def up
execute <<~SQL
UPDATE "users" SET "confirmed_at" = now(), "private_profile" = TRUE WHERE "users"."user_type" = 11
SQL
end
def down
# noop
end
end

View File

@ -10,31 +10,12 @@ class QueueBackfillSecurityFindingsProjectId < Gitlab::Database::Migration[2.2]
SUB_BATCH_SIZE = 100
def up
queue_batched_background_migration(
MIGRATION,
:security_findings,
:id,
:project_id,
:vulnerability_scanners,
:project_id,
:scanner_id,
job_interval: DELAY_INTERVAL,
batch_size: BATCH_SIZE,
sub_batch_size: SUB_BATCH_SIZE
)
# no-op because there was a bug in the original migration, which has been
# fixed by https://gitlab.com/gitlab-org/gitlab/-/merge_requests/171503
end
def down
delete_batched_background_migration(
MIGRATION,
:security_findings,
:id,
[
:project_id,
:vulnerability_scanners,
:project_id,
:scanner_id
]
)
# no-op because there was a bug in the original migration, which has been
# fixed by https://gitlab.com/gitlab-org/gitlab/-/merge_requests/171503
end
end

View File

@ -0,0 +1,35 @@
# frozen_string_literal: true
class RequeueBackfillSecurityFindingsProjectId < Gitlab::Database::Migration[2.2]
milestone '17.6'
restrict_gitlab_migration gitlab_schema: :gitlab_sec
MIGRATION = "BackfillSecurityFindingsProjectId"
DELAY_INTERVAL = 2.minutes
TABLE_NAME = :security_findings
BATCH_COLUMN = :id
MAX_BATCH_SIZE = 150_000
GITLAB_OPTIMIZED_BATCH_SIZE = 50_000
GITLAB_OPTIMIZED_SUB_BATCH_SIZE = 250
JOB_ARGS = %i[project_id vulnerability_scanners project_id scanner_id]
def up
delete_batched_background_migration(MIGRATION, TABLE_NAME, BATCH_COLUMN, JOB_ARGS)
queue_batched_background_migration(
MIGRATION,
TABLE_NAME,
BATCH_COLUMN,
*JOB_ARGS,
job_interval: DELAY_INTERVAL,
max_batch_size: MAX_BATCH_SIZE,
batch_size: GITLAB_OPTIMIZED_BATCH_SIZE,
sub_batch_size: GITLAB_OPTIMIZED_SUB_BATCH_SIZE
)
end
def down
delete_batched_background_migration(MIGRATION, TABLE_NAME, BATCH_COLUMN, JOB_ARGS)
end
end

View File

@ -0,0 +1 @@
93fc508504c8fb566244c2528d5b78d74c541d10f3e4a769c32fc18de464eedc

View File

@ -0,0 +1 @@
9428a6ad7128e71a67a2c23903ad1318a008f79b54d1bf9558d802cc378d9da3

View File

@ -2338,6 +2338,12 @@ You **turn on** or **turn off** a toggle. For example:
- Turn on the **blah** toggle.
## top-level group
Use lowercase for **top-level group** (hyphenated).
Do not use **root group**.
## TFA, two-factor authentication
Use [**2FA** and **two-factor authentication**](#2fa-two-factor-authentication) instead.

View File

@ -20,7 +20,7 @@ GitLab offers two Jira integrations. You can use one or both integrations
### Jira issues integration
> - Name [updated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/166555) to Jira issues integration in GitLab 17.6.
> - [Changed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/166555) feature name to Jira issues integration in GitLab 17.6.
You can use the [Jira issues integration](configure.md) developed by GitLab with
Jira Cloud, Jira Data Center, or Jira Server. With this integration, you can:

View File

@ -632,6 +632,24 @@ This change is a breaking change. You should [create a runner in the UI](https:/
<div class="deprecation breaking-change" data-milestone="18.0">
### Removal of `migrationState` field in `ContainerRepository` GraphQL API
<div class="deprecation-notes">
- Announced in GitLab <span class="milestone">17.6</span>
- Removal in GitLab <span class="milestone">18.0</span> ([breaking change](https://docs.gitlab.com/ee/update/terminology.html#breaking-change))
- To discuss this change or learn more, see the [deprecation issue](https://gitlab.com/gitlab-org/gitlab/-/issues/459869).
</div>
The `migrationState` field in the `ContainerRepositoryType` of GitLab's GraphQL API will be removed in GitLab 18.0. This deprecation is part of our efforts to streamline and improve our API.
To prepare for this change, we recommend reviewing and updating your GraphQL queries that interact with the `ContainerRepositoryType`. Remove any references to the `migrationState` field and adjust your application logic accordingly.
</div>
<div class="deprecation breaking-change" data-milestone="18.0">
### Remove `previousStageJobsOrNeeds` from GraphQL
<div class="deprecation-notes">

View File

@ -11,7 +11,8 @@ module API
before { authenticate_non_get! }
allow_access_with_scope :ai_workflows, if: ->(request) do
request.get? || request.head? || request.put?
request.get? || request.head? ||
(request.put? && request.path.match?(%r{/api/v\d+/projects/\d+/merge_requests/\d+$})) # Only allow basic MR updates
end
rescue_from ActiveRecord::QueryCanceled do |_e|
@ -713,8 +714,7 @@ module API
merge_request = find_project_merge_request(params[:merge_request_iid])
# Merge request can not be merged because the user doesn't have
# permissions to push into target branch
#
# permissions to push into target branch.
unauthorized! unless merge_request.can_be_merged_by?(current_user)
merge_when_pipeline_succeeds = to_boolean(params[:merge_when_pipeline_succeeds])

View File

@ -8,10 +8,10 @@ module API
include ::API::Helpers::RelatedResourcesHelpers
expose :url do |setting|
expose_path(api_v4_vscode_settings_sync_v1_resource_path(
resource_name: setting[:setting_type],
id: setting[:uuid]
))
resource_name = setting[:setting_type]
id = setting[:uuid]
path = "/api/v4/vscode/settings_sync/v1/resource/#{resource_name}/#{id}"
expose_path(path)
end
expose :created do |setting|
setting[:updated_at]&.to_i

View File

@ -24,7 +24,7 @@ module API
end
resource :vscode do
resource :settings_sync do
resource '/settings_sync(/:settings_context_hash)' do
content_type :json, 'application/json'
content_type :json, 'text/plain'

View File

@ -35,6 +35,14 @@ module BitbucketServer
commit.dig('committer', 'displayName')
end
def committer_name
commit.dig('committer', 'displayName')
end
def committer_username
commit.dig('committer', 'slug')
end
def committer_email
commit.dig('committer', 'emailAddress')
end
@ -53,6 +61,10 @@ module BitbucketServer
action == 'APPROVED'
end
def approver_name
raw.dig('user', 'displayName')
end
def approver_username
raw.dig('user', 'slug')
end
@ -65,6 +77,10 @@ module BitbucketServer
action == 'DECLINED'
end
def decliner_name
raw.dig('user', 'displayName')
end
def decliner_username
raw.dig('user', 'slug')
end
@ -80,12 +96,16 @@ module BitbucketServer
def to_hash
{
id: id,
committer_name: committer_user,
committer_user: committer_user,
committer_username: committer_username,
committer_email: committer_email,
merge_timestamp: merge_timestamp,
merge_commit: merge_commit,
approver_name: approver_name,
approver_username: approver_username,
approver_email: approver_email,
decliner_name: decliner_name,
decliner_username: decliner_username,
decliner_email: decliner_email,
created_at: created_at

View File

@ -37,6 +37,10 @@ module BitbucketServer
raw_comment['id']
end
def author_name
author['displayName']
end
def author_username
author['username'] ||
author['slug'] ||
@ -81,6 +85,7 @@ module BitbucketServer
{
id: id,
author_name: author_name,
author_email: author_email,
author_username: author_username,
note: note,

View File

@ -5,6 +5,14 @@ module Gitlab
class BackfillSecurityFindingsProjectId < BackfillDesiredShardingKeyJob
operation_name :backfill_security_findings_project_id
feature_category :vulnerability_management
scope_to ->(relation) { relation }
def perform
each_sub_batch do |sub_batch|
sub_batch.connection.execute(construct_query(sub_batch: sub_batch.where(backfill_column => nil)))
end
end
end
end
end

View File

@ -49,6 +49,8 @@ RSpec.describe BitbucketServer::Representation::Activity, feature_category: :imp
it { expect(subject.comment?).to be_falsey }
it { expect(subject.inline_comment?).to be_falsey }
it { expect(subject.committer_user).to eq('root') }
it { expect(subject.committer_name).to eq('root') }
it { expect(subject.committer_username).to eq('slug') }
it { expect(subject.committer_email).to eq('test.user@example.com') }
it { expect(subject.merge_timestamp).to be_a(Time) }
it { expect(subject.created_at).to be_a(Time) }
@ -60,6 +62,8 @@ RSpec.describe BitbucketServer::Representation::Activity, feature_category: :imp
a_hash_including(
id: 7,
committer_user: 'root',
committer_name: 'root',
committer_username: 'slug',
committer_email: 'test.user@example.com',
merge_commit: '839fa9a2d434eb697815b8fcafaecc51accfdbbc'
)
@ -76,6 +80,7 @@ RSpec.describe BitbucketServer::Representation::Activity, feature_category: :imp
it { expect(subject.inline_comment?).to be_falsey }
it { expect(subject.merge_event?).to be_falsey }
it { expect(subject.approved_event?).to be_truthy }
it { expect(subject.approver_name).to eq('root') }
it { expect(subject.approver_username).to eq('slug') }
it { expect(subject.approver_email).to eq('test.user@example.com') }
it { expect(subject.created_at).to be_a(Time) }
@ -85,6 +90,7 @@ RSpec.describe BitbucketServer::Representation::Activity, feature_category: :imp
expect(subject.to_hash).to match(
a_hash_including(
id: 15,
approver_name: 'root',
approver_username: 'slug',
approver_email: 'test.user@example.com'
)
@ -101,6 +107,7 @@ RSpec.describe BitbucketServer::Representation::Activity, feature_category: :imp
it { expect(subject.inline_comment?).to be_falsey }
it { expect(subject.merge_event?).to be_falsey }
it { expect(subject.declined_event?).to be_truthy }
it { expect(subject.decliner_name).to eq('root') }
it { expect(subject.decliner_username).to eq('slug') }
it { expect(subject.decliner_email).to eq('test.user@example.com') }
it { expect(subject.created_at).to be_a(Time) }
@ -110,6 +117,7 @@ RSpec.describe BitbucketServer::Representation::Activity, feature_category: :imp
expect(subject.to_hash).to match(
a_hash_including(
id: 18,
decliner_name: 'root',
decliner_username: 'slug',
decliner_email: 'test.user@example.com'
)

View File

@ -6,15 +6,19 @@ RSpec.describe BitbucketServer::Representation::Comment, feature_category: :impo
let(:activities) { Gitlab::Json.parse(fixture_file('importers/bitbucket_server/activities.json'))['values'] }
let(:comment) { activities.first }
subject { described_class.new(comment) }
subject(:comment_representation) { described_class.new(comment) }
describe '#id' do
it { expect(subject.id).to eq(9) }
it { expect(comment_representation.id).to eq(9) }
end
describe '#author_name' do
it { expect(comment_representation.author_name).to eq('root') }
end
describe '#author_username' do
it 'returns username' do
expect(subject.author_username).to eq('username')
expect(comment_representation.author_username).to eq('username')
end
context 'when username is absent' do
@ -23,7 +27,7 @@ RSpec.describe BitbucketServer::Representation::Comment, feature_category: :impo
end
it 'returns slug' do
expect(subject.author_username).to eq('slug')
expect(comment_representation.author_username).to eq('slug')
end
end
@ -34,55 +38,56 @@ RSpec.describe BitbucketServer::Representation::Comment, feature_category: :impo
end
it 'returns displayName' do
expect(subject.author_username).to eq('root')
expect(comment_representation.author_username).to eq('root')
end
end
end
describe '#author_email' do
it { expect(subject.author_email).to eq('test.user@example.com') }
it { expect(comment_representation.author_email).to eq('test.user@example.com') }
end
describe '#note' do
it { expect(subject.note).to eq('is this a new line?') }
it { expect(comment_representation.note).to eq('is this a new line?') }
end
describe '#created_at' do
it { expect(subject.created_at).to be_a(Time) }
it { expect(comment_representation.created_at).to be_a(Time) }
end
describe '#updated_at' do
it { expect(subject.created_at).to be_a(Time) }
it { expect(comment_representation.updated_at).to be_a(Time) }
end
describe '#comments' do
it { expect(subject.comments.count).to eq(4) }
it { expect(subject.comments).to all(be_a(described_class)) }
it { expect(subject.comments.map(&:note)).to match_array(["Hello world", "Ok", "hello", "hi"]) }
it { expect(comment_representation.comments.count).to eq(4) }
it { expect(comment_representation.comments).to all(be_a(described_class)) }
it { expect(comment_representation.comments.map(&:note)).to match_array(["Hello world", "Ok", "hello", "hi"]) }
# The thread should look like:
#
# is this a new line? (subject)
# is this a new line? (comment_representation)
# -> Hello world (first)
# -> Ok (third)
# -> Hi (fourth)
# -> hello (second)
it 'comments have the right parent' do
first, second, third, fourth = subject.comments[0..4]
first, second, third, fourth = comment_representation.comments[0..4]
expect(subject.parent_comment).to be_nil
expect(first.parent_comment).to eq(subject)
expect(second.parent_comment).to eq(subject)
expect(comment_representation.parent_comment).to be_nil
expect(first.parent_comment).to eq(comment_representation)
expect(second.parent_comment).to eq(comment_representation)
expect(third.parent_comment).to eq(first)
expect(fourth.parent_comment).to eq(first)
end
end
describe '#to_hash' do
it do
expect(subject.to_hash).to match(
specify do
expect(comment_representation.to_hash).to match(
a_hash_including(
id: 9,
author_name: 'root',
author_email: 'test.user@example.com',
author_username: 'username',
note: 'is this a new line?',

View File

@ -6,27 +6,14 @@ require_migration!
RSpec.describe QueueBackfillSecurityFindingsProjectId, migration: :gitlab_sec, feature_category: :vulnerability_management do
let!(:batched_migration) { described_class::MIGRATION }
it 'schedules a new batched migration' do
it 'does not 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: :security_findings,
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_sec,
job_arguments: [
:project_id,
:vulnerability_scanners,
:project_id,
:scanner_id
]
)
expect(batched_migration).not_to have_scheduled_batched_migration
}
end
end

View File

@ -0,0 +1,30 @@
# frozen_string_literal: true
require 'spec_helper'
require_migration!
RSpec.describe RequeueBackfillSecurityFindingsProjectId, migration: :gitlab_sec, feature_category: :vulnerability_management do
let!(:batched_migration) { described_class::MIGRATION }
let(:expected_job_args) { %i[project_id vulnerability_scanners project_id scanner_id] }
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: described_class::TABLE_NAME,
column_name: described_class::BATCH_COLUMN,
interval: described_class::DELAY_INTERVAL,
max_batch_size: described_class::MAX_BATCH_SIZE,
batch_size: described_class::GITLAB_OPTIMIZED_BATCH_SIZE,
sub_batch_size: described_class::GITLAB_OPTIMIZED_SUB_BATCH_SIZE,
gitlab_schema: :gitlab_sec,
job_arguments: expected_job_args
)
}
end
end
end

View File

@ -2810,6 +2810,19 @@ RSpec.describe API::MergeRequests, :aggregate_failures, feature_category: :sourc
expect(json_response['reviewers']).to be_empty
end
end
context 'with oauth token that has ai_workflows scope' do
let(:token) { create(:oauth_access_token, user: user, scopes: [:ai_workflows]) }
it "allows access" do
put api(
"/projects/#{project.id}/merge_requests/#{merge_request.iid}?title=new_title",
oauth_access_token: token
)
expect(response).to have_gitlab_http_status(:ok)
end
end
end
describe "POST /projects/:id/merge_requests/:merge_request_iid/context_commits" do
@ -3066,13 +3079,10 @@ RSpec.describe API::MergeRequests, :aggregate_failures, feature_category: :sourc
context 'with oauth token that has ai_workflows scope' do
let(:token) { create(:oauth_access_token, user: user, scopes: [:ai_workflows]) }
it "allows access" do
put api(
"/projects/#{project.id}/merge_requests/#{merge_request.iid}?title=new_title",
oauth_access_token: token
)
it "does not allow access" do
put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/merge", oauth_access_token: token)
expect(response).to have_gitlab_http_status(:ok)
expect(response).to have_gitlab_http_status(:forbidden)
end
end
@ -4003,6 +4013,16 @@ RSpec.describe API::MergeRequests, :aggregate_failures, feature_category: :sourc
end
describe 'PUT :id/merge_requests/:merge_request_iid/rebase' do
context 'with oauth token that has ai_workflows scope' do
let(:token) { create(:oauth_access_token, user: user, scopes: [:ai_workflows]) }
it "does not allow access" do
put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/rebase", oauth_access_token: token)
expect(response).to have_gitlab_http_status(:forbidden)
end
end
context 'when rebase can be performed' do
it 'enqueues a rebase of the merge request against the target branch' do
Sidekiq::Testing.fake! do