Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
098c9a1d51
commit
ef2c517527
|
|
@ -138,16 +138,6 @@ Layout/LineLength:
|
||||||
- 'app/helpers/import_helper.rb'
|
- 'app/helpers/import_helper.rb'
|
||||||
- 'app/helpers/issuables_helper.rb'
|
- 'app/helpers/issuables_helper.rb'
|
||||||
- 'app/helpers/labels_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/analytics/cycle_analytics/merge_request_stage_event.rb'
|
||||||
- 'app/models/application_record.rb'
|
- 'app/models/application_record.rb'
|
||||||
- 'app/models/application_setting.rb'
|
- 'app/models/application_setting.rb'
|
||||||
|
|
|
||||||
|
|
@ -299,7 +299,6 @@ RSpec/ExampleWithoutDescription:
|
||||||
- 'spec/lib/bitbucket/representation/issue_spec.rb'
|
- 'spec/lib/bitbucket/representation/issue_spec.rb'
|
||||||
- 'spec/lib/bitbucket/representation/pull_request_spec.rb'
|
- 'spec/lib/bitbucket/representation/pull_request_spec.rb'
|
||||||
- 'spec/lib/bitbucket_server/representation/activity_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_comment_spec.rb'
|
||||||
- 'spec/lib/bitbucket_server/representation/pull_request_spec.rb'
|
- 'spec/lib/bitbucket_server/representation/pull_request_spec.rb'
|
||||||
- 'spec/lib/bulk_imports/groups/pipelines/group_attributes_pipeline_spec.rb'
|
- 'spec/lib/bulk_imports/groups/pipelines/group_attributes_pipeline_spec.rb'
|
||||||
|
|
|
||||||
|
|
@ -1586,7 +1586,6 @@ RSpec/NamedSubject:
|
||||||
- 'spec/lib/bitbucket_server/collection_spec.rb'
|
- 'spec/lib/bitbucket_server/collection_spec.rb'
|
||||||
- 'spec/lib/bitbucket_server/connection_spec.rb'
|
- 'spec/lib/bitbucket_server/connection_spec.rb'
|
||||||
- 'spec/lib/bitbucket_server/representation/activity_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_comment_spec.rb'
|
||||||
- 'spec/lib/bitbucket_server/representation/pull_request_spec.rb'
|
- 'spec/lib/bitbucket_server/representation/pull_request_spec.rb'
|
||||||
- 'spec/lib/bitbucket_server/representation/repo_spec.rb'
|
- 'spec/lib/bitbucket_server/representation/repo_spec.rb'
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,6 @@ RSpec/RepeatedExampleGroupBody:
|
||||||
- 'spec/lib/api/entities/application_setting_spec.rb'
|
- '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_range_reference_filter_spec.rb'
|
||||||
- 'spec/lib/banzai/filter/references/commit_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/blob_helper_spec.rb'
|
||||||
- 'spec/lib/gitlab/ci/config/entry/release_spec.rb'
|
- 'spec/lib/gitlab/ci/config/entry/release_spec.rb'
|
||||||
- 'spec/lib/gitlab/ci/pipeline/seed/build_spec.rb'
|
- 'spec/lib/gitlab/ci/pipeline/seed/build_spec.rb'
|
||||||
|
|
|
||||||
|
|
@ -146,7 +146,10 @@ module VisibilityLevelHelper
|
||||||
def project_visibility_level_description(level)
|
def project_visibility_level_description(level)
|
||||||
case level
|
case level
|
||||||
when Gitlab::VisibilityLevel::PRIVATE
|
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
|
when Gitlab::VisibilityLevel::INTERNAL
|
||||||
s_("VisibilityLevel|The project can be accessed by any logged in user except external users.")
|
s_("VisibilityLevel|The project can be accessed by any logged in user except external users.")
|
||||||
when Gitlab::VisibilityLevel::PUBLIC
|
when Gitlab::VisibilityLevel::PUBLIC
|
||||||
|
|
@ -155,7 +158,9 @@ module VisibilityLevelHelper
|
||||||
end
|
end
|
||||||
|
|
||||||
def show_updated_public_description_for_setting(group)
|
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
|
end
|
||||||
|
|
||||||
def group_visibility_level_description(level, group = nil)
|
def group_visibility_level_description(level, group = nil)
|
||||||
|
|
@ -163,7 +168,9 @@ module VisibilityLevelHelper
|
||||||
when Gitlab::VisibilityLevel::PRIVATE
|
when Gitlab::VisibilityLevel::PRIVATE
|
||||||
s_("VisibilityLevel|The group and its projects can only be viewed by members.")
|
s_("VisibilityLevel|The group and its projects can only be viewed by members.")
|
||||||
when Gitlab::VisibilityLevel::INTERNAL
|
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
|
when Gitlab::VisibilityLevel::PUBLIC
|
||||||
unless show_updated_public_description_for_setting(group)
|
unless show_updated_public_description_for_setting(group)
|
||||||
return s_('VisibilityLevel|The group and any public projects can be viewed without any authentication.')
|
return s_('VisibilityLevel|The group and any public projects can be viewed without any authentication.')
|
||||||
|
|
@ -171,12 +178,16 @@ module VisibilityLevelHelper
|
||||||
|
|
||||||
Kernel.format(
|
Kernel.format(
|
||||||
s_(
|
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. ' \
|
'Public groups and projects will be indexed by search engines. ' \
|
||||||
'Read more about %{free_user_limit_doc_link_start}free user limits%{link_end}, ' \
|
'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}.'),
|
'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,
|
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
|
link_end: "</a>".html_safe
|
||||||
).html_safe
|
).html_safe
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,9 @@ module WhatsNewHelper
|
||||||
when 'current_tier'
|
when 'current_tier'
|
||||||
_("Only include features new to your current subscription tier.")
|
_("Only include features new to your current subscription tier.")
|
||||||
when 'disabled'
|
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
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,10 @@ module WikiHelper
|
||||||
end
|
end
|
||||||
|
|
||||||
def wiki_sidebar_toggle_button
|
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
|
end
|
||||||
|
|
||||||
# Produces a pure text breadcrumb for a given page.
|
# Produces a pure text breadcrumb for a given page.
|
||||||
|
|
@ -68,14 +71,25 @@ module WikiHelper
|
||||||
|
|
||||||
link_options = { action: action, direction: reversed_direction }
|
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
|
end
|
||||||
|
|
||||||
def wiki_empty_state_messages(wiki)
|
def wiki_empty_state_messages(wiki)
|
||||||
case wiki.container
|
case wiki.container
|
||||||
when Project
|
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_(
|
||||||
writable_body += s_("WikiEmpty| Have a Confluence wiki already? Use that instead.") if show_enable_confluence_integration?(wiki.container)
|
"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: {
|
writable: {
|
||||||
|
|
@ -84,18 +98,29 @@ module WikiHelper
|
||||||
},
|
},
|
||||||
readonly: {
|
readonly: {
|
||||||
title: s_('WikiEmpty|This wiki doesn\'t have any content yet'),
|
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
|
when Group
|
||||||
{
|
{
|
||||||
writable: {
|
writable: {
|
||||||
title: s_('WikiEmpty|Get started with wikis'),
|
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: {
|
readonly: {
|
||||||
title: s_('WikiEmpty|This wiki doesn\'t have any content yet'),
|
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
|
else
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,12 @@ module Emails
|
||||||
|
|
||||||
email_with_layout(
|
email_with_layout(
|
||||||
to: member.user.notification_email_for(notification_group),
|
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
|
end
|
||||||
|
|
||||||
# rubocop: disable CodeReuse/ActiveRecord
|
# rubocop: disable CodeReuse/ActiveRecord
|
||||||
|
|
@ -115,7 +120,12 @@ module Emails
|
||||||
private
|
private
|
||||||
|
|
||||||
def member_exists?
|
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?
|
member.present?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,16 @@ module Emails
|
||||||
end
|
end
|
||||||
|
|
||||||
# existing_commits - an array containing the first and last commits
|
# 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)
|
setup_merge_request_mail(merge_request_id, recipient_id)
|
||||||
|
|
||||||
@new_commits = new_commits
|
@new_commits = new_commits
|
||||||
|
|
@ -49,7 +58,13 @@ module Emails
|
||||||
end
|
end
|
||||||
|
|
||||||
# rubocop: disable CodeReuse/ActiveRecord
|
# 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)
|
setup_merge_request_mail(merge_request_id, recipient_id)
|
||||||
|
|
||||||
previous_assignees = []
|
previous_assignees = []
|
||||||
|
|
@ -62,7 +77,13 @@ module Emails
|
||||||
# rubocop: enable CodeReuse/ActiveRecord
|
# rubocop: enable CodeReuse/ActiveRecord
|
||||||
|
|
||||||
# rubocop: disable 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)
|
setup_merge_request_mail(merge_request_id, recipient_id)
|
||||||
|
|
||||||
@previous_reviewers = []
|
@previous_reviewers = []
|
||||||
|
|
@ -87,7 +108,13 @@ module Emails
|
||||||
mail_answer_thread(@merge_request, merge_request_thread_options(updated_by_user_id, reason))
|
mail_answer_thread(@merge_request, merge_request_thread_options(updated_by_user_id, reason))
|
||||||
end
|
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)
|
setup_merge_request_mail(merge_request_id, recipient_id)
|
||||||
|
|
||||||
@milestone = milestone
|
@milestone = milestone
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,10 @@ module Emails
|
||||||
@domain = domain
|
@domain = domain
|
||||||
@project = domain.project
|
@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(
|
mail_with_locale(
|
||||||
to: recipient.notification_email_for(@project.group),
|
to: recipient.notification_email_for(@project.group),
|
||||||
subject: subject(subject_text)
|
subject: subject(subject_text)
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,10 @@ module Emails
|
||||||
@target_url = user_settings_personal_access_tokens_url
|
@target_url = user_settings_personal_access_tokens_url
|
||||||
@token_name = token_name
|
@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
|
end
|
||||||
|
|
||||||
def access_token_about_to_expire_email(user, token_names, params = {})
|
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
|
@target_url = user_settings_personal_access_tokens_url
|
||||||
@days_to_expire = params.fetch(:days_to_expire, PersonalAccessToken::DAYS_TO_EXPIRE)
|
@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
|
end
|
||||||
|
|
||||||
def access_token_expired_email(user, token_names = [])
|
def access_token_expired_email(user, token_names = [])
|
||||||
|
|
@ -116,7 +126,10 @@ module Emails
|
||||||
@token_names = token_names
|
@token_names = token_names
|
||||||
@target_url = user_settings_personal_access_tokens_url
|
@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
|
end
|
||||||
|
|
||||||
def access_token_revoked_email(user, token_name, source = nil)
|
def access_token_revoked_email(user, token_name, source = nil)
|
||||||
|
|
@ -127,7 +140,10 @@ module Emails
|
||||||
@target_url = user_settings_personal_access_tokens_url
|
@target_url = user_settings_personal_access_tokens_url
|
||||||
@source = source
|
@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
|
end
|
||||||
|
|
||||||
def ssh_key_expired_email(user, fingerprints)
|
def ssh_key_expired_email(user, fingerprints)
|
||||||
|
|
@ -170,7 +186,12 @@ module Emails
|
||||||
|
|
||||||
email_with_layout(
|
email_with_layout(
|
||||||
to: @user.notification_email_or_default,
|
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
|
end
|
||||||
|
|
||||||
def disabled_two_factor_email(user)
|
def disabled_two_factor_email(user)
|
||||||
|
|
@ -178,7 +199,10 @@ module Emails
|
||||||
|
|
||||||
@user = user
|
@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
|
end
|
||||||
|
|
||||||
def new_email_address_added_email(user, email)
|
def new_email_address_added_email(user, email)
|
||||||
|
|
@ -198,7 +222,12 @@ module Emails
|
||||||
|
|
||||||
email_with_layout(
|
email_with_layout(
|
||||||
to: @user.notification_email_or_default,
|
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
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,13 @@ class NotifyPreview < ActionMailer::Preview
|
||||||
diff_refs: merge_request.diff_refs
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -119,7 +125,12 @@ class NotifyPreview < ActionMailer::Preview
|
||||||
end
|
end
|
||||||
|
|
||||||
def issues_csv_email
|
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
|
end
|
||||||
|
|
||||||
def new_issue_email
|
def new_issue_email
|
||||||
|
|
@ -190,7 +201,12 @@ class NotifyPreview < ActionMailer::Preview
|
||||||
|
|
||||||
def pages_domain_enabled_email
|
def pages_domain_enabled_email
|
||||||
cleanup do
|
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
|
Notify.pages_domain_enabled_email(pages_domain, user).message
|
||||||
end
|
end
|
||||||
|
|
@ -350,7 +366,10 @@ class NotifyPreview < ActionMailer::Preview
|
||||||
end
|
end
|
||||||
|
|
||||||
def github_gists_import_errors_email
|
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
|
end
|
||||||
|
|
||||||
def bulk_import_complete
|
def bulk_import_complete
|
||||||
|
|
@ -414,23 +433,25 @@ class NotifyPreview < ActionMailer::Preview
|
||||||
end
|
end
|
||||||
|
|
||||||
def custom_email_verification
|
def custom_email_verification
|
||||||
@custom_email_verification ||= project.service_desk_custom_email_verification || ServiceDesk::CustomEmailVerification.create!(
|
@custom_email_verification ||= project.service_desk_custom_email_verification ||
|
||||||
project: project,
|
ServiceDesk::CustomEmailVerification.create!(
|
||||||
token: 'XXXXXXXXXXXX',
|
project: project,
|
||||||
triggerer: user,
|
token: 'XXXXXXXXXXXX',
|
||||||
triggered_at: Time.current,
|
triggerer: user,
|
||||||
state: 'started'
|
triggered_at: Time.current,
|
||||||
)
|
state: 'started'
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def custom_email_credential
|
def custom_email_credential
|
||||||
@custom_email_credential ||= project.service_desk_custom_email_credential || ServiceDesk::CustomEmailCredential.create!(
|
@custom_email_credential ||= project.service_desk_custom_email_credential ||
|
||||||
project: project,
|
ServiceDesk::CustomEmailCredential.create!(
|
||||||
smtp_address: 'smtp.gmail.com', # Use gmail, because Gitlab::HTTP_V2::UrlBlocker resolves DNS
|
project: project,
|
||||||
smtp_port: 587,
|
smtp_address: 'smtp.gmail.com', # Use gmail, because Gitlab::HTTP_V2::UrlBlocker resolves DNS
|
||||||
smtp_username: 'user@gmail.com',
|
smtp_port: 587,
|
||||||
smtp_password: 'supersecret'
|
smtp_username: 'user@gmail.com',
|
||||||
)
|
smtp_password: 'supersecret'
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def service_desk_setting
|
def service_desk_setting
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,16 @@ class Analytics::CycleAnalytics::Aggregation < ApplicationRecord
|
||||||
include FromUnion
|
include FromUnion
|
||||||
include Analytics::CycleAnalytics::Parentable
|
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') }
|
scope :enabled, -> { where('enabled IS TRUE') }
|
||||||
|
|
||||||
def cursor_for(mode, model)
|
def cursor_for(mode, model)
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,8 @@ module Analytics
|
||||||
|
|
||||||
scope :assigned_to, ->(user) do
|
scope :assigned_to, ->(user) do
|
||||||
assignees_class = IssueAssignee
|
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)
|
where(condition.arel.exists)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,6 @@ module Ci
|
||||||
|
|
||||||
storage_location :ci_secure_files
|
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)
|
# Use Lockbox to encrypt/decrypt the stored file (registers CarrierWave callbacks)
|
||||||
encrypt(key: :key)
|
encrypt(key: :key)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,6 @@ module Terraform
|
||||||
|
|
||||||
storage_location :terraform_state
|
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
|
delegate :terraform_state, :project_id, to: :model
|
||||||
|
|
||||||
# Use Lockbox to encrypt/decrypt the stored file (registers CarrierWave callbacks)
|
# Use Lockbox to encrypt/decrypt the stored file (registers CarrierWave callbacks)
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,11 @@
|
||||||
reporter: trizzi # (required) GitLab username of the person reporting the deprecation
|
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
|
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
|
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.
|
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.
|
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.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -10,31 +10,12 @@ class QueueBackfillSecurityFindingsProjectId < Gitlab::Database::Migration[2.2]
|
||||||
SUB_BATCH_SIZE = 100
|
SUB_BATCH_SIZE = 100
|
||||||
|
|
||||||
def up
|
def up
|
||||||
queue_batched_background_migration(
|
# no-op because there was a bug in the original migration, which has been
|
||||||
MIGRATION,
|
# fixed by https://gitlab.com/gitlab-org/gitlab/-/merge_requests/171503
|
||||||
: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
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def down
|
def down
|
||||||
delete_batched_background_migration(
|
# no-op because there was a bug in the original migration, which has been
|
||||||
MIGRATION,
|
# fixed by https://gitlab.com/gitlab-org/gitlab/-/merge_requests/171503
|
||||||
:security_findings,
|
|
||||||
:id,
|
|
||||||
[
|
|
||||||
:project_id,
|
|
||||||
:vulnerability_scanners,
|
|
||||||
:project_id,
|
|
||||||
:scanner_id
|
|
||||||
]
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
93fc508504c8fb566244c2528d5b78d74c541d10f3e4a769c32fc18de464eedc
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
9428a6ad7128e71a67a2c23903ad1318a008f79b54d1bf9558d802cc378d9da3
|
||||||
|
|
@ -2338,6 +2338,12 @@ You **turn on** or **turn off** a toggle. For example:
|
||||||
|
|
||||||
- Turn on the **blah** toggle.
|
- Turn on the **blah** toggle.
|
||||||
|
|
||||||
|
## top-level group
|
||||||
|
|
||||||
|
Use lowercase for **top-level group** (hyphenated).
|
||||||
|
|
||||||
|
Do not use **root group**.
|
||||||
|
|
||||||
## TFA, two-factor authentication
|
## TFA, two-factor authentication
|
||||||
|
|
||||||
Use [**2FA** and **two-factor authentication**](#2fa-two-factor-authentication) instead.
|
Use [**2FA** and **two-factor authentication**](#2fa-two-factor-authentication) instead.
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ GitLab offers two Jira integrations. You can use one or both integrations
|
||||||
|
|
||||||
### Jira issues integration
|
### 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
|
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:
|
Jira Cloud, Jira Data Center, or Jira Server. With this integration, you can:
|
||||||
|
|
|
||||||
|
|
@ -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">
|
<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
|
### Remove `previousStageJobsOrNeeds` from GraphQL
|
||||||
|
|
||||||
<div class="deprecation-notes">
|
<div class="deprecation-notes">
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,8 @@ module API
|
||||||
before { authenticate_non_get! }
|
before { authenticate_non_get! }
|
||||||
|
|
||||||
allow_access_with_scope :ai_workflows, if: ->(request) do
|
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
|
end
|
||||||
|
|
||||||
rescue_from ActiveRecord::QueryCanceled do |_e|
|
rescue_from ActiveRecord::QueryCanceled do |_e|
|
||||||
|
|
@ -713,8 +714,7 @@ module API
|
||||||
merge_request = find_project_merge_request(params[:merge_request_iid])
|
merge_request = find_project_merge_request(params[:merge_request_iid])
|
||||||
|
|
||||||
# Merge request can not be merged because the user doesn't have
|
# 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)
|
unauthorized! unless merge_request.can_be_merged_by?(current_user)
|
||||||
|
|
||||||
merge_when_pipeline_succeeds = to_boolean(params[:merge_when_pipeline_succeeds])
|
merge_when_pipeline_succeeds = to_boolean(params[:merge_when_pipeline_succeeds])
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,10 @@ module API
|
||||||
include ::API::Helpers::RelatedResourcesHelpers
|
include ::API::Helpers::RelatedResourcesHelpers
|
||||||
|
|
||||||
expose :url do |setting|
|
expose :url do |setting|
|
||||||
expose_path(api_v4_vscode_settings_sync_v1_resource_path(
|
resource_name = setting[:setting_type]
|
||||||
resource_name: setting[:setting_type],
|
id = setting[:uuid]
|
||||||
id: setting[:uuid]
|
path = "/api/v4/vscode/settings_sync/v1/resource/#{resource_name}/#{id}"
|
||||||
))
|
expose_path(path)
|
||||||
end
|
end
|
||||||
expose :created do |setting|
|
expose :created do |setting|
|
||||||
setting[:updated_at]&.to_i
|
setting[:updated_at]&.to_i
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ module API
|
||||||
end
|
end
|
||||||
|
|
||||||
resource :vscode do
|
resource :vscode do
|
||||||
resource :settings_sync do
|
resource '/settings_sync(/:settings_context_hash)' do
|
||||||
content_type :json, 'application/json'
|
content_type :json, 'application/json'
|
||||||
content_type :json, 'text/plain'
|
content_type :json, 'text/plain'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,14 @@ module BitbucketServer
|
||||||
commit.dig('committer', 'displayName')
|
commit.dig('committer', 'displayName')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def committer_name
|
||||||
|
commit.dig('committer', 'displayName')
|
||||||
|
end
|
||||||
|
|
||||||
|
def committer_username
|
||||||
|
commit.dig('committer', 'slug')
|
||||||
|
end
|
||||||
|
|
||||||
def committer_email
|
def committer_email
|
||||||
commit.dig('committer', 'emailAddress')
|
commit.dig('committer', 'emailAddress')
|
||||||
end
|
end
|
||||||
|
|
@ -53,6 +61,10 @@ module BitbucketServer
|
||||||
action == 'APPROVED'
|
action == 'APPROVED'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def approver_name
|
||||||
|
raw.dig('user', 'displayName')
|
||||||
|
end
|
||||||
|
|
||||||
def approver_username
|
def approver_username
|
||||||
raw.dig('user', 'slug')
|
raw.dig('user', 'slug')
|
||||||
end
|
end
|
||||||
|
|
@ -65,6 +77,10 @@ module BitbucketServer
|
||||||
action == 'DECLINED'
|
action == 'DECLINED'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def decliner_name
|
||||||
|
raw.dig('user', 'displayName')
|
||||||
|
end
|
||||||
|
|
||||||
def decliner_username
|
def decliner_username
|
||||||
raw.dig('user', 'slug')
|
raw.dig('user', 'slug')
|
||||||
end
|
end
|
||||||
|
|
@ -80,12 +96,16 @@ module BitbucketServer
|
||||||
def to_hash
|
def to_hash
|
||||||
{
|
{
|
||||||
id: id,
|
id: id,
|
||||||
|
committer_name: committer_user,
|
||||||
committer_user: committer_user,
|
committer_user: committer_user,
|
||||||
|
committer_username: committer_username,
|
||||||
committer_email: committer_email,
|
committer_email: committer_email,
|
||||||
merge_timestamp: merge_timestamp,
|
merge_timestamp: merge_timestamp,
|
||||||
merge_commit: merge_commit,
|
merge_commit: merge_commit,
|
||||||
|
approver_name: approver_name,
|
||||||
approver_username: approver_username,
|
approver_username: approver_username,
|
||||||
approver_email: approver_email,
|
approver_email: approver_email,
|
||||||
|
decliner_name: decliner_name,
|
||||||
decliner_username: decliner_username,
|
decliner_username: decliner_username,
|
||||||
decliner_email: decliner_email,
|
decliner_email: decliner_email,
|
||||||
created_at: created_at
|
created_at: created_at
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,10 @@ module BitbucketServer
|
||||||
raw_comment['id']
|
raw_comment['id']
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def author_name
|
||||||
|
author['displayName']
|
||||||
|
end
|
||||||
|
|
||||||
def author_username
|
def author_username
|
||||||
author['username'] ||
|
author['username'] ||
|
||||||
author['slug'] ||
|
author['slug'] ||
|
||||||
|
|
@ -81,6 +85,7 @@ module BitbucketServer
|
||||||
|
|
||||||
{
|
{
|
||||||
id: id,
|
id: id,
|
||||||
|
author_name: author_name,
|
||||||
author_email: author_email,
|
author_email: author_email,
|
||||||
author_username: author_username,
|
author_username: author_username,
|
||||||
note: note,
|
note: note,
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,14 @@ module Gitlab
|
||||||
class BackfillSecurityFindingsProjectId < BackfillDesiredShardingKeyJob
|
class BackfillSecurityFindingsProjectId < BackfillDesiredShardingKeyJob
|
||||||
operation_name :backfill_security_findings_project_id
|
operation_name :backfill_security_findings_project_id
|
||||||
feature_category :vulnerability_management
|
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
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,8 @@ RSpec.describe BitbucketServer::Representation::Activity, feature_category: :imp
|
||||||
it { expect(subject.comment?).to be_falsey }
|
it { expect(subject.comment?).to be_falsey }
|
||||||
it { expect(subject.inline_comment?).to be_falsey }
|
it { expect(subject.inline_comment?).to be_falsey }
|
||||||
it { expect(subject.committer_user).to eq('root') }
|
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.committer_email).to eq('test.user@example.com') }
|
||||||
it { expect(subject.merge_timestamp).to be_a(Time) }
|
it { expect(subject.merge_timestamp).to be_a(Time) }
|
||||||
it { expect(subject.created_at).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(
|
a_hash_including(
|
||||||
id: 7,
|
id: 7,
|
||||||
committer_user: 'root',
|
committer_user: 'root',
|
||||||
|
committer_name: 'root',
|
||||||
|
committer_username: 'slug',
|
||||||
committer_email: 'test.user@example.com',
|
committer_email: 'test.user@example.com',
|
||||||
merge_commit: '839fa9a2d434eb697815b8fcafaecc51accfdbbc'
|
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.inline_comment?).to be_falsey }
|
||||||
it { expect(subject.merge_event?).to be_falsey }
|
it { expect(subject.merge_event?).to be_falsey }
|
||||||
it { expect(subject.approved_event?).to be_truthy }
|
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_username).to eq('slug') }
|
||||||
it { expect(subject.approver_email).to eq('test.user@example.com') }
|
it { expect(subject.approver_email).to eq('test.user@example.com') }
|
||||||
it { expect(subject.created_at).to be_a(Time) }
|
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(
|
expect(subject.to_hash).to match(
|
||||||
a_hash_including(
|
a_hash_including(
|
||||||
id: 15,
|
id: 15,
|
||||||
|
approver_name: 'root',
|
||||||
approver_username: 'slug',
|
approver_username: 'slug',
|
||||||
approver_email: 'test.user@example.com'
|
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.inline_comment?).to be_falsey }
|
||||||
it { expect(subject.merge_event?).to be_falsey }
|
it { expect(subject.merge_event?).to be_falsey }
|
||||||
it { expect(subject.declined_event?).to be_truthy }
|
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_username).to eq('slug') }
|
||||||
it { expect(subject.decliner_email).to eq('test.user@example.com') }
|
it { expect(subject.decliner_email).to eq('test.user@example.com') }
|
||||||
it { expect(subject.created_at).to be_a(Time) }
|
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(
|
expect(subject.to_hash).to match(
|
||||||
a_hash_including(
|
a_hash_including(
|
||||||
id: 18,
|
id: 18,
|
||||||
|
decliner_name: 'root',
|
||||||
decliner_username: 'slug',
|
decliner_username: 'slug',
|
||||||
decliner_email: 'test.user@example.com'
|
decliner_email: 'test.user@example.com'
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -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(:activities) { Gitlab::Json.parse(fixture_file('importers/bitbucket_server/activities.json'))['values'] }
|
||||||
let(:comment) { activities.first }
|
let(:comment) { activities.first }
|
||||||
|
|
||||||
subject { described_class.new(comment) }
|
subject(:comment_representation) { described_class.new(comment) }
|
||||||
|
|
||||||
describe '#id' do
|
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
|
end
|
||||||
|
|
||||||
describe '#author_username' do
|
describe '#author_username' do
|
||||||
it 'returns username' do
|
it 'returns username' do
|
||||||
expect(subject.author_username).to eq('username')
|
expect(comment_representation.author_username).to eq('username')
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when username is absent' do
|
context 'when username is absent' do
|
||||||
|
|
@ -23,7 +27,7 @@ RSpec.describe BitbucketServer::Representation::Comment, feature_category: :impo
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns slug' do
|
it 'returns slug' do
|
||||||
expect(subject.author_username).to eq('slug')
|
expect(comment_representation.author_username).to eq('slug')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -34,55 +38,56 @@ RSpec.describe BitbucketServer::Representation::Comment, feature_category: :impo
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns displayName' do
|
it 'returns displayName' do
|
||||||
expect(subject.author_username).to eq('root')
|
expect(comment_representation.author_username).to eq('root')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#author_email' do
|
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
|
end
|
||||||
|
|
||||||
describe '#note' do
|
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
|
end
|
||||||
|
|
||||||
describe '#created_at' do
|
describe '#created_at' do
|
||||||
it { expect(subject.created_at).to be_a(Time) }
|
it { expect(comment_representation.created_at).to be_a(Time) }
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#updated_at' do
|
describe '#updated_at' do
|
||||||
it { expect(subject.created_at).to be_a(Time) }
|
it { expect(comment_representation.updated_at).to be_a(Time) }
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#comments' do
|
describe '#comments' do
|
||||||
it { expect(subject.comments.count).to eq(4) }
|
it { expect(comment_representation.comments.count).to eq(4) }
|
||||||
it { expect(subject.comments).to all(be_a(described_class)) }
|
it { expect(comment_representation.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.map(&:note)).to match_array(["Hello world", "Ok", "hello", "hi"]) }
|
||||||
|
|
||||||
# The thread should look like:
|
# The thread should look like:
|
||||||
#
|
#
|
||||||
# is this a new line? (subject)
|
# is this a new line? (comment_representation)
|
||||||
# -> Hello world (first)
|
# -> Hello world (first)
|
||||||
# -> Ok (third)
|
# -> Ok (third)
|
||||||
# -> Hi (fourth)
|
# -> Hi (fourth)
|
||||||
# -> hello (second)
|
# -> hello (second)
|
||||||
it 'comments have the right parent' do
|
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(comment_representation.parent_comment).to be_nil
|
||||||
expect(first.parent_comment).to eq(subject)
|
expect(first.parent_comment).to eq(comment_representation)
|
||||||
expect(second.parent_comment).to eq(subject)
|
expect(second.parent_comment).to eq(comment_representation)
|
||||||
expect(third.parent_comment).to eq(first)
|
expect(third.parent_comment).to eq(first)
|
||||||
expect(fourth.parent_comment).to eq(first)
|
expect(fourth.parent_comment).to eq(first)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#to_hash' do
|
describe '#to_hash' do
|
||||||
it do
|
specify do
|
||||||
expect(subject.to_hash).to match(
|
expect(comment_representation.to_hash).to match(
|
||||||
a_hash_including(
|
a_hash_including(
|
||||||
id: 9,
|
id: 9,
|
||||||
|
author_name: 'root',
|
||||||
author_email: 'test.user@example.com',
|
author_email: 'test.user@example.com',
|
||||||
author_username: 'username',
|
author_username: 'username',
|
||||||
note: 'is this a new line?',
|
note: 'is this a new line?',
|
||||||
|
|
|
||||||
|
|
@ -6,27 +6,14 @@ require_migration!
|
||||||
RSpec.describe QueueBackfillSecurityFindingsProjectId, migration: :gitlab_sec, feature_category: :vulnerability_management do
|
RSpec.describe QueueBackfillSecurityFindingsProjectId, migration: :gitlab_sec, feature_category: :vulnerability_management do
|
||||||
let!(:batched_migration) { described_class::MIGRATION }
|
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|
|
reversible_migration do |migration|
|
||||||
migration.before -> {
|
migration.before -> {
|
||||||
expect(batched_migration).not_to have_scheduled_batched_migration
|
expect(batched_migration).not_to have_scheduled_batched_migration
|
||||||
}
|
}
|
||||||
|
|
||||||
migration.after -> {
|
migration.after -> {
|
||||||
expect(batched_migration).to have_scheduled_batched_migration(
|
expect(batched_migration).not_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
|
|
||||||
]
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -2810,6 +2810,19 @@ RSpec.describe API::MergeRequests, :aggregate_failures, feature_category: :sourc
|
||||||
expect(json_response['reviewers']).to be_empty
|
expect(json_response['reviewers']).to be_empty
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|
||||||
describe "POST /projects/:id/merge_requests/:merge_request_iid/context_commits" do
|
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
|
context 'with oauth token that has ai_workflows scope' do
|
||||||
let(:token) { create(:oauth_access_token, user: user, scopes: [:ai_workflows]) }
|
let(:token) { create(:oauth_access_token, user: user, scopes: [:ai_workflows]) }
|
||||||
|
|
||||||
it "allows access" do
|
it "does not allow access" do
|
||||||
put api(
|
put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/merge", oauth_access_token: token)
|
||||||
"/projects/#{project.id}/merge_requests/#{merge_request.iid}?title=new_title",
|
|
||||||
oauth_access_token: token
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:forbidden)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -4003,6 +4013,16 @@ RSpec.describe API::MergeRequests, :aggregate_failures, feature_category: :sourc
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'PUT :id/merge_requests/:merge_request_iid/rebase' do
|
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
|
context 'when rebase can be performed' do
|
||||||
it 'enqueues a rebase of the merge request against the target branch' do
|
it 'enqueues a rebase of the merge request against the target branch' do
|
||||||
Sidekiq::Testing.fake! do
|
Sidekiq::Testing.fake! do
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue