Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-09-01 12:11:56 +00:00
parent f60abc4315
commit a53033814d
68 changed files with 755 additions and 315 deletions

View file

@ -36,7 +36,6 @@ Layout/HashAlignment:
- 'spec/lib/gitlab/error_tracking/stack_trace_highlight_decorator_spec.rb'
- 'spec/lib/gitlab/etag_caching/middleware_spec.rb'
- 'spec/lib/gitlab/etag_caching/router/graphql_spec.rb'
- 'spec/lib/gitlab/git/repository_spec.rb'
- 'spec/lib/gitlab/grape_logging/formatters/lograge_with_timestamp_spec.rb'
- 'spec/lib/gitlab/import_export/attributes_finder_spec.rb'
- 'spec/lib/gitlab/import_export/group/object_builder_spec.rb'
@ -55,10 +54,8 @@ Layout/HashAlignment:
- 'spec/lib/gitlab/metrics/subscribers/active_record_spec.rb'
- 'spec/lib/gitlab/metrics/subscribers/load_balancing_spec.rb'
- 'spec/models/ci/build_spec.rb'
- 'spec/models/ci/pipeline_spec.rb'
- 'spec/models/ci/processable_spec.rb'
- 'spec/models/clusters/platforms/kubernetes_spec.rb'
- 'spec/models/commit_status_spec.rb'
- 'spec/models/container_registry/event_spec.rb'
- 'spec/models/deployment_spec.rb'
- 'spec/models/design_management/version_spec.rb'
@ -72,7 +69,6 @@ Layout/HashAlignment:
- 'spec/models/remote_mirror_spec.rb'
- 'spec/models/repository_spec.rb'
- 'spec/models/user_spec.rb'
- 'spec/presenters/clusters/cluster_presenter_spec.rb'
- 'spec/presenters/project_presenter_spec.rb'
- 'spec/routing/project_routing_spec.rb'
- 'spec/serializers/ci/lint/job_entity_spec.rb'
@ -80,30 +76,3 @@ Layout/HashAlignment:
- 'spec/serializers/deployment_entity_spec.rb'
- 'spec/serializers/environment_serializer_spec.rb'
- 'spec/serializers/merge_request_metrics_helper_spec.rb'
- 'spec/services/ci/create_downstream_pipeline_service_spec.rb'
- 'spec/services/ci/create_pipeline_service/logger_spec.rb'
- 'spec/services/ci/create_pipeline_service/tags_spec.rb'
- 'spec/services/ci/job_artifacts/create_service_spec.rb'
- 'spec/services/ci/retry_job_service_spec.rb'
- 'spec/services/deployments/link_merge_requests_service_spec.rb'
- 'spec/services/discussions/capture_diff_note_positions_service_spec.rb'
- 'spec/services/environments/stop_service_spec.rb'
- 'spec/services/event_create_service_spec.rb'
- 'spec/services/groups/import_export/import_service_spec.rb'
- 'spec/services/issuable/bulk_update_service_spec.rb'
- 'spec/services/issues/create_service_spec.rb'
- 'spec/services/merge_requests/build_service_spec.rb'
- 'spec/services/merge_requests/create_service_spec.rb'
- 'spec/services/merge_requests/update_service_spec.rb'
- 'spec/services/metrics/dashboard/clone_dashboard_service_spec.rb'
- 'spec/services/notes/create_service_spec.rb'
- 'spec/services/notes/destroy_service_spec.rb'
- 'spec/services/packages/debian/parse_debian822_service_spec.rb'
- 'spec/services/projects/destroy_service_spec.rb'
- 'spec/services/service_ping/submit_service_ping_service_spec.rb'
- 'spec/services/suggestions/apply_service_spec.rb'
- 'spec/services/work_items/widgets/description_service/update_service_spec.rb'
- 'spec/tooling/danger/datateam_spec.rb'
- 'spec/views/projects/tags/index.html.haml_spec.rb'
- 'spec/workers/emails_on_push_worker_spec.rb'
- 'spec/workers/pages_domain_ssl_renewal_cron_worker_spec.rb'

View file

