Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2024-08-06 15:07:35 +00:00
parent 9c7daf084d
commit ce86f6db28
68 changed files with 1033 additions and 377 deletions

View File

@ -264,6 +264,8 @@
{"name":"google-protobuf","version":"3.25.4","platform":"x86-mingw32","checksum":"fb00901bb3803ed361eb8e667af4d1ab2136c43ae870cc8b3a2ded08ceee2072"},
{"name":"google-protobuf","version":"3.25.4","platform":"x86_64-darwin","checksum":"bf53084c00d78a8a960af5a3fc5175c59f1f4708ddd00398781a2c3a3370c977"},
{"name":"google-protobuf","version":"3.25.4","platform":"x86_64-linux","checksum":"9e8e66fb5a00cf90f88f37b07e7da10ca9e176e28a3314fc80c4e7fdab120aeb"},
{"name":"google-cloud-storage_transfer","version":"1.2.0","platform":"ruby","checksum":"132901f50889e02a0d378e6117c6408cbfc4fdbd15c9d31fabec4f4189ef1658"},
{"name":"google-cloud-storage_transfer-v1","version":"0.8.0","platform":"ruby","checksum":"9dbef80275db556e046bb24139ca6559affe641d1e38b2537b8caaf2f8896176"},
{"name":"googleapis-common-protos","version":"1.4.0","platform":"ruby","checksum":"da2380fb5ab1563580816c74e8d684ac17512c3654c829a3ee84f6d6139de382"},
{"name":"googleapis-common-protos-types","version":"1.5.0","platform":"ruby","checksum":"5769cf7376abc86ef7f5897a4aaca1d5c5a3c49ddabeddd2c251fcf8155f858b"},
{"name":"googleauth","version":"1.8.1","platform":"ruby","checksum":"814adadaaa1221dce72a67131e3ecbd6d23491a161ec84fb15fd353b87d8c9e7"},
@ -725,7 +727,7 @@
{"name":"tty-prompt","version":"0.23.1","platform":"ruby","checksum":"fcdbce905238993f27eecfdf67597a636bc839d92192f6a0eef22b8166449ec8"},
{"name":"tty-reader","version":"0.9.0","platform":"ruby","checksum":"c62972c985c0b1566f0e56743b6a7882f979d3dc32ff491ed490a076f899c2b1"},
{"name":"tty-screen","version":"0.8.1","platform":"ruby","checksum":"6508657c38f32bdca64880abe201ce237d80c94146e1f9b911cba3c7823659a2"},
{"name":"typhoeus","version":"1.4.0","platform":"ruby","checksum":"fff9880d5dc35950e7706cf132fd297f377c049101794be1cf01c95567f642d4"},
{"name":"typhoeus","version":"1.4.1","platform":"ruby","checksum":"1c17db8364bd45ab302dc61e460173c3e69835896be88a3df07c206d5c55ef7c"},
{"name":"tzinfo","version":"2.0.6","platform":"ruby","checksum":"8daf828cc77bcf7d63b0e3bdb6caa47e2272dcfaf4fbfe46f8c3a9df087a829b"},
{"name":"uber","version":"0.1.0","platform":"ruby","checksum":"5beeb407ff807b5db994f82fa9ee07cfceaa561dad8af20be880bc67eba935dc"},
{"name":"undercover","version":"0.5.0","platform":"ruby","checksum":"ef99a8478be5466fb13fcd199f659ae308b81f71145a5a4e57428ff67d109fae"},

View File

@ -28,6 +28,8 @@ PATH
specs:
gitlab-backup-cli (0.0.1)
activesupport (< 7.2)
google-cloud-storage_transfer (~> 1.2.0)
googleauth (~> 1.8.1)
rainbow (~> 3.0)
thor (~> 1.3)
@ -843,6 +845,12 @@ GEM
googleauth (>= 0.16.2, < 2.a)
mini_mime (~> 1.0)
google-protobuf (3.25.4)
google-cloud-storage_transfer (1.2.0)
google-cloud-core (~> 1.6)
google-cloud-storage_transfer-v1 (>= 0.5, < 2.a)
google-cloud-storage_transfer-v1 (0.8.0)
gapic-common (>= 0.20.0, < 2.a)
google-cloud-errors (~> 1.0)
googleapis-common-protos (1.4.0)
google-protobuf (~> 3.14)
googleapis-common-protos-types (~> 1.2)
@ -1846,7 +1854,7 @@ GEM
tty-screen (~> 0.8)
wisper (~> 2.0)
tty-screen (0.8.1)
typhoeus (1.4.0)
typhoeus (1.4.1)
ethon (>= 0.9.0)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)

View File

@ -719,7 +719,13 @@ const Api = {
.replace(':id', encodeURIComponent(id))
.replace(':merge_request_iid', mergeRequestId);
return axios.post(url);
const params = {};
if (gon.features.asyncMergeRequestPipelineCreation) {
params.async = true;
}
return axios.post(url, params);
},
releases(id, options = {}) {

View File

@ -53,7 +53,7 @@ export default {
<template v-if="fileName">
<file-icon :file-name="fileName" :size="16" aria-hidden="true" css-classes="gl-mr-3" />
<strong
class="file-title-name mr-1 js-blob-header-filepath"
class="file-title-name mr-1 js-blob-header-filepath gl-break-all gl-text-decoration-none!"
data-testid="file-title-content"
>{{ fileName }}</strong
>

View File

@ -96,15 +96,15 @@ export default {
<div>
<form-errors-alert v-model="errors" />
<gl-card
class="gl-new-card gl-mt-0"
header-class="gl-new-card-header gl-flex-col"
body-class="gl-new-card-body gl-px-5 gl-py-4"
class="gl-mt-0"
header-class="gl-px-5 gl-py-4 gl-border-b-1 gl-border-b-solid gl-border-gray-100"
body-class="gl-px-5 gl-py-4"
>
<template #header>
<div class="gl-new-card-title-wrapper">
<h4 class="gl-new-card-title">{{ $options.i18n.cardHeaderTitle }}</h4>
<div class="gl-flex gl-grow">
<h4 class="gl-text-base gl-leading-24 gl-m-0">{{ $options.i18n.cardHeaderTitle }}</h4>
</div>
<p class="gl-new-card-description">{{ $options.i18n.cardHeaderDescription }}</p>
<p class="gl-text-subtle gl-text-sm gl-m-0">{{ $options.i18n.cardHeaderDescription }}</p>
</template>
<gl-form :id="$options.formId">
<gl-form-fields

View File

@ -1,8 +0,0 @@
mutation ProjectSetContinuousVulnerabilityScanning(
$input: ProjectSetContinuousVulnerabilityScanningInput!
) {
projectSetContinuousVulnerabilityScanning(input: $input) {
continuousVulnerabilityScanningEnabled
errors
}
}

View File

@ -1,9 +1,28 @@
@import 'framework/variables';
.build-page {
// color codes are based on http://en.wikipedia.org/wiki/File:Xterm_256color_chart.svg
// see also: https://gist.github.com/jasonm23/2868981
.term-bold {
font-weight: $gl-font-weight-bold;
}
.term-italic {
font-style: italic;
}
.term-conceal {
visibility: hidden;
}
.term-underline {
text-decoration: underline;
}
.term-cross {
text-decoration: line-through;
}
// logs color palette
// see: https://en.wikipedia.org/wiki/ANSI_escape_code#Colors
$black: #000;
$red: #ea1010;
$green: #090;
@ -21,24 +40,52 @@
$l-cyan: #00bdbd;
$l-white: #fff;
$colors: "black" $black $l-black,
"red" $red $l-red,
"green" $green $l-green,
"yellow" $yellow $l-yellow,
"blue" $blue $l-blue,
"magenta" $magenta $l-magenta,
"cyan" $cyan $l-cyan,
"white" $white $l-white;
@each $name, $color, $brightColor in $colors {
.term-fg-#{$name} {
color: $color;
}
.term-bg-#{$name} {
background-color: $color;
}
.term-fg-l-#{$name} {
color: $brightColor;
}
.term-bg-l-#{$name} {
background-color: $brightColor;
}
}
// 8-bit colors
// color codes are based on http://en.wikipedia.org/wiki/File:Xterm_256color_chart.svg
$xterm-colors: (
"0": $black,
"1": #800000,
"2": #008000,
"3": #808000,
"4": #000080,
"5": #800080,
"6": #008080,
"7": #c0c0c0,
"8": #808080,
"9": #f00,
"10": #0f0,
"11": #ff0,
"12": #00f,
"13": #f0f,
"14": #0ff,
"15": $white,
"16": $black,
"1": $red,
"2": $green,
"3": $yellow,
"4": $blue,
"5": $magenta,
"6": $cyan,
"7": $white,
"8": $l-black,
"9": $l-red,
"10": $l-green,
"11": $l-yellow,
"12": $l-blue,
"13": $l-magenta,
"14": $l-cyan,
"15": $l-white,
"16": #000,
"17": #00005f,
"18": #000087,
"19": #0000af,
@ -253,7 +300,7 @@
"228": #ffff87,
"229": #ffffaf,
"230": #ffffd7,
"231": $white,
"231": #fff,
"232": #080808,
"233": #121212,
"234": #1c1c1c,
@ -280,161 +327,11 @@
"255": #eee
);
.term-bold {
font-weight: $gl-font-weight-bold;
}
.term-italic {
font-style: italic;
}
.term-conceal {
visibility: hidden;
}
.term-underline {
text-decoration: underline;
}
.term-cross {
text-decoration: line-through;
}
.term-fg-black {
color: $black;
}
.term-fg-red {
color: $red;
}
.term-fg-green {
color: $green;
}
.term-fg-yellow {
color: $yellow;
}
.term-fg-blue {
color: $blue;
}
.term-fg-magenta {
color: $magenta;
}
.term-fg-cyan {
color: $cyan;
}
.term-fg-white {
color: $white;
}
.term-fg-l-black {
color: $l-black;
}
.term-fg-l-red {
color: $l-red;
}
.term-fg-l-green {
color: $l-green;
}
.term-fg-l-yellow {
color: $l-yellow;
}
.term-fg-l-blue {
color: $l-blue;
}
.term-fg-l-magenta {
color: $l-magenta;
}
.term-fg-l-cyan {
color: $l-cyan;
}
.term-fg-l-white {
color: $l-white;
}
.term-bg-black {
background-color: $black;
}
.term-bg-red {
background-color: $red;
}
.term-bg-green {
background-color: $green;
}
.term-bg-yellow {
background-color: $yellow;
}
.term-bg-blue {
background-color: $blue;
}
.term-bg-magenta {
background-color: $magenta;
}
.term-bg-cyan {
background-color: $cyan;
}
.term-bg-white {
background-color: $white;
}
.term-bg-l-black {
background-color: $l-black;
}
.term-bg-l-red {
background-color: $l-red;
}
.term-bg-l-green {
background-color: $l-green;
}
.term-bg-l-yellow {
background-color: $l-yellow;
}
.term-bg-l-blue {
background-color: $l-blue;
}
.term-bg-l-magenta {
background-color: $l-magenta;
}
.term-bg-l-cyan {
background-color: $l-cyan;
}
.term-bg-l-white {
background-color: $l-white;
}
@each $i, $color in $xterm-colors {
.xterm-fg-#{$i} {
color: $color;
}
}
@each $i, $color in $xterm-colors {
.xterm-bg-#{$i} {
background-color: $color;
}

