Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2024-07-15 15:26:39 +00:00
parent 4abd579c45
commit f0471bfa61
90 changed files with 1756 additions and 456 deletions

View File

@ -1,5 +1,5 @@
review-cleanup:
timeout: 15min
timeout: 30min
extends:
- .default-retry
- .review:rules:review-cleanup

View File

@ -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,

View File

@ -8,6 +8,7 @@ query getPipelineStageJobs($id: CiStageID!) {
id
action {
id
confirmationMessage
icon
path
title

View File

@ -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>

View File

@ -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>

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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: -> { {} }

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 }

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -405,6 +405,8 @@
- 1
- - import_issues_csv
- 2
- - import_load_placeholder_references
- 1
- - import_refresh_import_jid
- 1
- - incident_management

View File

@ -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'

View File

@ -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

View File

@ -0,0 +1 @@
dad465f0fdf57b03ba479c225238422df9008fcdca0cda7757feaac70c0040fa

View File

@ -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:

View File

@ -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.

View File

@ -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**.

View File

@ -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.

View File

@ -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-*"
}'
```

View File

@ -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,

View File

@ -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).

View File

@ -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).

View File

@ -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?

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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/)

View File

@ -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

View File

@ -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`

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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.

View File

@ -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)

View File

@ -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.

View File

@ -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

View File

@ -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/)

View File

@ -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).

View File

@ -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.

View File

@ -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

View File

@ -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).

View File

@ -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/)

View File

@ -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.

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -14,7 +14,7 @@ module Gitlab
end
def action_title
'Retry'
'Run again'
end
def action_button_title

View File

@ -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 ""

View File

@ -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",

View File

@ -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

View File

@ -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',

View File

@ -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);
});
});
});
});
});

View File

@ -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);
});
});
});

View File

@ -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',

View File

@ -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 }) => {

View File

@ -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: {

View File

@ -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>

View File

@ -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

View File

@ -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(),
};

View File

@ -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"

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 }

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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
}()

View File

@ -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() {

View File

@ -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()
}()

View File

@ -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
View File

@ -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"