@ -10,7 +10,6 @@ body.gl-dark {
--gray-50: #303030;
--gray-100: #404040;
--gray-200: #525252;
--gray-600: #bfbfbf;
--gray-700: #dbdbdb;
--gray-900: #fafafa;
--green-100: #0d532a;
@ -1791,9 +1790,6 @@ body.gl-dark {
.avatar {
background: rgba(255, 255, 255, 0.04);
}
.nav-sidebar li a {
color: var(--gray-600);
}
body.gl-dark {
--gl-theme-accent: #868686;
}

View file

@ -60,12 +60,6 @@
}
.nav-sidebar {
li {
a {
color: var(--gray-600);
}
}
.sidebar-sub-level-items.fly-out-list {
box-shadow: none;
border: 1px solid $border-color;
@ -78,7 +72,7 @@ aside.right-sidebar:not(.right-sidebar-merge-requests) {
}
body.gl-dark {
@include gitlab-theme($gray-900, $gray-400, $gray-500, $gray-900, $gray-900, $white);
@include gitlab-theme($gray-900, $gray-400, $gray-500, $gray-900, $white);
.terms {
.logo-text {

View file

@ -6,7 +6,6 @@ body {
$theme-blue-200,
$theme-blue-500,
$theme-blue-700,
$gray-900,
$theme-blue-900,
$white
);

View file

@ -7,7 +7,6 @@ body {
$gray-300,
$gray-500,
$gray-900,
$gray-900,
$white
);
}

View file

@ -6,7 +6,6 @@ body {
$theme-green-200,
$theme-green-500,
$theme-green-700,
$gray-900,
$theme-green-900,
$white
);

View file

@ -6,18 +6,22 @@
$search-and-nav-links,
$accent,
$border-and-box-shadow,
$sidebar-text,
$nav-svg-color,
$color-alternate
$navbar-theme-color,
$navbar-theme-contrast-color
) {
// Set custom properties
--gl-theme-accent: #{$accent};
$search-and-nav-links-a20: rgba($search-and-nav-links, 0.2);
$search-and-nav-links-a30: rgba($search-and-nav-links, 0.3);
$search-and-nav-links-a40: rgba($search-and-nav-links, 0.4);
$search-and-nav-links-a80: rgba($search-and-nav-links, 0.8);
// Header
.navbar-gitlab {
background-color: $nav-svg-color;
background-color: $navbar-theme-color;
.navbar-collapse {
color: $search-and-nav-links;
@ -37,7 +41,7 @@
> button {
&:hover,
&:focus {
background-color: rgba($search-and-nav-links, 0.2);
background-color: $search-and-nav-links-a20;
}
}
@ -45,13 +49,13 @@
&.dropdown.show {
> a,
> button {
color: $nav-svg-color;
background-color: $color-alternate;
color: $navbar-theme-color;
background-color: $navbar-theme-contrast-color;
}
}
&.line-separator {
border-left: 1px solid rgba($search-and-nav-links, 0.2);
border-left: 1px solid $search-and-nav-links-a20;
}
}
}
@ -65,12 +69,12 @@
color: $search-and-nav-links;
&.header-search-new {
color: $sidebar-text;
color: $gray-900;
}
> a {
.notification-dot {
border: 2px solid $nav-svg-color;
border: 2px solid $navbar-theme-color;
}
&.header-help-dropdown-toggle {
@ -88,7 +92,7 @@
&:hover,
&:focus {
@include media-breakpoint-up(sm) {
background-color: rgba($search-and-nav-links, 0.2);
background-color: $search-and-nav-links-a20;
}
svg {
@ -97,7 +101,7 @@
.notification-dot {
will-change: border-color, background-color;
border-color: adjust-color($nav-svg-color, $red: 33, $green: 33, $blue: 33);
border-color: adjust-color($navbar-theme-color, $red: 33, $green: 33, $blue: 33);
}
&.header-help-dropdown-toggle .notification-dot {
@ -108,12 +112,12 @@
&.active > a,
&.dropdown.show > a {
color: $nav-svg-color;
background-color: $color-alternate;
color: $navbar-theme-color;
background-color: $navbar-theme-contrast-color;
&:hover {
svg {
fill: $nav-svg-color;
fill: $navbar-theme-color;
}
}
@ -123,7 +127,7 @@
&.header-help-dropdown-toggle {
.notification-dot {
background-color: $nav-svg-color;
background-color: $navbar-theme-color;
}
}
}
@ -131,7 +135,7 @@
.impersonated-user,
.impersonated-user:hover {
svg {
fill: $nav-svg-color;
fill: $navbar-theme-color;
}
}
}
@ -142,30 +146,30 @@
> a {
&:hover,
&:focus {
background-color: rgba($search-and-nav-links, 0.2);
background-color: $search-and-nav-links-a20;
}
}
}
.header-search {
background-color: rgba($search-and-nav-links, 0.2) !important;
background-color: $search-and-nav-links-a20 !important;
border-radius: $border-radius-default;
&:hover {
background-color: rgba($search-and-nav-links, 0.3) !important;
background-color: $search-and-nav-links-a30 !important;
}
svg.gl-search-box-by-type-search-icon {
color: rgba($search-and-nav-links, 0.8);
color: $search-and-nav-links-a80;
}
input {
background-color: transparent;
color: rgba($search-and-nav-links, 0.8);
box-shadow: inset 0 0 0 1px rgba($search-and-nav-links, 0.4);
color: $search-and-nav-links-a80;
box-shadow: inset 0 0 0 1px $search-and-nav-links-a40;
&::placeholder {
color: rgba($search-and-nav-links, 0.8);
color: $search-and-nav-links-a80;
}
&:focus,
@ -178,27 +182,27 @@
.keyboard-shortcut-helper {
color: $search-and-nav-links;
background-color: rgba($search-and-nav-links, 0.2);
background-color: $search-and-nav-links-a20;
}
}
.search {
form {
background-color: rgba($search-and-nav-links, 0.2);
background-color: $search-and-nav-links-a20;
&:hover {
background-color: rgba($search-and-nav-links, 0.3);
background-color: $search-and-nav-links-a30;
}
}
.search-input::placeholder {
color: rgba($search-and-nav-links, 0.8);
color: $search-and-nav-links-a80;
}
.search-input-wrap {
.search-icon,
.clear-icon {
fill: rgba($search-and-nav-links, 0.8);
fill: $search-and-nav-links-a80;
}
}
@ -209,7 +213,7 @@
.search-input-wrap {
.search-icon {
fill: rgba($search-and-nav-links, 0.8);
fill: $search-and-nav-links-a80;
}
}
}
@ -217,7 +221,7 @@
// Sidebar
.nav-sidebar li.active > a {
color: $sidebar-text;
color: $gray-900;
}
.nav-sidebar {

View file

@ -6,7 +6,6 @@ body {
$indigo-200,
$indigo-500,
$indigo-700,
$gray-900,
$indigo-900,
$white
);

View file

@ -6,7 +6,6 @@ body {
$theme-light-blue-200,
$theme-light-blue-500,
$theme-light-blue-500,
$gray-900,
$theme-light-blue-700,
$white
);

View file

@ -6,7 +6,6 @@ body {
$gray-500,
$gray-700,
$gray-500,
$gray-900,
$gray-50,
$gray-500
);

View file

@ -6,7 +6,6 @@ body {
$theme-green-200,
$theme-green-500,
$theme-green-500,
$gray-900,
$theme-light-green-700,
$white
);

View file

@ -6,7 +6,6 @@ body {
$indigo-200,
$indigo-500,
$indigo-500,
$gray-900,
$indigo-700,
$white
);

View file

@ -6,7 +6,6 @@ body {
$theme-light-red-200,
$theme-light-red-500,
$theme-light-red-500,
$gray-900,
$theme-light-red-700,
$white
);

View file

@ -6,7 +6,6 @@ body {
$theme-red-200,
$theme-red-500,
$theme-red-700,
$gray-900,
$theme-red-900,
$white
);

View file

@ -0,0 +1,6 @@
- if link?
%a{ href: @href, **html_options }><
= badge_content
- else
%span{ **html_options }><
= badge_content

View file

@ -0,0 +1,72 @@
# frozen_string_literal: true
module Pajamas
class BadgeComponent < Pajamas::Component
def initialize(
text = nil,
icon: nil,
icon_classes: [],
icon_only: false,
href: nil,
size: :md,
variant: :muted,
**html_options
)
@text = text.presence
@icon = icon.to_s.presence
@icon_classes = Array.wrap(icon_classes)
@icon_only = @icon && icon_only
@href = href.presence
@size = filter_attribute(size.to_sym, SIZE_OPTIONS, default: :md)
@variant = filter_attribute(variant.to_sym, VARIANT_OPTIONS, default: :muted)
@html_options = html_options
end
private
SIZE_OPTIONS = [:sm, :md, :lg].freeze
VARIANT_OPTIONS = [:muted, :neutral, :info, :success, :warning, :danger].freeze
delegate :sprite_icon, to: :helpers
def badge_classes
["gl-badge", "badge", "badge-pill", "badge-#{@variant}", @size.to_s]
end
def icon_classes
classes = %w[gl-icon gl-badge-icon] + @icon_classes
classes.push("gl-mr-2") unless icon_only?
classes.join(" ")
end
def icon_only?
@icon_only
end
def link?
@href.present?
end
# Determines the rendered text content.
# The content slot takes presedence over the text param.
def text
content || @text
end
def badge_content
if icon_only?
sprite_icon(@icon, css_class: icon_classes)
elsif @icon.present?
sprite_icon(@icon, css_class: icon_classes) + text
else
text
end
end
def html_options
options = format_options(options: @html_options, css_classes: badge_classes)
options.merge!({ aria: { label: text }, role: "img" }) if icon_only?
options
end
end
end

View file

@ -1,25 +1,6 @@
# frozen_string_literal: true
module BadgesHelper
VARIANT_CLASSES = {
muted: "badge-muted",
neutral: "badge-neutral",
info: "badge-info",
success: "badge-success",
warning: "badge-warning",
danger: "badge-danger"
}.tap { |hash| hash.default = hash.fetch(:muted) }.freeze
SIZE_CLASSES = {
sm: "sm",
md: "md",
lg: "lg"
}.tap { |hash| hash.default = hash.fetch(:md) }.freeze
GL_BADGE_CLASSES = %w[gl-badge badge badge-pill].freeze
GL_ICON_CLASSES = %w[gl-icon gl-badge-icon].freeze
# Creates a GitLab UI badge.
#
# Examples:
@ -53,47 +34,16 @@ module BadgesHelper
#
# See also https://gitlab-org.gitlab.io/gitlab-ui/?path=/story/base-badge--default.
def gl_badge_tag(*args, &block)
# Merge the options and html_options hashes if both are present,
# because the badge component wants a flat list of keyword args.
args.compact!
hashes, params = args.partition { |a| a.is_a? Hash }
options_hash = hashes.reduce({}, :merge)
if block
build_gl_badge_tag(capture(&block), *args)
render Pajamas::BadgeComponent.new(**options_hash), &block
else
build_gl_badge_tag(*args)
render Pajamas::BadgeComponent.new(*params, **options_hash)
end
end
private
def build_gl_badge_tag(content, options = nil, html_options = nil)
options ||= {}
html_options ||= {}
icon_only = options[:icon_only]
variant_class = VARIANT_CLASSES[options.fetch(:variant, :muted)]
size_class = SIZE_CLASSES[options.fetch(:size, :md)]
icon_classes = GL_ICON_CLASSES.dup << options.fetch(:icon_classes, nil)
html_options = html_options.merge(
class: [
*GL_BADGE_CLASSES,
variant_class,
size_class,
*html_options[:class]
]
)
if icon_only
html_options['aria-label'] = content
html_options['role'] = 'img'
end
if options[:icon]
icon_classes << "gl-mr-2" unless icon_only
icon = sprite_icon(options[:icon], css_class: icon_classes.join(' '))
content = icon_only ? icon : icon + content
end
tag = html_options[:href].nil? ? :span : :a
content_tag(tag, content, html_options)
end
end

View file

@ -21,5 +21,9 @@ module Users
network: network
).order(credit_card_validated_at: :desc).includes(:user)
end
def similar_holder_names_count
self.class.where('lower(holder_name) = :value', value: holder_name.downcase).count
end
end
end

View file

@ -15,17 +15,13 @@ options:
events:
- i_package_composer_user
- i_package_conan_user
- i_package_container_user
- i_package_debian_user
- i_package_generic_user
- i_package_golang_user
- i_package_helm_user
- i_package_maven_user
- i_package_npm_user
- i_package_nuget_user
- i_package_pypi_user
- i_package_rubygems_user
- i_package_tag_user
- i_package_terraform_module_user
distribution:
- ee

View file

@ -15,17 +15,13 @@ options:
events:
- i_package_composer_user
- i_package_conan_user
- i_package_container_user
- i_package_debian_user
- i_package_generic_user
- i_package_golang_user
- i_package_helm_user
- i_package_maven_user
- i_package_npm_user
- i_package_nuget_user
- i_package_pypi_user
- i_package_rubygems_user
- i_package_tag_user
- i_package_terraform_module_user
distribution:
- ee

View file

@ -0,0 +1,20 @@
# frozen_string_literal: true
class ReplaceIndexOnCreditCardValidations < Gitlab::Database::Migration[2.0]
disable_ddl_transaction!
OLD_INDEX_NAME = 'index_user_credit_card_validations_meta_data_full_match'
NEW_INDEX_NAME = 'index_user_credit_card_validations_meta_data_full_match_lower'
OLD_FIELDS = [:holder_name, :expiration_date, :last_digits, :credit_card_validated_at]
NEW_FIELDS = 'lower(holder_name), expiration_date, last_digits, credit_card_validated_at'
def up
add_concurrent_index :user_credit_card_validations, NEW_FIELDS, name: NEW_INDEX_NAME
remove_concurrent_index :user_credit_card_validations, OLD_FIELDS, name: OLD_INDEX_NAME
end
def down
add_concurrent_index :user_credit_card_validations, OLD_FIELDS, name: OLD_INDEX_NAME
remove_concurrent_index :user_credit_card_validations, NEW_FIELDS, name: NEW_INDEX_NAME
end
end

View file

@ -0,0 +1 @@
4d8be5080046eff9c3736cd2494c02b2d2cb1eeea2753479617cb344bc5b1cbb

View file

@ -30341,7 +30341,7 @@ CREATE UNIQUE INDEX index_user_canonical_emails_on_user_id ON user_canonical_ema
CREATE UNIQUE INDEX index_user_canonical_emails_on_user_id_and_canonical_email ON user_canonical_emails USING btree (user_id, canonical_email);
CREATE INDEX index_user_credit_card_validations_meta_data_full_match ON user_credit_card_validations USING btree (holder_name, expiration_date, last_digits, credit_card_validated_at);
CREATE INDEX index_user_credit_card_validations_meta_data_full_match_lower ON user_credit_card_validations USING btree (lower(holder_name), expiration_date, last_digits, credit_card_validated_at);
CREATE INDEX index_user_credit_card_validations_meta_data_partial_match ON user_credit_card_validations USING btree (expiration_date, last_digits, network, credit_card_validated_at);

View file

@ -44,6 +44,7 @@ required number of seconds.
"required" : [
"project",
"user",
"credit_card",
"pipeline",
"builds",
"total_builds_count",
@ -85,6 +86,17 @@ required number of seconds.
"sign_in_count": { "type": "integer" }
}
},
"credit_card": {
"type": "object",
"required": [
"similar_cards_count",
"similar_holder_names_count"
],
"properties": {
"similar_cards_count": { "type": "integer" },
"similar_holder_names_count": { "type": "integer" }
}
},
"pipeline": {
"type": "object",
"required": [

View file

@ -33,6 +33,7 @@ GET /projects/:id/approvals
{
"approvals_before_merge": 2,
"reset_approvals_on_push": true,
"selective_code_owner_removals": false,
"disable_overriding_approvers_per_merge_request": false,
"merge_requests_author_approval": true,
"merge_requests_disable_committers_approval": false,
@ -57,16 +58,18 @@ POST /projects/:id/approvals
| ------------------------------------------------ | ------- | -------- | --------------------------------------------------------------------------------------------------- |
| `id` | integer or string | yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding) |
| `approvals_before_merge` | integer | no | How many approvals are required before an MR can be merged. Deprecated in 12.0 in favor of Approval Rules API. |
| `reset_approvals_on_push` | boolean | no | Reset approvals on a new push |
| `disable_overriding_approvers_per_merge_request` | boolean | no | Allow or prevent overriding approvers per MR |
| `merge_requests_author_approval` | boolean | no | Allow or prevent authors from self approving merge requests; `true` means authors can self approve |
| `merge_requests_disable_committers_approval` | boolean | no | Allow or prevent committers from self approving merge requests |
| `require_password_to_approve` | boolean | no | Require approver to enter a password to authenticate before adding the approval |
| `reset_approvals_on_push` | boolean | no | Reset approvals on a new push. |
| `selective_code_owner_removals` | boolean | no | Reset approvals from Code Owners if their files changed. Can be enabled only if `reset_approvals_on_push` is disabled. |
```json
{
"approvals_before_merge": 2,
"reset_approvals_on_push": true,
"selective_code_owner_removals": false,
"disable_overriding_approvers_per_merge_request": false,
"merge_requests_author_approval": false,
"merge_requests_disable_committers_approval": false,

View file

@ -159,21 +159,32 @@ and artifacts, will share the same value. We want to add the `partition_id`
column into all 6 problematic tables because we can avoid backfilling this data
when we decide it is time to start partitioning them.
We want to partition CI/CD data iteratively, so we will start with the
pipelines table, and create at least one, but likely two, partitions. The
pipelines table will be partitioned using the `LIST` partitioning strategy. It
is possible that, after some time, `p_ci_pipelines` will store data in two
partitions with IDs of `100` and `101`. Then we will try partitioning
`ci_builds`. Therefore we might want to use `RANGE` partitioning in
`p_ci_builds` with IDs `100` and `101`, because builds for the two logical
partitions used will still be stored in a single table.
We want to partition CI/CD data iteratively. We plan to start with the
`ci_builds_metadata` table, because this is the fastest growing table in the CI
database and want to contain this rapid growth. This table has also the most
simple access patterns - a row from it is being read when a build is exposed to
a runner, and other access patterns are relatively simple too. Starting with
`p_ci_builds_metadata` will allow us to achieve tangible and quantifiable
results earlier, and will become a new pattern that makes partitioning the
largest table possible. We will partition builds metadata using the `LIST`
partitioning strategy.
Once we have many partitions attached to `p_ci_builds_metadata`, with many
`partition_ids` we will choose another CI table to partition next. In that case
we might want to use `RANGE` partitioning in for that next table because
`p_ci_builds_metadata` will already have many physical partitions, and
therefore many logical `partition_ids` will be used at that time. For example,
if we choose `ci_builds` as the next partitioning candidate, after having
partitioned `p_ci_builds_metadata`, it will have many different values stored
in `ci_builds.partition_id`. Using `RANGE` partitioning in that case might be
easier.
Physical partitioning and logical partitioning will be separated, and a
strategy will be determined when we implement partitioning for the respective
database tables. Using `RANGE` partitioning works similarly to using `LIST`
partitioning in database tables other than `ci_pipelines`, but because we can
guarantee continuity of `partition_id` values, using `RANGE` partitioning might
be a better strategy.
strategy will be determined when we implement physical partitioning for the
respective database tables. Using `RANGE` partitioning works similarly to using
`LIST` partitioning in database tables, but because we can guarantee continuity
of `partition_id` values, using `RANGE` partitioning might be a better
strategy.
## Why do we want to use explicit logical partition ids?
@ -335,9 +346,116 @@ scope block takes an argument). Preloading instance dependent scopes is not
supported.
```
We also need to build a proof of concept for removing data on the PostgreSQL
side (using foreign keys with `ON DELETE CASCADE`) and removing data through
Rails associations, as this might be an important area of uncertainty.
### Foreign keys
Foreign keys must reference columns that either are a primary key or form a
unique constraint. We can define them using these strategies:
#### Between routing tables sharing partition ID
For relations that are part of the same pipeline hierarchy it is possible to
share the `partition_id` column to define the foreign key constraint:
```plaintext
p_ci_pipelines:
- id
- partition_id
p_ci_builds:
- id
- partition_id
- pipeline_id
```
In this case, `p_ci_builds.partition_id` indicates the partition for the build
and also for the pipeline. We can add a FK on the routing table using:
```sql
ALTER TABLE ONLY p_ci_builds
ADD CONSTRAINT fk_on_pipeline_and_partition
FOREIGN KEY (pipeline_id, partition_id)
REFERENCES p_ci_pipelines(id, partition_id) ON DELETE CASCADE;
```
#### Between routing tables with different partition IDs
It's not possible to reuse the `partition_id` for all relations in the CI domain,
so in this case we'll need to store the value as a different attribute. For
example, when canceling redundant pipelines we store on the old pipeline row
the ID of the new pipeline that cancelled it as `auto_canceled_by_id`:
```plaintext
p_ci_pipelines:
- id
- partition_id
- auto_canceled_by_id
- auto_canceled_by_partition_id
```
In this case we can't ensure that the canceling pipeline is part of the same
hierarchy as the canceled pipelines, so we need an extra attribute to store its
partition, `auto_canceled_by_partition_id`, and the FK becomes:
```sql
ALTER TABLE ONLY p_ci_pipelines
ADD CONSTRAINT fk_cancel_redundant_pieplines
FOREIGN KEY (auto_canceled_by_id, auto_canceled_by_partition_id)
REFERENCES p_ci_pipelines(id, partition_id) ON DELETE SET NULL;
```
#### Between routing tables and regular tables
Not all of the tables in the CI domain will be partitioned, so we'll have routing
tables that will reference non-partitioned tables, for example we reference
`external_pull_requests` from `ci_pipelines`:
```sql
FOREIGN KEY (external_pull_request_id)
REFERENCES external_pull_requests(id)
ON DELETE SET NULL
```
In this case we only need to move the FK definition from the partition level
to the routing table so that new pipeline partitions may use it:
```sql
ALTER TABLE p_ci_pipelines
ADD CONSTRAINT fk_external_request
FOREIGN KEY (external_pull_request_id)
REFERENCES external_pull_requests(id) ON DELETE SET NULL;
```
#### Between regular tables and routing tables
Most of the tables from the CI domain reference at least one table that will be
turned into a routing tables, for example `ci_pipeline_messages` references
`ci_pipelines`. These definitions will need to be updated to use the routing
tables and for this they will need a `partition_id` column:
```plaintext
p_ci_pipelines:
- id
- partition_id
ci_pipeline_messages:
- id
- pipeline_id
- pipeline_partition_id
```
The foreign key can be defined by using:
```sql
ALTER TABLE ci_pipeline_messages ADD CONSTRAINT fk_pipeline_partitioned
FOREIGN KEY (pipeline_id, pipeline_partition_id)
REFERENCES p_ci_pipelines(id, partition_id) ON DELETE CASCADE;
```
The old FK definition will need to be removed, otherwise new inserts in the
`ci_pipeline_messages` with pipeline IDs from non-zero partition will fail with
reference errors.
### Indexes
We [learned](https://gitlab.com/gitlab-org/gitlab/-/issues/360148) that `PostgreSQL`
does not allow to create a single index (unique or otherwise) across all partitions of a table.

View file

@ -14,7 +14,10 @@ module Banzai
return doc unless settings.kroki_enabled
diagram_selectors = ::Gitlab::Kroki.formats(settings)
.map { |diagram_type| %(pre[lang="#{diagram_type}"] > code) }
.map do |diagram_type|
%(pre[lang="#{diagram_type}"] > code,
pre > code[lang="#{diagram_type}"])
end
.join(', ')
xpath = Gitlab::Utils::Nokogiri.css_to_xpath(diagram_selectors)
@ -22,7 +25,7 @@ module Banzai
diagram_format = "svg"
doc.xpath(xpath).each do |node|
diagram_type = node.parent['lang']
diagram_type = node.parent['lang'] || node['lang']
diagram_src = node.content
image_src = create_image_src(diagram_type, diagram_format, diagram_src)
img_tag = Nokogiri::HTML::DocumentFragment.parse(%(<img src="#{image_src}" />))
@ -33,8 +36,8 @@ module Banzai
img_tag.set_attribute('hidden', '') if lazy_load
img_tag.set_attribute('class', 'js-render-kroki')
img_tag.set_attribute('data-diagram', node.parent['lang'])
img_tag.set_attribute('data-diagram-src', "data:text/plain;base64,#{Base64.strict_encode64(node.content)}")
img_tag.set_attribute('data-diagram', diagram_type)
img_tag.set_attribute('data-diagram-src', "data:text/plain;base64,#{Base64.strict_encode64(diagram_src)}")
node.parent.replace(img_tag)
end

View file

@ -9,8 +9,8 @@ module Banzai
Filter::AssetProxyFilter,
Filter::ExternalLinkFilter,
Filter::PlantumlFilter,
Filter::SyntaxHighlightFilter,
Filter::KrokiFilter
Filter::KrokiFilter,
Filter::SyntaxHighlightFilter
]
end

View file

@ -97,6 +97,10 @@ module Gitlab
last_sign_in_ip: current_user.last_sign_in_ip,
sign_in_count: current_user.sign_in_count
},
credit_card: {
similar_cards_count: current_user.credit_card_validation&.similar_records&.count.to_i,
similar_holder_names_count: current_user.credit_card_validation&.similar_holder_names_count.to_i
},
pipeline: {
sha: pipeline.sha,
ref: pipeline.ref,

View file

@ -31,7 +31,6 @@ module Gitlab
issues_edit
pipeline_authoring
quickactions
user_packages
].freeze
CATEGORIES_COLLECTED_FROM_METRICS_DEFINITIONS = %w[
@ -49,6 +48,7 @@ module Gitlab
source_code
terraform
testing
user_packages
work_items
].freeze

View file

@ -11894,15 +11894,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
msgid "DastProfiles|Do you want to discard this scanner profile?"
msgstr ""
msgid "DastProfiles|Do you want to discard this site profile?"
msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
msgid "DastProfiles|Edit profile"
msgstr ""
@ -27282,6 +27273,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
msgid "OnDemandScans|Discard changes"
msgstr ""
msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
msgstr ""
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@ -27303,6 +27300,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
msgid "OnDemandScans|Keep editing"
msgstr ""
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@ -27426,6 +27426,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
msgid "OnDemandScans|You have unsaved changes"
msgstr ""
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""

View file

@ -37,7 +37,8 @@ module RuboCop
USAGE_DATA_COUNTERS_EVENTS_YAML_GLOBS = [
File.expand_path("../../../config/metrics/aggregates/*.yml", __dir__),
File.expand_path("../../../lib/gitlab/usage_data_counters/known_events/*.yml", __dir__)
File.expand_path("../../../lib/gitlab/usage_data_counters/known_events/*.yml", __dir__),
File.expand_path("../../../ee/lib/ee/gitlab/usage_data_counters/known_events/*.yml", __dir__)
].freeze
class << self

View file

@ -0,0 +1,148 @@
# frozen_string_literal: true
require "spec_helper"
RSpec.describe Pajamas::BadgeComponent, type: :component do
let(:text) { "Hello" }
let(:options) { {} }
let(:html_options) { {} }
before do
render_inline(described_class.new(text, **options, **html_options))
end
describe "text param" do
it "is shown inside the badge" do
expect(page).to have_css ".gl-badge", text: text
end
end
describe "content slot" do
it "can be used instead of the text param" do
render_inline(described_class.new) do
"Slot content"
end
expect(page).to have_css ".gl-badge", text: "Slot content"
end
it "takes presendence over the text param" do
render_inline(described_class.new(text)) do
"Slot wins."
end
expect(page).to have_css ".gl-badge", text: "Slot wins."
end
end
describe "options" do
describe "icon" do
let(:options) { { icon: :tanuki } }
it "adds the correct icon and margin" do
expect(page).to have_css ".gl-icon.gl-badge-icon.gl-mr-2[data-testid='tanuki-icon']"
end
end
describe "icon_classes" do
let(:options) { { icon: :tanuki, icon_classes: icon_classes } }
context "as string" do
let(:icon_classes) { "js-special-badge-icon js-extra-special" }
it "combines custom classes and component classes" do
expect(page).to have_css \
".gl-icon.gl-badge-icon.gl-mr-2.js-special-badge-icon.js-extra-special[data-testid='tanuki-icon']"
end
end
context "as array" do
let(:icon_classes) { %w[js-special-badge-icon js-extra-special] }
it "combines custom classes and component classes" do
expect(page).to have_css \
".gl-icon.gl-badge-icon.gl-mr-2.js-special-badge-icon.js-extra-special[data-testid='tanuki-icon']"
end
end
end
describe "icon_only" do
let(:options) { { icon: :tanuki, icon_only: true } }
it "adds no extra margin to the icon" do
expect(page).not_to have_css ".gl-icon.gl-mr-2"
end
it "adds the text as ARIA label" do
expect(page).to have_css ".gl-badge[aria-label='#{text}'][role='img']"
end
end
describe "href" do
let(:options) { { href: "/foo" } }
it "makes the a badge a link" do
expect(page).to have_link text, class: "gl-badge", href: "/foo"
end
end
describe "size" do
where(:size) { [:sm, :md, :lg] }
with_them do
let(:options) { { size: size } }
it "adds size class" do
expect(page).to have_css ".gl-badge.#{size}"
end
end
context "with unknown size" do
let(:options) { { size: :xxl } }
it "adds the default size class" do
expect(page).to have_css ".gl-badge.md"
end
end
end
describe "variant" do
where(:variant) { [:muted, :neutral, :info, :success, :warning, :danger] }
with_them do
let(:options) { { variant: variant } }
it "adds variant class" do
expect(page).to have_css ".gl-badge.badge-#{variant}"
end
end
context "with unknown variant" do
let(:options) { { variant: :foo } }
it "adds the default variant class" do
expect(page).to have_css ".gl-badge.badge-muted"
end
end
end
end
describe "HTML options" do
let(:html_options) { { id: "badge-33", data: { foo: "bar" } } }
it "get added as HTML attributes" do
expect(page).to have_css ".gl-badge#badge-33[data-foo='bar']"
end
it "can be combined with component options in no particular order" do
render_inline(described_class.new(text, id: "badge-34", variant: :success, data: { foo: "baz" }, size: :sm))
expect(page).to have_css ".gl-badge.badge-success.sm#badge-34[data-foo='baz']"
end
context "with custom CSS classes" do
let(:html_options) { { id: "badge-35", class: "js-special-badge" } }
it "combines custom classes and component classes" do
expect(page).to have_css ".gl-badge.js-special-badge#badge-35"
end
end
end
end

View file

@ -0,0 +1,61 @@
# frozen_string_literal: true
module Pajamas
class BadgeComponentPreview < ViewComponent::Preview
# Badge
# ---
#
# See its design reference [here](https://design.gitlab.com/components/badge).
#
# @param icon select [~, star-o, issue-closed, tanuki]
# @param icon_only toggle
# @param href url
# @param size select [sm, md, lg]
# @param text text
# @param variant select [muted, neutral, info, success, warning, danger]
def default(icon: :tanuki, icon_only: false, href: nil, size: :md, text: "Tanuki", variant: :muted)
render Pajamas::BadgeComponent.new(
text,
icon: icon,
icon_only: icon_only,
href: href,
size: size,
variant: variant
)
end
# Using the content slot
# ---
#
# Use the content slot instead of the `text` param when things get more complicated than a plain string.
# All other options (`icon`, `size`, etc.) work as usual.
def slot
render Pajamas::BadgeComponent.new(size: :lg, variant: :info) do
"!ereht olleh".reverse.capitalize
end
end
# Custom HTML attributes and icon classes
# ---
#
# Any extra options passed into the component are treated as HTML attributes.
# This makes adding data or an id easy.
#
# CSS classes provided with the `class:` option are combined with the component classes.
#
# It is also possible to set custom `icon_classes:`.
#
# The order in which you provide these keywords doesn't matter.
def custom
render Pajamas::BadgeComponent.new(
"I'm special.",
class: "js-special-badge",
data: { count: 1 },
icon: :tanuki,
icon_classes: ["js-special-badge-icon"],
id: "special-badge-22",
variant: :success
)
end
end
end

View file

@ -354,13 +354,14 @@ RSpec.describe Projects::PipelineSchedulesController do
end
def go
put :update, params: {
put :update,
params: {
namespace_id: project.namespace.to_param,
project_id: project,
id: pipeline_schedule,
schedule: schedule
},
as: :html
as: :html
end
end

View file

@ -4,8 +4,9 @@ require 'spec_helper'
RSpec.describe Projects::ServiceDeskController do
let_it_be(:project) do
create(:project, :private, :custom_repo, service_desk_enabled: true,
files: { '.gitlab/issue_templates/service_desk.md' => 'template' })
create(:project, :private, :custom_repo,
service_desk_enabled: true,
files: { '.gitlab/issue_templates/service_desk.md' => 'template' })
end
let_it_be(:user) { create(:user) }

View file

@ -3,6 +3,7 @@
"required" : [
"project",
"user",
"credit_card",
"pipeline",
"builds",
"total_builds_count"
@ -43,6 +44,17 @@
"sign_in_count": { "type": "integer" }
}
},
"credit_card": {
"type": "object",
"required": [
"similar_cards_count",
"similar_holder_names_count"
],
"properties": {
"similar_cards_count": { "type": "integer" },
"similar_holder_names_count": { "type": "integer" }
}
},
"pipeline": {
"type": "object",
"required": [

View file

@ -182,7 +182,7 @@ RSpec.describe TabHelper do
context 'with data attributes' do
it 'creates a tab counter badge with the data attributes' do
expect(helper.gl_tab_counter_badge(1, { data: { some_attribute: 'foo' } })).to eq(
'<span data-some-attribute="foo" class="gl-badge badge badge-pill badge-muted sm gl-tab-counter-badge">1</span>'
'<span class="gl-badge badge badge-pill badge-muted sm gl-tab-counter-badge" data-some-attribute="foo">1</span>'
)
end
end

View file

@ -46,4 +46,12 @@ RSpec.describe Banzai::Filter::KrokiFilter do
expect(doc.to_s).to start_with '<img src="http://localhost:8000/nomnoml/svg/eNqLDsgsSixJrUmtTHXOL80rsVLwzCupKUrMTNHQtC7IzMlJTE_V0KyJyVNQiE5KTSxKidXVjS5ILCrKL4lFFrSyi07LL81RyM0vLckAysRGjxo8avCowaMGjxo8avCowaMGU8lgAE7mIdc=" hidden="" class="js-render-kroki" data-diagram="nomnoml" data-diagram-src="data:text/plain;base64,W1BpcmF0ZXxleWVDb3VudDog'
end
it 'allows the lang attribute on the code tag to support RST files processed by gitlab-markup gem' do
stub_application_setting(kroki_enabled: true, kroki_url: "http://localhost:8000")
text = '[Pirate|eyeCount: Int|raid();pillage()|\n [beard]--[parrot]\n [beard]-:>[foul mouth]\n]' * 25
doc = filter("<pre><code lang='nomnoml'>#{text}</code></pre>")
expect(doc.to_s).to start_with '<img src="http://localhost:8000/nomnoml/svg/eNqLDsgsSixJrUmtTHXOL80rsVLwzCupKUrMTNHQtC7IzMlJTE_V0KyJyVNQiE5KTSxKidXVjS5ILCrKL4lFFrSyi07LL81RyM0vLckAysRGjxo8avCowaMGjxo8avCowaMGU8lgAE7mIdc=" hidden="" class="js-render-kroki" data-diagram="nomnoml" data-diagram-src="data:text/plain;base64,W1BpcmF0ZXxleWVDb3VudDog'
end
end

View file

@ -179,6 +179,70 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Validate::External do
perform!
end
end
describe 'credit_card' do
context 'with no registered credit_card' do
it 'returns the expected credit card counts' do
expect(::Gitlab::HTTP).to receive(:post) do |_url, params|
payload = Gitlab::Json.parse(params[:body])
expect(payload['credit_card']['similar_cards_count']).to eq(0)
expect(payload['credit_card']['similar_holder_names_count']).to eq(0)
end
perform!
end
end
context 'with a registered credit card' do
let!(:credit_card) { create(:credit_card_validation, last_digits: 10, holder_name: 'Alice', user: user) }
it 'returns the expected credit card counts' do
expect(::Gitlab::HTTP).to receive(:post) do |_url, params|
payload = Gitlab::Json.parse(params[:body])
expect(payload['credit_card']['similar_cards_count']).to eq(1)
expect(payload['credit_card']['similar_holder_names_count']).to eq(1)
end
perform!
end
context 'with similar credit cards registered by other users' do
before do
create(:credit_card_validation, last_digits: 10, holder_name: 'Bob')
end
it 'returns the expected credit card counts' do
expect(::Gitlab::HTTP).to receive(:post) do |_url, params|
payload = Gitlab::Json.parse(params[:body])
expect(payload['credit_card']['similar_cards_count']).to eq(2)
expect(payload['credit_card']['similar_holder_names_count']).to eq(1)
end
perform!
end
end
context 'with similar holder names registered by other users' do
before do
create(:credit_card_validation, last_digits: 11, holder_name: 'Alice')
end
it 'returns the expected credit card counts' do
expect(::Gitlab::HTTP).to receive(:post) do |_url, params|
payload = Gitlab::Json.parse(params[:body])
expect(payload['credit_card']['similar_cards_count']).to eq(1)
expect(payload['credit_card']['similar_holder_names_count']).to eq(2)
end
perform!
end
end
end
end
end
context 'when EXTERNAL_VALIDATION_SERVICE_TOKEN is set' do

View file

@ -28,4 +28,15 @@ RSpec.describe Users::CreditCardValidation do
expect(subject.similar_records).to eq([match2, match1, subject])
end
end
describe '.similar_holder_names_count' do
subject!(:credit_card_validation) { create(:credit_card_validation, holder_name: 'ALICE M SMITH') }
let!(:match) { create(:credit_card_validation, holder_name: 'Alice M Smith') }
let!(:non_match) { create(:credit_card_validation, holder_name: 'Bob B Brown') }
it 'returns the count of cards with similar case insensitive holder names' do
expect(subject.similar_holder_names_count).to eq(2)
end
end
end

View file

@ -440,10 +440,7 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do
let!(:trigger_project_bridge) do
create(
:ci_bridge, status: :pending,
user: user,
options: trigger_downstream_project,
pipeline: child_pipeline
:ci_bridge, status: :pending, user: user, options: trigger_downstream_project, pipeline: child_pipeline
)
end

View file

@ -19,9 +19,9 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes
let(:counters) do
{
'count' => a_kind_of(Numeric),
'avg' => a_kind_of(Numeric),
'max' => a_kind_of(Numeric),
'min' => a_kind_of(Numeric)
'avg' => a_kind_of(Numeric),
'max' => a_kind_of(Numeric),
'min' => a_kind_of(Numeric)
}
end

View file

@ -37,7 +37,7 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes
context 'tags persistence' do
let(:config) do
{
build: {
build: {
script: 'ls',
stage: 'build',
tags: build_tag_list(label: 'build')

View file

@ -151,9 +151,8 @@ RSpec.describe Ci::JobArtifacts::CreateService do
expect { subject }.not_to change { Ci::JobArtifact.count }
expect(subject).to match(
a_hash_including(http_status: :bad_request,
message: 'another artifact of the same type already exists',
status: :error))
a_hash_including(
http_status: :bad_request, message: 'another artifact of the same type already exists', status: :error))
end
end
end

View file

@ -7,14 +7,13 @@ RSpec.describe Ci::RetryJobService do
let_it_be(:developer) { create(:user) }
let_it_be(:project) { create(:project, :repository) }
let_it_be(:pipeline) do
create(:ci_pipeline, project: project,
sha: 'b83d6e391c22777fca1ed3012fce84f633d7fed0')
create(:ci_pipeline, project: project, sha: 'b83d6e391c22777fca1ed3012fce84f633d7fed0')
end
let_it_be(:stage) do
create(:ci_stage, project: project,
pipeline: pipeline,
name: 'test')
pipeline: pipeline,
name: 'test')
end
let(:job_variables_attributes) { [{ key: 'MANUAL_VAR', value: 'manual test var' }] }
@ -31,9 +30,8 @@ RSpec.describe Ci::RetryJobService do
let_it_be(:downstream_project) { create(:project, :repository) }
let_it_be_with_refind(:job) do
create(
:ci_bridge, :success, pipeline: pipeline, downstream: downstream_project,
description: 'a trigger job', stage_id: stage.id
create(:ci_bridge, :success,
pipeline: pipeline, downstream: downstream_project, description: 'a trigger job', stage_id: stage.id
)
end
@ -133,9 +131,7 @@ RSpec.describe Ci::RetryJobService do
end
let!(:subsequent_bridge) do
create(:ci_bridge, :skipped, stage_idx: 2,
pipeline: pipeline,
stage: 'deploy')
create(:ci_bridge, :skipped, stage_idx: 2, pipeline: pipeline, stage: 'deploy')
end
it 'resumes pipeline processing in the subsequent stage' do
@ -245,10 +241,13 @@ RSpec.describe Ci::RetryJobService do
let(:environment_name) { 'review/$CI_COMMIT_REF_SLUG-$GITLAB_USER_ID' }
let!(:job) do
create(:ci_build, :with_deployment, environment: environment_name,
options: { environment: { name: environment_name } },
pipeline: pipeline, stage_id: stage.id, project: project,
user: other_developer)
create(:ci_build, :with_deployment,
environment: environment_name,
options: { environment: { name: environment_name } },
pipeline: pipeline,
stage_id: stage.id,
project: project,
user: other_developer)
end
it 'creates a new deployment' do

View file

@ -159,10 +159,10 @@ RSpec.describe Deployments::LinkMergeRequestsService do
it "doesn't link the same merge_request twice" do
create(:merge_request, :merged, merge_commit_sha: mr1_merge_commit_sha,
source_project: project)
source_project: project)
picked_mr = create(:merge_request, :merged, merge_commit_sha: '123abc',
source_project: project)
source_project: project)
# the first MR includes c1c67abba which is a cherry-pick of the fake picked_mr merge request
create(:track_mr_picking_note, noteable: picked_mr, project: project, commit_id: 'c1c67abbaf91f624347bb3ae96eabe3a1b742478')

View file

@ -18,8 +18,8 @@ RSpec.describe Discussions::CaptureDiffNotePositionsService do
def build_position(diff_refs, new_line: nil, old_line: nil)
path = 'files/markdown/ruby-style-guide.md'
Gitlab::Diff::Position.new(old_path: path, new_path: path,
new_line: new_line, old_line: old_line, diff_refs: diff_refs)
Gitlab::Diff::Position.new(
old_path: path, new_path: path, new_line: new_line, old_line: old_line, diff_refs: diff_refs)
end
def note_for(new_line: nil, old_line: nil)

View file

@ -208,8 +208,11 @@ RSpec.describe Environments::StopService do
context 'when pipeline is a branch pipeline for merge request' do
let(:pipeline) do
create(:ci_pipeline, source: :push, project: project, sha: merge_request.diff_head_sha,
merge_requests_as_head_pipeline: [merge_request])
create(:ci_pipeline,
source: :push,
project: project,
sha: merge_request.diff_head_sha,
merge_requests_as_head_pipeline: [merge_request])
end
it 'does not stop the active environment' do

View file

@ -420,9 +420,9 @@ RSpec.describe EventCreateService, :clean_gitlab_redis_cache, :clean_gitlab_redi
service.save_designs(author, create: [design])
expect_snowplow_event(
category: Gitlab::UsageDataCounters::TrackUniqueEvents::DESIGN_ACTION.to_s,
category: Gitlab::UsageDataCounters::TrackUniqueEvents::DESIGN_ACTION.to_s,
action: 'create',
namespace: design.project.namespace,
namespace: design.project.namespace,
user: author,
project: design.project,
label: 'design_users'
@ -433,9 +433,9 @@ RSpec.describe EventCreateService, :clean_gitlab_redis_cache, :clean_gitlab_redi
service.save_designs(author, update: [design])
expect_snowplow_event(
category: Gitlab::UsageDataCounters::TrackUniqueEvents::DESIGN_ACTION.to_s,
category: Gitlab::UsageDataCounters::TrackUniqueEvents::DESIGN_ACTION.to_s,
action: 'update',
namespace: design.project.namespace,
namespace: design.project.namespace,
user: author,
project: design.project,
label: 'design_users'
@ -481,9 +481,9 @@ RSpec.describe EventCreateService, :clean_gitlab_redis_cache, :clean_gitlab_redi
service.destroy_designs([design], author)
expect_snowplow_event(
category: Gitlab::UsageDataCounters::TrackUniqueEvents::DESIGN_ACTION.to_s,
category: Gitlab::UsageDataCounters::TrackUniqueEvents::DESIGN_ACTION.to_s,
action: 'destroy',
namespace: design.project.namespace,
namespace: design.project.namespace,
user: author,
project: design.project,
label: 'design_users'

View file

@ -149,9 +149,9 @@ RSpec.describe Groups::ImportExport::ImportService do
it 'logs the import success' do
expect(import_logger).to receive(:info).with(
group_id: group.id,
group_id: group.id,
group_name: group.name,
message: 'Group Import/Export: Import succeeded'
message: 'Group Import/Export: Import succeeded'
).once
subject
@ -161,9 +161,9 @@ RSpec.describe Groups::ImportExport::ImportService do
context 'when user does not have correct permissions' do
it 'logs the error and raises an exception' do
expect(import_logger).to receive(:error).with(
group_id: group.id,
group_id: group.id,
group_name: group.name,
message: a_string_including('Errors occurred')
message: a_string_including('Errors occurred')
)
expect { subject }.to raise_error(Gitlab::ImportExport::Error)
@ -186,9 +186,9 @@ RSpec.describe Groups::ImportExport::ImportService do
it 'logs the error and raises an exception' do
expect(import_logger).to receive(:error).with(
group_id: group.id,
group_id: group.id,
group_name: group.name,
message: a_string_including('Errors occurred')
message: a_string_including('Errors occurred')
).once
expect { subject }.to raise_error(Gitlab::ImportExport::Error)
@ -267,9 +267,9 @@ RSpec.describe Groups::ImportExport::ImportService do
it 'logs the import success' do
expect(import_logger).to receive(:info).with(
group_id: group.id,
group_id: group.id,
group_name: group.name,
message: 'Group Import/Export: Import succeeded'
message: 'Group Import/Export: Import succeeded'
).once
subject
@ -279,9 +279,9 @@ RSpec.describe Groups::ImportExport::ImportService do
context 'when user does not have correct permissions' do
it 'logs the error and raises an exception' do
expect(import_logger).to receive(:error).with(
group_id: group.id,
group_id: group.id,
group_name: group.name,
message: a_string_including('Errors occurred')
message: a_string_including('Errors occurred')
)
expect { subject }.to raise_error(Gitlab::ImportExport::Error)
@ -304,9 +304,9 @@ RSpec.describe Groups::ImportExport::ImportService do
it 'logs the error and raises an exception' do
expect(import_logger).to receive(:error).with(
group_id: group.id,
group_id: group.id,
group_name: group.name,
message: a_string_including('Errors occurred')
message: a_string_including('Errors occurred')
).once
expect { subject }.to raise_error(Gitlab::ImportExport::Error)
@ -328,9 +328,9 @@ RSpec.describe Groups::ImportExport::ImportService do
allow(Gitlab::Import::Logger).to receive(:build).and_return(import_logger)
expect(import_logger).to receive(:info).with(
group_id: group.id,
group_id: group.id,
group_name: group.name,
message: 'Group Import/Export: Import succeeded'
message: 'Group Import/Export: Import succeeded'
)
subject

View file

@ -47,7 +47,7 @@ RSpec.describe Issuable::BulkUpdateService do
let(:bulk_update_params) do
{
add_label_ids: add_labels.map(&:id),
add_label_ids: add_labels.map(&:id),
remove_label_ids: remove_labels.map(&:id)
}
end

View file

@ -416,7 +416,7 @@ RSpec.describe Issues::CreateService do
context "when issuable feature is private" do
before do
project.project_feature.update!(issues_access_level: ProjectFeature::PRIVATE,
merge_requests_access_level: ProjectFeature::PRIVATE)
merge_requests_access_level: ProjectFeature::PRIVATE)
end
levels = [Gitlab::VisibilityLevel::INTERNAL, Gitlab::VisibilityLevel::PUBLIC]

View file

@ -20,18 +20,30 @@ RSpec.describe MergeRequests::BuildService do
let(:merge_request) { service.execute }
let(:compare) { double(:compare, commits: commits) }
let(:commit_1) do
double(:commit_1, sha: 'f00ba6', safe_message: 'Initial commit',
gitaly_commit?: false, id: 'f00ba6', parent_ids: ['f00ba5'])
double(:commit_1,
sha: 'f00ba6',
safe_message: 'Initial commit',
gitaly_commit?: false,
id: 'f00ba6',
parent_ids: ['f00ba5'])
end
let(:commit_2) do
double(:commit_2, sha: 'f00ba7', safe_message: "Closes #1234 Second commit\n\nCreate the app",
gitaly_commit?: false, id: 'f00ba7', parent_ids: ['f00ba6'])
double(:commit_2,
sha: 'f00ba7',
safe_message: "Closes #1234 Second commit\n\nCreate the app",
gitaly_commit?: false,
id: 'f00ba7',
parent_ids: ['f00ba6'])
end
let(:commit_3) do
double(:commit_3, sha: 'f00ba8', safe_message: 'This is a bad commit message!',
gitaly_commit?: false, id: 'f00ba8', parent_ids: ['f00ba7'])
double(:commit_3,
sha: 'f00ba8',
safe_message: 'This is a bad commit message!',
gitaly_commit?: false,
id: 'f00ba8',
parent_ids: ['f00ba7'])
end
let(:commits) { nil }

View file

@ -434,7 +434,7 @@ RSpec.describe MergeRequests::CreateService, :clean_gitlab_redis_shared_state do
context "when issuable feature is private" do
before do
project.project_feature.update!(issues_access_level: ProjectFeature::PRIVATE,
merge_requests_access_level: ProjectFeature::PRIVATE)
merge_requests_access_level: ProjectFeature::PRIVATE)
end
levels = [Gitlab::VisibilityLevel::INTERNAL, Gitlab::VisibilityLevel::PUBLIC]

View file

@ -510,9 +510,9 @@ RSpec.describe MergeRequests::UpdateService, :mailer do
before do
create(:ci_pipeline,
project: project,
ref: merge_request.source_branch,
sha: merge_request.diff_head_sha,
status: :success)
ref: merge_request.source_branch,
sha: merge_request.diff_head_sha,
status: :success)
perform_enqueued_jobs do
@merge_request = service.execute(merge_request)

View file

@ -62,7 +62,7 @@ RSpec.describe Metrics::Dashboard::CloneDashboardService, :use_clean_rails_memor
start_branch: project.default_branch,
encoding: 'text',
file_path: ".gitlab/dashboards/custom_dashboard.yml",
file_content: file_content_hash.to_yaml
file_content: file_content_hash.to_yaml
}
end

View file

@ -134,8 +134,7 @@ RSpec.describe Notes::CreateService do
context 'in a merge request' do
let_it_be(:project_with_repo) { create(:project, :repository) }
let_it_be(:merge_request) do
create(:merge_request, source_project: project_with_repo,
target_project: project_with_repo)
create(:merge_request, source_project: project_with_repo, target_project: project_with_repo)
end
context 'noteable highlight cache clearing' do
@ -181,8 +180,7 @@ RSpec.describe Notes::CreateService do
it 'does not clear cache when note is not the first of the discussion' do
prev_note =
create(:diff_note_on_merge_request, noteable: merge_request,
project: project_with_repo)
create(:diff_note_on_merge_request, noteable: merge_request, project: project_with_repo)
reply_opts =
opts.merge(in_reply_to_discussion_id: prev_note.discussion_id,
type: 'DiffNote',

View file

@ -57,13 +57,11 @@ RSpec.describe Notes::DestroyService do
context 'in a merge request' do
let_it_be(:repo_project) { create(:project, :repository) }
let_it_be(:merge_request) do
create(:merge_request, source_project: repo_project,
target_project: repo_project)
create(:merge_request, source_project: repo_project, target_project: repo_project)
end
let_it_be(:note) do
create(:diff_note_on_merge_request, project: repo_project,
noteable: merge_request)
create(:diff_note_on_merge_request, project: repo_project, noteable: merge_request)
end
it 'does not track issue comment removal usage data' do
@ -84,9 +82,8 @@ RSpec.describe Notes::DestroyService do
end
it 'does not clear cache when note is not the first of the discussion' do
reply_note = create(:diff_note_on_merge_request, in_reply_to: note,
project: repo_project,
noteable: merge_request)
reply_note = create(:diff_note_on_merge_request,
in_reply_to: note, project: repo_project, noteable: merge_request)
expect(merge_request).not_to receive(:diffs)

View file

@ -77,7 +77,7 @@ RSpec.describe Packages::Debian::ParseDebian822Service do
'Depends' => '${shlibs:Depends}, ${misc:Depends}',
'Description' => "Some mostly empty lib\nUsed in GitLab tests.\n\nTesting another paragraph."
},
'Package: sample-udeb' => {
'Package: sample-udeb' => {
'Package' => 'sample-udeb',
'Package-Type' => 'udeb',
'Architecture' => 'any',

View file

@ -312,7 +312,7 @@ RSpec.describe Projects::DestroyService, :aggregate_failures, :event_store_publi
before do
stub_container_registry_tags(repository: project.full_path + '/image',
tags: ['tag'])
tags: ['tag'])
project.container_repositories << container_repository
end
@ -350,7 +350,7 @@ RSpec.describe Projects::DestroyService, :aggregate_failures, :event_store_publi
context 'when there are tags for legacy root repository' do
before do
stub_container_registry_tags(repository: project.full_path,
tags: ['tag'])
tags: ['tag'])
end
context 'when image repository tags deletion succeeds' do

View file

@ -386,12 +386,12 @@ RSpec.describe ServicePing::SubmitService do
let(:payload) do
{
uuid: 'uuid',
metric_a: metric_double,
metric_group: {
metric_a: metric_double,
metric_group: {
metric_b: metric_double
},
metric_without_timing: "value",
recorded_at: Time.current
metric_without_timing: "value",
recorded_at: Time.current
}
end
@ -399,10 +399,10 @@ RSpec.describe ServicePing::SubmitService do
{
metadata: {
uuid: 'uuid',
metrics: [
{ name: 'metric_a', time_elapsed: 123 },
{ name: 'metric_group.metric_b', time_elapsed: 123 }
]
metrics: [
{ name: 'metric_a', time_elapsed: 123 },
{ name: 'metric_group.metric_b', time_elapsed: 123 }
]
}
}
end

View file

@ -35,7 +35,7 @@ RSpec.describe Suggestions::ApplyService do
def apply(suggestions, custom_message = nil)
result = apply_service.new(user, *suggestions, message: custom_message).execute
suggestions.map { |suggestion| suggestion.reload }
suggestions.map(&:reload)
expect(result[:status]).to eq(:success)
end
@ -136,21 +136,20 @@ RSpec.describe Suggestions::ApplyService do
end
let(:merge_request) do
create(:merge_request, source_project: project,
target_project: project,
source_branch: 'master')
create(:merge_request,
source_project: project, target_project: project, source_branch: 'master')
end
let(:position) { build_position }
let(:diff_note) do
create(:diff_note_on_merge_request, noteable: merge_request,
position: position, project: project)
create(:diff_note_on_merge_request,
noteable: merge_request, position: position, project: project)
end
let(:suggestion) do
create(:suggestion, :content_from_repo, note: diff_note,
to_content: " raise RuntimeError, 'Explosion'\n # explosion?\n")
create(:suggestion, :content_from_repo,
note: diff_note, to_content: " raise RuntimeError, 'Explosion'\n # explosion?\n")
end
let(:suggestion2) do
@ -311,9 +310,9 @@ RSpec.describe Suggestions::ApplyService do
context 'when HEAD from position is different from source branch HEAD on repo' do
it 'returns error message' do
allow(suggestion).to receive(:appliable?) { true }
allow(suggestion.position).to receive(:head_sha) { 'old-sha' }
allow(suggestion.noteable).to receive(:source_branch_sha) { 'new-sha' }
allow(suggestion).to receive(:appliable?).and_return(true)
allow(suggestion.position).to receive(:head_sha).and_return('old-sha')
allow(suggestion.noteable).to receive(:source_branch_sha).and_return('new-sha')
result = apply_service.new(user, suggestion).execute
@ -430,7 +429,6 @@ RSpec.describe Suggestions::ApplyService do
suggestion1_diff = fetch_raw_diff(suggestion1)
suggestion2_diff = fetch_raw_diff(suggestion2)
# rubocop: disable Layout/TrailingWhitespace
expected_suggestion1_diff = <<-CONTENT.strip_heredoc
@@ -10,7 +10,7 @@ module Popen
end
@ -442,9 +440,6 @@ RSpec.describe Suggestions::ApplyService do
"PWD" => path
}
CONTENT
# rubocop: enable Layout/TrailingWhitespace
# rubocop: disable Layout/TrailingWhitespace
expected_suggestion2_diff = <<-CONTENT.strip_heredoc
@@ -28,7 +28,7 @@ module Popen
@ -455,8 +450,6 @@ RSpec.describe Suggestions::ApplyService do
@cmd_status = wait_thr.value.exitstatus
end
CONTENT
# rubocop: enable Layout/TrailingWhitespace
expect(suggestion1_diff.strip).to eq(expected_suggestion1_diff.strip)
expect(suggestion2_diff.strip).to eq(expected_suggestion2_diff.strip)
end
@ -508,10 +501,8 @@ RSpec.describe Suggestions::ApplyService do
end
let(:suggestion) do
create(:suggestion, :content_from_repo, note: diff_note,
lines_above: 2,
lines_below: 3,
to_content: "# multi\n# line\n")
create(:suggestion, :content_from_repo,
note: diff_note, lines_above: 2, lines_below: 3, to_content: "# multi\n# line\n")
end
let(:suggestions) { [suggestion] }
@ -568,7 +559,7 @@ RSpec.describe Suggestions::ApplyService do
end
let(:suggestion) do
create_suggestion( to_content: "", new_line: 13)
create_suggestion(to_content: "", new_line: 13)
end
let(:suggestions) { [suggestion] }
@ -616,14 +607,12 @@ RSpec.describe Suggestions::ApplyService do
context 'no permission' do
let(:merge_request) do
create(:merge_request, source_project: project,
target_project: project)
create(:merge_request, source_project: project, target_project: project)
end
let(:diff_note) do
create(:diff_note_on_merge_request, noteable: merge_request,
position: position,
project: project)
create(:diff_note_on_merge_request,
noteable: merge_request, position: position, project: project)
end
context 'user cannot write in project repo' do
@ -642,14 +631,12 @@ RSpec.describe Suggestions::ApplyService do
context 'patch is not appliable' do
let(:merge_request) do
create(:merge_request, source_project: project,
target_project: project)
create(:merge_request, source_project: project, target_project: project)
end
let(:diff_note) do
create(:diff_note_on_merge_request, noteable: merge_request,
position: position,
project: project)
create(:diff_note_on_merge_request,
noteable: merge_request, position: position, project: project)
end
before do
@ -669,7 +656,7 @@ RSpec.describe Suggestions::ApplyService do
let(:result) { apply_service.new(user, suggestion).execute }
before do
expect(suggestion.note).to receive(:latest_diff_file) { nil }
expect(suggestion.note).to receive(:latest_diff_file).and_return(nil)
end
it 'returns error message' do

View file

@ -13,7 +13,7 @@ RSpec.describe WorkItems::Widgets::DescriptionService::UpdateService do
let(:current_user) { author }
let(:work_item) do
create(:work_item, author: author, project: project, description: 'old description',
last_edited_at: Date.yesterday, last_edited_by: random_user
last_edited_at: Date.yesterday, last_edited_by: random_user
)
end

View file

@ -24,7 +24,7 @@ RSpec.describe Tooling::Danger::Datateam do
impacted: true,
impacted_files: %w(db/structure.sql)
},
'with structure.sql changes and Data Warehouse::Impact Check label' => {
'with structure.sql changes and Data Warehouse::Impact Check label' => {
modified_files: %w(db/structure.sql),
changed_lines: ['+group_id bigint NOT NULL)'],
mr_labels: ['Data Warehouse::Impact Check'],

View file

@ -7,8 +7,8 @@ RSpec.describe 'projects/tags/index.html.haml' do
let_it_be(:git_tag) { project.repository.tags.last }
let_it_be(:release) do
create(:release, project: project,
sha: git_tag.target_commit.sha,
tag: 'v1.1.0')
sha: git_tag.target_commit.sha,
tag: 'v1.1.0')
end
let(:pipeline) { create(:ci_pipeline, :success, project: project, ref: git_tag.name, sha: release.sha) }

View file

@ -51,7 +51,7 @@ RSpec.describe EmailsOnPushWorker, :mailer do
context "when push is a force push to delete commits" do
before do
data_force_push = data.stringify_keys.merge(
"after" => data[:before],
"after" => data[:before],
"before" => data[:after]
)

View file

@ -25,8 +25,8 @@ RSpec.describe PagesDomainSslRenewalCronWorker do
end
let!(:domain_with_failed_auto_ssl) do
create(:pages_domain, :without_certificate, :without_key, project: project,
auto_ssl_enabled: true, auto_ssl_failed: true)
create(:pages_domain, :without_certificate, :without_key,
project: project, auto_ssl_enabled: true, auto_ssl_failed: true)
end
let!(:domain_with_expired_auto_ssl) do