Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
4abd579c45
commit
f0471bfa61
|
|
@ -1,5 +1,5 @@
|
|||
review-cleanup:
|
||||
timeout: 15min
|
||||
timeout: 30min
|
||||
extends:
|
||||
- .default-retry
|
||||
- .review:rules:review-cleanup
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ import { BV_HIDE_TOOLTIP } from '~/lib/utils/constants';
|
|||
import { n__, s__ } from '~/locale';
|
||||
import Tracking from '~/tracking';
|
||||
import { TYPE_ISSUE } from '~/issues/constants';
|
||||
import { formatDate } from '~/lib/utils/datetime_utility';
|
||||
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
||||
import setActiveBoardItemMutation from 'ee_else_ce/boards/graphql/client/set_active_board_item.mutation.graphql';
|
||||
import AccessorUtilities from '~/lib/utils/accessor';
|
||||
|
|
@ -302,16 +301,6 @@ export default {
|
|||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* TODO: https://gitlab.com/gitlab-org/gitlab/-/issues/344619
|
||||
* This method also exists as a utility function in ee/../iterations/utils.js
|
||||
* Remove the duplication when the EE code is separated from this compoment.
|
||||
*/
|
||||
getIterationPeriod({ startDate, dueDate }) {
|
||||
const start = formatDate(startDate, 'mmm d, yyyy', true);
|
||||
const due = formatDate(dueDate, 'mmm d, yyyy', true);
|
||||
return `${start} - ${due}`;
|
||||
},
|
||||
updateLocalCollapsedStatus(collapsed) {
|
||||
this.$apollo.mutate({
|
||||
mutation: toggleCollapsedMutations[this.issuableType].mutation,
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ query getPipelineStageJobs($id: CiStageID!) {
|
|||
id
|
||||
action {
|
||||
id
|
||||
confirmationMessage
|
||||
icon
|
||||
path
|
||||
title
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
<script>
|
||||
import { GlButton, GlIcon, GlLoadingIcon, GlTooltipDirective } from '@gitlab/ui';
|
||||
|
||||
export default {
|
||||
name: 'JobActionButton',
|
||||
components: {
|
||||
GlButton,
|
||||
GlIcon,
|
||||
GlLoadingIcon,
|
||||
},
|
||||
directives: {
|
||||
GlTooltip: GlTooltipDirective,
|
||||
},
|
||||
props: {
|
||||
jobId: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
jobAction: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
jobName: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isLoading: false,
|
||||
showConfirmationModal: false,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<gl-button
|
||||
v-gl-tooltip.viewport.right
|
||||
:title="jobAction.title"
|
||||
:aria-label="jobAction.title"
|
||||
:disabled="isLoading"
|
||||
class="gl-rounded-full! gl-p-0! gl-w-6 gl-h-6"
|
||||
>
|
||||
<gl-loading-icon v-if="isLoading" size="sm" class="gl-m-2" />
|
||||
<gl-icon v-else :name="jobAction.icon" :size="12" />
|
||||
</gl-button>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -1,12 +1,15 @@
|
|||
<script>
|
||||
import { GlDisclosureDropdownItem, GlTooltipDirective } from '@gitlab/ui';
|
||||
import { sprintf } from '~/locale';
|
||||
import { capitalizeFirstCharacter } from '~/lib/utils/text_utility';
|
||||
import delayedJobMixin from '~/ci/mixins/delayed_job_mixin';
|
||||
import JobNameComponent from '~/ci/common/private/job_name_component.vue';
|
||||
import JobActionButton from './job_action_button.vue';
|
||||
|
||||
export default {
|
||||
name: 'JobItem',
|
||||
components: {
|
||||
JobActionButton,
|
||||
JobNameComponent,
|
||||
GlDisclosureDropdownItem,
|
||||
},
|
||||
|
|
@ -21,6 +24,9 @@ export default {
|
|||
},
|
||||
},
|
||||
computed: {
|
||||
hasJobAction() {
|
||||
return Boolean(this.status?.action?.id);
|
||||
},
|
||||
item() {
|
||||
return {
|
||||
text: this.job.name,
|
||||
|
|
@ -36,7 +42,7 @@ export default {
|
|||
if (this.isDelayedJob) {
|
||||
return sprintf(statusTooltip, { remainingTime: this.remainingTime });
|
||||
}
|
||||
return statusTooltip;
|
||||
return capitalizeFirstCharacter(statusTooltip);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
@ -44,13 +50,21 @@ export default {
|
|||
<template>
|
||||
<gl-disclosure-dropdown-item :item="item">
|
||||
<template #list-item>
|
||||
<job-name-component
|
||||
v-gl-tooltip.viewport.left
|
||||
class="-gl-my-2"
|
||||
:title="tooltipText"
|
||||
:name="job.name"
|
||||
:status="status"
|
||||
/>
|
||||
<div class="gl-flex -gl-my-2 gl-h-6">
|
||||
<job-name-component
|
||||
v-gl-tooltip.viewport.left
|
||||
class="-gl-my-2"
|
||||
:title="tooltipText"
|
||||
:name="job.name"
|
||||
:status="status"
|
||||
/>
|
||||
<job-action-button
|
||||
v-if="hasJobAction"
|
||||
:job-id="job.id"
|
||||
:job-action="status.action"
|
||||
:job-name="job.name"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</gl-disclosure-dropdown-item>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import {
|
|||
I18N_JOB_STATUS_ACTIVE,
|
||||
I18N_JOB_STATUS_IDLE,
|
||||
JOB_STATUS_ACTIVE,
|
||||
JOB_STATUS_RUNNING,
|
||||
JOB_STATUS_IDLE,
|
||||
} from '../constants';
|
||||
|
||||
|
|
@ -26,7 +25,6 @@ export default {
|
|||
badge() {
|
||||
switch (this.jobStatus) {
|
||||
case JOB_STATUS_ACTIVE:
|
||||
case JOB_STATUS_RUNNING:
|
||||
return {
|
||||
classes: 'gl-text-blue-600! gl-shadow-inner-1-gray-400 gl-border-blue-600!',
|
||||
label: I18N_JOB_STATUS_ACTIVE,
|
||||
|
|
|
|||
|
|
@ -164,7 +164,6 @@ export const STATUS_STALE = 'STALE';
|
|||
// CiRunnerJobExecutionStatus
|
||||
|
||||
export const JOB_STATUS_ACTIVE = 'ACTIVE';
|
||||
export const JOB_STATUS_RUNNING = 'RUNNING';
|
||||
export const JOB_STATUS_IDLE = 'IDLE';
|
||||
|
||||
// CiRunnerAccessLevel
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
module Import
|
||||
class SourceUserPlaceholderReference < ApplicationRecord
|
||||
include BulkInsertSafe
|
||||
|
||||
self.table_name = 'import_source_user_placeholder_references'
|
||||
|
||||
belongs_to :source_user, class_name: 'Import::SourceUser'
|
||||
|
|
@ -16,6 +18,36 @@ module Import
|
|||
|
||||
attribute :composite_key, :ind_jsonb
|
||||
|
||||
# If an element is ever added to this array, ensure that `.from_serialized` handles receiving
|
||||
# older versions of the array by filling in missing values with defaults. We'd keep that in place
|
||||
# for at least one release cycle to ensure backward compatibility.
|
||||
SERIALIZABLE_ATTRIBUTES = %w[
|
||||
composite_key
|
||||
model
|
||||
namespace_id
|
||||
numeric_key
|
||||
source_user_id
|
||||
user_reference_column
|
||||
].freeze
|
||||
|
||||
SerializationError = Class.new(StandardError)
|
||||
|
||||
def to_serialized
|
||||
Gitlab::Json.dump(attributes.slice(*SERIALIZABLE_ATTRIBUTES).to_h.values)
|
||||
end
|
||||
|
||||
class << self
|
||||
def from_serialized(serialized_reference)
|
||||
deserialized = Gitlab::Json.parse(serialized_reference)
|
||||
|
||||
raise SerializationError if deserialized.size != SERIALIZABLE_ATTRIBUTES.size
|
||||
|
||||
attributes = SERIALIZABLE_ATTRIBUTES.zip(deserialized).to_h
|
||||
|
||||
new(attributes.merge(created_at: Time.zone.now))
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def validate_numeric_or_composite_key_present
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ class NamespaceSetting < ApplicationRecord
|
|||
|
||||
enum jobs_to_be_done: { basics: 0, move_repository: 1, code_storage: 2, exploring: 3, ci: 4, other: 5 }, _suffix: true
|
||||
enum enabled_git_access_protocol: { all: 0, ssh: 1, http: 2 }, _suffix: true
|
||||
enum seat_control: { off: 0, user_cap: 1 }, _prefix: true
|
||||
|
||||
attribute :default_branch_protection_defaults, default: -> { {} }
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Import
|
||||
module PlaceholderReferences
|
||||
class BaseService
|
||||
include Services::ReturnServiceResponses
|
||||
|
||||
def initialize(import_source:, import_uid:)
|
||||
@import_source = import_source
|
||||
@import_uid = import_uid
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :import_source, :import_uid, :reference
|
||||
|
||||
def cache
|
||||
Gitlab::Cache::Import::Caching
|
||||
end
|
||||
|
||||
def cache_key
|
||||
@cache_key ||= [:'placeholder-reference', import_source, import_uid].join(':')
|
||||
end
|
||||
|
||||
def logger
|
||||
Framework::Logger
|
||||
end
|
||||
|
||||
def log_info(...)
|
||||
logger.info(logger_params(...))
|
||||
end
|
||||
|
||||
def log_error(...)
|
||||
logger.error(logger_params(...))
|
||||
end
|
||||
|
||||
def logger_params(message:, **params)
|
||||
params.merge(
|
||||
message: message,
|
||||
import_source: import_source,
|
||||
import_uid: import_uid
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Import
|
||||
module PlaceholderReferences
|
||||
class LoadService < BaseService
|
||||
BATCH_LIMIT = 500
|
||||
|
||||
def initialize(import_source:, import_uid:)
|
||||
super(import_source: import_source, import_uid: import_uid)
|
||||
|
||||
@processed_count = 0
|
||||
@error_count = 0
|
||||
end
|
||||
|
||||
def execute
|
||||
log_info(message: 'Processing placeholder references')
|
||||
|
||||
while (batch = next_batch).present?
|
||||
load!(batch)
|
||||
|
||||
# End this loop if we know that we cleared the set earlier.
|
||||
# This prevents processing just a few records at a time if an import is simultaneously writing data to Redis.
|
||||
break if batch.size < BATCH_LIMIT
|
||||
end
|
||||
|
||||
log_info(
|
||||
message: 'Processed placeholder references',
|
||||
processed_count: processed_count,
|
||||
error_count: error_count
|
||||
)
|
||||
|
||||
success(processed_count: processed_count, error_count: error_count)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_accessor :error_count, :processed_count
|
||||
|
||||
def next_batch
|
||||
cache.limited_values_from_set(cache_key, limit: BATCH_LIMIT)
|
||||
end
|
||||
|
||||
def load!(batch)
|
||||
to_load = batch.filter_map do |item|
|
||||
SourceUserPlaceholderReference.from_serialized(item)
|
||||
rescue JSON::ParserError, SourceUserPlaceholderReference::SerializationError => e
|
||||
log_error(item, e)
|
||||
nil
|
||||
end
|
||||
|
||||
begin
|
||||
bulk_insert!(to_load)
|
||||
rescue ActiveRecord::RecordInvalid => e
|
||||
# We optimise for all records being valid and only filter for validity
|
||||
# when there was a problem
|
||||
to_load.reject! do |item|
|
||||
next false if item.valid?
|
||||
|
||||
log_error(item.attributes, e)
|
||||
true
|
||||
end
|
||||
|
||||
# Try again
|
||||
bulk_insert!(to_load)
|
||||
rescue ActiveRecord::InvalidForeignKey => e
|
||||
# This is an unrecoverable situation where we allow the error to clear the batch
|
||||
log_error(to_load, e)
|
||||
end
|
||||
|
||||
clear_batch!(batch)
|
||||
end
|
||||
|
||||
def bulk_insert!(to_load)
|
||||
Import::SourceUserPlaceholderReference.bulk_insert!(to_load)
|
||||
end
|
||||
|
||||
def clear_batch!(batch)
|
||||
processed_count = batch.size
|
||||
|
||||
self.processed_count += processed_count
|
||||
|
||||
cache.set_remove(cache_key, batch)
|
||||
end
|
||||
|
||||
def log_error(item, exception)
|
||||
super(
|
||||
message: 'Error processing placeholder reference',
|
||||
item: item,
|
||||
exception: {
|
||||
class: exception.class,
|
||||
message: exception.message
|
||||
}
|
||||
)
|
||||
|
||||
self.error_count += 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Import
|
||||
module PlaceholderReferences
|
||||
class PushService < BaseService
|
||||
class << self
|
||||
def from_record(import_source:, import_uid:, source_user:, record:, user_reference_column:, composite_key: nil)
|
||||
numeric_key = record.id if composite_key.nil? && record.id.is_a?(Integer)
|
||||
|
||||
new(
|
||||
import_source: import_source,
|
||||
import_uid: import_uid,
|
||||
model: record.class,
|
||||
composite_key: composite_key,
|
||||
numeric_key: numeric_key,
|
||||
source_user_id: source_user.id,
|
||||
source_user_namespace_id: source_user.namespace_id,
|
||||
user_reference_column: user_reference_column
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(import_source:, import_uid:, source_user_id:, source_user_namespace_id:, model:, user_reference_column:, numeric_key: nil, composite_key: nil) # rubocop:disable Layout/LineLength -- Its easier to read being on one line
|
||||
super(import_source: import_source, import_uid: import_uid)
|
||||
|
||||
@reference = Import::SourceUserPlaceholderReference.new(
|
||||
model: model.name,
|
||||
source_user_id: source_user_id,
|
||||
namespace_id: source_user_namespace_id,
|
||||
user_reference_column: user_reference_column,
|
||||
numeric_key: numeric_key,
|
||||
composite_key: composite_key
|
||||
)
|
||||
end
|
||||
|
||||
def execute
|
||||
return error(reference.errors.full_messages, :bad_request) unless reference.valid?
|
||||
|
||||
serialized_reference = reference.to_serialized
|
||||
|
||||
cache.set_add(cache_key, serialized_reference, timeout: cache_ttl)
|
||||
|
||||
success(serialized_reference: serialized_reference)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :reference
|
||||
|
||||
def cache_ttl
|
||||
Gitlab::Cache::Import::Caching::TIMEOUT
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -5,6 +5,10 @@ module Packages
|
|||
class CheckManifestCoherenceService
|
||||
MismatchError = Class.new(StandardError)
|
||||
|
||||
PKG_TYPE = 'npm'
|
||||
MANIFEST_NOT_COHERENT_ERROR = 'Package manifest is not coherent'
|
||||
VERSION_NOT_COMPLIANT_ERROR = 'Version in package.json is not SemVer compliant'
|
||||
|
||||
delegate :npm_metadatum, to: :package, private: true
|
||||
delegate :package_json_scripts, to: :npm_metadatum, private: true, allow_nil: true
|
||||
|
||||
|
|
@ -16,7 +20,7 @@ module Packages
|
|||
def execute
|
||||
parsed_package_json = Gitlab::Json.parse(package_json_entry.read)
|
||||
|
||||
raise MismatchError, 'Package manifest is not coherent' unless coherent?(parsed_package_json)
|
||||
raise MismatchError, MANIFEST_NOT_COHERENT_ERROR unless coherent?(parsed_package_json)
|
||||
|
||||
ServiceResponse.success
|
||||
end
|
||||
|
|
@ -27,9 +31,18 @@ module Packages
|
|||
|
||||
def coherent?(package_json)
|
||||
package_json['name'] == package.name &&
|
||||
package_json['version'] == package.version &&
|
||||
same_version?(package_json['version'], package.version) &&
|
||||
(package_json['scripts'] || {}) == (package_json_scripts || {})
|
||||
end
|
||||
|
||||
def same_version?(version1, version2)
|
||||
v1 = SemverDialects.parse_version(PKG_TYPE, version1)
|
||||
v2 = SemverDialects.parse_version(PKG_TYPE, version2)
|
||||
|
||||
v1 == v2
|
||||
rescue SemverDialects::InvalidVersionError
|
||||
raise MismatchError, VERSION_NOT_COMPLIANT_ERROR
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
- return if @issue.work_item_type&.incident?
|
||||
|
||||
- requirements_link_url = help_page_path('user/project/issues/design_management', anchor: 'requirements')
|
||||
- requirements_link_url = help_page_path('user/project/issues/design_management', anchor: 'prerequisites')
|
||||
- requirements_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: requirements_link_url }
|
||||
- link_end = '</a>'.html_safe
|
||||
- enable_lfs_message = s_("DesignManagement|To upload designs, you'll need to enable LFS and have an admin enable hashed storage. %{requirements_link_start}More information%{requirements_link_end}").html_safe % { requirements_link_start: requirements_link_start, requirements_link_end: link_end }
|
||||
|
|
|
|||
|
|
@ -3252,6 +3252,15 @@
|
|||
:weight: 2
|
||||
:idempotent: true
|
||||
:tags: []
|
||||
- :name: import_load_placeholder_references
|
||||
:worker_name: Import::LoadPlaceholderReferencesWorker
|
||||
:feature_category: :importers
|
||||
:has_external_dependencies: false
|
||||
:urgency: :low
|
||||
:resource_boundary: :unknown
|
||||
:weight: 1
|
||||
:idempotent: true
|
||||
:tags: []
|
||||
- :name: import_refresh_import_jid
|
||||
:worker_name: Gitlab::Import::RefreshImportJidWorker
|
||||
:feature_category: :importers
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Import
|
||||
class LoadPlaceholderReferencesWorker
|
||||
include ApplicationWorker
|
||||
|
||||
data_consistency :delayed
|
||||
deduplicate :until_executed, if_deduplicated: :reschedule_once
|
||||
idempotent!
|
||||
feature_category :importers
|
||||
loggable_arguments 0, 1
|
||||
|
||||
def perform(import_source, import_uid, params = {})
|
||||
return unless Feature.enabled?(:bulk_import_user_mapping, User.actor_from_id(params['current_user_id']))
|
||||
|
||||
::Import::PlaceholderReferences::LoadService.new(
|
||||
import_source: import_source,
|
||||
import_uid: import_uid
|
||||
).execute
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -5,5 +5,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/157696
|
|||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/469499
|
||||
milestone: '17.2'
|
||||
group: group::ai framework
|
||||
type: gitlab_com_derisk
|
||||
default_enabled: false
|
||||
type: beta
|
||||
default_enabled: true
|
||||
|
|
@ -405,6 +405,8 @@
|
|||
- 1
|
||||
- - import_issues_csv
|
||||
- 2
|
||||
- - import_load_placeholder_references
|
||||
- 1
|
||||
- - import_refresh_import_jid
|
||||
- 1
|
||||
- - incident_management
|
||||
|
|
|
|||
|
|
@ -7,3 +7,4 @@ feature_category: security_policy_management
|
|||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/127212
|
||||
queued_migration_version: 20230721095223
|
||||
milestone: '16.5'
|
||||
finalized_by: '20240714231920'
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class FinalizeDeleteOrphansApprovalMergeRequestRules2 < Gitlab::Database::Migration[2.2]
|
||||
milestone '17.2'
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
restrict_gitlab_migration gitlab_schema: :gitlab_main
|
||||
|
||||
def up
|
||||
ensure_batched_background_migration_is_finished(
|
||||
job_class_name: 'DeleteOrphansApprovalMergeRequestRules2',
|
||||
table_name: :approval_merge_request_rules,
|
||||
column_name: :id,
|
||||
job_arguments: [],
|
||||
finalize: true
|
||||
)
|
||||
end
|
||||
|
||||
def down; end
|
||||
end
|
||||
|
|
@ -0,0 +1 @@
|
|||
dad465f0fdf57b03ba479c225238422df9008fcdca0cda7757feaac70c0040fa
|
||||
|
|
@ -108,7 +108,7 @@ You can remove the cookie-based language selector from the footer of the sign-in
|
|||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/375708) in GitLab 15.9.
|
||||
|
||||
Customize the icon, display name, short name, and description for your Progessive Web App (PWA). For more information, see [Progressive Web App](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps).
|
||||
Customize the icon, display name, short name, and description for your Progressive Web App (PWA). For more information, see [Progressive Web App](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps).
|
||||
|
||||
To add a Progressive Web App name and short name:
|
||||
|
||||
|
|
|
|||
|
|
@ -163,7 +163,7 @@ The adaptive limiter calibrates the limits every 30 seconds and:
|
|||
or CPU throttled for 50% or more of the observation time.
|
||||
|
||||
Otherwise, the limits increase by one until reaching the upper bound. For more information about technical implementation
|
||||
of this system, refer to [this blueprint](../../architecture/blueprints/gitaly_adaptive_concurrency_limit/index.md).
|
||||
of this system, refer to [the related design document](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/gitaly_adaptive_concurrency_limit/).
|
||||
|
||||
Adaptive limiting is enabled for each RPC or pack-objects cache individually. However, limits are calibrated at the same time.
|
||||
|
||||
|
|
|
|||
|
|
@ -181,10 +181,6 @@ All application settings have a [customizable cache expiry interval](../../admin
|
|||
You can archive old jobs to prevent them from being re-run individually. Archived jobs
|
||||
display a lock icon (**{lock}**) and **This job is archived** at the top of the job log.
|
||||
|
||||
Future work is planned to reduce the CI/CD footprint on the system for archived jobs
|
||||
by removing metadata stored in the database needed to run the job. See the [CI/CD data time decay](../../architecture/blueprints/ci_data_decay/index.md)
|
||||
blueprint for more details.
|
||||
|
||||
To set the duration for which the jobs are considered as old and expired:
|
||||
|
||||
1. On the left sidebar, at the bottom, select **Admin area**.
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ DETAILS:
|
|||
|
||||
WARNING:
|
||||
The Epics REST API was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/460668) in GitLab 17.0
|
||||
and is planned for removal in v5 of the API. Use the [Work Items API](../architecture/blueprints/work_items/index.md) instead.
|
||||
and is planned for removal in v5 of the API. Use the [Work Items API](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/work_items/) instead.
|
||||
This change is a breaking change.
|
||||
|
||||
Every API call to epic must be authenticated.
|
||||
|
|
|
|||
|
|
@ -115,3 +115,46 @@ curl --request POST \
|
|||
"minimum_access_level_for_delete": "maintainer"
|
||||
}'
|
||||
```
|
||||
|
||||
## Update a container registry protection rule
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/457518) in GitLab 17.2.
|
||||
|
||||
Update a container registry protection rule for a project.
|
||||
|
||||
```plaintext
|
||||
PATCH /api/v4/projects/:id/registry/protection/rules/:protection_rule_id
|
||||
```
|
||||
|
||||
Supported attributes:
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
|-----------------------------------|----------------|----------|-------------|
|
||||
| `id` | integer/string | Yes | ID or [URL-encoded path of the project](rest/index.md#namespaced-path-encoding) owned by the authenticated user. |
|
||||
| `protection_rule_id` | integer | Yes | ID of the protection rule to be updated. |
|
||||
| `repository_path_pattern` | string | No | Container repository path pattern protected by the protection rule. For example `flight/flight-*`. Wildcard character `*` allowed. |
|
||||
| `minimum_access_level_for_push` | string | No | Minimum GitLab access level to allow to push container images to the container registry. For example `maintainer`, `owner` or `admin`. Must be provided when `minimum_access_level_for_delete` is not set. To unset the value, use an empty string `""`. |
|
||||
| `minimum_access_level_for_delete` | string | No | Minimum GitLab access level to allow to delete container images in the container registry. For example `maintainer`, `owner`, `admin`. Must be provided when `minimum_access_level_for_push` is not set. To unset the value, use an empty string `""`. |
|
||||
|
||||
If successful, returns [`200`](rest/index.md#status-codes) and the updated protection rule.
|
||||
|
||||
Can return the following status codes:
|
||||
|
||||
- `200 OK`: The protection rule was patched successfully.
|
||||
- `400 Bad Request`: The patch is invalid.
|
||||
- `401 Unauthorized`: The access token is invalid.
|
||||
- `403 Forbidden`: The user does not have permission to patch the protection rule.
|
||||
- `404 Not Found`: The project was not found.
|
||||
- `422 Unprocessable Entity`: The protection rule could not be patched, for example, because the `repository_path_pattern` is already taken.
|
||||
|
||||
Example request:
|
||||
|
||||
```shell
|
||||
curl --request PATCH \
|
||||
--header "PRIVATE-TOKEN: <your_access_token>" \
|
||||
--header "Content-Type: application/json" \
|
||||
--url "https://gitlab.example.com/api/v4/projects/7/registry/protection/rules/32" \
|
||||
--data '{
|
||||
"repository_path_pattern": "flight/flight-*"
|
||||
}'
|
||||
```
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ runners. The legacy workflow that uses registration tokens is deprecated and wil
|
|||
|
||||
For information about the current development status of the new workflow, see [epic 7663](https://gitlab.com/groups/gitlab-org/-/epics/7663).
|
||||
|
||||
For information about the technical design and reasons for the new architecture, see [Next GitLab Runner Token Architecture](../../architecture/blueprints/runner_tokens/index.md).
|
||||
For information about the technical design and reasons for the new architecture, see [Next GitLab Runner Token Architecture](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/runner_tokens/).
|
||||
|
||||
If you experience problems or have concerns about the new runner registration workflow,
|
||||
or if the following information is not sufficient,
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ This feature requires two feature flags:
|
|||
projects. Requires the `activity_pub` flag to also be enabled.
|
||||
|
||||
Most of the implementation is being discussed in
|
||||
[an architecture blueprint](../../architecture/blueprints/activity_pub/index.md),
|
||||
[an architecture design document](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/activity_pub/),
|
||||
see this document for more information.
|
||||
|
||||
For now, see [how to implement an ActivityPub actor](actors/index.md).
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ info: Any user with at least the Maintainer role can merge updates to this conte
|
|||
|
||||
# AI Architecture
|
||||
|
||||
This document describes architecture shared by the GitLab Duo AI features. For historical motivation and goals of this architecture, see the [AI Gateway Architecture blueprint](../architecture/blueprints/ai_gateway/index.md).
|
||||
This document describes architecture shared by the GitLab Duo AI features. For historical motivation and goals of this architecture, see the [AI Gateway Architecture design document](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/ai_gateway/).
|
||||
|
||||
## Introduction
|
||||
|
||||
|
|
@ -69,7 +69,7 @@ AIGW -down-> Models : prompts
|
|||
|
||||
## SaaS-based AI abstraction layer
|
||||
|
||||
GitLab currently operates a cloud-hosted AI architecture. We will allow access to it for licensed self managed instances using the AI-gateway. See [the blueprint](../architecture/blueprints/ai_gateway/index.md) for details.
|
||||
GitLab currently operates a cloud-hosted AI architecture. We will allow access to it for licensed self managed instances using the AI-gateway. See [the design document](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/ai_gateway/) for details.
|
||||
|
||||
There are two primary reasons for this: the best AI models are cloud-based as they often depend on specialized hardware designed for this purpose, and operating self-managed infrastructure capable of AI at-scale and with appropriate performance is a significant undertaking. We are actively [tracking self-managed customers interested in AI](https://gitlab.com/gitlab-org/gitlab/-/issues/409183).
|
||||
|
||||
|
|
@ -112,7 +112,7 @@ The following models have been approved for use:
|
|||
### Vector stores
|
||||
|
||||
NOTE:
|
||||
There is a proposal to change vector stores for improving the quality of search results. See [RAG for GitLab Duo](../architecture/blueprints/gitlab_duo_rag/index.md) for more information.
|
||||
There is a proposal to change vector stores for improving the quality of search results. See [RAG for GitLab Duo](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/gitlab_duo_rag/) for more information.
|
||||
|
||||
The following vector stores have been approved for use:
|
||||
|
||||
|
|
@ -121,7 +121,7 @@ The following vector stores have been approved for use:
|
|||
### Indexing Update
|
||||
|
||||
NOTE:
|
||||
There is a proposal to change indexing update for improving the quality of search results. See [RAG for GitLab Duo](../architecture/blueprints/gitlab_duo_rag/index.md) for more information.
|
||||
There is a proposal to change indexing update for improving the quality of search results. See [RAG for GitLab Duo](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/gitlab_duo_rag/) for more information.
|
||||
|
||||
We are currently using sequential scan, which provides perfect recall. We are considering adding an index if we can ensure that it still produces accurate results, as noted in the `pgvector` indexing [documentation](https://github.com/pgvector/pgvector#indexing).
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ the help of [prompts](glossary.md) and [tools](#adding-a-new-tool).
|
|||
To answer a user's question asked in the Chat interface, GitLab sends a
|
||||
[GraphQL request](https://gitlab.com/gitlab-org/gitlab/-/blob/4cfd0af35be922045499edb8114652ba96fcba63/ee/app/graphql/mutations/ai/action.rb)
|
||||
to the Rails backend. Rails backend sends then instructions to the Large
|
||||
Language Model (LLM) through the [AI Gateway](../../architecture/blueprints/ai_gateway/index.md).
|
||||
Language Model (LLM) through the [AI Gateway](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/ai_gateway/).
|
||||
|
||||
## Which use cases lend themselves most to contributing to Chat?
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ to AI that you think could benefit from being in this list, add it!
|
|||
service is ready. Eventually, the AI Gateway will be used to host endpoints that
|
||||
proxy requests to AI providers, removing the need for the GitLab Rails monolith
|
||||
to integrate and communicate directly with third-party Large Language Models (LLMs).
|
||||
[Blueprint](../../architecture/blueprints/ai_gateway/index.md).
|
||||
[Design document](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/ai_gateway/).
|
||||
- **Air-Gapped Model**: A hosted model that is internal to an organisations intranet only. In the context of GitLab AI features, this could be connected to an air-gapped GitLab instance.
|
||||
- **Bring Your Own Model (BYOM)**: A third-party model to be connected to one or more GitLab Duo features. Could be an off-the-shelf Open Source (OS) model, a fine-tuned model, or a closed source model. GitLab is planning to support specific, validated BYOMs for GitLab Duo features, but does not currently support or plan to support general BYOM use for GitLab Duo features.
|
||||
- **Chat Evaluation**: automated mechanism for determining the helpfulness and
|
||||
|
|
@ -68,7 +68,7 @@ to AI that you think could benefit from being in this list, add it!
|
|||
on prompts with various third-party AI Services.
|
||||
[Code](https://gitlab.com/gitlab-org/modelops/applied-ml/code-suggestions/prompt-library).
|
||||
- **Prompt Registry**: stored, versioned prompts used to interact with third-party
|
||||
AI Services. [Blueprint](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/135872).
|
||||
AI Services. [Design document proposal MR (closed)](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/135872).
|
||||
- **Prompt**: Natural language instructions sent to an LLM to perform certain tasks. [Prompt guidelines](prompts.md).
|
||||
- **RAG (Retrieval Augmented Generation)**: RAG provide contextual data to an LLM as part of a query to personalise results. RAG is used to inject additional context into a prompt to decrease hallucinations and improve the quality of outputs.
|
||||
- **RAG Pipeline**: A mechanism used to take
|
||||
|
|
|
|||
|
|
@ -8,31 +8,198 @@ info: Any user with at least the Maintainer role can merge updates to this conte
|
|||
|
||||
For background of GitLab Cells, refer to the [design document](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/cells/).
|
||||
|
||||
## Essential and additional workflows
|
||||
## Choose either the `gitlab_main_cell` or `gitlab_main_clusterwide` schema
|
||||
|
||||
To make the application work within the GitLab Cells architecture, we need to fix various
|
||||
[workflows](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/cells/#2-workflows).
|
||||
Depending on the use case, your feature may be [cell-local or clusterwide](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/cells/#how-do-i-decide-whether-to-move-my-feature-to-the-cluster-cell-or-organization-level) and hence the tables used for the feature should also use the appropriate schema.
|
||||
|
||||
Here is the suggested approach:
|
||||
When you choose the appropriate schema for tables, consider the following guidelines as part of the [Cells](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/cells/) architecture:
|
||||
|
||||
1. Pick a workflow to fix.
|
||||
1. Firstly, we need to find out the tables that are affected while performing the chosen workflow. As an example, in [this note](https://gitlab.com/gitlab-org/gitlab/-/issues/428600#note_1610331742) we have described how to figure out the list of all tables that are affected when a project is created in a group.
|
||||
1. For each table affected for the chosen workflow, choose the approriate
|
||||
[GitLab schema](../database/multiple_databases.md#gitlab-schema).
|
||||
1. Identify all cross-joins, cross-transactions, and cross-database foreign keys for
|
||||
these tables.
|
||||
See the [multiple databases guide](../database/multiple_databases.md)
|
||||
on how to identify, and allowlist these items.
|
||||
1. Fix the cross-joins and cross-database foreign keys necessary for the
|
||||
workflow to work with GitLab Cells.
|
||||
See the [multiple databases guide](../database/multiple_databases.md)
|
||||
on how to fix these items.
|
||||
1. For the cross-joins, cross-transactions, and cross-database foreign keys that
|
||||
were not fixed, open and schedule issues to fix later.
|
||||
1. Confirm the fixes work by completing the workflow successfully in a local
|
||||
GDK running multiple cells.
|
||||
- Default to `gitlab_main_cell`: We expect most tables to be assigned to the `gitlab_main_cell` schema by default. Choose this schema if the data in the table is related to `projects` or `namespaces`.
|
||||
- Consult with the Tenant Scale group: If you believe that the `gitlab_main_clusterwide` schema is more suitable for a table, seek approval from the Tenant Scale group. This is crucial because it has scaling implications and may require reconsideration of the schema choice.
|
||||
|
||||
Refer to following epics for examples:
|
||||
To understand how existing tables are classified, you can use [this dashboard](https://manojmj.gitlab.io/tenant-scale-schema-progress/).
|
||||
|
||||
- [User can create group on Cell 2 while users are shared between Cells](https://gitlab.com/groups/gitlab-org/-/epics/9813)
|
||||
- [Essential workflows: User can create Project](https://gitlab.com/groups/gitlab-org/-/epics/11683)
|
||||
After a schema has been assigned, the merge request pipeline might fail due to one or more of the following reasons, which can be rectified by following the linked guidelines:
|
||||
|
||||
- [Cross-database joins](../database/multiple_databases.md#suggestions-for-removing-cross-database-joins)
|
||||
- [Cross-database transactions](../database/multiple_databases.md#fixing-cross-database-transactions)
|
||||
- [Cross-database foreign keys](../database/multiple_databases.md#foreign-keys-that-cross-databases)
|
||||
|
||||
## Defining a sharding key for all cell-local tables
|
||||
|
||||
All tables with the following `gitlab_schema` are considered "cell-local":
|
||||
|
||||
- `gitlab_main_cell`
|
||||
- `gitlab_ci`
|
||||
|
||||
All newly created cell-local tables are required to have a `sharding_key`
|
||||
defined in the corresponding `db/docs/` file for that table.
|
||||
|
||||
The purpose of the sharding key is documented in the
|
||||
[Organization isolation blueprint](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/organization/isolation/),
|
||||
but in short this column is used to provide a standard way of determining which
|
||||
Organization owns a particular row in the database. The column will be used in
|
||||
the future to enforce constraints on data not cross Organization boundaries. It
|
||||
will also be used in the future to provide a uniform way to migrate data
|
||||
between Cells.
|
||||
|
||||
The actual name of the foreign key can be anything but it must reference a row
|
||||
in `projects` or `groups`. The chosen `sharding_key` column must be non-nullable.
|
||||
|
||||
Setting multiple `sharding_key`, with nullable columns are also allowed, provided that
|
||||
the table has a check constraint that correctly ensures at least one of the keys must be non-nullable for a row in the table.
|
||||
See [`NOT NULL` constraints for multiple columns](../database/not_null_constraints.md#not-null-constraints-for-multiple-columns)
|
||||
for instructions on creating these constraints.
|
||||
|
||||
The following are examples of valid sharding keys:
|
||||
|
||||
- The table entries belong to a project only:
|
||||
|
||||
```yaml
|
||||
sharding_key:
|
||||
project_id: projects
|
||||
```
|
||||
|
||||
- The table entries belong to a project and the foreign key is `target_project_id`:
|
||||
|
||||
```yaml
|
||||
sharding_key:
|
||||
target_project_id: projects
|
||||
```
|
||||
|
||||
- The table entries belong to a namespace/group only:
|
||||
|
||||
```yaml
|
||||
sharding_key:
|
||||
namespace_id: namespaces
|
||||
```
|
||||
|
||||
- The table entries belong to a namespace/group only and the foreign key is `group_id`:
|
||||
|
||||
```yaml
|
||||
sharding_key:
|
||||
group_id: namespaces
|
||||
```
|
||||
|
||||
- The table entries belong to a namespace or a project:
|
||||
|
||||
```yaml
|
||||
sharding_key:
|
||||
project_id: projects
|
||||
namespace_id: namespaces
|
||||
```
|
||||
|
||||
### The sharding key must be immutable
|
||||
|
||||
The choice of a `sharding_key` should always be immutable. Therefore, if your feature
|
||||
requires a user experience which allows data to be moved between projects or
|
||||
groups/namespaces, then you may need to redesign the move feature to create new rows. An
|
||||
example of this can be seen in the
|
||||
[move an issue feature](../../user/project/issues/managing_issues.md#move-an-issue).
|
||||
This feature does not actually change the `project_id` column for an existing
|
||||
`issues` row but instead creates a new `issues` row and creates a link in the
|
||||
database from the original `issues` row. If there is a particularly challenging
|
||||
existing feature that needs to allow moving data you will need to reach out to
|
||||
the Tenant Scale team early on to discuss options for how to manage the
|
||||
sharding key.
|
||||
|
||||
### Using the same sharding key for projects and namespaces
|
||||
|
||||
Developers may also choose to use `namespace_id` only for tables that can
|
||||
belong to a project where the feature used by the table is being developed
|
||||
following the
|
||||
[Consolidating Groups and Projects blueprint](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/consolidating_groups_and_projects/).
|
||||
In that case the `namespace_id` would need to be the ID of the
|
||||
`ProjectNamespace` and not the group that the namespace belongs to.
|
||||
|
||||
### Define a `desired_sharding_key` to automatically backfill a `sharding_key`
|
||||
|
||||
We need to backfill a `sharding_key` to hundreds of tables that do not have one.
|
||||
This process will involve creating a merge request like
|
||||
<https://gitlab.com/gitlab-org/gitlab/-/merge_requests/136800> to add the new
|
||||
column, backfill the data from a related table in the database, and then create
|
||||
subsequent merge requests to add indexes, foreign keys and not-null
|
||||
constraints.
|
||||
|
||||
In order to minimize the amount of repetitive effort for developers we've
|
||||
introduced a concise declarative way to describe how to backfill the
|
||||
`sharding_key` for this specific table. This content will later be used in
|
||||
automation to create all the necessary merge requests.
|
||||
|
||||
An example of the `desired_sharding_key` was added in
|
||||
<https://gitlab.com/gitlab-org/gitlab/-/merge_requests/139336> and it looks like:
|
||||
|
||||
```yaml
|
||||
--- # db/docs/security_findings.yml
|
||||
table_name: security_findings
|
||||
classes:
|
||||
- Security::Finding
|
||||
|
||||
...
|
||||
|
||||
desired_sharding_key:
|
||||
project_id:
|
||||
references: projects
|
||||
backfill_via:
|
||||
parent:
|
||||
foreign_key: scanner_id
|
||||
table: vulnerability_scanners
|
||||
sharding_key: project_id
|
||||
belongs_to: scanner
|
||||
```
|
||||
|
||||
To understand best how this YAML data will be used you can map it onto
|
||||
the merge request we created manually in GraphQL
|
||||
<https://gitlab.com/gitlab-org/gitlab/-/merge_requests/136800>. The idea
|
||||
will be to automatically create this. The content of the YAML specifies
|
||||
the parent table and its `sharding_key` to backfill from in the batched
|
||||
background migration. It also specifies a `belongs_to` relation which
|
||||
will be added to the model to automatically populate the `sharding_key` in
|
||||
the `before_save`.
|
||||
|
||||
#### Define a `desired_sharding_key` when the parent table also has one
|
||||
|
||||
By default, a `desired_sharding_key` configuration will validate that the chosen `sharding_key`
|
||||
exists on the parent table. However, if the parent table also has a `desired_sharding_key` configuration
|
||||
and is itself waiting to be backfilled, you need to include the `awaiting_backfill_on_parent` field.
|
||||
For example:
|
||||
|
||||
```yaml
|
||||
desired_sharding_key:
|
||||
project_id:
|
||||
references: projects
|
||||
backfill_via:
|
||||
parent:
|
||||
foreign_key: package_file_id
|
||||
table: packages_package_files
|
||||
sharding_key: project_id
|
||||
belongs_to: package_file
|
||||
awaiting_backfill_on_parent: true
|
||||
```
|
||||
|
||||
There are likely edge cases where this `desired_sharding_key` structure is not
|
||||
suitable for backfilling a `sharding_key`. In such cases the team owning the
|
||||
table will need to create the necessary merge requests to add the
|
||||
`sharding_key` manually.
|
||||
|
||||
### Exempting certain tables from having sharding keys
|
||||
|
||||
Certain tables can be exempted from having sharding keys by adding
|
||||
|
||||
```yaml
|
||||
exempt_from_sharding: true
|
||||
```
|
||||
|
||||
to the table's database dictionary file. This can be used for:
|
||||
|
||||
- JiHu specific tables, since they do not have any data on the .com database. [!145905](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/145905)
|
||||
- tables that are marked to be dropped soon, like `operations_feature_flag_scopes`. [!147541](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/147541)
|
||||
- tables that mandatorily need to be present per cell to support a cell's operations, have unique data per cell, but cannot have a sharding key defined. For example, `zoekt_nodes`.
|
||||
|
||||
When tables are exempted from sharding key requirements, they also do not show up in our
|
||||
[progress dashboard](https://cells-progress-tracker-gitlab-org-tenant-scale-g-f4ad96bf01d25f.gitlab.io/sharding_keys).
|
||||
|
||||
Exempted tables must not have foreign key, or loose foreign key references, as
|
||||
this may cause the target cell's database to have foreign key violations when data is
|
||||
moved.
|
||||
See [#471182](https://gitlab.com/gitlab-org/gitlab/-/issues/471182) for examples and possible solutions.
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ info: Any user with at least the Maintainer role can merge updates to this conte
|
|||
|
||||
# Add new tables to the CI database
|
||||
|
||||
The [pipeline data partitioning](../../architecture/blueprints/ci_data_decay/pipeline_partitioning.md)
|
||||
design blueprint describes how to partition existing tables in the CI domain. However,
|
||||
The [pipeline data partitioning](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/ci_data_decay/pipeline_partitioning/)
|
||||
design document describes how to partition existing tables in the CI domain. However,
|
||||
you still need to add tables for new features. Sometimes these tables hold
|
||||
references to larger tables that need to be partitioned. To reduce future
|
||||
work, all tables that use a `belongs_to` association to partitionable tables
|
||||
|
|
|
|||
|
|
@ -297,4 +297,4 @@ sequenceDiagram
|
|||
|
||||
## References
|
||||
|
||||
- [Cloud Connector blueprints and ADRs](../../architecture/blueprints/cloud_connector/index.md)
|
||||
- [Cloud Connector design documents and ADRs](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/cloud_connector/)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ info: Any user with at least the Maintainer role can merge updates to this conte
|
|||
|
||||
With the [automated migration testing pipeline](https://gitlab.com/gitlab-org/database-team/gitlab-com-database-testing)
|
||||
we can automatically test migrations in a production-like environment (using [Database Lab](database_lab.md)).
|
||||
It is based on an [architecture blueprint](../../architecture/blueprints/database_testing/index.md).
|
||||
It is based on an [architecture design document](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/database_testing/).
|
||||
|
||||
Migration testing is enabled in the [GitLab project](https://gitlab.com/gitlab-org/gitlab)
|
||||
for changes that add a new database migration. Trigger this job manually by running the
|
||||
|
|
|
|||
|
|
@ -51,199 +51,13 @@ The usage of schema enforces the base class to be used:
|
|||
|
||||
### Choose either the `gitlab_main_cell` or `gitlab_main_clusterwide` schema
|
||||
|
||||
Depending on the use case, your feature may be [cell-local or clusterwide](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/cells/#how-do-i-decide-whether-to-move-my-feature-to-the-cluster-cell-or-organization-level) and hence the tables used for the feature should also use the appropriate schema.
|
||||
|
||||
When you choose the appropriate schema for tables, consider the following guidelines as part of the [Cells](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/cells/) architecture:
|
||||
|
||||
- Default to `gitlab_main_cell`: We expect most tables to be assigned to the `gitlab_main_cell` schema by default. Choose this schema if the data in the table is related to `projects` or `namespaces`.
|
||||
- Consult with the Tenant Scale group: If you believe that the `gitlab_main_clusterwide` schema is more suitable for a table, seek approval from the Tenant Scale group. This is crucial because it has scaling implications and may require reconsideration of the schema choice.
|
||||
|
||||
To understand how existing tables are classified, you can use [this dashboard](https://manojmj.gitlab.io/tenant-scale-schema-progress/).
|
||||
|
||||
After a schema has been assigned, the merge request pipeline might fail due to one or more of the following reasons, which can be rectified by following the linked guidelines:
|
||||
|
||||
- [Cross-database joins](#suggestions-for-removing-cross-database-joins)
|
||||
- [Cross-database transactions](#fixing-cross-database-transactions)
|
||||
- [Cross-database foreign keys](#foreign-keys-that-cross-databases)
|
||||
This content has been moved to a
|
||||
[new location](../cells/index.md#choose-either-the-gitlab_main_cell-or-gitlab_main_clusterwide-schema)
|
||||
|
||||
### Defining a sharding key for all cell-local tables
|
||||
|
||||
All tables with the following `gitlab_schema` are considered "cell-local":
|
||||
|
||||
- `gitlab_main_cell`
|
||||
- `gitlab_ci`
|
||||
|
||||
All newly created cell-local tables are required to have a `sharding_key`
|
||||
defined in the corresponding `db/docs/` file for that table.
|
||||
|
||||
The purpose of the sharding key is documented in the
|
||||
[Organization isolation blueprint](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/organization/isolation/),
|
||||
but in short this column is used to provide a standard way of determining which
|
||||
Organization owns a particular row in the database. The column will be used in
|
||||
the future to enforce constraints on data not cross Organization boundaries. It
|
||||
will also be used in the future to provide a uniform way to migrate data
|
||||
between Cells.
|
||||
|
||||
The actual name of the foreign key can be anything but it must reference a row
|
||||
in `projects` or `groups`. The chosen `sharding_key` column must be non-nullable.
|
||||
|
||||
Setting multiple `sharding_key`, with nullable columns are also allowed, provided that
|
||||
the table has a check constraint that correctly ensures at least one of the keys must be non-nullable for a row in the table.
|
||||
See [`NOT NULL` constraints for multiple columns](not_null_constraints.md#not-null-constraints-for-multiple-columns)
|
||||
for instructions on creating these constraints.
|
||||
|
||||
The following are examples of valid sharding keys:
|
||||
|
||||
- The table entries belong to a project only:
|
||||
|
||||
```yaml
|
||||
sharding_key:
|
||||
project_id: projects
|
||||
```
|
||||
|
||||
- The table entries belong to a project and the foreign key is `target_project_id`:
|
||||
|
||||
```yaml
|
||||
sharding_key:
|
||||
target_project_id: projects
|
||||
```
|
||||
|
||||
- The table entries belong to a namespace/group only:
|
||||
|
||||
```yaml
|
||||
sharding_key:
|
||||
namespace_id: namespaces
|
||||
```
|
||||
|
||||
- The table entries belong to a namespace/group only and the foreign key is `group_id`:
|
||||
|
||||
```yaml
|
||||
sharding_key:
|
||||
group_id: namespaces
|
||||
```
|
||||
|
||||
- The table entries belong to a namespace or a project:
|
||||
|
||||
```yaml
|
||||
sharding_key:
|
||||
project_id: projects
|
||||
namespace_id: namespaces
|
||||
```
|
||||
|
||||
#### The sharding key must be immutable
|
||||
|
||||
The choice of a `sharding_key` should always be immutable. Therefore, if your feature
|
||||
requires a user experience which allows data to be moved between projects or
|
||||
groups/namespaces, then you may need to redesign the move feature to create new rows. An
|
||||
example of this can be seen in the
|
||||
[move an issue feature](../../user/project/issues/managing_issues.md#move-an-issue).
|
||||
This feature does not actually change the `project_id` column for an existing
|
||||
`issues` row but instead creates a new `issues` row and creates a link in the
|
||||
database from the original `issues` row. If there is a particularly challenging
|
||||
existing feature that needs to allow moving data you will need to reach out to
|
||||
the Tenant Scale team early on to discuss options for how to manage the
|
||||
sharding key.
|
||||
|
||||
#### Using the same sharding key for projects and namespaces
|
||||
|
||||
Developers may also choose to use `namespace_id` only for tables that can
|
||||
belong to a project where the feature used by the table is being developed
|
||||
following the
|
||||
[Consolidating Groups and Projects blueprint](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/consolidating_groups_and_projects/).
|
||||
In that case the `namespace_id` would need to be the ID of the
|
||||
`ProjectNamespace` and not the group that the namespace belongs to.
|
||||
|
||||
#### Define a `desired_sharding_key` to automatically backfill a `sharding_key`
|
||||
|
||||
We need to backfill a `sharding_key` to hundreds of tables that do not have one.
|
||||
This process will involve creating a merge request like
|
||||
<https://gitlab.com/gitlab-org/gitlab/-/merge_requests/136800> to add the new
|
||||
column, backfill the data from a related table in the database, and then create
|
||||
subsequent merge requests to add indexes, foreign keys and not-null
|
||||
constraints.
|
||||
|
||||
In order to minimize the amount of repetitive effort for developers we've
|
||||
introduced a concise declarative way to describe how to backfill the
|
||||
`sharding_key` for this specific table. This content will later be used in
|
||||
automation to create all the necessary merge requests.
|
||||
|
||||
An example of the `desired_sharding_key` was added in
|
||||
<https://gitlab.com/gitlab-org/gitlab/-/merge_requests/139336> and it looks like:
|
||||
|
||||
```yaml
|
||||
--- # db/docs/security_findings.yml
|
||||
table_name: security_findings
|
||||
classes:
|
||||
- Security::Finding
|
||||
|
||||
...
|
||||
|
||||
desired_sharding_key:
|
||||
project_id:
|
||||
references: projects
|
||||
backfill_via:
|
||||
parent:
|
||||
foreign_key: scanner_id
|
||||
table: vulnerability_scanners
|
||||
sharding_key: project_id
|
||||
belongs_to: scanner
|
||||
```
|
||||
|
||||
To understand best how this YAML data will be used you can map it onto
|
||||
the merge request we created manually in GraphQL
|
||||
<https://gitlab.com/gitlab-org/gitlab/-/merge_requests/136800>. The idea
|
||||
will be to automatically create this. The content of the YAML specifies
|
||||
the parent table and its `sharding_key` to backfill from in the batched
|
||||
background migration. It also specifies a `belongs_to` relation which
|
||||
will be added to the model to automatically populate the `sharding_key` in
|
||||
the `before_save`.
|
||||
|
||||
##### Define a `desired_sharding_key` when the parent table also has one
|
||||
|
||||
By default, a `desired_sharding_key` configuration will validate that the chosen `sharding_key`
|
||||
exists on the parent table. However, if the parent table also has a `desired_sharding_key` configuration
|
||||
and is itself waiting to be backfilled, you need to include the `awaiting_backfill_on_parent` field.
|
||||
For example:
|
||||
|
||||
```yaml
|
||||
desired_sharding_key:
|
||||
project_id:
|
||||
references: projects
|
||||
backfill_via:
|
||||
parent:
|
||||
foreign_key: package_file_id
|
||||
table: packages_package_files
|
||||
sharding_key: project_id
|
||||
belongs_to: package_file
|
||||
awaiting_backfill_on_parent: true
|
||||
```
|
||||
|
||||
There are likely edge cases where this `desired_sharding_key` structure is not
|
||||
suitable for backfilling a `sharding_key`. In such cases the team owning the
|
||||
table will need to create the necessary merge requests to add the
|
||||
`sharding_key` manually.
|
||||
|
||||
##### Exempting certain tables from having sharding keys
|
||||
|
||||
Certain tables can be exempted from having sharding keys by adding
|
||||
|
||||
```yaml
|
||||
exempt_from_sharding: true
|
||||
```
|
||||
|
||||
to the table's database dictionary file. This can be used for:
|
||||
|
||||
- JiHu specific tables, since they do not have any data on the .com database. [!145905](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/145905)
|
||||
- tables that are marked to be dropped soon, like `operations_feature_flag_scopes`. [!147541](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/147541)
|
||||
- tables that mandatorily need to be present per cell to support a cell's operations, have unique data per cell, but cannot have a sharding key defined. For example, `zoekt_nodes`.
|
||||
|
||||
When tables are exempted from sharding key requirements, they also do not show up in our
|
||||
[progress dashboard](https://cells-progress-tracker-gitlab-org-tenant-scale-g-f4ad96bf01d25f.gitlab.io/sharding_keys).
|
||||
|
||||
Exempted tables must not have foreign key, or loose foreign key references, as
|
||||
this may cause the target cell's database to have foreign key violations when data is
|
||||
moved.
|
||||
See [#471182](https://gitlab.com/gitlab-org/gitlab/-/issues/471182) for examples and possible solutions.
|
||||
This content has been moved to a
|
||||
[new location](../cells/index.md#defining-a-sharding-key-for-all-cell-local-tables)
|
||||
|
||||
### The impact of `gitlab_schema`
|
||||
|
||||
|
|
|
|||
|
|
@ -48,7 +48,6 @@ that are coded across multiple repositories.
|
|||
| [Audit event types](../../../user/compliance/audit_event_types.md) | [Audit event development guidelines](../../audit_event_guide/index.md) | [Compliance](https://handbook.gitlab.com/handbook/engineering/development/sec/govern/compliance/) |
|
||||
| [Available custom role permissions](../../../user/custom_roles/abilities.md) | [Generated by Rake task](https://gitlab.com/gitlab-org/gitlab/-/blob/master/tooling/custom_roles/docs/templates/custom_abilities.md.erb) | [Authorization](https://handbook.gitlab.com/handbook/product/categories/#authorization-group)|
|
||||
| DAST vulnerability check documentation ([Example](../../../user/application_security/dast/browser/checks/798.19.md)) | [How to generate the Markdown](https://gitlab.com/gitlab-org/security-products/dast-cwe-checks/-/blob/main/doc/how-to-generate-the-markdown-documentation.md) | [Dynamic Analysis](https://handbook.gitlab.com/handbook/product/categories/#dynamic-analysis-group) |
|
||||
| Blueprints ([Example](../../../architecture/blueprints/ci_data_decay/pipeline_partitioning.md)) | | |
|
||||
| [The docs homepage](../../../index.md) | | [Technical Writing](https://handbook.gitlab.com/handbook/product/ux/technical-writing/) |
|
||||
|
||||
## Make an automation request
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ When the work ahead may affect more than a single group, stage or potentially an
|
|||
|
||||
This is well documented in the handbook, but to touch on it shortly, it is **the best way** to propose large changes and gather the required feedback and support to move forward. These documents are version controlled, keep evolving with time and are a great way to share a complex understanding across the entire organization. They also require a coach, which is a great way to involve someone with a lot of experience with larger changes. This process is shared across all engineering departments and is owned by the CTO.
|
||||
|
||||
To see all Design Documents, you can check the [Architecture at GitLab page](../../architecture/index.md)
|
||||
To see all Design Documents, you can check the [Architecture at GitLab page](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/)
|
||||
|
||||
### Frontend RFCs (deprecated)
|
||||
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ result (such as `ml-1` becoming `gl-ml-2`).
|
|||
### Tailwind CSS
|
||||
|
||||
We are in the process of migrating our CSS utility class setup to [Tailwind CSS](https://tailwindcss.com/).
|
||||
See the [Tailwind CSS blueprint](../../../architecture/blueprints/tailwindcss/index.md) for motivation, proposal,
|
||||
See the [Tailwind CSS design document](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/tailwindcss/) for motivation, proposal,
|
||||
and implementation details.
|
||||
|
||||
#### Tailwind CSS basics
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ All newly-introduced feature flags should be [disabled by default](https://handb
|
|||
WARNING:
|
||||
All newly-introduced feature flags should be [used with an actor](controls.md#percentage-based-actor-selection).
|
||||
|
||||
Blueprints:
|
||||
Design documents:
|
||||
|
||||
- (Latest) [Feature Flags usage in GitLab development and operations](../../architecture/blueprints/feature_flags_usage_in_dev_and_ops/index.md)
|
||||
- [Development Feature Flags Architecture](../../architecture/blueprints/feature_flags_development/index.md)
|
||||
- (Latest) [Feature Flags usage in GitLab development and operations](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/feature_flags_usage_in_dev_and_ops/)
|
||||
- [Development Feature Flags Architecture](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/feature_flags_development/)
|
||||
|
||||
This document is the subject of continued work as part of an epic to [improve internal usage of feature flags](https://gitlab.com/groups/gitlab-org/-/epics/3551). Raise any suggestions as new issues and attach them to the epic.
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ SaaS and self-managed installations.
|
|||
|
||||
## Consolidate groups and projects
|
||||
|
||||
- [Architecture blueprint](../../architecture/blueprints/consolidating_groups_and_projects/index.md)
|
||||
- [Architecture design document](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/consolidating_groups_and_projects/)
|
||||
|
||||
One facet of the Organization initiative is to consolidate groups and projects,
|
||||
addressing the feature disparity between them. Some features, such as epics, are
|
||||
|
|
@ -79,6 +79,6 @@ When working on settings, we need to make sure that:
|
|||
|
||||
## Related topics
|
||||
|
||||
- [Consolidating groups and projects](../../architecture/blueprints/consolidating_groups_and_projects/index.md)
|
||||
- [Consolidating groups and projects](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/consolidating_groups_and_projects/)
|
||||
architecture documentation
|
||||
- [Organization user documentation](../../user/organization/index.md)
|
||||
|
|
|
|||
|
|
@ -299,4 +299,4 @@ If a new user with a role `Guest` is added to a member role that includes enable
|
|||
|
||||
### Modular Policies
|
||||
|
||||
In an effort to support the [GitLab Modular Monolith blueprint](../../architecture/blueprints/modular_monolith/index.md) the [Authorization group](https://handbook.gitlab.com/handbook/engineering/development/sec/govern/authorization/) is [collaborating](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/153348) with the [Create:IDE group](https://handbook.gitlab.com/handbook/engineering/development/dev/create/ide/). Once a POC is implemented, the findings will be [discussed](https://gitlab.com/gitlab-org/gitlab/-/issues/454934) and the Authorization group will make a decision of what the modular design of policies will be going forward.
|
||||
In an effort to support the [GitLab Modular Monolith design document](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/modular_monolith/) the [Authorization group](https://handbook.gitlab.com/handbook/engineering/development/sec/govern/authorization/) is [collaborating](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/153348) with the [Create:IDE group](https://handbook.gitlab.com/handbook/engineering/development/dev/create/ide/). Once a POC is implemented, the findings will be [discussed](https://gitlab.com/gitlab-org/gitlab/-/issues/454934) and the Authorization group will make a decision of what the modular design of policies will be going forward.
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ for STI, exposed to the user, or if it would be a breaking change.
|
|||
## Bounded contexts
|
||||
|
||||
See the [Bounded Contexts working group](https://handbook.gitlab.com/handbook/company/working-groups/bounded-contexts/) and
|
||||
[GitLab Modular Monolith blueprint](../architecture/blueprints/modular_monolith/index.md) for more context on the
|
||||
[GitLab Modular Monolith design document](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/modular_monolith/) for more context on the
|
||||
goals, motivations, and direction related to Bounded Contexts.
|
||||
|
||||
### Use namespaces to define bounded contexts
|
||||
|
|
|
|||
|
|
@ -59,8 +59,8 @@ Once you have a project identified to use:
|
|||
- [Metrics](../../operations/metrics.md)
|
||||
- [Logs](../../operations/logs.md)
|
||||
|
||||
## Related blueprints
|
||||
## Related design documents
|
||||
|
||||
- [GitLab Observability in GitLab.com and Self-Managed GitLab Instances](../../architecture/blueprints/observability_for_self_managed/index.md)
|
||||
- [GitLab Observability - Metrics](../../architecture/blueprints/observability_metrics/index.md)
|
||||
- [GitLab Observability - Logging](../../architecture/blueprints/observability_logging/index.md)
|
||||
- [GitLab Observability in GitLab.com and Self-Managed GitLab Instances](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/observability_for_self_managed/)
|
||||
- [GitLab Observability - Metrics](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/observability_metrics/)
|
||||
- [GitLab Observability - Logging](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/observability_logging/)
|
||||
|
|
|
|||
|
|
@ -31,9 +31,8 @@ DETAILS:
|
|||
|
||||
- Help you write and understand code faster, get up to speed on the status of projects,
|
||||
and quickly learn about GitLab by answering your questions in a chat window.
|
||||
- LLM: Anthropic [`claude-3-sonnet-20240229`](https://console.cloud.google.com/vertex-ai/publishers/anthropic/model-garden/claude-3-sonnet),
|
||||
Anthropic [`claude-3-5-sonnet-20240620`](https://console.cloud.google.com/vertex-ai/publishers/anthropic/model-garden/claude-3-5-sonnet),
|
||||
Anthropic [`claude-3-haiku-20240307`](https://console.cloud.google.com/vertex-ai/publishers/anthropic/model-garden/claude-3-haiku),
|
||||
[`claude-2.1`](https://docs.anthropic.com/en/docs/legacy-model-guide#anthropics-legacy-models),
|
||||
and [Vertex AI Search](https://cloud.google.com/enterprise-search).
|
||||
- <i class="fa fa-youtube-play youtube" aria-hidden="true"></i> [Watch overview](https://www.youtube.com/watch?v=ZQBAuf-CTAY&list=PLFGfElNsQthYDx0A_FaNNfUm9NHsK6zED)
|
||||
- [View documentation](../gitlab_duo_chat.md).
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ DETAILS:
|
|||
**Tier:** GitLab.com and Self-managed: For a limited time, Premium and Ultimate. In the future, [GitLab Duo Pro or Enterprise](../../subscriptions/subscription-add-ons.md). <br>GitLab Dedicated: GitLab Duo Pro or Enterprise.
|
||||
**Offering:** GitLab.com, Self-managed, GitLab Dedicated
|
||||
**Editors:** GitLab UI, Web IDE, VS Code, and JetBrains IDEs
|
||||
**LLMs:** Anthropic: [`claude-3-sonnet-20240229`](https://docs.anthropic.com/en/docs/models-overview#claude-3-a-new-generation-of-ai), [Vertex AI Search](https://cloud.google.com/enterprise-search)
|
||||
**LLMs:** Anthropic: [`claude-3-5-sonnet-20240620`](https://console.cloud.google.com/vertex-ai/publishers/anthropic/model-garden/claude-3-5-sonnet), [Vertex AI Search](https://cloud.google.com/enterprise-search)
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/117695) for GitLab.com in GitLab 16.0.
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/451215) ability to ask doc-related questions on self-managed in GitLab 17.0 [with a flag](../../administration/feature_flags.md) named `ai_gateway_docs_search`. Enabled by default.
|
||||
|
|
@ -34,7 +34,7 @@ DETAILS:
|
|||
**Tier:** GitLab.com and Self-managed: For a limited time, Premium and Ultimate. In the future, [GitLab Duo Pro or Enterprise](../../subscriptions/subscription-add-ons.md). <br>GitLab Dedicated: GitLab Duo Pro or Enterprise.
|
||||
**Offering:** GitLab.com, Self-managed, GitLab Dedicated
|
||||
**Editors:** GitLab UI, Web IDE, VS Code, JetBrains IDEs
|
||||
**LLMs:** Anthropic: [`claude-3-sonnet-20240229`](https://docs.anthropic.com/en/docs/models-overview#claude-3-a-new-generation-of-ai)
|
||||
**LLMs:** Anthropic: [`claude-3-5-sonnet-20240620`](https://console.cloud.google.com/vertex-ai/publishers/anthropic/model-garden/claude-3-5-sonnet)
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/122235) for GitLab.com in GitLab 16.1.
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/122235) for self-managed and GitLab Dedicated in GitLab 16.8.
|
||||
|
|
@ -60,11 +60,12 @@ DETAILS:
|
|||
**Tier:** GitLab.com and Self-managed: For a limited time, Premium and Ultimate. In the future, [GitLab Duo Pro or Enterprise](../../subscriptions/subscription-add-ons.md). <br>GitLab Dedicated: GitLab Duo Pro or Enterprise.
|
||||
**Offering:** GitLab.com, Self-managed, GitLab Dedicated
|
||||
**Editors:** GitLab UI, Web IDE, VS Code, JetBrains IDEs
|
||||
**LLMs:** Anthropic: [`claude-3-sonnet-20240229`](https://docs.anthropic.com/en/docs/models-overview#claude-3-a-new-generation-of-ai)
|
||||
**LLMs:** Anthropic: [`claude-3-5-sonnet-20240620`](https://console.cloud.google.com/vertex-ai/publishers/anthropic/model-garden/claude-3-5-sonnet)
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/423524) for GitLab.com in GitLab 16.7.
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/423524) for self-managed and GitLab Dedicated in GitLab 16.8.
|
||||
> - [Updated LLM](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/149619) from `claude-2.1` to `claude-3-sonnet` in GitLab 17.2.
|
||||
> - [Updated LLM](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/157696) from `claude-3-sonnet` to `claude-3-5-sonnet` in GitLab 17.2.
|
||||
|
||||
You can ask GitLab Duo Chat to create a CI/CD configuration:
|
||||
|
||||
|
|
@ -99,7 +100,7 @@ DETAILS:
|
|||
**Tier:** GitLab.com and Self-managed: For a limited time, Premium and Ultimate. In the future, [GitLab Duo Pro or Enterprise](../../subscriptions/subscription-add-ons.md). <br>GitLab Dedicated: GitLab Duo Pro or Enterprise.
|
||||
**Offering:** GitLab.com, Self-managed, GitLab Dedicated
|
||||
**Editors:** Web IDE, VS Code, JetBrains IDEs
|
||||
**LLMs:** Anthropic: [`claude-3-sonnet-20240229`](https://docs.anthropic.com/en/docs/models-overview#claude-3-a-new-generation-of-ai)
|
||||
**LLMs:** Anthropic: [`claude-3-5-sonnet-20240620`](https://console.cloud.google.com/vertex-ai/publishers/anthropic/model-garden/claude-3-5-sonnet)
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/429915) for GitLab.com in GitLab 16.7.
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/429915) for self-managed and GitLab Dedicated in GitLab 16.8.
|
||||
|
|
@ -127,7 +128,7 @@ DETAILS:
|
|||
**Tier:** GitLab.com and Self-managed: For a limited time, Premium and Ultimate. In the future, [GitLab Duo Pro or Enterprise](../../subscriptions/subscription-add-ons.md). <br>GitLab Dedicated: GitLab Duo Pro or Enterprise.
|
||||
**Offering:** GitLab.com, Self-managed, GitLab Dedicated
|
||||
**Editors:** Web IDE, VS Code, JetBrains IDEs
|
||||
**LLMs:** Anthropic: [`claude-3-sonnet-20240229`](https://docs.anthropic.com/en/docs/models-overview#claude-3-a-new-generation-of-ai)
|
||||
**LLMs:** Anthropic: [`claude-3-5-sonnet-20240620`](https://console.cloud.google.com/vertex-ai/publishers/anthropic/model-garden/claude-3-5-sonnet)
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/429915) for GitLab.com in GitLab 16.7.
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/429915) for self-managed and GitLab Dedicated in GitLab 16.8.
|
||||
|
|
@ -148,7 +149,7 @@ DETAILS:
|
|||
**Tier:** GitLab.com and Self-managed: For a limited time, Premium and Ultimate. In the future, [GitLab Duo Pro or Enterprise](../../subscriptions/subscription-add-ons.md). <br>GitLab Dedicated: GitLab Duo Pro or Enterprise.
|
||||
**Offering:** GitLab.com, Self-managed, GitLab Dedicated
|
||||
**Editors:** Web IDE, VS Code, JetBrains IDEs
|
||||
**LLMs:** Anthropic: [`claude-3-sonnet-20240229`](https://docs.anthropic.com/en/docs/models-overview#claude-3-a-new-generation-of-ai)
|
||||
**LLMs:** Anthropic: [`claude-3-5-sonnet-20240620`](https://console.cloud.google.com/vertex-ai/publishers/anthropic/model-garden/claude-3-5-sonnet)
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/429915) for GitLab.com in GitLab 16.7.
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/429915) for self-managed and GitLab Dedicated in GitLab 16.8.
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ On GitLab self-managed, when you configure the ClickHouse integration, the Click
|
|||
For more information, see:
|
||||
|
||||
- [ClickHouse integration guidelines](../../../integration/clickhouse.md)
|
||||
- [ClickHouse usage at GitLab](../../../architecture/blueprints/clickhouse_usage/index.md)
|
||||
- [ClickHouse usage at GitLab](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/clickhouse_usage/)
|
||||
|
||||
## Contribution analytics GraphQL API
|
||||
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ Follow this [epic](https://gitlab.com/groups/gitlab-org/configure/-/epics/8)
|
|||
for updates.
|
||||
|
||||
You can find technical information about why we moved away from cluster certificates into
|
||||
the GitLab agent model on the [agent's blueprint documentation](../../../architecture/blueprints/gitlab_to_kubernetes_communication/index.md).
|
||||
the GitLab agent model on the [agent's design document](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/gitlab_to_kubernetes_communication/).
|
||||
|
||||
If you need more time to migrate to GitLab agent, you can [enable the feature flag](../../../administration/feature_flags.md)
|
||||
named `certificate_based_clusters`, which was [introduced in GitLab 15.0](../../../update/deprecations.md#self-managed-certificate-based-integration-with-kubernetes).
|
||||
|
|
|
|||
|
|
@ -69,8 +69,8 @@ To view the organizations you have access to:
|
|||
## Switch organizations
|
||||
|
||||
NOTE:
|
||||
Switching between organizations is not supported in [Cells 1.0](../../architecture/blueprints/cells/iterations/cells-1.0.md),
|
||||
but is supported in [Cells 1.5](../../architecture/blueprints/cells/iterations/cells-1.5.md).
|
||||
Switching between organizations is not supported in [Cells 1.0](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/cells/iterations/cells-1.0/),
|
||||
but is supported in [Cells 1.5](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/cells/iterations/cells-1.5/).
|
||||
|
||||
To switch organizations:
|
||||
|
||||
|
|
@ -112,4 +112,4 @@ The Organization description field supports a limited subset of [GitLab Flavored
|
|||
## Related topics
|
||||
|
||||
- [Organization developer documentation](../../development/organization/index.md)
|
||||
- [Organization blueprint](../../architecture/blueprints/organization/index.md)
|
||||
- [Organization design document](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/organization/)
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ viewed and addressed.
|
|||
For a video overview, see [Design Management](https://www.youtube.com/watch?v=CCMtCqdK_aM).
|
||||
<!-- Video published on 2019-07-11 -->
|
||||
|
||||
## Requirements
|
||||
## Prerequisites
|
||||
|
||||
- [Git Large File Storage (LFS)](../../../topics/git/lfs/index.md) must be enabled:
|
||||
- On GitLab.com, LFS is already enabled.
|
||||
|
|
@ -58,14 +58,6 @@ You can upload files of the following types as designs:
|
|||
|
||||
Support for PDF files is tracked in [issue 32811](https://gitlab.com/gitlab-org/gitlab/-/issues/32811).
|
||||
|
||||
## Known issues
|
||||
|
||||
- Design Management data isn't deleted when:
|
||||
- [A project is destroyed](https://gitlab.com/gitlab-org/gitlab/-/issues/13429).
|
||||
- [An issue is deleted](https://gitlab.com/gitlab-org/gitlab/-/issues/13427).
|
||||
- Design Management data [can be replicated](../../../administration/geo/replication/datatypes.md#limitations-on-replicationverification)
|
||||
and in GitLab 16.1 and later it can be [verified by Geo as well](https://gitlab.com/gitlab-org/gitlab/-/issues/355660).
|
||||
|
||||
## View a design
|
||||
|
||||
The **Designs** section is in the issue description.
|
||||
|
|
@ -194,6 +186,17 @@ To archive multiple designs at once:
|
|||
1. Select the checkboxes on the designs you want to archive.
|
||||
1. Select **Archive selected**.
|
||||
|
||||
## Design management data persistence
|
||||
|
||||
- Design Management data is not deleted when:
|
||||
- [A project is destroyed](https://gitlab.com/gitlab-org/gitlab/-/issues/13429).
|
||||
- [An issue is deleted](https://gitlab.com/gitlab-org/gitlab/-/issues/13427).
|
||||
|
||||
### Replicate design management data
|
||||
|
||||
Design Management data [can be replicated](../../../administration/geo/replication/datatypes.md#limitations-on-replicationverification)
|
||||
and in GitLab 16.1 and later it can be [verified by Geo as well](https://gitlab.com/gitlab-org/gitlab/-/issues/355660).
|
||||
|
||||
## Markdown and rich text editors for descriptions
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/388449) in GitLab 16.1 [with a flag](../../../administration/feature_flags.md) named `content_editor_on_issues`. Disabled by default.
|
||||
|
|
|
|||
|
|
@ -691,7 +691,7 @@ module API
|
|||
redirect_params = {}
|
||||
if content_disposition
|
||||
response_disposition = ActionDispatch::Http::ContentDisposition.format(disposition: content_disposition, filename: file.filename)
|
||||
redirect_params[:query] = { 'response-content-disposition': response_disposition, 'response-content-type': file.content_type }
|
||||
redirect_params[:query] = { 'response-content-disposition' => response_disposition, 'response-content-type' => file.content_type }
|
||||
end
|
||||
|
||||
file_url = ObjectStorage::CDN::FileUrl.new(file: file, ip_address: ip_address, redirect_params: redirect_params)
|
||||
|
|
|
|||
|
|
@ -69,6 +69,48 @@ module API
|
|||
present response[:container_registry_protection_rule],
|
||||
with: Entities::Projects::ContainerRegistry::Protection::Rule
|
||||
end
|
||||
|
||||
params do
|
||||
requires :protection_rule_id, type: Integer,
|
||||
desc: 'The ID of the container protection rule'
|
||||
end
|
||||
resource ':protection_rule_id' do
|
||||
desc 'Update a container protection rule for a project' do
|
||||
success Entities::Projects::ContainerRegistry::Protection::Rule
|
||||
failure [
|
||||
{ code: 400, message: 'Bad Request' },
|
||||
{ code: 401, message: 'Unauthorized' },
|
||||
{ code: 403, message: 'Forbidden' },
|
||||
{ code: 404, message: 'Not Found' },
|
||||
{ code: 422, message: 'Unprocessable Entity' }
|
||||
]
|
||||
tags %w[projects]
|
||||
hidden true
|
||||
end
|
||||
params do
|
||||
optional :repository_path_pattern, type: String,
|
||||
desc: 'Container repository path pattern protected by the protection rule.
|
||||
For example `flight/flight-*`. Wildcard character `*` allowed.'
|
||||
optional :minimum_access_level_for_push, type: String,
|
||||
values: ContainerRegistry::Protection::Rule.minimum_access_level_for_pushes.keys << "",
|
||||
desc: 'Minimum GitLab access level to allow to push container images to the container registry.
|
||||
For example maintainer, owner or admin. To unset the value, use an empty string `""`.'
|
||||
optional :minimum_access_level_for_delete, type: String,
|
||||
values: ContainerRegistry::Protection::Rule.minimum_access_level_for_deletes.keys << "",
|
||||
desc: 'Minimum GitLab access level to allow to delete container images in the container registry.
|
||||
For example maintainer, owner or admin. To unset the value, use an empty string `""`.'
|
||||
end
|
||||
patch do
|
||||
protection_rule = user_project.container_registry_protection_rules.find(params[:protection_rule_id])
|
||||
response = ::ContainerRegistry::Protection::UpdateRuleService.new(protection_rule,
|
||||
current_user: current_user, params: declared_params(include_missing: false)).execute
|
||||
|
||||
render_api_error!({ error: response.message }, :unprocessable_entity) if response.error?
|
||||
|
||||
present response[:container_registry_protection_rule],
|
||||
with: Entities::Projects::ContainerRegistry::Protection::Rule
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -154,6 +154,30 @@ module Gitlab
|
|||
end
|
||||
end
|
||||
|
||||
# Returns a limited number of random values from the set.
|
||||
#
|
||||
# raw_key - The key of the set to check.
|
||||
# limit - Number of values to return (default: 1).
|
||||
def self.limited_values_from_set(raw_key, limit: 1)
|
||||
key = cache_key_for(raw_key)
|
||||
|
||||
with_redis do |redis|
|
||||
redis.srandmember(key, limit)
|
||||
end
|
||||
end
|
||||
|
||||
# Removes the given values from the set.
|
||||
#
|
||||
# raw_key - The key of the set to check.
|
||||
# values - Array of values to remove from set.
|
||||
def self.set_remove(raw_key, values = [])
|
||||
key = cache_key_for(raw_key)
|
||||
|
||||
with_redis do |redis|
|
||||
redis.srem(key, values)
|
||||
end
|
||||
end
|
||||
|
||||
# Sets multiple keys to given values.
|
||||
#
|
||||
# mapping - A Hash mapping the cache keys to their values.
|
||||
|
|
@ -263,7 +287,7 @@ module Gitlab
|
|||
# raw_key - The key of the list to add to.
|
||||
# value - The field value to add to the list.
|
||||
# timeout - The new timeout of the key.
|
||||
# limit - The maximum number of members in the set. Older members will be trimmed to this limit.
|
||||
# limit - The maximum number of members in the list. Older members will be trimmed to this limit.
|
||||
def self.list_add(raw_key, value, timeout: TIMEOUT, limit: nil)
|
||||
validate_redis_value!(value)
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def action_title
|
||||
'Retry'
|
||||
'Run again'
|
||||
end
|
||||
|
||||
def action_button_title
|
||||
|
|
|
|||
|
|
@ -21672,6 +21672,9 @@ msgstr ""
|
|||
msgid "Expires %{preposition} %{expires_at}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Expiring soon"
|
||||
msgstr ""
|
||||
|
||||
msgid "Explain why you're reporting the user."
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -36169,6 +36172,9 @@ msgstr ""
|
|||
msgid "Observability|Tracing, Metrics & Logs"
|
||||
msgstr ""
|
||||
|
||||
msgid "Observability|Usage by day"
|
||||
msgstr ""
|
||||
|
||||
msgid "Observability|View our %{docsLink} for further instructions on how to use these features."
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -47329,13 +47335,16 @@ msgstr ""
|
|||
msgid "Secrets|Enter a key name"
|
||||
msgstr ""
|
||||
|
||||
msgid "Secrets|Expires"
|
||||
msgstr ""
|
||||
|
||||
msgid "Secrets|Failed to load secret. Please try again later."
|
||||
msgstr ""
|
||||
|
||||
msgid "Secrets|Intervals"
|
||||
msgstr ""
|
||||
|
||||
msgid "Secrets|Last accessed"
|
||||
msgid "Secrets|Last used"
|
||||
msgstr ""
|
||||
|
||||
msgid "Secrets|Name"
|
||||
|
|
@ -47371,6 +47380,9 @@ msgstr ""
|
|||
msgid "Secrets|Set expiration"
|
||||
msgstr ""
|
||||
|
||||
msgid "Secrets|Status"
|
||||
msgstr ""
|
||||
|
||||
msgid "Secrets|Stored secrets"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -67,12 +67,12 @@
|
|||
"@gitlab/favicon-overlay": "2.0.0",
|
||||
"@gitlab/fonts": "^1.3.0",
|
||||
"@gitlab/svgs": "3.105.0",
|
||||
"@gitlab/ui": "^86.4.0",
|
||||
"@gitlab/ui": "^86.9.1",
|
||||
"@gitlab/web-ide": "^0.0.1-dev-20240613133550",
|
||||
"@mattiasbuelens/web-streams-adapter": "^0.1.0",
|
||||
"@rails/actioncable": "7.0.8-4",
|
||||
"@rails/ujs": "7.0.8-4",
|
||||
"@sentry/browser": "8.15.0",
|
||||
"@sentry/browser": "8.17.0",
|
||||
"@snowplow/browser-plugin-client-hints": "^3.9.0",
|
||||
"@snowplow/browser-plugin-form-tracking": "^3.9.0",
|
||||
"@snowplow/browser-plugin-ga-cookies": "^3.9.0",
|
||||
|
|
|
|||
|
|
@ -20,6 +20,11 @@ module QA
|
|||
new_date.strftime("%b %-d, %Y")
|
||||
end
|
||||
|
||||
def format_date_without_year(date)
|
||||
new_date = DateTime.strptime(date, "%Y/%m/%d")
|
||||
new_date.strftime("%b %-d")
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def current_date
|
||||
|
|
|
|||
|
|
@ -22,14 +22,16 @@ class SetPipelineName
|
|||
CODE = ['retrieve-tests-metadata'].freeze
|
||||
QA_GDK = ['e2e:test-on-gdk'].freeze
|
||||
REVIEW_APP = ['start-review-app-pipeline'].freeze
|
||||
# TODO: Please remove `trigger-omnibus-and-follow-up-e2e` and `follow-up-e2e:package-and-test-ee`
|
||||
# after 2025-04-08 in this project
|
||||
# rubocop:disable Layout/LineLength -- Easier to read if longer
|
||||
# TODO:
|
||||
# * Please remove `trigger-omnibus-and-follow-up-e2e` and `follow-up-e2e:package-and-test-ee` after 2025-04-08 in this project
|
||||
# * Please remove `follow-up:trigger-omnibus` after 2025-06-13 in this project
|
||||
#
|
||||
# `trigger-omnibus-and-follow-up-e2e` was renamed to `follow-up:trigger-omnibus` on 2024-04-08 via
|
||||
# https://gitlab.com/gitlab-org/gitlab/-/merge_requests/147908/diffs?pin=c11467759d7eae77ed84e02a5445e21704c8d8e5#c11467759d7eae77ed84e02a5445e21704c8d8e5_105_104
|
||||
#
|
||||
# `follow-up-e2e:package-and-test-ee` was renamed to `follow-up:e2e:package-and-test-ee` on 2024-04-08 via
|
||||
# https://gitlab.com/gitlab-org/gitlab/-/merge_requests/147908/diffs?pin=c11467759d7eae77ed84e02a5445e21704c8d8e5#c11467759d7eae77ed84e02a5445e21704c8d8e5_136_137
|
||||
# Timeline:
|
||||
# * `trigger-omnibus-and-follow-up-e2e` was renamed to `follow-up:trigger-omnibus` on 2024-04-08 via https://gitlab.com/gitlab-org/gitlab/-/merge_requests/147908/diffs?pin=c11467759d7eae77ed84e02a5445e21704c8d8e5#c11467759d7eae77ed84e02a5445e21704c8d8e5_105_104
|
||||
# * `follow-up-e2e:package-and-test-ee` was renamed to `follow-up:e2e:package-and-test-ee` on 2024-04-08 via https://gitlab.com/gitlab-org/gitlab/-/merge_requests/147908/diffs?pin=c11467759d7eae77ed84e02a5445e21704c8d8e5#c11467759d7eae77ed84e02a5445e21704c8d8e5_136_137
|
||||
# * `follow-up:trigger-omnibus` was moved in `follow-up:e2e:package-and-test-ee` on 2024-06-13 via https://gitlab.com/gitlab-org/gitlab/-/merge_requests/155701
|
||||
# rubocop:enable Layout/LineLength
|
||||
QA = [
|
||||
'e2e:package-and-test-ce',
|
||||
'e2e:package-and-test-ee',
|
||||
|
|
|
|||
|
|
@ -0,0 +1,70 @@
|
|||
import { GlButton, GlIcon } from '@gitlab/ui';
|
||||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
|
||||
import JobActionButton from '~/ci/pipeline_mini_graph/job_action_button.vue';
|
||||
import { mockJobActions } from './mock_data';
|
||||
|
||||
describe('JobActionButton', () => {
|
||||
let wrapper;
|
||||
|
||||
const jobAction = mockJobActions[0];
|
||||
|
||||
const defaultProps = {
|
||||
jobAction,
|
||||
jobId: 'gid://gitlab/Ci::Build/5521',
|
||||
jobName: 'test_job',
|
||||
};
|
||||
|
||||
const createComponent = ({ props = {} } = {}) => {
|
||||
wrapper = shallowMountExtended(JobActionButton, {
|
||||
propsData: {
|
||||
...defaultProps,
|
||||
...props,
|
||||
},
|
||||
});
|
||||
|
||||
return waitForPromises();
|
||||
};
|
||||
|
||||
const findActionButton = () => wrapper.findComponent(GlButton);
|
||||
const findActionIcon = () => wrapper.findComponent(GlIcon);
|
||||
|
||||
describe('when mounted', () => {
|
||||
beforeEach(() => {
|
||||
createComponent();
|
||||
});
|
||||
|
||||
it('renders the action icon', () => {
|
||||
expect(findActionIcon().exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('renders the tooltip', () => {
|
||||
expect(findActionButton().exists()).toBe(true);
|
||||
});
|
||||
|
||||
describe('job action button', () => {
|
||||
describe.each`
|
||||
action | icon | tooltip | mockIndex
|
||||
${'cancel'} | ${'cancel'} | ${'Cancel'} | ${0}
|
||||
${'run'} | ${'play'} | ${'Run'} | ${1}
|
||||
${'retry'} | ${'retry'} | ${'Run again'} | ${2}
|
||||
${'unschedule'} | ${'time-out'} | ${'Unschedule'} | ${3}
|
||||
`('$action action', ({ icon, mockIndex, tooltip }) => {
|
||||
beforeEach(() => {
|
||||
createComponent({ props: { jobAction: mockJobActions[mockIndex] } });
|
||||
});
|
||||
|
||||
it('displays the correct icon', () => {
|
||||
expect(findActionIcon().exists()).toBe(true);
|
||||
expect(findActionIcon().props('name')).toBe(icon);
|
||||
});
|
||||
|
||||
it('displays the correct tooltip', () => {
|
||||
expect(findActionButton().exists()).toBe(true);
|
||||
expect(findActionButton().attributes('title')).toBe(tooltip);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import { capitalizeFirstCharacter } from '~/lib/utils/text_utility';
|
||||
import JobItem from '~/ci/pipeline_mini_graph/job_item.vue';
|
||||
import JobNameComponent from '~/ci/common/private/job_name_component.vue';
|
||||
|
||||
|
|
@ -39,9 +40,9 @@ describe('JobItem', () => {
|
|||
});
|
||||
|
||||
it('sets the correct tooltip for the job item', () => {
|
||||
expect(findJobNameComponent().attributes('title')).toBe(
|
||||
mockPipelineJob.detailedStatus.tooltip,
|
||||
);
|
||||
const tooltip = capitalizeFirstCharacter(mockPipelineJob.detailedStatus.tooltip);
|
||||
|
||||
expect(findJobNameComponent().attributes('title')).toBe(tooltip);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -70,6 +70,42 @@ export const pipelineStage = {
|
|||
},
|
||||
};
|
||||
|
||||
// for `job_action_button_spec.js`
|
||||
export const mockJobActions = [
|
||||
{
|
||||
__typename: 'StatusAction',
|
||||
confirmationMessage: null,
|
||||
id: 'Ci::Build-pending-1001',
|
||||
icon: 'cancel',
|
||||
path: '/flightjs/Flight/-/jobs/1001/cancel',
|
||||
title: 'Cancel',
|
||||
},
|
||||
{
|
||||
__typename: 'StatusAction',
|
||||
confirmationMessage: null,
|
||||
id: 'Ci::Build-manual-1001',
|
||||
icon: 'play',
|
||||
path: '/flightjs/Flight/-/jobs/1001/play',
|
||||
title: 'Run',
|
||||
},
|
||||
{
|
||||
__typename: 'StatusAction',
|
||||
confirmationMessage: null,
|
||||
id: 'Ci::Build-success-1001',
|
||||
icon: 'retry',
|
||||
path: '/flightjs/Flight/-/jobs/1001/retry',
|
||||
title: 'Run again',
|
||||
},
|
||||
{
|
||||
__typename: 'StatusAction',
|
||||
confirmationMessage: null,
|
||||
id: 'Ci::Build-scheduled-1001',
|
||||
icon: 'time-out',
|
||||
path: '/flightjs/Flight/-/jobs/1001/unschedule',
|
||||
title: 'Unschedule',
|
||||
},
|
||||
];
|
||||
|
||||
// for `job_item_spec.js`
|
||||
export const mockPipelineJob = {
|
||||
__typename: 'CiJob',
|
||||
|
|
@ -80,6 +116,7 @@ export const mockPipelineJob = {
|
|||
action: {
|
||||
__typename: 'StatusAction',
|
||||
id: 'Ci::Build-success-1001',
|
||||
confirmationMessage: null,
|
||||
icon: 'cancel',
|
||||
path: '/flightjs/Flight/-/jobs/1001/cancel',
|
||||
title: 'Cancel',
|
||||
|
|
@ -113,6 +150,7 @@ export const mockPipelineStageJobs = {
|
|||
action: {
|
||||
__typename: 'StatusAction',
|
||||
id: 'Ci::Build-success-1001',
|
||||
confirmationMessage: null,
|
||||
icon: 'retry',
|
||||
path: '/flightjs/Flight/-/jobs/1001/retry',
|
||||
title: 'Retry',
|
||||
|
|
@ -136,6 +174,7 @@ export const mockPipelineStageJobs = {
|
|||
action: {
|
||||
__typename: 'StatusAction',
|
||||
id: 'Ci::Build-success-1002',
|
||||
confirmationMessage: null,
|
||||
icon: 'retry',
|
||||
path: '/flightjs/Flight/-/jobs/1001/retry',
|
||||
title: 'Retry',
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import {
|
|||
I18N_JOB_STATUS_ACTIVE,
|
||||
I18N_JOB_STATUS_IDLE,
|
||||
JOB_STATUS_ACTIVE,
|
||||
JOB_STATUS_RUNNING,
|
||||
JOB_STATUS_IDLE,
|
||||
} from '~/ci/runner/constants';
|
||||
|
||||
|
|
@ -24,10 +23,9 @@ describe('RunnerTypeBadge', () => {
|
|||
};
|
||||
|
||||
it.each`
|
||||
jobStatus | classes | text
|
||||
${JOB_STATUS_ACTIVE} | ${['gl-text-blue-600!', 'gl-border-blue-600!']} | ${I18N_JOB_STATUS_ACTIVE}
|
||||
${JOB_STATUS_RUNNING} | ${['gl-text-blue-600!', 'gl-border-blue-600!']} | ${I18N_JOB_STATUS_ACTIVE}
|
||||
${JOB_STATUS_IDLE} | ${['gl-text-gray-700!', 'gl-border-gray-500!']} | ${I18N_JOB_STATUS_IDLE}
|
||||
jobStatus | classes | text
|
||||
${JOB_STATUS_ACTIVE} | ${['gl-text-blue-600!', 'gl-border-blue-600!']} | ${I18N_JOB_STATUS_ACTIVE}
|
||||
${JOB_STATUS_IDLE} | ${['gl-text-gray-700!', 'gl-border-gray-500!']} | ${I18N_JOB_STATUS_IDLE}
|
||||
`(
|
||||
'renders $jobStatus job status with "$text" text and styles',
|
||||
({ jobStatus, classes, text }) => {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { selectedRect as getSelectedRect } from '@tiptap/pm/tables';
|
|||
import { nextTick } from 'vue';
|
||||
import { mountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import { stubComponent } from 'helpers/stub_component';
|
||||
import { useFakeRequestAnimationFrame } from 'helpers/fake_request_animation_frame';
|
||||
import TableCellBaseWrapper from '~/content_editor/components/wrappers/table_cell_base.vue';
|
||||
import { createTestEditor, mockChainedCommands, emitEditorEvent } from '../../test_utils';
|
||||
|
||||
|
|
@ -14,6 +15,9 @@ describe('content/components/wrappers/table_cell_base', () => {
|
|||
let editor;
|
||||
let node;
|
||||
|
||||
// Stubbing requestAnimationFrame because GlDisclosureDropdown uses it to delay its `action` event.
|
||||
useFakeRequestAnimationFrame();
|
||||
|
||||
const createWrapper = (propsData = { cellType: 'td' }) => {
|
||||
wrapper = mountExtended(TableCellBaseWrapper, {
|
||||
propsData: {
|
||||
|
|
|
|||
|
|
@ -94,11 +94,7 @@ exports[`FindingsDrawer General Rendering matches the snapshot with detected bad
|
|||
<span
|
||||
class="badge badge-pill badge-warning gl-badge text-capitalize"
|
||||
>
|
||||
<span
|
||||
class="gl-badge-content"
|
||||
>
|
||||
detected
|
||||
</span>
|
||||
detected
|
||||
</span>
|
||||
</p>
|
||||
</li>
|
||||
|
|
@ -318,11 +314,7 @@ exports[`FindingsDrawer General Rendering matches the snapshot with dismissed ba
|
|||
<span
|
||||
class="badge badge-pill badge-warning gl-badge text-capitalize"
|
||||
>
|
||||
<span
|
||||
class="gl-badge-content"
|
||||
>
|
||||
detected
|
||||
</span>
|
||||
detected
|
||||
</span>
|
||||
</p>
|
||||
</li>
|
||||
|
|
|
|||
|
|
@ -11,11 +11,7 @@ exports[`~/releases/components/issuable_stats.vue matches snapshot 1`] = `
|
|||
<span
|
||||
class="badge badge-muted badge-pill gl-badge"
|
||||
>
|
||||
<span
|
||||
class="gl-badge-content"
|
||||
>
|
||||
10
|
||||
</span>
|
||||
10
|
||||
</span>
|
||||
</span>
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { getAllByRole } from '@testing-library/dom';
|
|||
import { nextTick } from 'vue';
|
||||
import { mount } from '@vue/test-utils';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import { useFakeRequestAnimationFrame } from 'helpers/fake_request_animation_frame';
|
||||
import { OPEN_REVERT_MODAL, OPEN_CHERRY_PICK_MODAL } from '~/projects/commit/constants';
|
||||
import modalEventHub from '~/projects/commit/event_hub';
|
||||
import MergedComponent from '~/vue_merge_request_widget/components/states/mr_widget_merged.vue';
|
||||
|
|
@ -41,6 +42,9 @@ describe('MRWidgetMerged', () => {
|
|||
targetBranch,
|
||||
};
|
||||
|
||||
// Stubbing requestAnimationFrame because GlDisclosureDropdown uses it to delay its `action` event.
|
||||
useFakeRequestAnimationFrame();
|
||||
|
||||
const service = {
|
||||
removeSourceBranch: () => nextTick(),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -7,11 +7,7 @@ exports[`Beta badge component renders the badge 1`] = `
|
|||
href="#"
|
||||
target="_self"
|
||||
>
|
||||
<span
|
||||
class="gl-badge-content"
|
||||
>
|
||||
Beta
|
||||
</span>
|
||||
Beta
|
||||
</a>
|
||||
<div
|
||||
class="gl-popover"
|
||||
|
|
|
|||
|
|
@ -7,11 +7,7 @@ exports[`Experiment badge component renders the badge 1`] = `
|
|||
href="#"
|
||||
target="_self"
|
||||
>
|
||||
<span
|
||||
class="gl-badge-content"
|
||||
>
|
||||
Experiment
|
||||
</span>
|
||||
Experiment
|
||||
</a>
|
||||
<div
|
||||
class="gl-popover"
|
||||
|
|
|
|||
|
|
@ -1194,6 +1194,28 @@ RSpec.describe API::Helpers, feature_category: :shared do
|
|||
subject
|
||||
end
|
||||
end
|
||||
|
||||
context 'with content_disposition' do
|
||||
let(:filename) { artifact.file.filename }
|
||||
let(:redirect_params) do
|
||||
{
|
||||
query: {
|
||||
'response-content-disposition' => "attachment; filename=\"#{filename}\"; filename*=UTF-8''#{filename}",
|
||||
'response-content-type' => 'application/zip'
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
subject { helper.present_artifacts_file!(artifact.file, content_disposition: :attachment) }
|
||||
|
||||
it 'redirects as an attachment' do
|
||||
expect(helper).to receive(:redirect)
|
||||
expect(ObjectStorage::CDN::FileUrl).to receive(:new)
|
||||
.with(file: anything, ip_address: anything, redirect_params: redirect_params).and_call_original
|
||||
|
||||
subject
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -139,6 +139,60 @@ RSpec.describe Gitlab::Cache::Import::Caching, :clean_gitlab_redis_shared_state,
|
|||
end
|
||||
end
|
||||
|
||||
describe '.limited_values_from_set' do
|
||||
it 'returns empty array when the set does not exist' do
|
||||
expect(described_class.limited_values_from_set('foo')).to eq([])
|
||||
end
|
||||
|
||||
it 'returns a single random member from the set' do
|
||||
described_class.set_add('foo', 10)
|
||||
described_class.set_add('foo', 20)
|
||||
|
||||
result = described_class.limited_values_from_set('foo')
|
||||
|
||||
expect(result.size).to eq(1)
|
||||
expect(result.first).to be_in(%w[10 20])
|
||||
end
|
||||
|
||||
it 'returns multiple random members from the set with `limit:`' do
|
||||
described_class.set_add('foo', 10)
|
||||
described_class.set_add('foo', 20)
|
||||
described_class.set_add('foo', 30)
|
||||
|
||||
result = described_class.limited_values_from_set('foo', limit: 2)
|
||||
|
||||
expect(result.size).to eq(2)
|
||||
expect(result).to all(be_in(%w[10 20 30]))
|
||||
end
|
||||
end
|
||||
|
||||
describe '.set_remove' do
|
||||
it 'returns 0 when the set does not exist' do
|
||||
expect(described_class.set_remove('foo', 1)).to eq(0)
|
||||
end
|
||||
|
||||
it 'removes a single value from the set' do
|
||||
described_class.set_add('foo', 10)
|
||||
described_class.set_add('foo', 20)
|
||||
|
||||
result = described_class.set_remove('foo', 20)
|
||||
|
||||
expect(result).to eq(1)
|
||||
expect(described_class.values_from_set('foo')).to contain_exactly('10')
|
||||
end
|
||||
|
||||
it 'removes a collection of values from the set' do
|
||||
described_class.set_add('foo', 10)
|
||||
described_class.set_add('foo', 20)
|
||||
described_class.set_add('foo', 30)
|
||||
|
||||
result = described_class.set_remove('foo', [10, 30])
|
||||
|
||||
expect(result).to eq(2)
|
||||
expect(described_class.values_from_set('foo')).to contain_exactly('20')
|
||||
end
|
||||
end
|
||||
|
||||
describe '.hash_add' do
|
||||
it 'adds a value to a hash' do
|
||||
described_class.hash_add('foo', 1, 1)
|
||||
|
|
@ -286,7 +340,7 @@ RSpec.describe Gitlab::Cache::Import::Caching, :clean_gitlab_redis_shared_state,
|
|||
end
|
||||
|
||||
describe '.values_from_list' do
|
||||
it 'returns empty hash when the list is empty' do
|
||||
it 'returns empty array when the list is empty' do
|
||||
expect(described_class.values_from_list('foo')).to eq([])
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ RSpec.describe Gitlab::Ci::Status::Build::Factory do
|
|||
expect(status.label).to eq 'failed (allowed to fail)'
|
||||
expect(status).to have_details
|
||||
expect(status).to have_action
|
||||
expect(status.action_title).to include 'Retry'
|
||||
expect(status.action_title).to include 'Run again'
|
||||
expect(status.action_path).to include 'retry'
|
||||
end
|
||||
end
|
||||
|
|
@ -150,7 +150,7 @@ RSpec.describe Gitlab::Ci::Status::Build::Factory do
|
|||
expect(status.label).to eq s_('CiStatusLabel|failed')
|
||||
expect(status).to have_details
|
||||
expect(status).to have_action
|
||||
expect(status.action_title).to include 'Retry'
|
||||
expect(status.action_title).to include 'Run again'
|
||||
expect(status.action_path).to include 'retry'
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ RSpec.describe Gitlab::Ci::Status::Build::Retryable do
|
|||
end
|
||||
|
||||
describe '#action_title' do
|
||||
it { expect(subject.action_title).to eq 'Retry' }
|
||||
it { expect(subject.action_title).to eq 'Run again' }
|
||||
end
|
||||
|
||||
describe '#action_button_title' do
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ RSpec.describe 'new tables with gitlab_main schema', feature_category: :cell do
|
|||
`gitlab_main_clusterwide` schema.
|
||||
|
||||
To choose an appropriate schema for this table from among `gitlab_main_cell` and `gitlab_main_clusterwide`, please refer
|
||||
to our guidelines at https://docs.gitlab.com/ee/development/database/multiple_databases.html#guidelines-on-choosing-between-gitlab_main_cell-and-gitlab_main_clusterwide-schema, or consult with the Tenant Scale group.
|
||||
to our guidelines at https://docs.gitlab.com/ee/development/cells/index.html#guidelines-on-choosing-between-gitlab_main_cell-and-gitlab_main_clusterwide-schema, or consult with the Tenant Scale group.
|
||||
|
||||
Please see issue https://gitlab.com/gitlab-org/gitlab/-/issues/424990 to understand why this change is being enforced.
|
||||
HEREDOC
|
||||
|
|
|
|||
|
|
@ -273,7 +273,7 @@ RSpec.describe 'new tables missing sharding_key', feature_category: :cell do
|
|||
Starting from GitLab #{starting_from_milestone}, we expect all new tables to define a `sharding_key`.
|
||||
|
||||
To choose an appropriate sharding_key for this table please refer
|
||||
to our guidelines at https://docs.gitlab.com/ee/development/database/multiple_databases.html#defining-a-sharding-key-for-all-cell-local-tables, or consult with the Tenant Scale group.
|
||||
to our guidelines at https://docs.gitlab.com/ee/development/cells/index.html#defining-a-sharding-key-for-all-cell-local-tables, or consult with the Tenant Scale group.
|
||||
HEREDOC
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -42,4 +42,78 @@ RSpec.describe Import::SourceUserPlaceholderReference, feature_category: :import
|
|||
|
||||
expect { reference.source_user.destroy! }.to change { described_class.count }.by(-1)
|
||||
end
|
||||
|
||||
describe 'SERIALIZABLE_ATTRIBUTES' do
|
||||
subject(:constant) { described_class::SERIALIZABLE_ATTRIBUTES }
|
||||
|
||||
it 'is the expected list of attribute names' do
|
||||
expected_elements = %w[
|
||||
composite_key
|
||||
model
|
||||
namespace_id
|
||||
numeric_key
|
||||
source_user_id
|
||||
user_reference_column
|
||||
]
|
||||
|
||||
failure_message = <<-MSG
|
||||
Before fixing this spec, ensure that `#{described_class.name}.from_serialized`
|
||||
handles receiving older versions of the array by filling in missing values with defaults.
|
||||
|
||||
You are seeing this message because SERIALIZABLE_ATTRIBUTES has changed.
|
||||
MSG
|
||||
|
||||
expect(constant).to eq(expected_elements), failure_message
|
||||
end
|
||||
end
|
||||
|
||||
describe '#to_serialized' do
|
||||
let(:reference) do
|
||||
build(:import_source_user_placeholder_reference,
|
||||
numeric_key: 1,
|
||||
namespace_id: 2,
|
||||
source_user_id: 3,
|
||||
user_reference_column: 'foo',
|
||||
model: 'Model',
|
||||
composite_key: { key: 1 }
|
||||
)
|
||||
end
|
||||
|
||||
subject(:serialized) { reference.to_serialized }
|
||||
|
||||
it { is_expected.to eq('[{"key":1},"Model",2,1,3,"foo"]') }
|
||||
end
|
||||
|
||||
describe '.from_serialized' do
|
||||
subject(:from_serialized) { described_class.from_serialized(serialized) }
|
||||
|
||||
context 'when serialized reference is valid' do
|
||||
let(:serialized) { '[{"key":1},"Model",2,null,3,"foo"]' }
|
||||
|
||||
it 'returns a valid SourceUserPlaceholderReference' do
|
||||
expect(from_serialized).to be_a(described_class)
|
||||
.and(be_valid)
|
||||
.and(have_attributes(
|
||||
composite_key: { key: 1 },
|
||||
model: 'Model',
|
||||
numeric_key: nil,
|
||||
namespace_id: 2,
|
||||
source_user_id: 3,
|
||||
user_reference_column: 'foo'
|
||||
))
|
||||
end
|
||||
|
||||
it 'sets the created_at' do
|
||||
expect(from_serialized.created_at).to be_like_time(Time.zone.now)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when serialized reference has different number of elements than expected' do
|
||||
let(:serialized) { '[{"key":1},"Model",2,null,3]' }
|
||||
|
||||
it 'raises an exception' do
|
||||
expect { from_serialized }.to raise_error(described_class::SerializationError)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -62,6 +62,26 @@ RSpec.describe API::ProjectContainerRegistryProtectionRules, :aggregate_failures
|
|||
end
|
||||
end
|
||||
|
||||
shared_examples 'rejecting container registry protection rules request when handling rule ids' do
|
||||
context 'when the rule id is invalid' do
|
||||
let(:url) { "/projects/#{project.id}/registry/protection/rules/invalid" }
|
||||
|
||||
it_behaves_like 'returning response status', :bad_request
|
||||
end
|
||||
|
||||
context 'when the rule id does not exist' do
|
||||
let(:url) { "/projects/#{project.id}/registry/protection/rules/#{non_existing_record_id}" }
|
||||
|
||||
it_behaves_like 'returning response status', :not_found
|
||||
end
|
||||
|
||||
context 'when the container registry protection rule does belong to another project' do
|
||||
let(:url) { "/projects/#{other_project.id}/registry/protection/rules/#{container_registry_protection_rule.id}" }
|
||||
|
||||
it_behaves_like 'returning response status', :not_found
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET /projects/:id/registry/protection/rules' do
|
||||
let(:url) { "/projects/#{project.id}/registry/protection/rules" }
|
||||
|
||||
|
|
@ -201,4 +221,126 @@ RSpec.describe API::ProjectContainerRegistryProtectionRules, :aggregate_failures
|
|||
it_behaves_like 'returning response status', :unauthorized
|
||||
end
|
||||
end
|
||||
|
||||
describe 'PATCH /projects/:id/registry/protection/rules/:protection_rule_id' do
|
||||
let(:url) { "/projects/#{project.id}/registry/protection/rules/#{container_registry_protection_rule.id}" }
|
||||
|
||||
subject(:patch_container_registry_protection_rule) { patch(api(url, api_user), params: params) }
|
||||
|
||||
it_behaves_like 'rejecting project container protection rules request when not enough permissions'
|
||||
|
||||
context 'for maintainer' do
|
||||
let(:api_user) { maintainer }
|
||||
let_it_be(:changed_repository_path_pattern) do
|
||||
"#{container_registry_protection_rule.repository_path_pattern}-changed"
|
||||
end
|
||||
|
||||
context 'with full changeset' do
|
||||
before do
|
||||
params[:repository_path_pattern] = changed_repository_path_pattern
|
||||
end
|
||||
|
||||
it 'updates a container registry protection rule' do
|
||||
patch_container_registry_protection_rule
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(json_response).to match(hash_including({
|
||||
'project_id' => container_registry_protection_rule.project.id,
|
||||
'repository_path_pattern' => changed_repository_path_pattern,
|
||||
'minimum_access_level_for_push' => container_registry_protection_rule.minimum_access_level_for_push,
|
||||
'minimum_access_level_for_delete' => container_registry_protection_rule.minimum_access_level_for_delete
|
||||
}))
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a single change' do
|
||||
let(:params) { { repository_path_pattern: changed_repository_path_pattern } }
|
||||
|
||||
it 'updates a container registry protection rule' do
|
||||
patch_container_registry_protection_rule
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(json_response["repository_path_pattern"]).to eq(changed_repository_path_pattern)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with minimum_access_level_to_push set to nil' do
|
||||
before do
|
||||
params[:minimum_access_level_for_push] = ""
|
||||
end
|
||||
|
||||
it 'clears the minimum_access_level_to_push' do
|
||||
patch_container_registry_protection_rule
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(json_response["minimum_access_level_for_push"]).to be_nil
|
||||
end
|
||||
|
||||
context 'with minimum_access_level_to_delete set to nil as well' do
|
||||
before do
|
||||
params[:minimum_access_level_for_delete] = ""
|
||||
end
|
||||
|
||||
it_behaves_like 'returning response status', :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
context 'with minimum_access_level_to_delete set to nil' do
|
||||
before do
|
||||
params[:minimum_access_level_for_delete] = ""
|
||||
end
|
||||
|
||||
it 'clears the minimum_access_level_to_delete' do
|
||||
patch_container_registry_protection_rule
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(json_response["minimum_access_level_for_delete"]).to be_nil
|
||||
end
|
||||
|
||||
context 'with minimum_access_level_to_push set to nil as well' do
|
||||
before do
|
||||
params[:minimum_access_level_for_push] = ""
|
||||
end
|
||||
|
||||
it_behaves_like 'returning response status', :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
context 'with invalid repository_path_pattern' do
|
||||
before do
|
||||
params[:repository_path_pattern] = "not in enum"
|
||||
end
|
||||
|
||||
it_behaves_like 'returning response status', :unprocessable_entity
|
||||
end
|
||||
|
||||
context 'with invalid minimum_access_level_for_push' do
|
||||
before do
|
||||
params[:minimum_access_level_for_push] = "not in enum"
|
||||
end
|
||||
|
||||
it_behaves_like 'returning response status', :bad_request
|
||||
end
|
||||
|
||||
context 'with already existing repository_path_pattern' do
|
||||
before do
|
||||
other_protection_rule = create(:container_registry_protection_rule, project: project,
|
||||
repository_path_pattern: "#{project.full_path}/path")
|
||||
params[:repository_path_pattern] = other_protection_rule.repository_path_pattern
|
||||
end
|
||||
|
||||
it_behaves_like 'returning response status', :unprocessable_entity
|
||||
end
|
||||
|
||||
it_behaves_like 'rejecting container registry protection rules request when handling rule ids'
|
||||
it_behaves_like 'rejecting container registry protection rules request when enough permissions'
|
||||
end
|
||||
|
||||
context 'with invalid token' do
|
||||
subject(:patch_container_registry_protection_rule) do
|
||||
patch(api(url), headers: headers_with_invalid_token, params: params)
|
||||
end
|
||||
|
||||
it_behaves_like 'returning response status', :unauthorized
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,280 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Import::PlaceholderReferences::LoadService, feature_category: :importers do
|
||||
let(:import_source) { Import::SOURCE_DIRECT_TRANSFER }
|
||||
let(:import_uid) { 1 }
|
||||
|
||||
describe '#execute', :aggregate_failures, :clean_gitlab_redis_shared_state do
|
||||
let_it_be(:source_user) { create(:import_source_user) }
|
||||
let_it_be(:valid_reference) do
|
||||
{
|
||||
composite_key: nil,
|
||||
numeric_key: 1,
|
||||
model: 'Foo',
|
||||
namespace_id: source_user.namespace_id,
|
||||
source_user_id: source_user.id,
|
||||
user_reference_column: 'user_id'
|
||||
}
|
||||
end
|
||||
|
||||
let(:valid_reference_2) { valid_reference.merge(model: 'Bar') }
|
||||
let(:valid_reference_3) { valid_reference.merge(model: 'Baz') }
|
||||
|
||||
subject(:result) { described_class.new(import_source: import_source, import_uid: import_uid).execute }
|
||||
|
||||
it 'loads data pushed with `Import::PlaceholderReferences::PushService`' do
|
||||
record = create(:note)
|
||||
|
||||
Import::PlaceholderReferences::PushService.from_record(
|
||||
import_source: import_source,
|
||||
import_uid: import_uid,
|
||||
source_user: source_user,
|
||||
record: record,
|
||||
user_reference_column: :author_id
|
||||
).execute
|
||||
|
||||
expect(Import::SourceUserPlaceholderReference)
|
||||
.to receive(:bulk_insert!)
|
||||
.with(kind_of(Array))
|
||||
.and_call_original
|
||||
|
||||
expect_log_message(:info, message: 'Processing placeholder references')
|
||||
expect_log_message(:info, message: 'Processed placeholder references', processed_count: 1, error_count: 0)
|
||||
expect(result).to be_success
|
||||
expect(result.payload).to eq(processed_count: 1, error_count: 0)
|
||||
expect(Import::SourceUserPlaceholderReference.all).to contain_exactly(
|
||||
have_attributes(
|
||||
composite_key: nil,
|
||||
numeric_key: record.id,
|
||||
model: record.class.name,
|
||||
namespace_id: source_user.namespace_id,
|
||||
source_user_id: source_user.id,
|
||||
user_reference_column: 'author_id'
|
||||
)
|
||||
)
|
||||
expect(set_members_count).to eq(0)
|
||||
end
|
||||
|
||||
it 'loads data to PostgreSQL in batches' do
|
||||
push(valid_reference)
|
||||
push(valid_reference_2)
|
||||
push(valid_reference_3)
|
||||
|
||||
stub_const("#{described_class}::BATCH_LIMIT", 2)
|
||||
|
||||
expect(Import::SourceUserPlaceholderReference)
|
||||
.to receive(:bulk_insert!)
|
||||
.with(kind_of(Array))
|
||||
.twice
|
||||
.and_call_original
|
||||
expect(Gitlab::Cache::Import::Caching)
|
||||
.to receive(:limited_values_from_set)
|
||||
.twice
|
||||
.and_call_original
|
||||
expect_log_message(:info, message: 'Processing placeholder references')
|
||||
expect_log_message(:info, message: 'Processed placeholder references', processed_count: 3, error_count: 0)
|
||||
expect(result).to be_success
|
||||
expect(result.payload).to eq(processed_count: 3, error_count: 0)
|
||||
expect(Import::SourceUserPlaceholderReference.all).to contain_exactly(
|
||||
have_attributes(valid_reference),
|
||||
have_attributes(valid_reference_2),
|
||||
have_attributes(valid_reference_3)
|
||||
)
|
||||
expect(set_members_count).to eq(0)
|
||||
end
|
||||
|
||||
it 'does not load data for another import_uid' do
|
||||
push(valid_reference)
|
||||
|
||||
result = described_class.new(import_source: import_source, import_uid: 2).execute
|
||||
|
||||
expect(result).to be_success
|
||||
expect(result.payload).to eq(processed_count: 0, error_count: 0)
|
||||
expect(Import::SourceUserPlaceholderReference.count).to eq(0)
|
||||
expect(set_members_count).to eq(1)
|
||||
end
|
||||
|
||||
it 'does not load data for another import_source' do
|
||||
push(valid_reference)
|
||||
|
||||
result = described_class.new(
|
||||
import_source: ::Import::SOURCE_PROJECT_EXPORT_IMPORT,
|
||||
import_uid: import_uid
|
||||
).execute
|
||||
|
||||
expect(result).to be_success
|
||||
expect(result.payload).to eq(processed_count: 0, error_count: 0)
|
||||
expect(Import::SourceUserPlaceholderReference.count).to eq(0)
|
||||
expect(set_members_count).to eq(1)
|
||||
end
|
||||
|
||||
context 'when something in the batch has an unexpected schema' do
|
||||
let(:invalid_reference) { valid_reference.merge(foo: 'bar') }
|
||||
|
||||
before do
|
||||
stub_const("#{described_class}::BATCH_LIMIT", 2)
|
||||
|
||||
push(valid_reference)
|
||||
push(valid_reference_2)
|
||||
# We can't use `#push` because that validates,
|
||||
# so push directly to the set.
|
||||
Gitlab::Cache::Import::Caching.set_add(cache_key, invalid_reference.values.to_json)
|
||||
push(valid_reference_3)
|
||||
end
|
||||
|
||||
it 'loads just the valid data, and clears the set' do
|
||||
expect_log_message(:error,
|
||||
message: 'Error processing placeholder reference',
|
||||
exception: {
|
||||
class: Import::SourceUserPlaceholderReference::SerializationError,
|
||||
message: 'Import::SourceUserPlaceholderReference::SerializationError'
|
||||
},
|
||||
item: invalid_reference.values.to_json
|
||||
)
|
||||
expect_log_message(:info, message: 'Processed placeholder references', processed_count: 4, error_count: 1)
|
||||
expect(result).to be_success
|
||||
expect(result.payload).to eq(processed_count: 4, error_count: 1)
|
||||
expect(Import::SourceUserPlaceholderReference.all).to contain_exactly(
|
||||
have_attributes(valid_reference),
|
||||
have_attributes(valid_reference_2),
|
||||
have_attributes(valid_reference_3)
|
||||
)
|
||||
expect(set_members_count).to eq(0)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when loading to PostgreSQL fails due to an ActiveRecord::RecordInvalid' do
|
||||
let(:invalid_reference) { valid_reference.except(:source_user_id) }
|
||||
|
||||
before do
|
||||
stub_const("#{described_class}::BATCH_LIMIT", 2)
|
||||
|
||||
push(invalid_reference)
|
||||
push(valid_reference)
|
||||
push(valid_reference_2)
|
||||
push(valid_reference_3)
|
||||
end
|
||||
|
||||
it 'loads just the valid data, and clears the list' do
|
||||
expect_log_message(:error,
|
||||
message: 'Error processing placeholder reference',
|
||||
exception: {
|
||||
class: ActiveRecord::RecordInvalid,
|
||||
message: "Validation failed: Source user can't be blank"
|
||||
},
|
||||
item: hash_including(invalid_reference.stringify_keys)
|
||||
)
|
||||
expect(result).to be_success
|
||||
expect(result.payload).to eq(processed_count: 4, error_count: 1)
|
||||
expect(Import::SourceUserPlaceholderReference.all).to contain_exactly(
|
||||
have_attributes(valid_reference),
|
||||
have_attributes(valid_reference_2),
|
||||
have_attributes(valid_reference_3)
|
||||
)
|
||||
expect(set_members_count).to eq(0)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when loading to PostgreSQL fails due to ActiveRecord::InvalidForeignKey' do
|
||||
let(:invalid_reference) { valid_reference.merge(source_user_id: non_existing_record_id) }
|
||||
|
||||
before do
|
||||
stub_const("#{described_class}::BATCH_LIMIT", 2)
|
||||
|
||||
push(invalid_reference)
|
||||
push(valid_reference)
|
||||
push(valid_reference_2)
|
||||
push(valid_reference_3)
|
||||
end
|
||||
|
||||
it 'logs the error and clears the failing batch but continues' do
|
||||
expect_log_message(:error,
|
||||
message: 'Error processing placeholder reference',
|
||||
exception: {
|
||||
class: ActiveRecord::InvalidForeignKey,
|
||||
message: include('PG::ForeignKeyViolation')
|
||||
},
|
||||
item: have_attributes(size: 2).and(include(have_attributes(invalid_reference)))
|
||||
)
|
||||
expect(result).to be_success
|
||||
expect(Import::SourceUserPlaceholderReference.count).to eq(2)
|
||||
expect(set_members_count).to eq(0)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when loading to PostgreSQL fails for an unhandled reason' do
|
||||
before do
|
||||
allow(Import::SourceUserPlaceholderReference)
|
||||
.to receive(:bulk_insert!)
|
||||
.and_raise(ActiveRecord::ConnectionTimeoutError)
|
||||
end
|
||||
|
||||
it 'bubbles the exception and does not clear the set' do
|
||||
push(valid_reference)
|
||||
|
||||
expect { result }.to raise_error(ActiveRecord::ConnectionTimeoutError)
|
||||
expect(Import::SourceUserPlaceholderReference.count).to eq(0)
|
||||
expect(set_members_count).to eq(1)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when fetching set from Redis fails' do
|
||||
before do
|
||||
allow(Gitlab::Cache::Import::Caching)
|
||||
.to receive(:limited_values_from_set)
|
||||
.and_raise(Redis::ConnectionError)
|
||||
end
|
||||
|
||||
it 'bubbles the exception, does not load any data, and does not clear the set' do
|
||||
push(valid_reference)
|
||||
|
||||
expect { result }.to raise_error(Redis::ConnectionError)
|
||||
expect(Import::SourceUserPlaceholderReference.count).to eq(0)
|
||||
expect(set_members_count).to eq(1)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when clearing the set from Redis fails' do
|
||||
before do
|
||||
allow(Gitlab::Cache::Import::Caching)
|
||||
.to receive(:set_remove)
|
||||
.and_raise(Redis::ConnectionError)
|
||||
end
|
||||
|
||||
it 'bubbles the exception and does not clear the set, but does load the data' do
|
||||
push(valid_reference)
|
||||
|
||||
expect { result }.to raise_error(Redis::ConnectionError)
|
||||
# (We tolerate that this leads to duplicate records loaded to PostgreSQL!)
|
||||
expect(Import::SourceUserPlaceholderReference.count).to eq(1)
|
||||
expect(set_members_count).to eq(1)
|
||||
end
|
||||
end
|
||||
|
||||
def push(reference)
|
||||
serialized = Import::SourceUserPlaceholderReference.new(reference).to_serialized
|
||||
Gitlab::Cache::Import::Caching.set_add(cache_key, serialized)
|
||||
end
|
||||
|
||||
def expect_log_message(type, **params)
|
||||
allow(Import::Framework::Logger).to receive(type)
|
||||
expect(Import::Framework::Logger).to receive(type)
|
||||
.with(params.merge(import_source: import_source, import_uid: import_uid))
|
||||
end
|
||||
|
||||
def set_members_count
|
||||
Gitlab::Cache::Import::Caching.with_redis do |redis|
|
||||
redis.scard(Gitlab::Cache::Import::Caching.cache_key_for(cache_key))
|
||||
end
|
||||
end
|
||||
|
||||
def cache_key
|
||||
Import::PlaceholderReferences::BaseService.new(
|
||||
import_source: import_source,
|
||||
import_uid: import_uid
|
||||
).send(:cache_key)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Import::PlaceholderReferences::PushService, :aggregate_failures, :clean_gitlab_redis_shared_state, feature_category: :importers do
|
||||
let(:import_source) { Import::SOURCE_DIRECT_TRANSFER }
|
||||
let(:import_uid) { 1 }
|
||||
|
||||
describe '#execute' do
|
||||
let(:composite_key) { nil }
|
||||
let(:numeric_key) { 9 }
|
||||
|
||||
subject(:result) do
|
||||
described_class.new(
|
||||
import_source: import_source,
|
||||
import_uid: import_uid,
|
||||
source_user_id: 123,
|
||||
source_user_namespace_id: 234,
|
||||
model: MergeRequest,
|
||||
user_reference_column: 'author_id',
|
||||
numeric_key: numeric_key,
|
||||
composite_key: composite_key
|
||||
).execute
|
||||
end
|
||||
|
||||
it 'pushes data to Redis' do
|
||||
expected_result = [nil, 'MergeRequest', 234, 9, 123, 'author_id'].to_json
|
||||
|
||||
expect(result).to be_success
|
||||
expect(result.payload).to eq(serialized_reference: expected_result)
|
||||
expect(set).to contain_exactly(expected_result)
|
||||
end
|
||||
|
||||
context 'when composite_key is provided' do
|
||||
let(:numeric_key) { nil }
|
||||
let(:composite_key) { { 'foo' => 1 } }
|
||||
|
||||
it 'pushes data to Redis containing the composite_key' do
|
||||
expected_result = [
|
||||
{ 'foo' => 1 }, 'MergeRequest', 234, nil, 123, 'author_id'
|
||||
].to_json
|
||||
|
||||
expect(result).to be_success
|
||||
expect(result.payload).to eq(serialized_reference: expected_result)
|
||||
expect(set).to contain_exactly(expected_result)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when is invalid' do
|
||||
let(:composite_key) { { 'foo' => 1 } }
|
||||
|
||||
it 'does not push data to Redis' do
|
||||
expect(result).to be_error
|
||||
expect(result.message).to include('numeric_key or composite_key must be present')
|
||||
expect(set).to be_empty
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.from_record' do
|
||||
let_it_be(:source_user) { create(:import_source_user) }
|
||||
let_it_be(:record) { create(:merge_request) }
|
||||
|
||||
subject(:result) do
|
||||
described_class.from_record(
|
||||
import_source: import_source,
|
||||
import_uid: import_uid,
|
||||
source_user: source_user,
|
||||
record: record,
|
||||
user_reference_column: 'author_id'
|
||||
).execute
|
||||
end
|
||||
|
||||
it 'pushes data to Redis' do
|
||||
expected_result = [nil, 'MergeRequest', source_user.namespace_id, record.id, source_user.id, 'author_id'].to_json
|
||||
|
||||
expect(result).to be_success
|
||||
expect(result.payload).to eq(serialized_reference: expected_result)
|
||||
expect(set).to contain_exactly(expected_result)
|
||||
end
|
||||
end
|
||||
|
||||
def set
|
||||
Gitlab::Cache::Import::Caching.values_from_set(cache_key)
|
||||
end
|
||||
|
||||
def cache_key
|
||||
Import::PlaceholderReferences::BaseService.new(
|
||||
import_source: import_source,
|
||||
import_uid: import_uid
|
||||
).send(:cache_key)
|
||||
end
|
||||
end
|
||||
|
|
@ -5,6 +5,7 @@ require 'spec_helper'
|
|||
RSpec.describe Packages::Npm::CheckManifestCoherenceService, :aggregate_failures, feature_category: :package_registry do
|
||||
let_it_be(:package) { build(:npm_package) }
|
||||
let_it_be(:package_metadata) { build(:npm_metadatum, package: package) }
|
||||
|
||||
let(:tar_header) { Gem::Package::TarHeader.new(name: 'json', size: package_json.size, mode: 0o644, prefix: '') }
|
||||
let(:package_json_entry) { Gem::Package::TarReader::Entry.new(tar_header, StringIO.new(package_json)) }
|
||||
let(:service) { described_class.new(package, package_json_entry) }
|
||||
|
|
@ -60,6 +61,48 @@ RSpec.describe Packages::Npm::CheckManifestCoherenceService, :aggregate_failures
|
|||
end
|
||||
end
|
||||
|
||||
context 'with auto corrected version' do
|
||||
where(:version_in_payload, :version_in_tarball, :coherent, :error_message) do
|
||||
'5.0.3' | '5.0.3' | true | nil
|
||||
'5.0.3' | '5.0.4' | false | described_class::MANIFEST_NOT_COHERENT_ERROR
|
||||
'5.0.3' | 'v5.0.3' | true | nil
|
||||
'5.0.3' | '5.0.3+build' | true | nil
|
||||
'5.0.3' | 'v5.0.3+build' | true | nil
|
||||
'5.0.3-test' | '5.0.3-test+build' | true | nil
|
||||
'5.0.3-test' | 'v5.0.3-test+build' | true | nil
|
||||
'5.0.3-test' | 'v5.0.3+build-test' | false | described_class::MANIFEST_NOT_COHERENT_ERROR
|
||||
'5.0.3' | 'v5.0.3+build-test' | true | nil
|
||||
'5.0.3' | '=5.0.3' | false | described_class::VERSION_NOT_COMPLIANT_ERROR
|
||||
'5.1.3' | '05.01.03' | true | nil
|
||||
'5.1.3-beta.1' | '5.1.3-beta.01' | true | nil
|
||||
'5.0.3' | ' =5.0.3' | false | described_class::VERSION_NOT_COMPLIANT_ERROR
|
||||
'5.0.3-beta' | '5.0.3beta' | false | described_class::VERSION_NOT_COMPLIANT_ERROR
|
||||
end
|
||||
|
||||
with_them do
|
||||
let(:package_json) do
|
||||
{
|
||||
name: package.name,
|
||||
version: version_in_tarball,
|
||||
scripts: package_metadata.package_json_scripts
|
||||
}.to_json
|
||||
end
|
||||
|
||||
before do
|
||||
package.version = version_in_payload
|
||||
end
|
||||
|
||||
if params[:coherent]
|
||||
it { is_expected.to be_success }
|
||||
else
|
||||
it 'raises a mismatch error' do
|
||||
expect { execute_service }
|
||||
.to raise_error(described_class::MismatchError, error_message)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the package metadata is missing' do
|
||||
let(:package_json) { { name: package_name, version: package_version, scripts: {} }.to_json }
|
||||
|
||||
|
|
|
|||
|
|
@ -5,5 +5,13 @@ module Features
|
|||
def iteration_period(iteration)
|
||||
"#{iteration.start_date.to_fs(:medium)} - #{iteration.due_date.to_fs(:medium)}"
|
||||
end
|
||||
|
||||
def iteration_period_display_no_year(iteration)
|
||||
"#{iteration.start_date.strftime('%b %-d')} - #{iteration.due_date.strftime('%b %-d')}"
|
||||
end
|
||||
|
||||
def iteration_period_display(iteration)
|
||||
"#{iteration.start_date.strftime('%b %-d')} - #{iteration.due_date.strftime('%b %-d, %Y')}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -538,6 +538,7 @@ def find_and_click_clear(selector)
|
|||
end
|
||||
|
||||
RSpec.shared_examples 'work items iteration' do
|
||||
include Features::IterationHelpers
|
||||
let(:work_item_iteration_selector) { '[data-testid="work-item-iteration"]' }
|
||||
let_it_be(:iteration_cadence) { create(:iterations_cadence, group: group, active: true) }
|
||||
let_it_be(:iteration) do
|
||||
|
|
@ -594,7 +595,7 @@ RSpec.shared_examples 'work items iteration' do
|
|||
|
||||
within(work_item_iteration_selector) do
|
||||
expect(page).to have_text(iteration_cadence.title)
|
||||
expect(page).to have_text(iteration.period)
|
||||
expect(page).to have_text(iteration_period_display(iteration))
|
||||
end
|
||||
|
||||
find_and_click_edit(work_item_iteration_selector)
|
||||
|
|
@ -611,7 +612,7 @@ RSpec.shared_examples 'work items iteration' do
|
|||
wait_for_requests
|
||||
|
||||
select_listbox_item(iteration.period)
|
||||
expect(page).to have_text(iteration.period)
|
||||
expect(page).to have_text(iteration_period_display(iteration))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Import::LoadPlaceholderReferencesWorker, feature_category: :importers do
|
||||
let(:user) { create(:user) }
|
||||
let(:import_source) { 'test_source' }
|
||||
let(:uid) { 123 }
|
||||
let(:params) { { 'current_user_id' => user.id } }
|
||||
|
||||
describe '#perform' do
|
||||
subject(:perform) { described_class.new.perform(import_source, uid, params) }
|
||||
|
||||
it 'executes LoadService' do
|
||||
expect_next_instance_of(Import::PlaceholderReferences::LoadService) do |service|
|
||||
expect(service).to receive(:execute)
|
||||
end
|
||||
|
||||
perform
|
||||
end
|
||||
|
||||
it_behaves_like 'an idempotent worker' do
|
||||
let(:job_args) { [import_source, uid, params] }
|
||||
end
|
||||
|
||||
context 'when bulk_import_user_mapping feature is disabled' do
|
||||
before do
|
||||
stub_feature_flags(bulk_import_user_mapping: false)
|
||||
end
|
||||
|
||||
it 'does not execute LoadService' do
|
||||
expect(Import::PlaceholderReferences::LoadService).not_to receive(:new)
|
||||
|
||||
perform
|
||||
end
|
||||
|
||||
context 'when bulk_import_user_mapping feature is enabled for the user' do
|
||||
before do
|
||||
stub_feature_flags(bulk_import_user_mapping: user)
|
||||
end
|
||||
|
||||
it 'executes LoadService' do
|
||||
expect_next_instance_of(Import::PlaceholderReferences::LoadService) do |service|
|
||||
expect(service).to receive(:execute)
|
||||
end
|
||||
|
||||
perform
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -15,7 +15,7 @@ module Tooling
|
|||
:warning: You have added `gitlab_main_clusterwide` as the schema for this table. We expect most tables to use the
|
||||
`gitlab_main_cell` schema instead, as using the clusterwide schema can have significant scaling implications.
|
||||
|
||||
Please see the [guidelines on choosing gitlab schema](https://docs.gitlab.com/ee/development/database/multiple_databases.html#guidelines-on-choosing-between-gitlab_main_cell-and-gitlab_main_clusterwide-schema) for more information.
|
||||
Please see the [guidelines on choosing gitlab schema](https://docs.gitlab.com/ee/development/cells/index.html#guidelines-on-choosing-between-gitlab_main_cell-and-gitlab_main_clusterwide-schema) for more information.
|
||||
|
||||
Please consult with ~"group::tenant scale" if you believe that the clusterwide schema is the best fit for this table.
|
||||
MESSAGE_MARKDOWN
|
||||
|
|
|
|||
|
|
@ -70,9 +70,6 @@ internal/api/api.go:153:2: var-naming: don't use ALL_CAPS in Go names; use Camel
|
|||
internal/api/block_test.go:61:34: response body must be closed (bodyclose)
|
||||
internal/api/channel_settings.go:57:28: G402: TLS MinVersion too low. (gosec)
|
||||
internal/api/channel_settings_test.go:125:22: SA1019: dialer.TLSClientConfig.RootCAs.Subjects has been deprecated since Go 1.18: if s was returned by SystemCertPool, Subjects will not include the system roots. (staticcheck)
|
||||
internal/badgateway/roundtripper_test.go:76:3: go-require: require must only be used in the goroutine running the test function (testifylint)
|
||||
internal/badgateway/roundtripper_test.go:80:3: go-require: require must only be used in the goroutine running the test function (testifylint)
|
||||
internal/badgateway/roundtripper_test.go:81:3: go-require: require must only be used in the goroutine running the test function (testifylint)
|
||||
internal/builds/register.go:120: Function 'RegisterHandler' is too long (66 > 60) (funlen)
|
||||
internal/channel/channel.go:128:31: response body must be closed (bodyclose)
|
||||
internal/config/config.go:1:1: package-comments: should have a package comment (revive)
|
||||
|
|
@ -139,8 +136,7 @@ internal/git/responsewriter.go:45:1: exported: exported function NewHTTPResponse
|
|||
internal/git/responsewriter.go:52:1: exported: exported method HTTPResponseWriter.Log should have comment or be unexported (revive)
|
||||
internal/git/snapshot.go:27:2: exported: exported var SendSnapshot should have comment or be unexported (revive)
|
||||
internal/git/upload-pack.go:37:16: Error return value of `cw.Flush` is not checked (errcheck)
|
||||
internal/git/upload-pack_test.go:70:2: error-is-as: use require.ErrorIs (testifylint)
|
||||
internal/git/upload-pack_test.go:85:3: go-require: require must only be used in the goroutine running the test function (testifylint)
|
||||
internal/git/upload-pack_test.go:71:2: error-is-as: use require.ErrorIs (testifylint)
|
||||
internal/gitaly/blob.go:1:1: package-comments: should have a package comment (revive)
|
||||
internal/gitaly/blob.go:14:6: exported: exported type BlobClient should have comment or be unexported (revive)
|
||||
internal/gitaly/blob.go:18:1: exported: exported method BlobClient.SendBlob should have comment or be unexported (revive)
|
||||
|
|
@ -206,13 +202,6 @@ internal/redis/keywatcher.go:150:1: exported: exported method KeyWatcher.Shutdow
|
|||
internal/redis/keywatcher.go:232:23: Error return value of `kw.conn.Unsubscribe` is not checked (errcheck)
|
||||
internal/redis/keywatcher.go:252:1: exported: exported method KeyWatcher.WatchKey should have comment or be unexported (revive)
|
||||
internal/redis/keywatcher_test.go:120:38: unnecessary leading newline (whitespace)
|
||||
internal/redis/keywatcher_test.go:189:5: go-require: require must only be used in the goroutine running the test function (testifylint)
|
||||
internal/redis/keywatcher_test.go:190:5: go-require: require must only be used in the goroutine running the test function (testifylint)
|
||||
internal/redis/keywatcher_test.go:241:6: go-require: require must only be used in the goroutine running the test function (testifylint)
|
||||
internal/redis/keywatcher_test.go:242:6: go-require: require must only be used in the goroutine running the test function (testifylint)
|
||||
internal/redis/keywatcher_test.go:267:3: go-require: require must only be used in the goroutine running the test function (testifylint)
|
||||
internal/redis/keywatcher_test.go:268:3: go-require: require must only be used in the goroutine running the test function (testifylint)
|
||||
internal/redis/keywatcher_test.go:273:3: go-require: require must only be used in the goroutine running the test function (testifylint)
|
||||
internal/redis/redis.go:1:1: ST1000: at least one file in a package should have a package comment (stylecheck)
|
||||
internal/redis/redis.go:16:2: blank-imports: a blank import should be only in a main or test package, or have a comment justifying it (revive)
|
||||
internal/redis/redis.go:21:24: var-declaration: should omit type error from declaration of var errSentinelMasterAddr; it will be inferred from the right-hand side (revive)
|
||||
|
|
@ -239,9 +228,6 @@ internal/upload/destination/destination.go:117: Function 'Upload' has too many s
|
|||
internal/upload/destination/multi_hash.go:4:2: G501: Blocklisted import crypto/md5: weak cryptographic primitive (gosec)
|
||||
internal/upload/destination/multi_hash.go:5:2: G505: Blocklisted import crypto/sha1: weak cryptographic primitive (gosec)
|
||||
internal/upload/destination/objectstore/object_test.go:127:4: go-require: do not use assert.FailNow in http handlers (testifylint)
|
||||
internal/upload/destination/objectstore/s3_object_test.go:105:4: go-require: require must only be used in the goroutine running the test function (testifylint)
|
||||
internal/upload/destination/objectstore/s3_object_test.go:109:4: go-require: require must only be used in the goroutine running the test function (testifylint)
|
||||
internal/upload/destination/objectstore/s3_object_test.go:110:4: go-require: require must only be used in the goroutine running the test function (testifylint)
|
||||
internal/upload/destination/objectstore/test/objectstore_stub.go:4:2: G501: Blocklisted import crypto/md5: weak cryptographic primitive (gosec)
|
||||
internal/upload/destination/objectstore/test/objectstore_stub.go:169:13: G401: Use of weak cryptographic primitive (gosec)
|
||||
internal/upload/destination/objectstore/upload_strategy.go:29: internal/upload/destination/objectstore/upload_strategy.go:29: Line contains TODO/BUG/FIXME/NOTE/OPTIMIZE/HACK: "TODO: consider adding the context to the..." (godox)
|
||||
|
|
|
|||
|
|
@ -70,9 +70,6 @@ internal/api/api.go:153:2: var-naming: don't use ALL_CAPS in Go names; use Camel
|
|||
internal/api/block_test.go:61:34: response body must be closed (bodyclose)
|
||||
internal/api/channel_settings.go:57:28: G402: TLS MinVersion too low. (gosec)
|
||||
internal/api/channel_settings_test.go:125:22: SA1019: dialer.TLSClientConfig.RootCAs.Subjects has been deprecated since Go 1.18: if s was returned by [SystemCertPool], Subjects will not include the system roots. (staticcheck)
|
||||
internal/badgateway/roundtripper_test.go:76:3: go-require: require must only be used in the goroutine running the test function (testifylint)
|
||||
internal/badgateway/roundtripper_test.go:80:3: go-require: require must only be used in the goroutine running the test function (testifylint)
|
||||
internal/badgateway/roundtripper_test.go:81:3: go-require: require must only be used in the goroutine running the test function (testifylint)
|
||||
internal/builds/register.go:120: Function 'RegisterHandler' is too long (66 > 60) (funlen)
|
||||
internal/channel/channel.go:128:31: response body must be closed (bodyclose)
|
||||
internal/config/config.go:1:1: package-comments: should have a package comment (revive)
|
||||
|
|
@ -139,8 +136,7 @@ internal/git/responsewriter.go:45:1: exported: exported function NewHTTPResponse
|
|||
internal/git/responsewriter.go:52:1: exported: exported method HTTPResponseWriter.Log should have comment or be unexported (revive)
|
||||
internal/git/snapshot.go:27:2: exported: exported var SendSnapshot should have comment or be unexported (revive)
|
||||
internal/git/upload-pack.go:37:16: Error return value of `cw.Flush` is not checked (errcheck)
|
||||
internal/git/upload-pack_test.go:70:2: error-is-as: use require.ErrorIs (testifylint)
|
||||
internal/git/upload-pack_test.go:85:3: go-require: require must only be used in the goroutine running the test function (testifylint)
|
||||
internal/git/upload-pack_test.go:71:2: error-is-as: use require.ErrorIs (testifylint)
|
||||
internal/gitaly/blob.go:1:1: package-comments: should have a package comment (revive)
|
||||
internal/gitaly/blob.go:14:6: exported: exported type BlobClient should have comment or be unexported (revive)
|
||||
internal/gitaly/blob.go:18:1: exported: exported method BlobClient.SendBlob should have comment or be unexported (revive)
|
||||
|
|
@ -206,13 +202,6 @@ internal/redis/keywatcher.go:150:1: exported: exported method KeyWatcher.Shutdow
|
|||
internal/redis/keywatcher.go:232:23: Error return value of `kw.conn.Unsubscribe` is not checked (errcheck)
|
||||
internal/redis/keywatcher.go:252:1: exported: exported method KeyWatcher.WatchKey should have comment or be unexported (revive)
|
||||
internal/redis/keywatcher_test.go:120:38: unnecessary leading newline (whitespace)
|
||||
internal/redis/keywatcher_test.go:189:5: go-require: require must only be used in the goroutine running the test function (testifylint)
|
||||
internal/redis/keywatcher_test.go:190:5: go-require: require must only be used in the goroutine running the test function (testifylint)
|
||||
internal/redis/keywatcher_test.go:241:6: go-require: require must only be used in the goroutine running the test function (testifylint)
|
||||
internal/redis/keywatcher_test.go:242:6: go-require: require must only be used in the goroutine running the test function (testifylint)
|
||||
internal/redis/keywatcher_test.go:267:3: go-require: require must only be used in the goroutine running the test function (testifylint)
|
||||
internal/redis/keywatcher_test.go:268:3: go-require: require must only be used in the goroutine running the test function (testifylint)
|
||||
internal/redis/keywatcher_test.go:273:3: go-require: require must only be used in the goroutine running the test function (testifylint)
|
||||
internal/redis/redis.go:1:1: ST1000: at least one file in a package should have a package comment (stylecheck)
|
||||
internal/redis/redis.go:16:2: blank-imports: a blank import should be only in a main or test package, or have a comment justifying it (revive)
|
||||
internal/redis/redis.go:21:24: var-declaration: should omit type error from declaration of var errSentinelMasterAddr; it will be inferred from the right-hand side (revive)
|
||||
|
|
@ -239,9 +228,6 @@ internal/upload/destination/destination.go:117: Function 'Upload' has too many s
|
|||
internal/upload/destination/multi_hash.go:4:2: G501: Blocklisted import crypto/md5: weak cryptographic primitive (gosec)
|
||||
internal/upload/destination/multi_hash.go:5:2: G505: Blocklisted import crypto/sha1: weak cryptographic primitive (gosec)
|
||||
internal/upload/destination/objectstore/object_test.go:127:4: go-require: do not use assert.FailNow in http handlers (testifylint)
|
||||
internal/upload/destination/objectstore/s3_object_test.go:105:4: go-require: require must only be used in the goroutine running the test function (testifylint)
|
||||
internal/upload/destination/objectstore/s3_object_test.go:109:4: go-require: require must only be used in the goroutine running the test function (testifylint)
|
||||
internal/upload/destination/objectstore/s3_object_test.go:110:4: go-require: require must only be used in the goroutine running the test function (testifylint)
|
||||
internal/upload/destination/objectstore/test/objectstore_stub.go:4:2: G501: Blocklisted import crypto/md5: weak cryptographic primitive (gosec)
|
||||
internal/upload/destination/objectstore/test/objectstore_stub.go:169:13: G401: Use of weak cryptographic primitive (gosec)
|
||||
internal/upload/destination/objectstore/upload_strategy.go:29: internal/upload/destination/objectstore/upload_strategy.go:29: Line contains TODO/BUG/FIXME/NOTE/OPTIMIZE/HACK: "TODO: consider adding the context to the..." (godox)
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import (
|
|||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
|
@ -73,12 +74,12 @@ func TestClientDisconnect499(t *testing.T) {
|
|||
|
||||
go func() {
|
||||
req, err := http.NewRequestWithContext(clientContext, "GET", ts.URL, nil)
|
||||
require.NoError(t, err, "build request")
|
||||
assert.NoError(t, err, "build request")
|
||||
|
||||
rt := NewRoundTripper(false, http.DefaultTransport)
|
||||
response, err := rt.RoundTrip(req)
|
||||
require.NoError(t, err, "perform roundtrip")
|
||||
require.NoError(t, response.Body.Close())
|
||||
assert.NoError(t, err, "perform roundtrip")
|
||||
assert.NoError(t, response.Body.Close())
|
||||
|
||||
clientResponse <- response
|
||||
}()
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
|
|
@ -82,7 +83,7 @@ func startSmartHTTPServer(t testing.TB, s gitalypb.SmartHTTPServiceServer) strin
|
|||
srv := grpc.NewServer(testhelper.WithSidechannel())
|
||||
gitalypb.RegisterSmartHTTPServiceServer(srv, s)
|
||||
go func() {
|
||||
require.NoError(t, srv.Serve(ln))
|
||||
assert.NoError(t, srv.Serve(ln))
|
||||
}()
|
||||
|
||||
t.Cleanup(func() {
|
||||
|
|
|
|||
|
|
@ -186,8 +186,8 @@ func TestKeyChangesWhenWatching(t *testing.T) {
|
|||
<-ready
|
||||
val, err := kw.WatchKey(ctx, runnerKey, tc.watchValue, time.Second)
|
||||
|
||||
require.NoError(t, err, "Expected no error")
|
||||
require.Equal(t, tc.expectedStatus, val, "Expected value")
|
||||
assert.NoError(t, err, "Expected no error")
|
||||
assert.Equal(t, tc.expectedStatus, val, "Expected value")
|
||||
}()
|
||||
|
||||
processMessages(t, kw, 1, tc.processedValue, ready, wg)
|
||||
|
|
@ -238,8 +238,8 @@ func TestKeyChangesParallel(t *testing.T) {
|
|||
<-ready
|
||||
val, err := kw.WatchKey(ctx, runnerKey, tc.watchValue, time.Second)
|
||||
|
||||
require.NoError(t, err, "Expected no error")
|
||||
require.Equal(t, tc.expectedStatus, val, "Expected value")
|
||||
assert.NoError(t, err, "Expected no error")
|
||||
assert.Equal(t, tc.expectedStatus, val, "Expected value")
|
||||
}()
|
||||
}
|
||||
|
||||
|
|
@ -264,13 +264,13 @@ func TestShutdown(t *testing.T) {
|
|||
defer wg.Done()
|
||||
val, err := kw.WatchKey(ctx, runnerKey, "something", 10*time.Second)
|
||||
|
||||
require.NoError(t, err, "Expected no error")
|
||||
require.Equal(t, WatchKeyStatusNoChange, val, "Expected value not to change")
|
||||
assert.NoError(t, err, "Expected no error")
|
||||
assert.Equal(t, WatchKeyStatusNoChange, val, "Expected value not to change")
|
||||
}()
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
require.Eventually(t, func() bool { return countSubscribers(kw, runnerKey) == 1 }, 10*time.Second, time.Millisecond)
|
||||
assert.Eventually(t, func() bool { return countSubscribers(kw, runnerKey) == 1 }, 10*time.Second, time.Millisecond)
|
||||
|
||||
kw.Shutdown()
|
||||
}()
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import (
|
|||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
"github.com/aws/aws-sdk-go/service/s3"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"gitlab.com/gitlab-org/gitlab/workhorse/internal/config"
|
||||
|
|
@ -102,12 +103,12 @@ func TestConcurrentS3ObjectUpload(t *testing.T) {
|
|||
defer cancel()
|
||||
|
||||
object, err := NewS3Object(objectName, creds, config)
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// copy data
|
||||
n, err := object.Consume(ctx, strings.NewReader(test.ObjectContent), deadline)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, test.ObjectSize, n, "Uploaded file mismatch")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, test.ObjectSize, n, "Uploaded file mismatch")
|
||||
|
||||
test.S3ObjectExists(t, sess, config, objectName, test.ObjectContent)
|
||||
wg.Done()
|
||||
|
|
|
|||
120
yarn.lock
120
yarn.lock
|
|
@ -1357,10 +1357,10 @@
|
|||
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-3.105.0.tgz#0d15978755093b79e5c9b883a27d8074bf316e9a"
|
||||
integrity sha512-JrXE7T3j+9FyQakG4XVg/uhXL3TZvmFgTuggaiSXdyelVOqAzYdPPm1oergdp8C+Io0zBKlLkJv/4nWG3AgkfQ==
|
||||
|
||||
"@gitlab/ui@^86.4.0":
|
||||
version "86.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-86.4.0.tgz#816f28cc062240f2b7daabe2280c55487949661f"
|
||||
integrity sha512-ZgXHotqp5XmpfwJtH7V4o/dLH32uqMACNWuqiP3Dj3AAm/X693LYq5HLDH8lfIXk5FeIVVlAeE6wMOmsdiIluQ==
|
||||
"@gitlab/ui@^86.9.1":
|
||||
version "86.9.1"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-86.9.1.tgz#ec02a4c77c3642a3b0b5391ede6a15dedde31628"
|
||||
integrity sha512-91mctMAFNgugLf7Q126cZvMfhT0W5j5cPOA7oSKk8flCyGODDt7X3g53gDwQZ0kCYDp1oHXK9MenE+BWxK0Elw==
|
||||
dependencies:
|
||||
"@floating-ui/dom" "1.4.3"
|
||||
echarts "^5.3.2"
|
||||
|
|
@ -2029,76 +2029,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.15.0":
|
||||
version "8.15.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry-internal/browser-utils/-/browser-utils-8.15.0.tgz#d78646d02d76445ba71c7ed423d749b192ef010c"
|
||||
integrity sha512-DquySUQRnmMyRbfHH9t/JDwPMsVaCOCzV/6XmMb7s4FDYTWSCSpumJEYfiyJCuI9NeebPHZWF7LZCHH4glSAJQ==
|
||||
"@sentry-internal/browser-utils@8.17.0":
|
||||
version "8.17.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry-internal/browser-utils/-/browser-utils-8.17.0.tgz#4424ed92c7ac1c5aa43c0b2a45d1484d5e78e3c5"
|
||||
integrity sha512-BEYBIDX1y8paKsDk8PmjYfAYFNS+KSeEhOwJTr/RWjvx/Fyb5ZF2q4u7qMjeNFLcxKnMkQTGYE9CYf/7XWs4bA==
|
||||
dependencies:
|
||||
"@sentry/core" "8.15.0"
|
||||
"@sentry/types" "8.15.0"
|
||||
"@sentry/utils" "8.15.0"
|
||||
"@sentry/core" "8.17.0"
|
||||
"@sentry/types" "8.17.0"
|
||||
"@sentry/utils" "8.17.0"
|
||||
|
||||
"@sentry-internal/feedback@8.15.0":
|
||||
version "8.15.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry-internal/feedback/-/feedback-8.15.0.tgz#c28a176208dd53025c7aff4eb4df0fa19f48e0c4"
|
||||
integrity sha512-W6XiLpw7fL1A0KaHxIH45nbC2M8uagrMoBnMZ1NcqE4AoSe7VtoDqPsLvQ7MgMXwsBYiPu2AItRnKoGFS/dUBA==
|
||||
"@sentry-internal/feedback@8.17.0":
|
||||
version "8.17.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry-internal/feedback/-/feedback-8.17.0.tgz#0dedd7b1b71d4859e119433acb0ff265950724bd"
|
||||
integrity sha512-lFypwCqqcwgh++8sPZw9hAEKphXSgPIdSqoXakgwSKxGx2pCIBbzeyOWzUeBpGfBkTw813HiuRwNY+e0dF6b4Q==
|
||||
dependencies:
|
||||
"@sentry/core" "8.15.0"
|
||||
"@sentry/types" "8.15.0"
|
||||
"@sentry/utils" "8.15.0"
|
||||
"@sentry/core" "8.17.0"
|
||||
"@sentry/types" "8.17.0"
|
||||
"@sentry/utils" "8.17.0"
|
||||
|
||||
"@sentry-internal/replay-canvas@8.15.0":
|
||||
version "8.15.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry-internal/replay-canvas/-/replay-canvas-8.15.0.tgz#0201eaae000678bdb0b054cd558f5c1bb0882454"
|
||||
integrity sha512-gfezIuvf94wY748l5wSy4pRm+45GjiBm0Q/KLnXROLZKmbI7MTJrdQXA2Oxut848iISTQo4/LimecFnBDiaGtw==
|
||||
"@sentry-internal/replay-canvas@8.17.0":
|
||||
version "8.17.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry-internal/replay-canvas/-/replay-canvas-8.17.0.tgz#b17353b28e105eda3464e3de027de355c6c9816c"
|
||||
integrity sha512-2FAbd/65zjPzlUQK/cKBzNPIClBKSPrHzfuB1ZM102JwSpvS5sljzhLvxua17uwU9V1Z9pbOT1pu5KtkUyc7lQ==
|
||||
dependencies:
|
||||
"@sentry-internal/replay" "8.15.0"
|
||||
"@sentry/core" "8.15.0"
|
||||
"@sentry/types" "8.15.0"
|
||||
"@sentry/utils" "8.15.0"
|
||||
"@sentry-internal/replay" "8.17.0"
|
||||
"@sentry/core" "8.17.0"
|
||||
"@sentry/types" "8.17.0"
|
||||
"@sentry/utils" "8.17.0"
|
||||
|
||||
"@sentry-internal/replay@8.15.0":
|
||||
version "8.15.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry-internal/replay/-/replay-8.15.0.tgz#5382790b9c5b0d223aebdf50ad84135463eecd32"
|
||||
integrity sha512-d4cA8pjr0CGHkTe8ulqMROYCX3bMHBAi/7DJBr11i4MdNCUl+/pndA9C5TiFv0sFzk/hDZQZS3J+MfGp56ZQHw==
|
||||
"@sentry-internal/replay@8.17.0":
|
||||
version "8.17.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry-internal/replay/-/replay-8.17.0.tgz#4208957f24a4072c3d5b7f587d0735a2d563dbac"
|
||||
integrity sha512-SBNXBbXEd4WdCIIa/9mkcrwUjxJxSQtYakJ00Xvv/gwqR1rmRFOVqVjLXisryDXCucdD4Rp5MqRT9H+BcSNVtg==
|
||||
dependencies:
|
||||
"@sentry-internal/browser-utils" "8.15.0"
|
||||
"@sentry/core" "8.15.0"
|
||||
"@sentry/types" "8.15.0"
|
||||
"@sentry/utils" "8.15.0"
|
||||
"@sentry-internal/browser-utils" "8.17.0"
|
||||
"@sentry/core" "8.17.0"
|
||||
"@sentry/types" "8.17.0"
|
||||
"@sentry/utils" "8.17.0"
|
||||
|
||||
"@sentry/browser@8.15.0":
|
||||
version "8.15.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-8.15.0.tgz#81223a4ee448c7ffed662301314132c3f442ec08"
|
||||
integrity sha512-Tx4eFgAqa8tedg30+Cgr7qFocWHise8p3jb/RSNs+TCEBXLVtQidHHVZMO71FWUAC86D7woo5hMKTt+UmB8pgg==
|
||||
"@sentry/browser@8.17.0":
|
||||
version "8.17.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-8.17.0.tgz#d6d7dc7dd73e6adf246494082fa31a4da41012ad"
|
||||
integrity sha512-dPMvxyS+ogu7/3+AI83U2IKaLO7hZUt3J35mtgOQhAETcyNaLZtJP1VNUAcW/VhJa3TMCfmG5A1+dkBp8A6cdA==
|
||||
dependencies:
|
||||
"@sentry-internal/browser-utils" "8.15.0"
|
||||
"@sentry-internal/feedback" "8.15.0"
|
||||
"@sentry-internal/replay" "8.15.0"
|
||||
"@sentry-internal/replay-canvas" "8.15.0"
|
||||
"@sentry/core" "8.15.0"
|
||||
"@sentry/types" "8.15.0"
|
||||
"@sentry/utils" "8.15.0"
|
||||
"@sentry-internal/browser-utils" "8.17.0"
|
||||
"@sentry-internal/feedback" "8.17.0"
|
||||
"@sentry-internal/replay" "8.17.0"
|
||||
"@sentry-internal/replay-canvas" "8.17.0"
|
||||
"@sentry/core" "8.17.0"
|
||||
"@sentry/types" "8.17.0"
|
||||
"@sentry/utils" "8.17.0"
|
||||
|
||||
"@sentry/core@8.15.0":
|
||||
version "8.15.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-8.15.0.tgz#a8df0a6bfeedad090c7b5ac3f76656f8ed28ac70"
|
||||
integrity sha512-RjuEq/34VjNmxlfzq+485jG63/Vst90svQapLwVgBZWgM8jxrLyCRXHU0wfBc7/1IhV/T9GYAplrJQAkG4J9Ow==
|
||||
"@sentry/core@8.17.0":
|
||||
version "8.17.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-8.17.0.tgz#95145bedaf1a338b7f045598d4fafee3471f689b"
|
||||
integrity sha512-s62O0Re6WcvaVbH1IEeAWmj/ca8UhaRoFaDnc5TR68reOycBrgnqCNq3qHxBsELOA6NJowoK+T29DDGs9QVXhQ==
|
||||
dependencies:
|
||||
"@sentry/types" "8.15.0"
|
||||
"@sentry/utils" "8.15.0"
|
||||
"@sentry/types" "8.17.0"
|
||||
"@sentry/utils" "8.17.0"
|
||||
|
||||
"@sentry/types@8.15.0":
|
||||
version "8.15.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-8.15.0.tgz#bcf1eca41bccbc6eb434cc14ab7364eed41a8c17"
|
||||
integrity sha512-AZc9nSHKuNH8P/7ihmq5fbZBiQ7Gr35kJq9Tad9eVuOgL8D+2b6Vqu/61ljVVlMFI0tBGFsSkWJ/00PfBcVKWg==
|
||||
"@sentry/types@8.17.0":
|
||||
version "8.17.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-8.17.0.tgz#0100b5decb2bac55a6c6a231de1db2c79bad7f89"
|
||||
integrity sha512-v0nI0+ajiGTijhF1W/ryn2+zFVFr6VPn6lao3W4qKj9MlltIHa4/uuGzTaiCFwoPw7g5bZ1Q09SStpDXVMkz2A==
|
||||
|
||||
"@sentry/utils@8.15.0":
|
||||
version "8.15.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-8.15.0.tgz#31890e97e77bd27484c0831402e1666d3e289b03"
|
||||
integrity sha512-1ISmyYFuRHJbGun0gUYscyz1aP6RfILUldNAUwQwF0Ycu8YOi4n8uwJRN0aov6cCi41tnZWOMBagSeLxbJiJgQ==
|
||||
"@sentry/utils@8.17.0":
|
||||
version "8.17.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-8.17.0.tgz#758207b3e0534aacce97b50f8d418b129c9f8b9f"
|
||||
integrity sha512-HHtAPLOlvzhwgfYzxtuPnLUoGRMtMrFvopkii74zmx/1ZD4VN4PYPB2E5KFf3c18pTovw+kxF0ux6VrGiyAHsw==
|
||||
dependencies:
|
||||
"@sentry/types" "8.15.0"
|
||||
"@sentry/types" "8.17.0"
|
||||
|
||||
"@sinclair/typebox@^0.27.8":
|
||||
version "0.27.8"
|
||||
|
|
|
|||
Loading…
Reference in New Issue