View File

@ -24,11 +24,13 @@ class ProfilesController < Profiles::ApplicationController
end
def reset_feed_token
Users::UpdateService.new(current_user, user: @user).execute! do |user|
user.reset_feed_token!
end
service = Users::ResetFeedTokenService.new(current_user, user: @user).execute
flash[:notice] = s_('Profiles|Feed token was successfully reset')
if service.success?
flash[:notice] = service.message
else
flash[:alert] = service.message
end
redirect_to user_settings_personal_access_tokens_path
end

View File

@ -45,6 +45,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
push_frontend_feature_flag(:notifications_todos_buttons, current_user)
push_frontend_feature_flag(:pinned_file, project)
push_frontend_feature_flag(:reviewer_assign_drawer, current_user)
push_frontend_feature_flag(:async_merge_request_pipeline_creation, current_user)
end
around_action :allow_gitaly_ref_name_caching, only: [:index, :show, :diffs, :rapid_diffs, :discussions]

View File

@ -20,7 +20,6 @@ module Projects
gitlab_ci_present: project.has_ci_config_file?,
gitlab_ci_history_path: gitlab_ci_history_path,
security_training_enabled: project.security_training_available?,
continuous_vulnerability_scans_enabled: continuous_vulnerability_scans_enabled,
container_scanning_for_registry_enabled: container_scanning_for_registry_enabled,
pre_receive_secret_detection_available:
Gitlab::CurrentSettings.current_application_settings.pre_receive_secret_detection_enabled,
@ -113,7 +112,6 @@ module Projects
project.security_setting
end
def continuous_vulnerability_scans_enabled; end
def container_scanning_for_registry_enabled; end
def pre_receive_secret_detection_enabled; end
end

View File

@ -142,8 +142,6 @@ module Repositories
end
def verify_commit_range!(from, to)
return unless Feature.enabled?(:changelog_commits_limitation, @project)
commits = @project.repository.commits_by(oids: [from, to])
raise Gitlab::Changelog::Error, "Invalid or not found commit value in the given range" unless commits.count == 2

View File

@ -0,0 +1,59 @@
# frozen_string_literal: true
module Users
class ResetFeedTokenService < BaseService
VALID_SOURCES = %i[self].freeze
def initialize(current_user = nil, user: nil, source: nil)
@current_user = current_user
@user = user
@source = source
@source = :self if @current_user && !@source
raise ArgumentError unless user
raise ArgumentError unless VALID_SOURCES.include?(@source)
end
def execute
return ServiceResponse.error(message: s_('Not permitted to reset user feed token')) unless reset_permitted?
result = Users::UpdateService.new(current_user, user: user).execute(&:reset_feed_token!)
if result[:status] == :success
log_event
ServiceResponse.success(message: success_message)
else
ServiceResponse.error(message: error_message)
end
end
private
attr_reader :user, :source
def error_message
s_('Profiles|Feed token could not be reset')
end
def success_message
s_('Profiles|Feed token was successfully reset')
end
def reset_permitted?
case source
when :self
Ability.allowed?(current_user, :update_user, user)
end
end
def log_event
Gitlab::AppLogger.info(
class: self.class.name,
message: "User Feed Token Reset",
source: source,
reset_by: current_user&.username,
reset_for: user.username,
user_id: user.id)
end
end
end

View File

@ -2,10 +2,10 @@
- project = local_assigns.fetch(:project)
= render Pajamas::CardComponent.new(card_options: { class: 'gl-new-card', data: { testid: 'export-project-content' } }, header_options: { class: 'gl-new-card-header' }, body_options: { class: 'gl-new-card-body gl-px-5 gl-py-4' }) do |c|
= render Pajamas::CardComponent.new(card_options: { data: { testid: 'export-project-content' } }, header_options: { class: 'gl-px-5 gl-py-4 gl-border-b-1 gl-border-b-solid gl-border-gray-100' }, body_options: { class: 'gl-px-5 gl-py-4' }) do |c|
- c.with_header do
.gl-new-card-title-wrapper
%h4.gl-new-card-title= _('Export project')
.gl-flex.gl-grow
%h4.gl-text-base.gl-leading-24.gl-m-0= _('Export project')
- c.with_body do
%p

View File

@ -3,10 +3,10 @@
- issues_count = Projects::AllIssuesCountService.new(project).count
- forks_count = Projects::ForksCountService.new(project).count
= render Pajamas::CardComponent.new(card_options: { class: 'gl-new-card' }, header_options: { class: 'gl-new-card-header gl-flex-direction-column' }, body_options: { class: 'gl-new-card-body gl-bg-red-50 gl-px-5 gl-py-4' }) do |c|
= render Pajamas::CardComponent.new(header_options: { class: 'gl-px-5 gl-py-4 gl-border-b-1 gl-border-b-solid gl-border-gray-100' }, body_options: { class: 'gl-bg-red-50 gl-px-5 gl-py-4' }) do |c|
- c.with_header do
.gl-new-card-title-wrapper
%h4.gl-new-card-title.danger-title= _('Delete project')
.gl-flex.gl-grow
%h4.gl-text-base.gl-leading-24.gl-m-0.danger-title= _('Delete project')
- c.with_body do
%p

View File

@ -1,11 +1,11 @@
- return unless @project.forked? && can?(current_user, :remove_fork_project, @project)
- remove_form_id = "js-remove-project-fork-form"
= render Pajamas::CardComponent.new(card_options: { class: 'gl-new-card' }, header_options: { class: 'gl-new-card-header gl-flex-direction-column' }, body_options: { class: 'gl-new-card-body gl-px-5 gl-py-4' }) do |c|
= render Pajamas::CardComponent.new(header_options: { class: 'gl-px-5 gl-py-4 gl-border-b-1 gl-border-b-solid gl-border-gray-100' }, body_options: { class: 'gl-px-5 gl-py-4' }) do |c|
- c.with_header do
.gl-new-card-title-wrapper
%h4.gl-new-card-title.danger-title= _('Remove fork relationship')
%p.gl-new-card-description
.gl-flex.gl-grow
%h4.gl-text-base.gl-leading-24.gl-m-0.danger-title= _('Remove fork relationship')
%p.gl-text-subtle.gl-text-sm.gl-m-0
= remove_fork_project_description_message(@project)
- c.with_body do

View File

@ -3,11 +3,11 @@
- hidden_input_id = "new_namespace_id"
- initial_data = { button_text: s_('ProjectSettings|Transfer project'), confirm_danger_message: transfer_project_message(@project), confirm_button_text: transfer_project_confirm_button, phrase: @project.name, target_form_id: form_id, target_hidden_input_id: hidden_input_id, project_id: @project.id }
= render Pajamas::CardComponent.new(card_options: { class: 'gl-new-card', data: { testid: 'transfer-project-content' } }, header_options: { class: 'gl-new-card-header gl-flex-direction-column' }, body_options: { class: 'gl-new-card-body gl-px-5 gl-py-4' }) do |c|
= render Pajamas::CardComponent.new(card_options: { data: { testid: 'transfer-project-content' } }, header_options: { class: 'gl-px-5 gl-py-4 gl-border-b-1 gl-border-b-solid gl-border-gray-100' }, body_options: { class: 'gl-px-5 gl-py-4' }) do |c|
- c.with_header do
.gl-new-card-title-wrapper
%h4.gl-new-card-title.warning-title= _('Transfer project')
%p.gl-new-card-description
.gl-flex.gl-grow
%h4.gl-text-base.gl-leading-24.gl-m-0= _('Transfer project')
%p.gl-text-subtle.gl-text-sm.gl-m-0
- link = link_to('', help_page_path('user/project/settings/migrate_projects', anchor: 'transfer-a-project-to-another-namespace'), target: '_blank', rel: 'noopener noreferrer')
= safe_format(_("Transfer your project into another namespace. %{link_start}Learn more.%{link_end}"), tag_pair(link, :link_start, :link_end))

View File

