Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
9c7daf084d
commit
ce86f6db28
|
|
@ -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"},
|
||||
|
|
|
|||
10
Gemfile.lock
10
Gemfile.lock
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 = {}) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
mutation ProjectSetContinuousVulnerabilityScanning(
|
||||
$input: ProjectSetContinuousVulnerabilityScanningInput!
|
||||
) {
|
||||
projectSetContinuousVulnerabilityScanning(input: $input) {
|
||||
continuousVulnerabilityScanningEnabled
|
||||
errors
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
||||
|
|
|
|||
|
|
@ -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 project’s 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 project’s 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'
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -0,0 +1 @@
|
|||
bdf178477b3fc98645295a491ddf14a01917c8f259a03ace23ba3c282d699017
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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).
|
||||
|
|
|
|||
|
|
@ -6,3 +6,8 @@ Gemfile/MissingFeatureCategory:
|
|||
|
||||
Rails/Exit:
|
||||
Enabled: false
|
||||
|
||||
# Configuration parameters: AllowSubject.
|
||||
RSpec/MultipleMemoizedHelpers:
|
||||
Max: 25
|
||||
AllowSubject: true
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
112
yarn.lock
|
|
@ -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"
|
||||
|
|
|
|||
Loading…
Reference in New Issue