@ -52,57 +52,58 @@
- c.with_body do
= render_if_exists 'projects/settings/restore', project: @project
= render Pajamas::CardComponent.new(card_options: { class: 'gl-new-card gl-mt-0' }, header_options: { class: 'gl-new-card-header gl-flex-direction-column' }, body_options: { class: 'gl-new-card-body gl-px-5 gl-py-4' }) do |c|
- c.with_header do
.gl-new-card-title-wrapper
%h4.gl-new-card-title= _('Housekeeping')
%p.gl-new-card-description
= _('Runs a number of housekeeping tasks within the current repository, such as compressing file revisions and removing unreachable objects.')
= link_to _('Learn more.'), help_page_path('administration/housekeeping'), target: '_blank', rel: 'noopener noreferrer'
.gl-flex.gl-gap-5.gl-flex-col
= render Pajamas::CardComponent.new(header_options: { class: 'gl-px-5 gl-py-4 gl-border-b-1 gl-border-b-solid gl-border-gray-100' }, body_options: { class: 'gl-px-5 gl-py-4' }) do |c|
- c.with_header do
.gl-flex.gl-grow
%h4.gl-text-base.gl-leading-24.gl-m-0= _('Housekeeping')
%p.gl-text-subtle.gl-text-sm.gl-m-0
= _('Runs a number of housekeeping tasks within the current repository, such as compressing file revisions and removing unreachable objects.')
= link_to _('Learn more.'), help_page_path('administration/housekeeping'), target: '_blank', rel: 'noopener noreferrer'
- c.with_body do
.gl-display-flex.gl-flex-wrap.gl-gap-3
= render Pajamas::ButtonComponent.new(method: :post, href: housekeeping_project_path(@project)) do
= _('Run housekeeping')
#js-project-prune-unreachable-objects-button{ data: { prune_objects_path: housekeeping_project_path(@project, prune: true), prune_objects_doc_path: help_page_path('administration/housekeeping', anchor: 'prune-unreachable-objects') } }
- c.with_body do
.gl-flex.gl-flex-wrap.gl-gap-3
= render Pajamas::ButtonComponent.new(method: :post, href: housekeeping_project_path(@project)) do
= _('Run housekeeping')
#js-project-prune-unreachable-objects-button{ data: { prune_objects_path: housekeeping_project_path(@project, prune: true), prune_objects_doc_path: help_page_path('administration/housekeeping', anchor: 'prune-unreachable-objects') } }
= render 'export', project: @project
= render 'export', project: @project
= render_if_exists 'projects/settings/archive'
= render_if_exists 'projects/settings/archive'
= render Pajamas::CardComponent.new(card_options: { class: 'gl-new-card rename-repository' }, header_options: { class: 'gl-new-card-header gl-flex-direction-column' }, body_options: { class: 'gl-new-card-body gl-px-5 gl-py-4' }) do |c|
- c.with_header do
.gl-new-card-title-wrapper
%h4.gl-new-card-title.warning-title= _('Change path')
%p.gl-new-card-description
- link = link_to('', help_page_path('user/project/working_with_projects', anchor: 'rename-a-repository'), target: '_blank', rel: 'noopener noreferrer')
= safe_format(_("A projects repository name defines its URL (the one you use to access the project via a browser) and its place on the file disk where GitLab is installed. %{link_start}Learn more.%{link_end}"), tag_pair(link, :link_start, :link_end))
= render Pajamas::CardComponent.new(header_options: { class: 'gl-px-5 gl-py-4 gl-border-b-1 gl-border-b-solid gl-border-gray-100' }, body_options: { class: 'gl-px-5 gl-py-4' }) do |c|
- c.with_header do
.gl-flex.gl-grow
%h4.gl-text-base.gl-leading-24.gl-m-0= _('Change path')
%p.gl-text-subtle.gl-text-sm.gl-m-0
- link = link_to('', help_page_path('user/project/working_with_projects', anchor: 'rename-a-repository'), target: '_blank', rel: 'noopener noreferrer')
= safe_format(_("A projects repository name defines its URL (the one you use to access the project via a browser) and its place on the file disk where GitLab is installed. %{link_start}Learn more.%{link_end}"), tag_pair(link, :link_start, :link_end))
- c.with_body do
= render 'projects/errors'
= gitlab_ui_form_for @project do |f|
.form-group
%ul
%li= _("Be careful. Renaming a project's repository can have unintended side effects.")
%li= _('You will need to update your local repositories to point to the new location.')
- if ContainerRegistry::GitlabApiClient.supports_gitlab_api?
%li= s_('ContainerRegistry|While the rename is in progress, new uploads to the container registry are blocked. Ongoing uploads may fail and need to be retried.')
- if @project.deployment_platform.present?
%p= _('Your deployment services will be broken, you will need to manually fix the services after renaming.')
= f.label :path, _('Path'), class: 'label-bold'
- c.with_body do
= render 'projects/errors'
= gitlab_ui_form_for @project do |f|
.form-group
.input-group
.input-group-prepend
.input-group-text
#{Gitlab::Utils.append_path(root_url, @project.namespace.full_path)}/
= f.text_field :path, class: 'form-control gl-form-input-xl', data: { testid: 'project-path-field' }
= f.submit _('Change path'), class: "btn-danger", data: { testid: 'change-path-button' }, pajamas_button: true
%ul
%li= _("Be careful. Renaming a project's repository can have unintended side effects.")
%li= _('You will need to update your local repositories to point to the new location.')
- if ContainerRegistry::GitlabApiClient.supports_gitlab_api?
%li= s_('ContainerRegistry|While the rename is in progress, new uploads to the container registry are blocked. Ongoing uploads may fail and need to be retried.')
- if @project.deployment_platform.present?
%p= _('Your deployment services will be broken, you will need to manually fix the services after renaming.')
= f.label :path, _('Path'), class: 'label-bold'
.form-group
.input-group
.input-group-prepend
.input-group-text
#{Gitlab::Utils.append_path(root_url, @project.namespace.full_path)}/
= f.text_field :path, class: 'form-control gl-form-input-xl', data: { testid: 'project-path-field' }
= f.submit _('Change path'), class: "btn-danger", data: { testid: 'change-path-button' }, pajamas_button: true
= render 'transfer', project: @project
= render 'transfer', project: @project
= render 'remove_fork', project: @project
= render 'remove_fork', project: @project
= render 'remove', project: @project
= render 'remove', project: @project
- else
- if can?(current_user, :archive_project, @project)
= render_if_exists 'projects/settings/archive'

View File

@ -1,9 +1,9 @@
- return unless can?(current_user, :archive_project, @project)
= render Pajamas::CardComponent.new(card_options: { class: 'gl-new-card' }, header_options: { class: 'gl-new-card-header gl-flex-direction-column' }, body_options: { class: 'gl-new-card-body gl-px-5 gl-py-4' }) do |c|
= render Pajamas::CardComponent.new(header_options: { class: 'gl-px-5 gl-py-4 gl-border-b-1 gl-border-b-solid gl-border-gray-100' }, body_options: { class: 'gl-px-5 gl-py-4' }) do |c|
- c.with_header do
.gl-new-card-title-wrapper
%h4.gl-new-card-title.warning-title
.gl-flex.gl-grow
%h4.gl-text-base.gl-leading-24.gl-m-0
- if @project.archived?
= _('Unarchive project')
- else

View File

@ -1,8 +0,0 @@
---
name: changelog_commits_limitation
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/89032
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/364101
milestone: '15.1'
type: development
group: group::source code
default_enabled: true

View File

@ -0,0 +1,9 @@
---
name: async_merge_request_pipeline_creation
feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/463355
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/161407
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/476625
milestone: '17.3'
group: group::pipeline authoring
type: wip
default_enabled: false

View File

@ -824,6 +824,9 @@ Gitlab.ee do
Settings.cron_jobs['search_index_curation_worker'] ||= {}
Settings.cron_jobs['search_index_curation_worker']['cron'] ||= '*/1 * * * *'
Settings.cron_jobs['search_index_curation_worker']['job_class'] ||= 'Search::IndexCurationWorker'
Settings.cron_jobs['search_elastic_metrics_update_cron_worker'] ||= {}
Settings.cron_jobs['search_elastic_metrics_update_cron_worker']['cron'] ||= '*/1 * * * *'
Settings.cron_jobs['search_elastic_metrics_update_cron_worker']['job_class'] ||= 'Search::Elastic::MetricsUpdateCronWorker'
Settings.cron_jobs['pause_control_resume_worker'] ||= {}
Settings.cron_jobs['pause_control_resume_worker']['cron'] ||= '*/5 * * * *'
Settings.cron_jobs['pause_control_resume_worker']['job_class'] ||= 'PauseControl::ResumeWorker'

View File

@ -0,0 +1,21 @@
# frozen_string_literal: true
class AddIndexGitlabSubscriptionHistoryOnNamespaceId < Gitlab::Database::Migration[2.2]
disable_ddl_transaction!
milestone '17.3'
TABLE_NAME = :gitlab_subscription_histories
INDEX_NAME = 'index_gitlab_subscription_histories_on_namespace_id'
def up
add_concurrent_index(
TABLE_NAME,
:namespace_id,
name: INDEX_NAME
)
end
def down
remove_concurrent_index_by_name(TABLE_NAME, name: INDEX_NAME)
end
end

View File

@ -0,0 +1 @@
bdf178477b3fc98645295a491ddf14a01917c8f259a03ace23ba3c282d699017

View File

@ -27664,6 +27664,8 @@ CREATE INDEX index_gin_ci_pending_builds_on_namespace_traversal_ids ON ci_pendin
CREATE INDEX index_gitlab_subscription_histories_on_gitlab_subscription_id ON gitlab_subscription_histories USING btree (gitlab_subscription_id);
CREATE INDEX index_gitlab_subscription_histories_on_namespace_id ON gitlab_subscription_histories USING btree (namespace_id);
CREATE INDEX index_gitlab_subscriptions_on_end_date_and_namespace_id ON gitlab_subscriptions USING btree (end_date, namespace_id);
CREATE INDEX index_gitlab_subscriptions_on_hosted_plan_id_and_trial ON gitlab_subscriptions USING btree (hosted_plan_id, trial);

View File

@ -1113,6 +1113,7 @@ The [secure files API](../api/secure_files.md) enforces the following limits:
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/89032) in GitLab 15.1 [with a flag](../administration/feature_flags.md) named `changelog_commits_limitation`. Disabled by default.
> - [Enabled on GitLab.com and by default on self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/33893) in GitLab 15.3.
> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/364101) in GitLab 17.3. Feature flag `changelog_commits_limitation` removed.
The [changelog API](../api/repositories.md#add-changelog-data-to-a-changelog-file) enforces the following limits:

View File

@ -186,6 +186,8 @@ The following metrics are available:
| `dependency_linker_usage` | Counter | 16.8 | The number of times dependency linker is used | `used_on` |
| `gitlab_keeparound_refs_requested_total` | Counter | 16.10 | Counts the number of keep-around refs requested to be created | `source` |
| `gitlab_keeparound_refs_created_total` | Counter | 16.10 | Counts the number of keep-around refs actually created | `source` |
| `search_advanced_index_repair_total` | Counter | 17.3 | Counts the number of index repair operations | `document_type` |
| `search_advanced_boolean_settings` | Gauge | 17.3 | Current state of Advanced search boolean settings | `name` |
## Metrics controlled by a feature flag

View File

@ -7862,6 +7862,10 @@ Input type: `ProjectSetComplianceFrameworkInput`
Enable/disable Continuous Vulnerability Scanning for the given project.
DETAILS:
**Deprecated** in GitLab 17.3.
CVS has been enabled permanently. See [this epic](https://gitlab.com/groups/gitlab-org/-/epics/11474) for more information.
Input type: `ProjectSetContinuousVulnerabilityScanningInput`
#### Arguments

View File

@ -308,7 +308,9 @@ Example response:
## Add changelog data to a changelog file
> - Commit range limits [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/89032) in GitLab 15.1 [with a flag](../administration/feature_flags.md) named `changelog_commits_limitation`. Enabled by default.
> - Commit range limits [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/89032) in GitLab 15.1 [with a flag](../administration/feature_flags.md) named `changelog_commits_limitation`. Disabled by default.
> - [Enabled on GitLab.com and by default on self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/33893) in GitLab 15.3.
> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/364101) in GitLab 17.3. Feature flag `changelog_commits_limitation` removed.
Generate changelog data based on commits in a repository.
@ -337,7 +339,7 @@ Changelogs support these attributes:
| `file` | string | no | The file to commit the changes to. Defaults to `CHANGELOG.md`. |
| `from` | string | no | The SHA of the commit that marks the beginning of the range of commits to include in the changelog. This commit isn't included in the changelog. |
| `message` | string | no | The commit message to use when committing the changes. Defaults to `Add changelog for version X`, where `X` is the value of the `version` argument. |
| `to` | string | no | The SHA of the commit that marks the end of the range of commits to include in the changelog. This commit _is_ included in the changelog. Defaults to the branch specified in the `branch` attribute. Limited to 15000 commits unless the feature flag `changelog_commits_limitation` is disabled. |
| `to` | string | no | The SHA of the commit that marks the end of the range of commits to include in the changelog. This commit _is_ included in the changelog. Defaults to the branch specified in the `branch` attribute. Limited to 15000 commits. |
| `trailer` | string | no | The Git trailer to use for including commits. Defaults to `Changelog`. Case-sensitive: `Example` does not match `example` or `eXaMpLE`. |
### Requirements for `from` attribute

View File

@ -1108,6 +1108,10 @@ Scripts you specify in `after_script` execute in a new shell, separate from any
In GitLab 16.3 and earlier, the timeout is hard-coded to 5 minutes.
- Don't affect the job's exit code. If the `script` section succeeds and the
`after_script` times out or fails, the job exits with code `0` (`Job Succeeded`).
- There is a known issue with using [CI/CD job tokens](../jobs/ci_job_token.md) with `after_script`.
You can use a job token for authentication in `after_script` commands, but the token
immediately becomes invalid if the job is cancelled. See [issue](https://gitlab.com/gitlab-org/gitlab/-/issues/473376)
for more details.
If a job times out, the `after_script` commands do not execute.
[An issue exists](https://gitlab.com/gitlab-org/gitlab/-/issues/15603) to add support for executing `after_script` commands for timed-out jobs.

View File

@ -37,6 +37,7 @@ This strategy:
- Only supports the Authorization Grant flow, which is most common for client-server
applications like GitLab.
- Cannot fetch user information from more than one URL.
- Cannot fetch user information from the access token in JWT format.
- Has not been tested with user information formats, except JSON.
## Configure the OAuth 2.0 provider

View File

@ -97,8 +97,7 @@ process, store and query your analytics data.
DETAILS:
**Offering:** GitLab.com
On GitLab.com, if you signed up for beta, you can use a GitLab-managed provider offered only in the Google Cloud Platform zone `us-central-1`.
To sign up, contact the GitLab [sales team](https://about.gitlab.com/sales/).
On GitLab.com you can use a GitLab-managed provider offered only in the Google Cloud Platform zone `us-central-1`. This service is offered only in beta.
If GitLab manages your product analytics provider, then your analytics data is retained for one year.
You can request to delete your data at any time by [contacting support](https://about.gitlab.com/support/#contact-support).

View File

@ -6,3 +6,8 @@ Gemfile/MissingFeatureCategory:
Rails/Exit:
Enabled: false
# Configuration parameters: AllowSubject.
RSpec/MultipleMemoizedHelpers:
Max: 25
AllowSubject: true

View File

@ -11,6 +11,8 @@ PATH
specs:
gitlab-backup-cli (0.0.1)
activesupport (< 7.2)
google-cloud-storage_transfer (~> 1.2.0)
googleauth (~> 1.8.1)
rainbow (~> 3.0)
thor (~> 1.3)
@ -27,26 +29,86 @@ GEM
i18n (>= 1.6, < 2)
minitest (>= 5.1)
tzinfo (~> 2.0)
addressable (2.8.6)
public_suffix (>= 2.0.2, < 6.0)
ast (2.4.2)
base64 (0.2.0)
concurrent-ruby (1.2.3)
diff-lcs (1.5.0)
factory_bot (6.4.6)
activesupport (>= 5.0.0)
faraday (2.9.0)
faraday-net_http (>= 2.0, < 3.2)
faraday-net_http (3.1.0)
net-http
faraday-retry (2.2.1)
faraday (~> 2.0)
gapic-common (0.20.0)
faraday (>= 1.9, < 3.a)
faraday-retry (>= 1.0, < 3.a)
google-protobuf (~> 3.14)
googleapis-common-protos (>= 1.3.12, < 2.a)
googleapis-common-protos-types (>= 1.3.1, < 2.a)
googleauth (~> 1.0)
grpc (~> 1.36)
gitlab-styles (11.0.0)
rubocop (~> 1.57.1)
rubocop-graphql (~> 0.18)
rubocop-performance (~> 1.15)
rubocop-rails (~> 2.17)
rubocop-rspec (~> 2.22)
google-cloud-core (1.7.0)
google-cloud-env (>= 1.0, < 3.a)
google-cloud-errors (~> 1.0)
google-cloud-env (2.1.1)
faraday (>= 1.0, < 3.a)
google-cloud-errors (1.4.0)
google-cloud-storage_transfer (1.2.0)
google-cloud-core (~> 1.6)
google-cloud-storage_transfer-v1 (>= 0.5, < 2.a)
google-cloud-storage_transfer-v1 (0.8.0)
gapic-common (>= 0.20.0, < 2.a)
google-cloud-errors (~> 1.0)
google-protobuf (3.25.3)
google-protobuf (3.25.3-arm64-darwin)
google-protobuf (3.25.3-x86_64-linux)
googleapis-common-protos (1.5.0)
google-protobuf (~> 3.18)
googleapis-common-protos-types (~> 1.7)
grpc (~> 1.41)
googleapis-common-protos-types (1.14.0)
google-protobuf (~> 3.18)
googleauth (1.8.1)
faraday (>= 0.17.3, < 3.a)
jwt (>= 1.4, < 3.0)
multi_json (~> 1.11)
os (>= 0.9, < 2.0)
signet (>= 0.16, < 2.a)
grpc (1.63.0)
google-protobuf (~> 3.25)
googleapis-common-protos-types (~> 1.0)
grpc (1.63.0-arm64-darwin)
google-protobuf (~> 3.25)
googleapis-common-protos-types (~> 1.0)
grpc (1.63.0-x86_64-linux)
google-protobuf (~> 3.25)
googleapis-common-protos-types (~> 1.0)
i18n (1.14.4)
concurrent-ruby (~> 1.0)
json (2.6.3)
jwt (2.8.1)
base64
language_server-protocol (3.17.0.3)
minitest (5.22.3)
multi_json (1.15.0)
net-http (0.4.1)
uri
os (1.1.4)
parallel (1.23.0)
parser (3.2.2.4)
ast (~> 2.4.1)
racc
public_suffix (5.0.5)
racc (1.7.3)
rack (3.0.8)
rainbow (3.1.1)
@ -97,10 +159,16 @@ GEM
rubocop-capybara (~> 2.17)
rubocop-factory_bot (~> 2.22)
ruby-progressbar (1.13.0)
signet (0.19.0)
addressable (~> 2.8)
faraday (>= 0.17.5, < 3.a)
jwt (>= 1.5, < 3.0)
multi_json (~> 1.10)
thor (1.3.0)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
unicode-display_width (2.5.0)
uri (0.13.0)
PLATFORMS
arm64-darwin-21

View File

@ -25,6 +25,8 @@ Gem::Specification.new do |spec|
spec.require_paths = ["lib"]
spec.add_dependency "activesupport", "< 7.2"
spec.add_dependency "googleauth", "~> 1.8.1" # https://gitlab.com/gitlab-org/gitlab/-/issues/449019
spec.add_dependency "google-cloud-storage_transfer", "~> 1.2.0"
spec.add_dependency "rainbow", "~> 3.0"
spec.add_dependency "thor", "~> 1.3"

View File

@ -11,14 +11,19 @@ module Gitlab
# It also allows for multiple backups to happen in parallel
# without one overwriting data from another
class BackupExecutor
attr_reader :context, :metadata, :workdir, :archive_directory
attr_reader :context, :metadata, :workdir, :archive_directory, :backup_bucket, :wait_for_completion,
:registry_bucket, :service_account_file
# @param [Gitlab::Backup::Cli::SourceContext] context
def initialize(context:)
def initialize(context:, backup_options: {})
@context = context
@metadata = build_metadata
@workdir = create_temporary_workdir!
@archive_directory = context.backup_basedir.join(metadata.backup_id)
@backup_bucket = backup_options["backup_bucket"]
@registry_bucket = backup_options["registry_bucket"]
@wait_for_completion = backup_options["wait_for_completion"]
@service_account_file = backup_options["service_account_file"]
end
def execute
@ -42,17 +47,34 @@ module Gitlab
def execute_all_tasks
# TODO: when we migrate targets to the new codebase, recreate options to have only what we need here
# https://gitlab.com/gitlab-org/gitlab/-/issues/454906
options = ::Backup::Options.new
options = ::Backup::Options.new(
remote_directory: backup_bucket,
container_registry_bucket: registry_bucket,
service_account_file: service_account_file
)
tasks = []
Gitlab::Backup::Cli::Tasks.build_each(context: context, options: options) do |task|
Gitlab::Backup::Cli::Output.info("Executing Backup of #{task.human_name}...")
duration = measure_duration do
task.backup!(workdir, metadata.backup_id)
tasks << { name: task.human_name, result: task.backup!(workdir, metadata.backup_id) }
end
next unless task.object_storage?
Gitlab::Backup::Cli::Output.success("Finished Backup of #{task.human_name}! (#{duration.in_seconds}s)")
end
if wait_for_completion
tasks.each do |task|
next unless task[:result].respond_to?(:wait_until_done!)
wait_for_task(task[:result])
end
else
Gitlab::Backup::Cli::Output.info('Backup tasks completed! Not waiting for object storage tasks to complete')
end
end
# Write the backup_information.json data to disk
@ -83,6 +105,17 @@ module Gitlab
ActiveSupport::Duration.build(Time.now - start)
end
def wait_for_task(task)
Gitlab::Backup::Cli::Output.info("Waiting for Backup of #{task.name} to finish...")
r = task.wait_until_done!
if r.error?
Gitlab::Backup::Cli::Output.error("Backup of #{task.name} failed!")
else
Gitlab::Backup::Cli::Output.success("Finished Backup of #{task.name}!")
end
end
end
end
end

View File

@ -7,6 +7,25 @@ module Gitlab
class BackupSubcommand < Command
package_name 'Backup'
EXECUTOR_OPTIONS = %w[backup_bucket wait_for_completion registry_bucket service_account_file].freeze
class_option :backup_bucket,
desc: "When backing up object storage, this is the bucket to backup to",
required: false
class_option :wait_for_completion,
desc: "Wait for object storage backups to complete",
type: :boolean,
default: true
class_option :registry_bucket,
desc: "When backing up registry from object storage, this is the source bucket",
required: false
class_option :service_account_file,
desc: "JSON file containing the Google service account credentials",
default: "/etc/gitlab/backup-account-credentials.json"
desc 'all', 'Creates a backup including repositories, database and local files'
def all
duration = measure_duration do
@ -15,7 +34,9 @@ module Gitlab
end
Gitlab::Backup::Cli::Output.success("Environment loaded. (#{duration.in_seconds}s)")
backup_executor = Gitlab::Backup::Cli::BackupExecutor.new(context: build_context)
backup_executor = Gitlab::Backup::Cli::BackupExecutor.new(
context: build_context, backup_options: executor_options
)
backup_id = backup_executor.metadata.backup_id
duration = measure_duration do
@ -46,6 +67,10 @@ module Gitlab
ActiveSupport::Duration.build(Time.now - start)
end
def executor_options
options.select { |key, _| EXECUTOR_OPTIONS.include?(key) }
end
end
end
end

View File

@ -102,6 +102,10 @@ module Gitlab
absolute_path(path).join('uploads')
end
def config(object_type)
Gitlab.config[object_type]
end
def env
@env ||= ActiveSupport::EnvironmentInquirer.new(
ENV["RAILS_ENV"].presence || ENV["RACK_ENV"].presence || "development")

View File

@ -6,6 +6,7 @@ module Gitlab
module Targets
autoload :Target, 'gitlab/backup/cli/targets/target'
autoload :Database, 'gitlab/backup/cli/targets/database'
autoload :ObjectStorage, 'gitlab/backup/cli/targets/object_storage'
end
end
end

View File

@ -0,0 +1,26 @@
# frozen_string_literal: true
require "google/cloud/storage_transfer"
require_relative "object_storage/google"
module Gitlab
module Backup
module Cli
module Targets
class ObjectStorage
SUPPORTED_PROVIDERS = [
"Google"
].freeze
def self.find_task(object_type, options, config)
# For objects that don't use the consolidated config (like the registry), try the global
# object_store for connection information. This will go away with a config file
# https://gitlab.com/gitlab-org/gitlab/-/issues/475114
const_get(config.object_store.connection.provider, false).new(object_type, options, config)
end
end
end
end
end
end

View File

@ -0,0 +1,93 @@
# frozen_string_literal: true
require "google/cloud/storage_transfer"
module Gitlab
module Backup
module Cli
module Targets
class ObjectStorage
class Google < Target
attr_accessor :object_type, :backup_bucket, :client, :config
def initialize(object_type, options, config)
check_env
@object_type = object_type
@backup_bucket = options.remote_directory
@config = config
@client = ::Google::Cloud::StorageTransfer.storage_transfer_service
end
def dump(_, backup_id)
response = find_or_create_job(backup_id)
run_request = {
project_id: job_spec(backup_id)[:project_id],
job_name: response.name
}
client.run_transfer_job run_request
end
def job_name
"transferJobs/#{object_type}-backup"
end
def job_spec(backup_id)
{
project_id: config.object_store.connection.google_project,
name: job_name,
transfer_spec: {
gcs_data_source: {
bucket_name: config.object_store.remote_directory
},
gcs_data_sink: {
bucket_name: backup_bucket,
# NOTE: The trailing '/' is required
path: "backups/#{backup_id}/#{object_type}/"
}
},
status: :ENABLED
}
end
private
def check_env
# We expect service account credentials to be passed via env variables. If they are not, attempt
# to use the local service account credentials and warn.
return unless ENV.key?("GOOGLE_CLOUD_CREDENTIALS") || ENV.key?("GOOGLE_APPLICATION_CREDENTIALS")
log.warning("No credentials provided.")
log.warning("If we're in GCP, we will attempt to use the machine service account.")
log.warning("This is not recommended.")
end
def find_or_create_job(backup_id)
begin
response = client.get_transfer_job(
job_name: job_name, project_id: config.object_store.connection.google_project
)
log.info("Existing job for #{object_type} found, using")
job_update = job_spec(backup_id)
job_update.delete(:project_id)
client.update_transfer_job(
job_name: job_name,
project_id: config.object_store.connection.google_project,
transfer_job: job_update
)
rescue ::Google::Cloud::NotFoundError
log.info("Existing job for #{object_type} not found, creating one")
response = client.create_transfer_job transfer_job: job_spec(backup_id)
end
response
end
def log
Gitlab::Backup::Cli::Output
end
end
end
end
end
end
end

View File

@ -14,7 +14,7 @@ module Gitlab
private
def target
::Backup::Targets::Files.new(nil, storage_path, options: options, excludes: ['tmp'])
check_object_storage(::Backup::Targets::Files.new(nil, storage_path, options: options, excludes: ['tmp']))
end
def storage_path = context.ci_job_artifacts_path

View File

@ -14,7 +14,7 @@ module Gitlab
private
def target
::Backup::Targets::Files.new(nil, storage_path, options: options, excludes: ['tmp'])
check_object_storage(::Backup::Targets::Files.new(nil, storage_path, options: options, excludes: ['tmp']))
end
def storage_path = context.ci_secure_files_path

View File

@ -14,7 +14,7 @@ module Gitlab
private
def target
::Backup::Targets::Files.new(nil, storage_path, options: options)
check_object_storage(::Backup::Targets::Files.new(nil, storage_path, options: options))
end
def storage_path = context.ci_lfs_path

View File

@ -14,7 +14,7 @@ module Gitlab
private
def target
::Backup::Targets::Files.new(nil, storage_path, options: options, excludes: ['tmp'])
check_object_storage(::Backup::Targets::Files.new(nil, storage_path, options: options, excludes: ['tmp']))
end
def storage_path = context.packages_path

View File

@ -18,7 +18,9 @@ module Gitlab
private
def target
::Backup::Targets::Files.new(nil, storage_path, options: options, excludes: [LEGACY_PAGES_TMP_PATH])
check_object_storage(
::Backup::Targets::Files.new(nil, storage_path, options: options, excludes: [LEGACY_PAGES_TMP_PATH])
)
end
def storage_path = context.pages_path

View File

@ -13,10 +13,25 @@ module Gitlab
def destination_path = 'registry.tar.gz'
def object_storage?
!options.container_registry_bucket.nil?
end
# Registry does not use consolidated object storage config.
def config
settings = {
object_store: {
connection: Gitlab::Backup::Cli::SourceContext.new.config('object_store').connection.to_hash,
remote_directory: options.container_registry_bucket
}
}
GitlabSettings::Options.build(settings)
end
private
def target
::Backup::Targets::Files.new(nil, storage_path, options: options)
check_object_storage(::Backup::Targets::Files.new(nil, storage_path, options: options))
end
def storage_path = context.registry_path

View File

@ -68,12 +68,35 @@ module Gitlab
enabled
end
def config
Gitlab::Backup::Cli::SourceContext.new.config(id)
end
def object_storage?
return false unless config
return false unless config.respond_to?(:object_store) && config.object_store.enabled
return false unless Gitlab::Backup::Cli::Targets::ObjectStorage::SUPPORTED_PROVIDERS.include?(
config.object_store.connection.provider
)
true
end
private
# The target factory method
def target
raise NotImplementedError
end
def check_object_storage(file_target)
if object_storage?
::Gitlab::Backup::Cli::Targets::ObjectStorage.find_task(id, options, config)
else
file_target
end
end
end
end
end

View File

@ -14,7 +14,7 @@ module Gitlab
private
def target
::Backup::Targets::Files.new(nil, storage_path, options: options, excludes: ['tmp'])
check_object_storage(::Backup::Targets::Files.new(nil, storage_path, options: options, excludes: ['tmp']))
end
def storage_path = context.terraform_state_path

View File

@ -14,7 +14,7 @@ module Gitlab
private
def target
::Backup::Targets::Files.new(nil, storage_path, options: options, excludes: ['tmp'])
check_object_storage(::Backup::Targets::Files.new(nil, storage_path, options: options, excludes: ['tmp']))
end
def storage_path = context.upload_path

View File

@ -0,0 +1,18 @@
# frozen_string_literal: true
require "google/cloud/storage_transfer/v1"
FactoryBot.define do
factory :google_cloud_storage_transfer_job, class: "Google::Cloud::StorageTransfer::V1::TransferJob" do
name { "fake_transfer_job" }
transfer_spec
end
factory :transfer_spec, class: "Google::Cloud::StorageTransfer::V1::TransferSpec" do
gcs_data_sink
end
factory :gcs_data_sink, class: "Google::Cloud::StorageTransfer::V1::GcsData" do
path { '/foo' }
end
end

View File

@ -0,0 +1,14 @@
# frozen_string_literal: true
require 'spec_helper'
require 'thor'
RSpec.describe Gitlab::Backup::Cli::Commands::BackupSubcommand do
describe "#executor_options" do
it "returns the expected hash" do
expect(described_class.new.send(:executor_options).keys).to eq(
%w[wait_for_completion service_account_file]
)
end
end
end

View File

@ -0,0 +1,95 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::Backup::Cli::Targets::ObjectStorage::Google do
let(:gitlab_config) { class_double("GitlabSettings::Settings") }
let(:supported_object_store) do
instance_double(
"GitlabSettings::Options",
enabled: true,
connection: instance_double("GitlabSettings::Options", provider: "Google")
)
end
let(:supported_config) { instance_double("GitlabSettings::Options", object_store: supported_object_store) }
let(:client) { instance_double("::Google::Cloud::StorageTransfer::V1::StorageTransferService::Client") }
let(:existing_transfer_job) { build(:google_cloud_storage_transfer_job) }
let(:new_transfer_job_spec) do
{
name: "transferJobs/fake_object-backup",
project_id: "fake_project",
transfer_spec: {
gcs_data_source: {
bucket_name: "fake_source_bucket"
},
gcs_data_sink: {
bucket_name: "fake_backup_bucket",
path: "backups/12345/fake_object/"
}
},
status: :ENABLED
}
end
let(:backup_options) { instance_double("Gitlab::Backup::Options", remote_directory: 'fake_backup_bucket') }
before do
allow(Gitlab).to receive(:config).and_return(gitlab_config)
allow(::Google::Cloud::StorageTransfer).to receive(:storage_transfer_service).and_return(client)
end
subject(:object_storage) { described_class.new("fake_object", backup_options, supported_config) }
describe "#dump" do
let(:supported_provider) do
instance_double(
"GitlabSettings::Options", provider: "Google", google_application_default: true, google_project: "fake_project"
)
end
let(:supported_object_store) do
instance_double(
"GitlabSettings::Options", enabled: true, connection: supported_provider, remote_directory: "fake_source_bucket"
)
end
let(:supported_config) { instance_double("GitlabSettings::Options", object_store: supported_object_store) }
before do
allow(gitlab_config).to receive(:[]).with('fake_object').and_return(supported_config)
end
context "when job exists" do
before do
allow(client).to receive(:get_transfer_job).and_return(existing_transfer_job)
end
it "reuses existing job" do
updated_spec = new_transfer_job_spec
expect(client).to receive(:update_transfer_job).with(
job_name: updated_spec[:name],
project_id: updated_spec.delete(:project_id),
transfer_job: updated_spec
)
expect(client).to receive(:run_transfer_job).with({ job_name: "fake_transfer_job", project_id: "fake_project" })
object_storage.dump(nil, 12345)
end
end
context "when job does not exist" do
before do
allow(client).to receive(:get_transfer_job).with(
job_name: "transferJobs/fake_object-backup", project_id: "fake_project"
).and_raise(::Google::Cloud::NotFoundError)
allow(client).to receive(:run_transfer_job)
end
it "creates a new job" do
expect(client).to receive(:create_transfer_job)
.with(transfer_job: new_transfer_job_spec).and_return(existing_transfer_job)
object_storage.dump(nil, 12345)
end
end
end
end

View File

@ -13,6 +13,14 @@ RSpec.describe 'gitlab-backup-cli backup subcommand', type: :thor do
gitlab-backup-cli backup all # Creates a backup including repositories, database and local files
gitlab-backup-cli backup help [COMMAND] # Describe subcommands or one specific subcommand
Options:
[--backup-bucket=BACKUP_BUCKET] # When backing up object storage, this is the bucket to backup to
[--wait-for-completion], [--no-wait-for-completion] # Wait for object storage backups to complete
# Default: true
[--registry-bucket=REGISTRY_BUCKET] # When backing up registry from object storage, this is the source bucket
[--service-account-file=SERVICE_ACCOUNT_FILE] # JSON file containing the Google service account credentials
# Default: /etc/gitlab/backup-account-credentials.json
COMMAND
end

View File

@ -588,13 +588,29 @@ module API
]
tags %w[merge_requests]
end
params do
optional :async, type: Boolean, default: false,
desc: 'Indicates if the merge request pipeline creation should be performed asynchronously. If set to `true`, the pipeline will be created outside of the API request and the endpoint will return an empty response with a `202` status code. When the response is `202`, the creation can still fail outside of this request.'
end
post ':id/merge_requests/:merge_request_iid/pipelines', urgency: :low, feature_category: :pipeline_composition do
pipeline = ::MergeRequests::CreatePipelineService
.new(project: user_project, current_user: current_user, params: { allow_duplicate: true })
.execute(find_merge_request_with_access(params[:merge_request_iid]))
.payload
pipeline = nil
merge_request = find_merge_request_with_access(params[:merge_request_iid])
if pipeline.nil?
merge_request_params = { allow_duplicate: true }
if params[:async]
::MergeRequests::CreatePipelineWorker # rubocop:disable CodeReuse/Worker -- Worker wraps service and another service wrapping that is pointless
.perform_async(user_project.id, current_user.id, merge_request.id, merge_request_params)
else
pipeline = ::MergeRequests::CreatePipelineService
.new(project: user_project, current_user: current_user, params: merge_request_params)
.execute(merge_request)
.payload
end
if params[:async]
status :accepted
elsif pipeline.nil?
not_allowed!
elsif pipeline.persisted?
status :ok

View File

@ -149,6 +149,13 @@ module Backup
# @return [Boolean] whether to use `--rsyncable` flag with gzip
attr_accessor :gzip_rsyncable
# If the container registry is using object storage, this is the bucket that is used
# @return [String|Nil]
attr_accessor :container_registry_bucket
# If we are backing up object storage in GCP, this is a file containing the service account credentials to use
attr_accessor :service_account_file
# rubocop:disable Metrics/ParameterLists -- This is a data object with all possible CMD options
def initialize(
backup_id: nil, previous_backup: nil, incremental: false, force: false, strategy: Strategy::STREAM,
@ -156,7 +163,8 @@ module Backup
max_parallelism: nil, max_storage_parallelism: nil,
repository_storages: [], repository_paths: [], skip_repository_paths: [],
repositories_server_side_backup: false, remote_directory: nil,
compression_options: CompressionOptions.new, gzip_rsyncable: false)
compression_options: CompressionOptions.new, gzip_rsyncable: false, container_registry_bucket: nil,
service_account_file: nil)
@backup_id = backup_id
@previous_backup = previous_backup
@incremental = incremental
@ -173,6 +181,8 @@ module Backup
@skip_repositories_paths = skip_repository_paths
@compression_options = compression_options
@gzip_rsyncable = gzip_rsyncable
@container_registry_bucket = container_registry_bucket
@service_account_file = service_account_file
end
# rubocop:enable Metrics/ParameterLists

View File

@ -35420,6 +35420,9 @@ msgstr ""
msgid "Not permitted to destroy framework"
msgstr ""
msgid "Not permitted to reset user feed token"
msgstr ""
msgid "Not ready yet. Try again later."
msgstr ""
@ -40911,6 +40914,9 @@ msgstr ""
msgid "Profiles|Expires"
msgstr ""
msgid "Profiles|Feed token could not be reset"
msgstr ""
msgid "Profiles|Feed token was successfully reset"
msgstr ""
@ -48758,7 +48764,7 @@ msgstr ""
msgid "SecurityReports|All tools"
msgstr ""
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly."
msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your project's security configuration to make sure you've set up your security scans correctly."
msgstr ""
msgid "SecurityReports|Change status"
@ -48914,16 +48920,19 @@ msgstr ""
msgid "SecurityReports|Jira Issue Created"
msgstr ""
msgid "SecurityReports|Learn more about security configuration"
msgstr ""
msgid "SecurityReports|Learn more about setting up your dashboard"
msgstr ""
msgid "SecurityReports|Manage and track vulnerabilities identified in projects within your group. Vulnerabilities in projects are shown here when security testing is configured."
msgid "SecurityReports|Manage and track vulnerabilities identified in projects within your group. Vulnerabilities in projects are shown here when security testing is configured and a pipeline has been run on the default branch."
msgstr ""
msgid "SecurityReports|Manage and track vulnerabilities identified in your project. Vulnerabilities are shown here when security testing is configured."
msgid "SecurityReports|Manage and track vulnerabilities identified in your project. Vulnerabilities are shown here when security testing is configured and a pipeline has been run on the default branch."
msgstr ""
msgid "SecurityReports|Manage and track vulnerabilities identified in your selected projects. Vulnerabilities for selected projects with security testing configured are shown here."
msgid "SecurityReports|Manage and track vulnerabilities identified in your selected projects. Vulnerabilities for selected projects are shown here when security testing is configured and a pipeline has been run on the default branch."
msgstr ""
msgid "SecurityReports|Maximum selected projects limit reached"

View File

@ -76,7 +76,7 @@
"@mattiasbuelens/web-streams-adapter": "^0.1.0",
"@rails/actioncable": "7.0.8-4",
"@rails/ujs": "7.0.8-4",
"@sentry/browser": "8.20.0",
"@sentry/browser": "8.23.0",
"@snowplow/browser-plugin-client-hints": "^3.24.2",
"@snowplow/browser-plugin-form-tracking": "^3.24.2",
"@snowplow/browser-plugin-ga-cookies": "^3.24.2",

View File

@ -26,6 +26,7 @@ module QA
def initialize
@logger = Runtime::Logger.logger
@fq_filename = "fast_quarantine-gitlab.txt"
@fq_download_filename = ENV['RSPEC_FAST_QUARANTINE_FILE'] || @fq_filename
end
# Fetch and save fast quarantine file
@ -45,7 +46,7 @@ module QA
private
attr_reader :logger, :fq_filename
attr_reader :logger, :fq_filename, :fq_download_filename
# Force path to be relative to ruby process in order to avoid issues when dealing with different execution
# contexts of qa docker container and CI runner environment
@ -56,7 +57,7 @@ module QA
def download_fast_quarantine
logger.debug(" downloading fast quarantine file")
response = get(
"https://gitlab-org.gitlab.io/quality/engineering-productivity/fast-quarantine/rspec/#{fq_filename}",
"https://gitlab-org.gitlab.io/quality/engineering-productivity/fast-quarantine/rspec/#{fq_download_filename}",
verify_ssl: true
)
raise "Failed to download fast quarantine file: #{response.code}" if response.code != HTTP_STATUS_OK

View File

@ -3,8 +3,9 @@
RSpec.describe QA::Specs::Helpers::FastQuarantine do
include QA::Support::Helpers::StubEnv
let(:quarantine_file) { "fast_quarantine-gitlab.txt" }
let(:response) { instance_double(RestClient::Response, code: 200, body: fq_contents) }
let(:fq_path) { File.join(QA::Runtime::Path.qa_root, "tmp", "fast_quarantine-gitlab.txt") }
let(:fq_path) { File.join(QA::Runtime::Path.qa_root, "tmp", quarantine_file) }
let(:fq_contents) { "fast_quarantine_contents" }
before do
@ -17,12 +18,12 @@ RSpec.describe QA::Specs::Helpers::FastQuarantine do
# silence log messages during test execution
allow(QA::Runtime::Logger).to receive(:logger).and_return(instance_double(ActiveSupport::Logger, debug: nil))
allow(QA::Runtime::Logger).to receive(:debug)
described_class.configure!
end
it "configures fast quarantine" do
expect(RSpec).to have_received(:configure)
it "configures fast quarantine, using defaults" do
ENV.delete('RSPEC_FAST_QUARANTINE_FILE') # ensure variable is not set if other test is run first
described_class.configure!
expect(File).to have_received(:write).with(fq_path, fq_contents)
expect(RestClient::Request).to have_received(:execute).with(
method: :get,
@ -30,4 +31,18 @@ RSpec.describe QA::Specs::Helpers::FastQuarantine do
verify_ssl: true
)
end
it "configures with 'RSPEC_FAST_QUARANTINE_FILE'" do
download_file = 'fast_quarantine-dedicated.txt'
ENV['RSPEC_FAST_QUARANTINE_FILE'] = download_file
described_class.configure!
expect(File).to have_received(:write).with(fq_path, fq_contents)
expect(RestClient::Request).to have_received(:execute).with(
method: :get,
url: "https://gitlab-org.gitlab.io/quality/engineering-productivity/fast-quarantine/rspec/#{download_file}",
verify_ssl: true
)
end
end

View File

@ -65,23 +65,59 @@ RSpec.describe 'Profile account page', :js, feature_category: :user_profile do
end
end
it 'allows resetting of feed token' do
visit user_settings_personal_access_tokens_path
describe 'when I reset my feed token' do
context 'when resetting succeeds' do
it 'allows resetting of feed token' do
visit user_settings_personal_access_tokens_path
previous_token = ''
previous_token = ''
within_testid('feed-token-container') do
previous_token = find_field('Feed token').value
within_testid('feed-token-container') do
previous_token = find_field('Feed token').value
click_link('reset this token')
click_link('reset this token')
end
accept_gl_confirm
expect(page).to have_content('Feed token was successfully reset')
within_testid('feed-token-container') do
click_button('Click to reveal')
expect(find_field('Feed token').value).not_to eq(previous_token)
end
end
end
accept_gl_confirm
context 'when resetting fails' do
before do
allow_next_instance_of(Users::UpdateService) do |service|
allow(service).to receive(:execute).and_return({ status: :error })
end
end
within_testid('feed-token-container') do
click_button('Click to reveal')
it 'shows an error and the old feed token' do
visit user_settings_personal_access_tokens_path
expect(find_field('Feed token').value).not_to eq(previous_token)
previous_token = ''
within_testid('feed-token-container') do
previous_token = find_field('Feed token').value
click_link('reset this token')
end
accept_gl_confirm
expect(page).to have_content('Feed token could not be reset')
within_testid('feed-token-container') do
click_button('Click to reveal')
expect(find_field('Feed token').value).to eq(previous_token)
end
end
end
end

View File

@ -951,6 +951,52 @@ describe('Api', () => {
});
});
describe('postMergeRequestPipeline', () => {
const dummyProjectId = 5;
const dummyMergeRequestIid = 123;
const expectedUrl = `${dummyUrlRoot}/api/${dummyApiVersion}/projects/5/merge_requests/123/pipelines`;
const features = { asyncMergeRequestPipelineCreation: true };
beforeEach(() => {
mock = new MockAdapter(axios);
window.gon.features = features;
});
it('creates a merge request pipeline async', () => {
jest.spyOn(axios, 'post');
mock.onPost(expectedUrl).replyOnce(HTTP_STATUS_OK, {
id: 456,
});
return Api.postMergeRequestPipeline(dummyProjectId, {
mergeRequestId: dummyMergeRequestIid,
}).then(({ data }) => {
expect(data.id).toBe(456);
expect(axios.post).toHaveBeenCalledWith(expectedUrl, { async: true });
});
});
describe('when asyncMergeRequestPipelineCreation is disabled', () => {
it('creates a merge request pipeline synchronously', () => {
window.gon.features.asyncMergeRequestPipelineCreation = false;
jest.spyOn(axios, 'post');
mock.onPost(expectedUrl).replyOnce(HTTP_STATUS_OK, {
id: 456,
});
return Api.postMergeRequestPipeline(dummyProjectId, {
mergeRequestId: dummyMergeRequestIid,
}).then(({ data }) => {
expect(data.id).toBe(456);
expect(axios.post).toHaveBeenCalledWith(expectedUrl, {});
});
});
});
});
describe('projectForks', () => {
it('gets forked projects', () => {
const dummyProjectPath = 'gitlab-org/gitlab-ce';

View File

@ -12,7 +12,7 @@ exports[`Blob Header Filepath rendering matches the snapshot 1`] = `
size="16"
/>
<strong
class="file-title-name js-blob-header-filepath mr-1"
class="file-title-name gl-break-all gl-text-decoration-none! js-blob-header-filepath mr-1"
data-testid="file-title-content"
>
foo/bar/dummy.md

View File

@ -1,5 +1,5 @@
import { GlBadge } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import BlobHeaderFilepath from '~/blob/components/blob_header_filepath.vue';
import { numberToHumanSize } from '~/lib/utils/number_utils';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
@ -13,7 +13,7 @@ describe('Blob Header Filepath', () => {
let wrapper;
function createComponent(blobProps = {}, options = {}, propsData = {}) {
wrapper = shallowMount(BlobHeaderFilepath, {
wrapper = shallowMountExtended(BlobHeaderFilepath, {
propsData: {
blob: { ...MockBlob, ...blobProps },
...propsData,
@ -22,6 +22,7 @@ describe('Blob Header Filepath', () => {
});
}
const getById = (id) => wrapper.findByTestId(id);
const findBadge = () => wrapper.findComponent(GlBadge);
describe('rendering', () => {
@ -59,6 +60,13 @@ describe('Blob Header Filepath', () => {
expect(wrapper.find('small').exists()).toBe(false);
});
it('should have classes', () => {
createComponent();
expect(getById('file-title-content').attributes('class')).toEqual(
'file-title-name mr-1 js-blob-header-filepath gl-break-all gl-text-decoration-none!',
);
});
it('renders LFS badge if LFS if enabled', () => {
createComponent({ storedExternally: true, externalStorage: 'lfs' });
expect(findBadge().text()).toBe('LFS');

View File

@ -53,7 +53,10 @@ RSpec.describe Gitlab::BackgroundMigration::BackfillUserDetails, schema: 2024071
end
end
context 'when upsert raises an error' do
context 'when upsert raises an error', quarantine: {
type: :flaky,
issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/477109'
} do
before do
allow(described_class::UserDetail).to receive(:upsert_all).and_raise(Exception, '_error_')
end

View File

@ -2100,6 +2100,20 @@ RSpec.describe API::MergeRequests, :aggregate_failures, feature_category: :sourc
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to be_a Hash
end
context 'when async is requested', :sidekiq_inline do
let(:request) do
post api("/projects/#{project.id}/merge_requests/#{merge_request_iid}/pipelines", authenticated_user), params: { async: true }
end
it 'creates the pipeline async' do
expect(MergeRequests::CreatePipelineWorker).to receive(:perform_async).and_call_original
expect { request }.to change(Ci::Pipeline, :count).by(1)
expect(response).to have_gitlab_http_status(:accepted)
end
end
end
context 'when unauthorized' do

View File

@ -185,16 +185,6 @@ RSpec.describe Repositories::ChangelogService, feature_category: :source_code_ma
it 'raises an exception' do
expect { service.execute(commit_to_changelog: false) }.to raise_error(Gitlab::Changelog::Error)
end
context 'when feature flag is off' do
before do
stub_feature_flags(changelog_commits_limitation: false)
end
it 'returns the changelog' do
expect(service.execute(commit_to_changelog: false)).to include('Title 1', 'Title 2', 'Title 3')
end
end
end
context 'with specified changelog config file path' do

View File

@ -0,0 +1,71 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Users::ResetFeedTokenService, feature_category: :system_access do
shared_examples_for 'a successfully reset token' do
it { expect(reset.success?).to be true }
it { expect { reset }.to change { user.feed_token } }
it 'logs the event' do
expect(Gitlab::AppLogger).to receive(:info).with(
class: described_class.to_s,
message: 'User Feed Token Reset',
source: :self,
reset_by: reset_by,
reset_for: user.username,
user_id: user.id)
reset
end
end
shared_examples_for 'an unsuccessfully reset token' do
it { expect(reset.success?).to be false }
it { expect { reset }.not_to change { user.feed_token } }
end
describe '#execute' do
subject(:reset) { service.execute }
let(:service) { described_class.new(current_user, user: user) }
let_it_be(:admin) { create(:admin) }
let_it_be(:alex) { create(:user) }
context 'when current_user is an administrator' do
context 'when admin mode is enabled', :enable_admin_mode do
let(:current_user) { admin }
let(:user) { alex }
it_behaves_like 'a successfully reset token' do
let(:reset_by) { current_user.username }
end
end
context 'when admin mode is disabled' do
let(:current_user) { admin }
let(:user) { alex }
it_behaves_like 'an unsuccessfully reset token'
end
end
context 'when current_user is not an administrator' do
let(:current_user) { alex }
context 'when user is a different user' do
let(:user) { admin }
it_behaves_like 'an unsuccessfully reset token'
end
context 'when user is current_user' do
let(:user) { alex }
it_behaves_like 'a successfully reset token' do
let(:reset_by) { current_user.username }
end
end
end
end
end

112
yarn.lock
View File

@ -2031,76 +2031,76 @@
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.14.3.tgz#5b2fb4d8cd44c05deef8a7b0e6deb9ccb8939d18"
integrity sha512-/BypzV0H1y1HzgYpxqRaXGBRqfodgoBBCcsrujT6QRcakDQdfU+Lq9PENPh5jB4I44YWq+0C2eHsHya+nZY1sA==
"@sentry-internal/browser-utils@8.20.0":
version "8.20.0"
resolved "https://registry.yarnpkg.com/@sentry-internal/browser-utils/-/browser-utils-8.20.0.tgz#26837f889cff1caf09ddfd6ca7f0adad9256b981"
integrity sha512-GGYNiELnT4ByidHyS4/M8UF8Oxagm5R13QyTncQGq8nZcQhcFZ9mdxLnf1/R4+j44Fph2Cgzafe8jGP/AMA9zw==
"@sentry-internal/browser-utils@8.23.0":
version "8.23.0"
resolved "https://registry.yarnpkg.com/@sentry-internal/browser-utils/-/browser-utils-8.23.0.tgz#a99f465a2d35d6a6f8b298adafcb4049c8af19f4"
integrity sha512-PQ0S7MRP8REo1iF+qZHNuLF+Qh7fuULA56tw0CRzTO1j7y87hQz9EJ8L0fBewuOitFQhSrZ7bfjJt9lIDTMfTQ==
dependencies:
"@sentry/core" "8.20.0"
"@sentry/types" "8.20.0"
"@sentry/utils" "8.20.0"
"@sentry/core" "8.23.0"
"@sentry/types" "8.23.0"
"@sentry/utils" "8.23.0"
"@sentry-internal/feedback@8.20.0":
version "8.20.0"
resolved "https://registry.yarnpkg.com/@sentry-internal/feedback/-/feedback-8.20.0.tgz#8e4ab43bb4048951f6670bd48b2af04deb75eff4"
integrity sha512-mFvAoVpVShkDB2AgEr/dE96NSTPKI/lGMBznZMg7ZEcwZhLfH7HvLYCadIskRfzqFTLOUpbm9ciIO4SyR/4bDA==
"@sentry-internal/feedback@8.23.0":
version "8.23.0"
resolved "https://registry.yarnpkg.com/@sentry-internal/feedback/-/feedback-8.23.0.tgz#4932ce3f86ec8040b78c00f1416247df4107eec0"
integrity sha512-xDwUohTOAW2Vwv9Vc6T2k8s8lvmQQck0YLmiafLbM2uqfyd2g3azRmWYQIsASSru2KdMYXgoLhZ/A0FGUlte9w==
dependencies:
"@sentry/core" "8.20.0"
"@sentry/types" "8.20.0"
"@sentry/utils" "8.20.0"
"@sentry/core" "8.23.0"
"@sentry/types" "8.23.0"
"@sentry/utils" "8.23.0"
"@sentry-internal/replay-canvas@8.20.0":
version "8.20.0"
resolved "https://registry.yarnpkg.com/@sentry-internal/replay-canvas/-/replay-canvas-8.20.0.tgz#abaa845278dd397670fb01baed505f751b1c9989"
integrity sha512-LXV/pMH9KMw6CtImenMsiBkYIFIc97pDJ/rC7mVImKIROQ45fxGp/JBXM4Id0GENyA2+SySMWVQCAAapSfHZTw==
"@sentry-internal/replay-canvas@8.23.0":
version "8.23.0"
resolved "https://registry.yarnpkg.com/@sentry-internal/replay-canvas/-/replay-canvas-8.23.0.tgz#9e12b7949c305b1d1de2c494c5ff60f80a1518a5"
integrity sha512-Guqy+Ae0ZdNNBFnkHFT6bbyzUcW/8liTUZUQS3fdHkaav4qKIPAdMGob2e09GKczf5zSaaobiChsMpaXMLHlMA==
dependencies:
"@sentry-internal/replay" "8.20.0"
"@sentry/core" "8.20.0"
"@sentry/types" "8.20.0"
"@sentry/utils" "8.20.0"
"@sentry-internal/replay" "8.23.0"
"@sentry/core" "8.23.0"
"@sentry/types" "8.23.0"
"@sentry/utils" "8.23.0"
"@sentry-internal/replay@8.20.0":
version "8.20.0"
resolved "https://registry.yarnpkg.com/@sentry-internal/replay/-/replay-8.20.0.tgz#1e1b514651a6c499609cb90cf8df3ee18a1df157"
integrity sha512-sCiI7SOAHq5XsxkixtoMofeSyKd/hVgDV+4145f6nN9m7nLzig4PBQwh2SgK2piJ2mfaXfqcdzA1pShPYldaJA==
"@sentry-internal/replay@8.23.0":
version "8.23.0"
resolved "https://registry.yarnpkg.com/@sentry-internal/replay/-/replay-8.23.0.tgz#91a4c33d1aaa168fc18c5aeacd5c0de57e028277"
integrity sha512-3HeLMgtJoQvX6FHw2kzo3vlLElMyNWLIaJl5BtUzVnQw1fEoV8R3Mwrn02nwW3IFIPUv0O+xn/Icx6InenfBqQ==
dependencies:
"@sentry-internal/browser-utils" "8.20.0"
"@sentry/core" "8.20.0"
"@sentry/types" "8.20.0"
"@sentry/utils" "8.20.0"
"@sentry-internal/browser-utils" "8.23.0"
"@sentry/core" "8.23.0"
"@sentry/types" "8.23.0"
"@sentry/utils" "8.23.0"
"@sentry/browser@8.20.0":
version "8.20.0"
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-8.20.0.tgz#6644d3223f891b031684210d89511275a5d6c657"
integrity sha512-JDZbCreY44/fHYN28QzsAwEHXa2rc1hzM6GE4RSlXCdAhNfrjVxyYDxhw/50pVEHZg1WXxf7ZmERjocV5VJHsw==
"@sentry/browser@8.23.0":
version "8.23.0"
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-8.23.0.tgz#4c5af74583193cea6c6b8dff6c70203f660bc4df"
integrity sha512-KyoFp4et+y26wn99sXRp6+vme1Gha8DPQo2DbO64IR49tqkBXr8/D1QkpV3rqkPdttH7fefFNvaM4h3+9d6OtQ==
dependencies:
"@sentry-internal/browser-utils" "8.20.0"
"@sentry-internal/feedback" "8.20.0"
"@sentry-internal/replay" "8.20.0"
"@sentry-internal/replay-canvas" "8.20.0"
"@sentry/core" "8.20.0"
"@sentry/types" "8.20.0"
"@sentry/utils" "8.20.0"
"@sentry-internal/browser-utils" "8.23.0"
"@sentry-internal/feedback" "8.23.0"
"@sentry-internal/replay" "8.23.0"
"@sentry-internal/replay-canvas" "8.23.0"
"@sentry/core" "8.23.0"
"@sentry/types" "8.23.0"
"@sentry/utils" "8.23.0"
"@sentry/core@8.20.0":
version "8.20.0"
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-8.20.0.tgz#c50d082033a44295d2fe9140319f912ba1f946d3"
integrity sha512-R81snuw+67VT4aCxr6ShST/s0Y6FlwN2YczhDwaGyzumn5rlvA6A4JtQDeExduNoDDyv4T3LrmW8wlYZn3CJJw==
"@sentry/core@8.23.0":
version "8.23.0"
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-8.23.0.tgz#76a46f5295857eebd8ec7d9be9341df31e0b66e3"
integrity sha512-o0tHpxwi5WxjaQPtY+BPkG8FliM4QB91QKoi2QclWvR9t9jUgMWZ4ikziybNiKICZRXtN9B6wSBWlPVWfsiN6A==
dependencies:
"@sentry/types" "8.20.0"
"@sentry/utils" "8.20.0"
"@sentry/types" "8.23.0"
"@sentry/utils" "8.23.0"
"@sentry/types@8.20.0":
version "8.20.0"
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-8.20.0.tgz#f0f50c84eb768df8b55ee7b41459fec2d39d0d5e"
integrity sha512-6IP278KojOpiAA7vrd1hjhUyn26cl0n0nGsShzic5ztCVs92sTeVRnh7MTB9irDVtAbOEyt/YH6go3h+Jia1pA==
"@sentry/types@8.23.0":
version "8.23.0"
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-8.23.0.tgz#02912ee3be3bc79a40d2d5313d035b524de27d7e"
integrity sha512-oJbZ04chsz3Gqro3GJuAAcEsJ7RVjk3k4TvAMxmhN5tQUqwvKFtvWjfskcF75ECzY+8Qge6PI7eXoibkhjx8sg==
"@sentry/utils@8.20.0":
version "8.20.0"
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-8.20.0.tgz#fcbf46c8e8c8eccbf1db532b087547eb4f6c449c"
integrity sha512-+1I5H8dojURiEUGPliDwheQk8dhjp8uV1sMccR/W/zjFrt4wZyPs+Ttp/V7gzm9LDJoNek9tmELert/jQqWTgg==
"@sentry/utils@8.23.0":
version "8.23.0"
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-8.23.0.tgz#532fb96a645345e2492eb1338b823b95f1078ac7"
integrity sha512-g+rkk+vFQnAz7xHGUTHXybA9qFdp1mtv3JGXtFKlLxPm8bKpzbBlJA3FiX4E7ai/Ksbv0N+K7c5fDth3LX3wAA==
dependencies:
"@sentry/types" "8.20.0"
"@sentry/types" "8.23.0"
"@sinclair/typebox@^0.27.8":
version "0.27.8"