Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
f81141c25d
commit
d9b3f39aca
49 changed files with 555 additions and 561 deletions
|
@ -1 +1 @@
|
|||
c8a29dc9fd507cab8835b2e1152b94a6ac96de35
|
||||
a8d42fb628ab8d4cbde0481d25724963f73e7931
|
||||
|
|
|
@ -20,7 +20,7 @@ import {
|
|||
} from '../constants';
|
||||
import eventHub from '../event_hub';
|
||||
import { DIFF_FILE, GENERIC_ERROR } from '../i18n';
|
||||
import { collapsedType, isCollapsed, getShortShaFromFile } from '../utils/diff_file';
|
||||
import { collapsedType, getShortShaFromFile } from '../utils/diff_file';
|
||||
import DiffContent from './diff_content.vue';
|
||||
import DiffFileHeader from './diff_file_header.vue';
|
||||
|
||||
|
@ -84,7 +84,7 @@ export default {
|
|||
return {
|
||||
isLoadingCollapsedDiff: false,
|
||||
forkMessageVisible: false,
|
||||
isCollapsed: isCollapsed(this.file),
|
||||
hasToggled: false,
|
||||
};
|
||||
},
|
||||
i18n: {
|
||||
|
@ -102,9 +102,7 @@ export default {
|
|||
return getShortShaFromFile(this.file);
|
||||
},
|
||||
showLoadingIcon() {
|
||||
return (
|
||||
this.idState.isLoadingCollapsedDiff || (!this.file.renderIt && !this.idState.isCollapsed)
|
||||
);
|
||||
return this.idState.isLoadingCollapsedDiff || (!this.file.renderIt && !this.isCollapsed);
|
||||
},
|
||||
hasDiff() {
|
||||
return hasDiff(this.file);
|
||||
|
@ -148,13 +146,13 @@ export default {
|
|||
return collapsedType(this.file) === DIFF_FILE_MANUAL_COLLAPSE;
|
||||
},
|
||||
showBody() {
|
||||
return !this.idState.isCollapsed || this.automaticallyCollapsed;
|
||||
return !this.isCollapsed || this.automaticallyCollapsed;
|
||||
},
|
||||
showWarning() {
|
||||
return this.idState.isCollapsed && this.automaticallyCollapsed && !this.viewDiffsFileByFile;
|
||||
return this.isCollapsed && this.automaticallyCollapsed && !this.viewDiffsFileByFile;
|
||||
},
|
||||
showContent() {
|
||||
return !this.idState.isCollapsed && !this.isFileTooLarge;
|
||||
return !this.isCollapsed && !this.isFileTooLarge;
|
||||
},
|
||||
showLocalFileReviews() {
|
||||
const loggedIn = Boolean(gon.current_user_id);
|
||||
|
@ -165,6 +163,13 @@ export default {
|
|||
codequalityDiffForFile() {
|
||||
return this.codequalityDiff?.files?.[this.file.file_path] || [];
|
||||
},
|
||||
isCollapsed() {
|
||||
if (collapsedType(this.file) !== DIFF_FILE_MANUAL_COLLAPSE) {
|
||||
return this.viewDiffsFileByFile ? false : this.file.viewer?.automaticallyCollapsed;
|
||||
}
|
||||
|
||||
return this.file.viewer?.manuallyCollapsed;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
'file.id': {
|
||||
|
@ -176,23 +181,11 @@ export default {
|
|||
},
|
||||
'file.file_hash': {
|
||||
handler: function hashChangeWatch(newHash, oldHash) {
|
||||
this.isCollapsed = isCollapsed(this.file);
|
||||
|
||||
if (newHash && oldHash && !this.hasDiff && !this.preRender) {
|
||||
this.requestDiff();
|
||||
}
|
||||
},
|
||||
},
|
||||
'file.viewer.automaticallyCollapsed': {
|
||||
handler: function autoChangeWatch(automaticValue) {
|
||||
this.handleAutomaticallyCollapsed(automaticValue);
|
||||
},
|
||||
},
|
||||
'file.viewer.manuallyCollapsed': {
|
||||
handler: function manualChangeWatch(manualValue) {
|
||||
this.handleManualChangeWatch(manualValue);
|
||||
},
|
||||
},
|
||||
},
|
||||
created() {
|
||||
if (this.preRender) return;
|
||||
|
@ -208,8 +201,6 @@ export default {
|
|||
}
|
||||
|
||||
this.manageViewedEffects();
|
||||
this.handleAutomaticallyCollapsed(this.file.viewer.automaticallyCollapsed);
|
||||
this.handleManualChangeWatch(this.file.viewer.manuallyCollapsed);
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (this.preRender) return;
|
||||
|
@ -224,12 +215,18 @@ export default {
|
|||
'setFileCollapsedByUser',
|
||||
]),
|
||||
manageViewedEffects() {
|
||||
if (this.reviewed && !this.idState.isCollapsed && this.showLocalFileReviews) {
|
||||
if (
|
||||
!this.idState.hasToggled &&
|
||||
this.reviewed &&
|
||||
!this.isCollapsed &&
|
||||
this.showLocalFileReviews
|
||||
) {
|
||||
this.handleToggle();
|
||||
this.idState.hasToggled = true;
|
||||
}
|
||||
},
|
||||
expandAllListener() {
|
||||
if (this.idState.isCollapsed) {
|
||||
if (this.isCollapsed) {
|
||||
this.handleToggle();
|
||||
}
|
||||
},
|
||||
|
@ -251,7 +248,7 @@ export default {
|
|||
});
|
||||
},
|
||||
handleToggle({ viaUserInteraction = false } = {}) {
|
||||
const collapsingNow = !this.idState.isCollapsed;
|
||||
const collapsingNow = !this.isCollapsed;
|
||||
const contentElement = this.$el.querySelector(`#diff-content-${this.file.file_hash}`);
|
||||
|
||||
this.setFileCollapsedByUser({
|
||||
|
@ -297,16 +294,6 @@ export default {
|
|||
hideForkMessage() {
|
||||
this.idState.forkMessageVisible = false;
|
||||
},
|
||||
handleAutomaticallyCollapsed(automaticValue) {
|
||||
if (collapsedType(this.file) !== DIFF_FILE_MANUAL_COLLAPSE) {
|
||||
this.idState.isCollapsed = this.viewDiffsFileByFile ? false : automaticValue;
|
||||
}
|
||||
},
|
||||
handleManualChangeWatch(manualValue) {
|
||||
if (manualValue !== null) {
|
||||
this.idState.isCollapsed = manualValue;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -328,7 +315,7 @@ export default {
|
|||
:diff-file="file"
|
||||
:collapsible="true"
|
||||
:reviewed="reviewed"
|
||||
:expanded="!idState.isCollapsed"
|
||||
:expanded="!isCollapsed"
|
||||
:add-merge-request-buttons="true"
|
||||
:view-diffs-file-by-file="viewDiffsFileByFile"
|
||||
:show-local-file-reviews="showLocalFileReviews"
|
||||
|
|
33
app/controllers/users/unsubscribes_controller.rb
Normal file
33
app/controllers/users/unsubscribes_controller.rb
Normal file
|
@ -0,0 +1,33 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Users
|
||||
class UnsubscribesController < ApplicationController
|
||||
skip_before_action :authenticate_user!
|
||||
|
||||
feature_category :users
|
||||
|
||||
def show
|
||||
@user = get_user
|
||||
end
|
||||
|
||||
def create
|
||||
@user = get_user
|
||||
|
||||
if @user
|
||||
@user.admin_unsubscribe!
|
||||
Notify.send_unsubscribed_notification(@user.id).deliver_later
|
||||
end
|
||||
|
||||
redirect_to new_user_session_path, notice: 'You have been unsubscribed'
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def get_user
|
||||
@email = Base64.urlsafe_decode64(params[:email])
|
||||
User.find_by(email: @email)
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
end
|
||||
end
|
|
@ -65,7 +65,7 @@ module Types
|
|||
end
|
||||
|
||||
def visible?(context)
|
||||
return false if feature_flag.present? && !Feature.enabled?(feature_flag)
|
||||
return false if feature_flag.present? && !Feature.enabled?(feature_flag, default_enabled: :yaml)
|
||||
|
||||
super
|
||||
end
|
||||
|
|
19
app/mailers/emails/admin_notification.rb
Normal file
19
app/mailers/emails/admin_notification.rb
Normal file
|
@ -0,0 +1,19 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Emails
|
||||
module AdminNotification
|
||||
def send_admin_notification(user_id, subject, body)
|
||||
user = User.find(user_id)
|
||||
email = user.notification_email
|
||||
@unsubscribe_url = unsubscribe_url(email: Base64.urlsafe_encode64(email))
|
||||
@body = body
|
||||
mail to: email, subject: subject
|
||||
end
|
||||
|
||||
def send_unsubscribed_notification(user_id)
|
||||
user = User.find(user_id)
|
||||
email = user.notification_email
|
||||
mail to: email, subject: "Unsubscribed from GitLab administrator notifications"
|
||||
end
|
||||
end
|
||||
end
|
|
@ -22,6 +22,7 @@ class Notify < ApplicationMailer
|
|||
include Emails::Reviews
|
||||
include Emails::ServiceDesk
|
||||
include Emails::InProductMarketing
|
||||
include Emails::AdminNotification
|
||||
|
||||
helper TimeboxesHelper
|
||||
helper MergeRequestsHelper
|
||||
|
|
|
@ -193,8 +193,7 @@ module Ci
|
|||
|
||||
acts_as_taggable
|
||||
|
||||
add_authentication_token_field :token,
|
||||
encrypted: -> { Gitlab::Ci::Features.require_builds_token_encryption? ? :required : :optional }
|
||||
add_authentication_token_field :token, encrypted: :required
|
||||
|
||||
before_save :ensure_token
|
||||
before_destroy { unscoped_project }
|
||||
|
|
|
@ -1026,8 +1026,6 @@ module Ci
|
|||
end
|
||||
|
||||
def can_generate_codequality_reports?
|
||||
return false unless ::Gitlab::Ci::Features.display_quality_on_mr_diff?(project)
|
||||
|
||||
has_reports?(Ci::JobArtifact.codequality_reports)
|
||||
end
|
||||
|
||||
|
|
|
@ -1556,8 +1556,6 @@ class MergeRequest < ApplicationRecord
|
|||
end
|
||||
|
||||
def has_codequality_mr_diff_report?
|
||||
return false unless ::Gitlab::Ci::Features.display_quality_on_mr_diff?(project)
|
||||
|
||||
actual_head_pipeline&.has_codequality_mr_diff_report?
|
||||
end
|
||||
|
||||
|
|
|
@ -78,7 +78,7 @@ module Namespaces
|
|||
alias_method :recursive_self_and_descendant_ids, :self_and_descendant_ids
|
||||
|
||||
def object_hierarchy(ancestors_base)
|
||||
Gitlab::ObjectHierarchy.new(ancestors_base, options: { use_distinct: Feature.enabled?(:use_distinct_in_object_hierarchy, self) })
|
||||
Gitlab::ObjectHierarchy.new(ancestors_base)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1305,6 +1305,10 @@ class User < ApplicationRecord
|
|||
save if notification_email_changed? || public_email_changed? || commit_email_changed?
|
||||
end
|
||||
|
||||
def admin_unsubscribe!
|
||||
update_column :admin_email_unsubscribed_at, Time.current
|
||||
end
|
||||
|
||||
def set_projects_limit
|
||||
# `User.select(:id)` raises
|
||||
# `ActiveModel::MissingAttributeError: missing attribute: projects_limit`
|
||||
|
|
|
@ -28,7 +28,7 @@ module Ci
|
|||
groups = ::Group.joins(:runner_namespaces).merge(runner.runner_namespaces)
|
||||
|
||||
hierarchy_groups = Gitlab::ObjectHierarchy
|
||||
.new(groups, options: { use_distinct: ::Feature.enabled?(:use_distinct_in_register_job_object_hierarchy) })
|
||||
.new(groups)
|
||||
.base_and_descendants
|
||||
|
||||
projects = Project.where(namespace_id: hierarchy_groups)
|
||||
|
|
7
app/views/notify/send_admin_notification.html.haml
Normal file
7
app/views/notify/send_admin_notification.html.haml
Normal file
|
@ -0,0 +1,7 @@
|
|||
= simple_format @body
|
||||
|
||||
\----
|
||||
|
||||
%p
|
||||
Don't want to receive updates from GitLab administrators?
|
||||
= link_to 'Unsubscribe', @unsubscribe_url
|
6
app/views/notify/send_admin_notification.text.haml
Normal file
6
app/views/notify/send_admin_notification.text.haml
Normal file
|
@ -0,0 +1,6 @@
|
|||
= h @body
|
||||
|
||||
\-----
|
||||
|
||||
Don't want to receive updates from GitLab administrators?
|
||||
Unsubscribe here: #{@unsubscribe_url}
|
|
@ -0,0 +1,2 @@
|
|||
%p
|
||||
You have been unsubscribed from receiving GitLab administrator notifications.
|
|
@ -0,0 +1 @@
|
|||
You have been unsubscribed from receiving GitLab administrator notifications.
|
11
app/views/users/unsubscribes/show.html.haml
Normal file
11
app/views/users/unsubscribes/show.html.haml
Normal file
|
@ -0,0 +1,11 @@
|
|||
- page_title _("Unsubscribe"), _("Admin Notifications")
|
||||
%h3.page-title Unsubscribe from Admin notifications
|
||||
|
||||
%hr
|
||||
= form_tag unsubscribe_path(Base64.urlsafe_encode64(@email)) do
|
||||
%p
|
||||
Yes, I want to unsubscribe
|
||||
%strong= @email
|
||||
from any further admin emails.
|
||||
.form-actions
|
||||
= submit_tag 'Unsubscribe', class: 'gl-button btn btn-confirm'
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
name: ci_builds_tokens_required_encryption
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/63874
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/333566
|
||||
milestone: '14.0'
|
||||
type: development
|
||||
group: group::pipeline execution
|
||||
default_enabled: false
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
name: codequality_mr_diff
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/47938
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/284140
|
||||
milestone: '13.7'
|
||||
type: development
|
||||
group: group::testing
|
||||
default_enabled: false
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
name: use_distinct_for_all_object_hierarchy
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/57052
|
||||
rollout_issue_url:
|
||||
milestone: '13.11'
|
||||
type: development
|
||||
group: group::database
|
||||
default_enabled: false
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
name: use_distinct_in_object_hierarchy
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/56509
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/324644
|
||||
milestone: '13.10'
|
||||
type: development
|
||||
group: group::optimize
|
||||
default_enabled: false
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
name: use_distinct_in_register_job_object_hierarchy
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/57045
|
||||
rollout_issue_url:
|
||||
milestone: '13.11'
|
||||
type: development
|
||||
group: group::pipeline execution
|
||||
default_enabled: false
|
|
@ -1,5 +1,8 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
get 'unsubscribes/:email', to: 'users/unsubscribes#show', as: :unsubscribe
|
||||
post 'unsubscribes/:email', to: 'users/unsubscribes#create'
|
||||
|
||||
# Allows individual providers to be directed to a chosen controller
|
||||
# Call from inside devise_scope
|
||||
def override_omniauth(provider, controller, path_prefix = '/users/auth')
|
||||
|
|
|
@ -1,51 +1,8 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ScheduleMergeRequestDiffUsersBackgroundMigration < ActiveRecord::Migration[6.1]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
# The number of rows to process in a single migration job.
|
||||
#
|
||||
# The minimum interval for background migrations is two minutes. On staging we
|
||||
# observed we can process roughly 20 000 rows in a minute. Based on the total
|
||||
# number of rows on staging, this translates to a total processing time of
|
||||
# roughly 14 days.
|
||||
#
|
||||
# By using a batch size of 40 000, we maintain a rate of roughly 20 000 rows
|
||||
# per minute, hopefully keeping the total migration time under two weeks;
|
||||
# instead of four weeks.
|
||||
BATCH_SIZE = 40_000
|
||||
|
||||
MIGRATION_NAME = 'MigrateMergeRequestDiffCommitUsers'
|
||||
|
||||
class MergeRequestDiff < ActiveRecord::Base
|
||||
self.table_name = 'merge_request_diffs'
|
||||
end
|
||||
|
||||
def up
|
||||
start = MergeRequestDiff.minimum(:id).to_i
|
||||
max = MergeRequestDiff.maximum(:id).to_i
|
||||
delay = BackgroundMigrationWorker.minimum_interval
|
||||
|
||||
# The table merge_request_diff_commits contains _a lot_ of rows (roughly 400
|
||||
# 000 000 on staging). Iterating a table that large to determine job ranges
|
||||
# would take a while.
|
||||
#
|
||||
# To avoid that overhead, we simply schedule fixed ranges according to the
|
||||
# minimum and maximum IDs. The background migration in turn only processes
|
||||
# rows that actually exist.
|
||||
while start < max
|
||||
stop = start + BATCH_SIZE
|
||||
|
||||
migrate_in(delay, MIGRATION_NAME, [start, stop])
|
||||
|
||||
Gitlab::Database::BackgroundMigrationJob
|
||||
.create!(class_name: MIGRATION_NAME, arguments: [start, stop])
|
||||
|
||||
delay += BackgroundMigrationWorker.minimum_interval
|
||||
start += BATCH_SIZE
|
||||
end
|
||||
# no-op
|
||||
end
|
||||
|
||||
def down
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ResetJobTokenScopeEnabled < ActiveRecord::Migration[6.1]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
def up
|
||||
with_lock_retries do
|
||||
remove_column :project_ci_cd_settings, :job_token_scope_enabled
|
||||
add_column :project_ci_cd_settings, :job_token_scope_enabled, :boolean, default: false, null: false
|
||||
end
|
||||
end
|
||||
|
||||
def down
|
||||
# Irreversible
|
||||
end
|
||||
end
|
|
@ -0,0 +1,58 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class RescheduleMergeRequestDiffUsersBackgroundMigration < ActiveRecord::Migration[6.1]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
# The number of rows to process in a single migration job.
|
||||
#
|
||||
# The minimum interval for background migrations is two minutes. On staging we
|
||||
# observed we can process roughly 20 000 rows in a minute. Based on the total
|
||||
# number of rows on staging, this translates to a total processing time of
|
||||
# roughly 14 days.
|
||||
#
|
||||
# By using a batch size of 40 000, we maintain a rate of roughly 20 000 rows
|
||||
# per minute, hopefully keeping the total migration time under two weeks;
|
||||
# instead of four weeks.
|
||||
BATCH_SIZE = 40_000
|
||||
|
||||
MIGRATION_NAME = 'MigrateMergeRequestDiffCommitUsers'
|
||||
|
||||
class MergeRequestDiff < ActiveRecord::Base
|
||||
self.table_name = 'merge_request_diffs'
|
||||
end
|
||||
|
||||
def up
|
||||
start = MergeRequestDiff.minimum(:id).to_i
|
||||
max = MergeRequestDiff.maximum(:id).to_i
|
||||
delay = BackgroundMigrationWorker.minimum_interval
|
||||
|
||||
Gitlab::Database::BackgroundMigrationJob
|
||||
.where(class_name: MIGRATION_NAME)
|
||||
.delete_all
|
||||
|
||||
# The table merge_request_diff_commits contains _a lot_ of rows (roughly 400
|
||||
# 000 000 on staging). Iterating a table that large to determine job ranges
|
||||
# would take a while.
|
||||
#
|
||||
# To avoid that overhead, we simply schedule fixed ranges according to the
|
||||
# minimum and maximum IDs. The background migration in turn only processes
|
||||
# rows that actually exist.
|
||||
while start < max
|
||||
stop = start + BATCH_SIZE
|
||||
|
||||
migrate_in(delay, MIGRATION_NAME, [start, stop])
|
||||
|
||||
Gitlab::Database::BackgroundMigrationJob
|
||||
.create!(class_name: MIGRATION_NAME, arguments: [start, stop])
|
||||
|
||||
delay += BackgroundMigrationWorker.minimum_interval
|
||||
start += BATCH_SIZE
|
||||
end
|
||||
end
|
||||
|
||||
def down
|
||||
# no-op
|
||||
end
|
||||
end
|
1
db/schema_migrations/20210628124505
Normal file
1
db/schema_migrations/20210628124505
Normal file
|
@ -0,0 +1 @@
|
|||
7add197fec50d8da5bcdbca83115558480668c26ad3a3fefc4ab93c07f34f63a
|
1
db/schema_migrations/20210708130419
Normal file
1
db/schema_migrations/20210708130419
Normal file
|
@ -0,0 +1 @@
|
|||
8545d6575c9dacec6796882677c4403cf3559430518e8709bf390f20717413d7
|
|
@ -14,7 +14,7 @@ Available only in APIv4.
|
|||
## Render an arbitrary Markdown document
|
||||
|
||||
```plaintext
|
||||
POST /api/v4/markdown
|
||||
POST /markdown
|
||||
```
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
|
|
|
@ -4,21 +4,24 @@ group: Ecosystem
|
|||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
# GitLab.com for Jira Cloud app **(FREE SAAS)**
|
||||
# GitLab.com for Jira Cloud app **(FREE)**
|
||||
|
||||
## GitLab.com for Jira Cloud app **(FREE SAAS)**
|
||||
|
||||
You can integrate GitLab.com and Jira Cloud using the
|
||||
[GitLab.com for Jira Cloud](https://marketplace.atlassian.com/apps/1221011/gitlab-com-for-jira-cloud)
|
||||
app in the Atlassian Marketplace. The user configuring GitLab.com for Jira Cloud must have
|
||||
app in the Atlassian Marketplace. The user configuring GitLab.com for Jira Cloud app must have
|
||||
[Maintainer](../../user/permissions.md) permissions in the GitLab.com namespace.
|
||||
|
||||
This integration method supports [smart commits](dvcs.md#smart-commits).
|
||||
|
||||
This method is recommended when using GitLab.com and Jira Cloud because data is
|
||||
synchronized in real-time. The DVCS connector updates data only once per hour.
|
||||
If you are not using both of these environments, use the [Jira DVCS Connector](dvcs.md) method.
|
||||
If you are not using both of these environments, use the [Jira DVCS Connector](dvcs.md) method or
|
||||
[steps to install GitLab.com for Jira Cloud app for self-managed instances](#install-the-gitlabcom-for-jira-cloud-app-for-self-managed-instances).
|
||||
|
||||
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
|
||||
For a walkthrough of the integration with GitLab.com for Jira Cloud, watch
|
||||
For a walkthrough of the integration with GitLab.com for Jira Cloud app, watch
|
||||
[Configure GitLab.com Jira Could Integration using Marketplace App](https://youtu.be/SwR-g1s1zTo) on YouTube.
|
||||
|
||||
1. Go to **Jira Settings > Apps > Find new apps**, then search for GitLab.
|
||||
|
@ -52,10 +55,10 @@ After a namespace is added:
|
|||
|
||||
Support for syncing past branch and commit data [is planned](https://gitlab.com/gitlab-org/gitlab/-/issues/263240).
|
||||
|
||||
## Install the GitLab.com for Jira Cloud application for self-managed instances **(FREE SELF)**
|
||||
## Install the GitLab.com for Jira Cloud app for self-managed instances **(FREE SELF)**
|
||||
|
||||
If your GitLab instance is self-managed, you must follow some
|
||||
extra steps to install the GitLab.com for Jira Cloud application.
|
||||
extra steps to install the GitLab.com for Jira Cloud app.
|
||||
|
||||
Each Jira Cloud application must be installed from a single location. Jira fetches
|
||||
a [manifest file](https://developer.atlassian.com/cloud/jira/platform/connect-app-descriptor/)
|
||||
|
@ -121,9 +124,9 @@ for details.
|
|||
NOTE:
|
||||
DVCS means distributed version control system.
|
||||
|
||||
## Troubleshooting GitLab.com for Jira Cloud
|
||||
## Troubleshooting GitLab.com for Jira Cloud app
|
||||
|
||||
The GitLab.com for Jira Cloud app uses an iframe to add namespaces on the settings page. Some browsers block cross-site cookies. This can lead to a message saying that the user needs to log in on GitLab.com even though the user is already logged in.
|
||||
The GitLab.com for Jira Cloud app uses an iframe to add namespaces on the settings page. Some browsers block cross-site cookies, which can lead to a message saying that the user needs to log in on GitLab.com even though the user is already logged in.
|
||||
|
||||
> "You need to sign in or sign up before continuing."
|
||||
|
||||
|
|
|
@ -31,11 +31,6 @@ This integration connects all GitLab projects to projects in the Jira instance i
|
|||
including the projects in its subgroups.
|
||||
- A personal namespace: Connects the projects in that personal namespace to Jira.
|
||||
|
||||
This differs from the [Jira integration](index.md),
|
||||
where the mapping is between one GitLab project and the entire Jira instance.
|
||||
You can install both integrations to take advantage of both sets of features.
|
||||
A [feature comparison](index.md#direct-feature-comparison) is available.
|
||||
|
||||
## Use the integration
|
||||
|
||||
After the integration is [set up on GitLab and Jira](#configure-the-integration), you can:
|
||||
|
@ -44,7 +39,8 @@ After the integration is [set up on GitLab and Jira](#configure-the-integration)
|
|||
commit messages, and merge request titles.
|
||||
- See the linked branches, commits, and merge requests in Jira issues:
|
||||
|
||||
Merge requests are called "pull requests" in Jira issues.
|
||||
At this time, merge requests are called "pull requests" in Jira issues.
|
||||
This name may change in a future Jira release.
|
||||
|
||||
Select the links to see your GitLab repository data.
|
||||
|
||||
|
@ -72,13 +68,13 @@ To simplify administration, we recommend that a GitLab group maintainer or group
|
|||
|
||||
| Jira usage | GitLab.com customers need | GitLab self-managed customers need |
|
||||
|------------|---------------------------|------------------------------------|
|
||||
| [Atlassian cloud](https://www.atlassian.com/cloud) | The [GitLab.com for Jira Cloud](https://marketplace.atlassian.com/apps/1221011/gitlab-com-for-jira-cloud?hosting=cloud&tab=overview) application installed from the [Atlassian Marketplace](https://marketplace.atlassian.com). This offers real-time sync between GitLab and Jira. | The [GitLab.com for Jira Cloud](https://marketplace.atlassian.com/apps/1221011/gitlab-com-for-jira-cloud?hosting=cloud&tab=overview), using a workaround process. See the documentation for [installing the GitLab Jira Cloud application for self-managed instances](connect-app.md#install-the-gitlabcom-for-jira-cloud-application-for-self-managed-instances) for more information. |
|
||||
| [Atlassian cloud](https://www.atlassian.com/cloud) | The [GitLab.com for Jira Cloud](https://marketplace.atlassian.com/apps/1221011/gitlab-com-for-jira-cloud?hosting=cloud&tab=overview) application installed from the [Atlassian Marketplace](https://marketplace.atlassian.com). This offers real-time sync between GitLab and Jira. | The [GitLab.com for Jira Cloud](https://marketplace.atlassian.com/apps/1221011/gitlab-com-for-jira-cloud?hosting=cloud&tab=overview), using a workaround process. See the documentation for [installing the GitLab Jira Cloud application for self-managed instances](connect-app.md#install-the-gitlabcom-for-jira-cloud-app-for-self-managed-instances) for more information. |
|
||||
| Your own server | The Jira DVCS (distributed version control system) connector. This syncs data hourly. | The [Jira DVCS Connector](dvcs.md). |
|
||||
|
||||
Each GitLab project can be configured to connect to an entire Jira instance. That means after
|
||||
configuration, one GitLab project can interact with all Jira projects in that instance. For:
|
||||
|
||||
- The [view Jira issues](issues.md#view-jira-issues) feature, you must associate a GitLab project with a
|
||||
- The [view Jira issues](issues.md#view-jira-issues) feature **(PREMIUM)**, you must associate a GitLab project with a
|
||||
specific Jira project.
|
||||
- Other features, you do not have to explicitly associate a GitLab project with any single Jira
|
||||
project.
|
||||
|
@ -86,16 +82,16 @@ configuration, one GitLab project can interact with all Jira projects in that in
|
|||
If you have a single Jira instance, you can pre-fill the settings. For more information, read the
|
||||
documentation for [central administration of project integrations](../../user/admin_area/settings/project_integration_management.md).
|
||||
|
||||
To enable the Jira service in GitLab, you must:
|
||||
To enable the integration in GitLab, you must:
|
||||
|
||||
1. [Configure the project in Jira](dvcs.md#configure-jira-for-dvcs).
|
||||
1. [Configure the project in Jira](index.md#jira-integration).
|
||||
The supported Jira versions are `v6.x`, `v7.x`, and `v8.x`.
|
||||
1. [Enter the correct values in GitLab](#configure-gitlab).
|
||||
|
||||
### Configure GitLab
|
||||
|
||||
To enable the integration in your GitLab project, after you
|
||||
[configure your Jira project](dvcs.md#configure-jira-for-dvcs):
|
||||
[configure your Jira project](index.md#jira-integration):
|
||||
|
||||
1. Ensure your GitLab installation does not use a relative URL, as described in
|
||||
[Limitations](#limitations).
|
||||
|
@ -114,12 +110,14 @@ To enable the integration in your GitLab project, after you
|
|||
this GitLab project, such as `https://jira.example.com`.
|
||||
- **Jira API URL**: The base URL to the Jira instance API, such as `https://jira-api.example.com`.
|
||||
Defaults to the **Web URL** value if not set. Leave blank if using **Jira on Atlassian cloud**.
|
||||
- **Username or Email**: Created when you [configured Jira](dvcs.md#configure-jira-for-dvcs).
|
||||
- **Username or Email**:
|
||||
For **Jira Server**, use `username`. For **Jira on Atlassian cloud**, use `email`.
|
||||
- **Password/API token**: Created when you [configured Jira](dvcs.md#configure-jira-for-dvcs).
|
||||
See [authentication in Jira](index.md#authentication-in-jira).
|
||||
- **Password/API token**:
|
||||
Use `password` for **Jira Server** or `API token` for **Jira on Atlassian cloud**.
|
||||
1. To enable users to view Jira issues inside the GitLab project, select **Enable Jira issues** and
|
||||
enter a Jira project key. **(PREMIUM)**
|
||||
See [authentication in Jira](index.md#authentication-in-jira).
|
||||
1. To enable users to view Jira issues inside the GitLab project **(PREMIUM)**, select **Enable Jira issues** and
|
||||
enter a Jira project key.
|
||||
|
||||
You can display issues only from a single Jira project in a given GitLab project.
|
||||
|
||||
|
@ -127,7 +125,7 @@ To enable the integration in your GitLab project, after you
|
|||
If you enable Jira issues with this setting, all users with access to this GitLab project
|
||||
can view all issues from the specified Jira project.
|
||||
|
||||
1. To enable issue creation for vulnerabilities, select **Enable Jira issues creation from vulnerabilities**.
|
||||
1. To enable issue creation for vulnerabilities **(ULTIMATE)**, select **Enable Jira issues creation from vulnerabilities**.
|
||||
1. Select the **Jira issue type**. If the dropdown is empty, select refresh (**{retry}**) and try again.
|
||||
1. To verify the Jira connection is working, select **Test settings**.
|
||||
1. Select **Save changes**.
|
||||
|
|
|
@ -7,9 +7,9 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
# Jira DVCS connector **(FREE)**
|
||||
|
||||
Use the Jira DVCS (distributed version control system) connector if you self-host
|
||||
either your Jira instance or your GitLab instance, and you want to sync information
|
||||
between them. If you use Jira Cloud and GitLab.com, you should use the
|
||||
[GitLab for Jira app](connect-app.md) unless you specifically need the DVCS connector.
|
||||
your Jira instance, and you want to sync information
|
||||
between GitLab and Jira. If you use Jira Cloud and GitLab.com, you should use the
|
||||
[GitLab.com for Jira Cloud app](connect-app.md) unless you specifically need the DVCS connector.
|
||||
|
||||
When you configure the Jira DVCS connector, make sure your GitLab and Jira instances
|
||||
are accessible.
|
||||
|
@ -67,8 +67,9 @@ your integration.
|
|||
|
||||
1. In GitLab, [create a user](../../user/profile/account/create_accounts.md) for Jira to
|
||||
use to connect to GitLab. For Jira to access all projects,
|
||||
a user with [Administrator](../../user/permissions.md) permissions must
|
||||
create the user.
|
||||
a user with [administrator](../../user/permissions.md) permissions must
|
||||
create the user with administrator permissions.
|
||||
1. Sign in as the `jira` user.
|
||||
1. In the top right corner, click the account's avatar, and select **Edit profile**.
|
||||
1. In the left sidebar, select **Applications**.
|
||||
1. In the **Name** field, enter a descriptive name for the integration, such as `Jira`.
|
||||
|
@ -90,9 +91,6 @@ your integration.
|
|||
|
||||
## Configure Jira for DVCS
|
||||
|
||||
If you use Jira Cloud, use the [GitLab for Jira app](connect-app.md)
|
||||
unless you specifically need the DVCS Connector.
|
||||
|
||||
Configure this connection when you want to import all GitLab commits and branches,
|
||||
for the groups you specify, into Jira. This import takes a few minutes and, after
|
||||
it completes, refreshes every 60 minutes:
|
||||
|
|
|
@ -7,12 +7,12 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
# Jira integrations **(FREE)**
|
||||
|
||||
If your organization uses [Jira](https://www.atlassian.com/software/jira) issues,
|
||||
you can [migrate](../../user/project/import/jira.md) your issues from Jira and work
|
||||
you can [migrate your issues from Jira](../../user/project/import/jira.md) **(PREMIUM)** and work
|
||||
exclusively in GitLab. However, if you'd like to continue to use Jira, you can
|
||||
integrate it with GitLab. GitLab offers two types of Jira integrations, and you
|
||||
can use one or both depending on the capabilities you need.
|
||||
can use one or both depending on the capabilities you need. It is recommended that you enable both.
|
||||
|
||||
## Compare Jira integrations
|
||||
## Compare integrations
|
||||
|
||||
After you set up one or both of these integrations, you can cross-reference activity
|
||||
in your GitLab project with any of your projects in Jira.
|
||||
|
@ -20,10 +20,13 @@ in your GitLab project with any of your projects in Jira.
|
|||
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
|
||||
For an overview, see [Agile Management - GitLab-Jira Basic Integration](https://www.youtube.com/watch?v=fWvwkx5_00E&feature=youtu.be).
|
||||
|
||||
### Per-project Jira integration
|
||||
### Jira integration
|
||||
|
||||
This integration connects a single GitLab project to a Jira instance. The Jira instance
|
||||
can be hosted by you or in [Atlassian cloud](https://www.atlassian.com/cloud):
|
||||
This integration connects one or more GitLab project to a Jira instance. The Jira instance
|
||||
can be hosted by you or in [Atlassian cloud](https://www.atlassian.com/cloud).
|
||||
The supported Jira versions are `v6.x`, `v7.x`, and `v8.x`.
|
||||
To simplify administration, we recommend that a GitLab group maintainer or group owner
|
||||
(or instance administrator in the case of self-managed GitLab) set up the integration.
|
||||
|
||||
- *If your installation uses Jira Cloud,* use the
|
||||
[GitLab for Jira app](connect-app.md).
|
||||
|
@ -48,7 +51,7 @@ displays in the [development panel](https://support.atlassian.com/jira-software-
|
|||
| Add Jira time tracking to an issue. | No. | Yes. Time can be specified using Jira Smart Commits. |
|
||||
| Use a Git commit or merge request to transition or close a Jira issue. | Yes. Only a single transition type, typically configured to close the issue by setting it to Done. | Yes. Transition to any state using Jira Smart Commits. |
|
||||
| Display a list of Jira issues. | Yes. **(PREMIUM)** | No. |
|
||||
| Create a Jira issue from a vulnerability or finding. **(ULTIMATE)** | Yes. | No. |
|
||||
| Create a Jira issue from a vulnerability or finding. | Yes. **(ULTIMATE)** | No. |
|
||||
|
||||
## Authentication in Jira
|
||||
|
||||
|
@ -64,10 +67,10 @@ The process for configuring Jira depends on whether you host Jira on your own se
|
|||
|
||||
## Privacy considerations
|
||||
|
||||
If you integrate a private GitLab project with Jira using the [**Per-project Jira integration**](#per-project-jira-integration),
|
||||
If you integrate a private GitLab project with Jira using the [**Jira integration**](#jira-integration),
|
||||
actions in GitLab issues and merge requests linked to a Jira issue leak information
|
||||
about the private project to non-administrator Jira users. If your installation uses Jira Cloud,
|
||||
you can use the [GitLab for Jira app](connect-app.md) to avoid this risk.
|
||||
you can use the [GitLab.com for Jira Cloud app](connect-app.md) to avoid this risk.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
|
||||
# Jira integration issue management **(FREE)**
|
||||
|
||||
Integrating issue management with Jira requires you to [configure Jira](development_panel.md#configure-the-integration)
|
||||
Integrating issue management with Jira requires you to [configure Jira](index.md#jira-integration)
|
||||
and [enable the Jira service](development_panel.md#configure-gitlab) in GitLab.
|
||||
After you configure and enable the integration, you can reference and close Jira
|
||||
issues by mentioning the Jira ID in GitLab commits and merge requests.
|
||||
|
@ -122,7 +122,7 @@ Issues are grouped into tabs based on their
|
|||
- **Closed** tab: All issues with a Jira status categorized as Done.
|
||||
- **All** tab: All issues of any status.
|
||||
|
||||
## Search and filter the issues list
|
||||
### Search and filter the issues list **(PREMIUM)**
|
||||
|
||||
To refine the list of issues, use the search bar to search for any text
|
||||
contained in an issue summary (title) or description. Use any combination
|
||||
|
|
|
@ -59,6 +59,7 @@ See also the Code Climate list of [Supported Languages for Maintainability](http
|
|||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/267612) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 13.11.
|
||||
> - [Deployed behind a feature flag](../../../user/feature_flags.md), disabled by default.
|
||||
> - [Enabled by default](https://gitlab.com/gitlab-org/gitlab/-/issues/284140) in GitLab 13.12.
|
||||
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/284140) in GitLab 14.1.
|
||||
> - [Feature enhanced](https://gitlab.com/gitlab-org/gitlab/-/issues/2526) in GitLab 14.0.
|
||||
|
||||
Changes to files in merge requests can cause Code Quality to fall if merged. In these cases,
|
||||
|
|
|
@ -172,19 +172,17 @@ module Gitlab
|
|||
# Updates rows in merge_request_diff_commits with their new
|
||||
# commit_author_id and committer_id values.
|
||||
def update_commit_rows(to_update, user_mapping)
|
||||
MergeRequestDiffCommitUser.transaction do
|
||||
to_update.each_slice(UPDATES_PER_QUERY) do |slice|
|
||||
updates = {}
|
||||
to_update.each_slice(UPDATES_PER_QUERY) do |slice|
|
||||
updates = {}
|
||||
|
||||
slice.each do |(diff_id, order), (author, committer)|
|
||||
author_id = user_mapping[author]&.id
|
||||
committer_id = user_mapping[committer]&.id
|
||||
slice.each do |(diff_id, order), (author, committer)|
|
||||
author_id = user_mapping[author]&.id
|
||||
committer_id = user_mapping[committer]&.id
|
||||
|
||||
updates[[diff_id, order]] = [author_id, committer_id]
|
||||
end
|
||||
|
||||
bulk_update_commit_rows(updates)
|
||||
updates[[diff_id, order]] = [author_id, committer_id]
|
||||
end
|
||||
|
||||
bulk_update_commit_rows(updates)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -22,17 +22,9 @@ module Gitlab
|
|||
::Feature.enabled?(:ci_trace_log_invalid_chunks, project, type: :ops, default_enabled: false)
|
||||
end
|
||||
|
||||
def self.display_quality_on_mr_diff?(project)
|
||||
::Feature.enabled?(:codequality_mr_diff, project, default_enabled: :yaml)
|
||||
end
|
||||
|
||||
def self.gldropdown_tags_enabled?
|
||||
::Feature.enabled?(:gldropdown_tags, default_enabled: :yaml)
|
||||
end
|
||||
|
||||
def self.require_builds_token_encryption?
|
||||
Feature.enabled?(:ci_builds_tokens_required_encryption, default_enabled: :yaml)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -65,32 +65,9 @@ module Gitlab
|
|||
# Note: By default the order is breadth-first
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def base_and_ancestors(upto: nil, hierarchy_order: nil)
|
||||
if use_distinct?
|
||||
expose_depth = hierarchy_order.present?
|
||||
hierarchy_order ||= :asc
|
||||
|
||||
# if hierarchy_order is given, the calculated `depth` should be present in SELECT
|
||||
if expose_depth
|
||||
recursive_query = base_and_ancestors_cte(upto, hierarchy_order).apply_to(unscoped_model.all).distinct
|
||||
read_only(unscoped_model.from(Arel::Nodes::As.new(recursive_query.arel, objects_table)).order(depth: hierarchy_order))
|
||||
else
|
||||
recursive_query = base_and_ancestors_cte(upto).apply_to(unscoped_model.all)
|
||||
|
||||
if skip_ordering?
|
||||
recursive_query = recursive_query.distinct
|
||||
else
|
||||
recursive_query = recursive_query.reselect(*recursive_query.arel.projections, 'ROW_NUMBER() OVER () as depth').distinct
|
||||
recursive_query = unscoped_model.from(Arel::Nodes::As.new(recursive_query.arel, objects_table))
|
||||
recursive_query = remove_depth_and_maintain_order(recursive_query, hierarchy_order: hierarchy_order)
|
||||
end
|
||||
|
||||
read_only(recursive_query)
|
||||
end
|
||||
else
|
||||
recursive_query = base_and_ancestors_cte(upto, hierarchy_order).apply_to(unscoped_model.all)
|
||||
recursive_query = recursive_query.order(depth: hierarchy_order) if hierarchy_order
|
||||
read_only(recursive_query)
|
||||
end
|
||||
recursive_query = base_and_ancestors_cte(upto, hierarchy_order).apply_to(unscoped_model.all)
|
||||
recursive_query = recursive_query.order(depth: hierarchy_order) if hierarchy_order
|
||||
read_only(recursive_query)
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
|
||||
|
@ -101,27 +78,7 @@ module Gitlab
|
|||
# and incremented as we go down the descendant tree
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def base_and_descendants(with_depth: false)
|
||||
if use_distinct?
|
||||
# Always calculate `depth`, remove it later if with_depth is false
|
||||
if with_depth
|
||||
base_cte = base_and_descendants_cte(with_depth: true).apply_to(unscoped_model.all).distinct
|
||||
read_only(unscoped_model.from(Arel::Nodes::As.new(base_cte.arel, objects_table)).order(depth: :asc))
|
||||
else
|
||||
base_cte = base_and_descendants_cte.apply_to(unscoped_model.all)
|
||||
|
||||
if skip_ordering?
|
||||
base_cte = base_cte.distinct
|
||||
else
|
||||
base_cte = base_cte.reselect(*base_cte.arel.projections, 'ROW_NUMBER() OVER () as depth').distinct
|
||||
base_cte = unscoped_model.from(Arel::Nodes::As.new(base_cte.arel, objects_table))
|
||||
base_cte = remove_depth_and_maintain_order(base_cte, hierarchy_order: :asc)
|
||||
end
|
||||
|
||||
read_only(base_cte)
|
||||
end
|
||||
else
|
||||
read_only(base_and_descendants_cte(with_depth: with_depth).apply_to(unscoped_model.all))
|
||||
end
|
||||
read_only(base_and_descendants_cte(with_depth: with_depth).apply_to(unscoped_model.all))
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
|
||||
|
@ -158,11 +115,6 @@ module Gitlab
|
|||
ancestors_scope = unscoped_model.from(ancestors_table)
|
||||
descendants_scope = unscoped_model.from(descendants_table)
|
||||
|
||||
if use_distinct?
|
||||
ancestors_scope = ancestors_scope.distinct
|
||||
descendants_scope = descendants_scope.distinct
|
||||
end
|
||||
|
||||
relation = unscoped_model
|
||||
.with
|
||||
.recursive(ancestors.to_arel, descendants.to_arel)
|
||||
|
@ -177,23 +129,6 @@ module Gitlab
|
|||
|
||||
private
|
||||
|
||||
# Use distinct on the Namespace queries to avoid bad planner behavior in PG11.
|
||||
def use_distinct?
|
||||
return unless model <= Namespace
|
||||
# Global use_distinct_for_all_object_hierarchy takes precedence over use_distinct_in_object_hierarchy
|
||||
return true if Feature.enabled?(:use_distinct_for_all_object_hierarchy)
|
||||
return options[:use_distinct] if options.key?(:use_distinct)
|
||||
|
||||
false
|
||||
end
|
||||
|
||||
# Skips the extra ordering when using distinct on the namespace queries
|
||||
def skip_ordering?
|
||||
return options[:skip_ordering] if options.key?(:skip_ordering)
|
||||
|
||||
false
|
||||
end
|
||||
|
||||
# Remove the extra `depth` field using an INNER JOIN to avoid breaking UNION queries
|
||||
# and ordering the rows based on the `depth` column to maintain the row order.
|
||||
#
|
||||
|
|
36
spec/controllers/users/unsubscribes_controller_spec.rb
Normal file
36
spec/controllers/users/unsubscribes_controller_spec.rb
Normal file
|
@ -0,0 +1,36 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Users::UnsubscribesController do
|
||||
let!(:user) { create :user, email: 'me@example.com' }
|
||||
|
||||
describe "show" do
|
||||
it "responds with success" do
|
||||
get :show, params: { email: Base64.urlsafe_encode64('me@example.com') }
|
||||
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
it "behaves the same if email address isn't known in the system" do
|
||||
get :show, params: { email: Base64.urlsafe_encode64('i@dont_exists.com') }
|
||||
|
||||
assert_response :success
|
||||
end
|
||||
end
|
||||
|
||||
describe "create" do
|
||||
it "unsubscribes the connected user" do
|
||||
post :create, params: { email: Base64.urlsafe_encode64('me@example.com') }
|
||||
|
||||
assert user.reload.admin_email_unsubscribed_at
|
||||
end
|
||||
|
||||
# Don't tell if the email does not exists
|
||||
it "behaves the same if email address isn't known in the system" do
|
||||
post :create, params: { email: Base64.urlsafe_encode64('i@dont_exists.com') }
|
||||
|
||||
assert_response :redirect
|
||||
end
|
||||
end
|
||||
end
|
|
@ -28,14 +28,25 @@ RSpec.describe 'Graphql Field feature flags' do
|
|||
end
|
||||
end
|
||||
|
||||
it 'returns the value when feature is enabled' do
|
||||
expect(subject['item']).to eq('name' => test_object.name)
|
||||
it 'checks YAML definition for default_enabled' do
|
||||
# Exception is indicative of a check for YAML definition
|
||||
expect { subject }.to raise_error(Feature::InvalidFeatureFlagError, /The feature flag YAML definition for '#{feature_flag}' does not exist/)
|
||||
end
|
||||
|
||||
it 'returns nil when the feature is disabled' do
|
||||
stub_feature_flags(feature_flag => false)
|
||||
context 'skipping YAML check' do
|
||||
before do
|
||||
skip_default_enabled_yaml_check
|
||||
end
|
||||
|
||||
expect(subject).to be_nil
|
||||
it 'returns the value when feature is enabled' do
|
||||
expect(subject['item']).to eq('name' => test_object.name)
|
||||
end
|
||||
|
||||
it 'returns nil when the feature is disabled' do
|
||||
stub_feature_flags(feature_flag => false)
|
||||
|
||||
expect(subject).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -130,14 +130,25 @@ RSpec.describe Types::BaseField do
|
|||
skip_feature_flags_yaml_validation
|
||||
end
|
||||
|
||||
it 'returns false if the feature is not enabled' do
|
||||
stub_feature_flags(flag => false)
|
||||
|
||||
expect(field.visible?(context)).to eq(false)
|
||||
it 'checks YAML definition for default_enabled' do
|
||||
# Exception is indicative of a check for YAML definition
|
||||
expect { field.visible?(context) }.to raise_error(Feature::InvalidFeatureFlagError, /The feature flag YAML definition for '#{flag}' does not exist/)
|
||||
end
|
||||
|
||||
it 'returns true if the feature is enabled' do
|
||||
expect(field.visible?(context)).to eq(true)
|
||||
context 'skipping YAML check' do
|
||||
before do
|
||||
skip_default_enabled_yaml_check
|
||||
end
|
||||
|
||||
it 'returns false if the feature is not enabled' do
|
||||
stub_feature_flags(flag => false)
|
||||
|
||||
expect(field.visible?(context)).to eq(false)
|
||||
end
|
||||
|
||||
it 'returns true if the feature is enabled' do
|
||||
expect(field.visible?(context)).to eq(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -9,265 +9,178 @@ RSpec.describe Gitlab::ObjectHierarchy do
|
|||
|
||||
let(:options) { {} }
|
||||
|
||||
shared_context 'Gitlab::ObjectHierarchy test cases' do
|
||||
describe '#base_and_ancestors' do
|
||||
describe '#base_and_ancestors' do
|
||||
let(:relation) do
|
||||
described_class.new(Group.where(id: child2.id), options: options).base_and_ancestors
|
||||
end
|
||||
|
||||
it 'includes the base rows' do
|
||||
expect(relation).to include(child2)
|
||||
end
|
||||
|
||||
it 'includes all of the ancestors' do
|
||||
expect(relation).to include(parent, child1)
|
||||
end
|
||||
|
||||
it 'can find ancestors upto a certain level' do
|
||||
relation = described_class.new(Group.where(id: child2), options: options).base_and_ancestors(upto: child1)
|
||||
|
||||
expect(relation).to contain_exactly(child2)
|
||||
end
|
||||
|
||||
it 'uses ancestors_base #initialize argument' do
|
||||
relation = described_class.new(Group.where(id: child2.id), Group.none, options: options).base_and_ancestors
|
||||
|
||||
expect(relation).to include(parent, child1, child2)
|
||||
end
|
||||
|
||||
it 'does not allow the use of #update_all' do
|
||||
expect { relation.update_all(share_with_group_lock: false) }
|
||||
.to raise_error(ActiveRecord::ReadOnlyRecord)
|
||||
end
|
||||
|
||||
describe 'hierarchy_order option' do
|
||||
let(:relation) do
|
||||
described_class.new(Group.where(id: child2.id), options: options).base_and_ancestors
|
||||
described_class.new(Group.where(id: child2.id), options: options).base_and_ancestors(hierarchy_order: hierarchy_order)
|
||||
end
|
||||
|
||||
it 'includes the base rows' do
|
||||
expect(relation).to include(child2)
|
||||
end
|
||||
context ':asc' do
|
||||
let(:hierarchy_order) { :asc }
|
||||
|
||||
it 'includes all of the ancestors' do
|
||||
expect(relation).to include(parent, child1)
|
||||
end
|
||||
|
||||
it 'can find ancestors upto a certain level' do
|
||||
relation = described_class.new(Group.where(id: child2), options: options).base_and_ancestors(upto: child1)
|
||||
|
||||
expect(relation).to contain_exactly(child2)
|
||||
end
|
||||
|
||||
it 'uses ancestors_base #initialize argument' do
|
||||
relation = described_class.new(Group.where(id: child2.id), Group.none, options: options).base_and_ancestors
|
||||
|
||||
expect(relation).to include(parent, child1, child2)
|
||||
end
|
||||
|
||||
it 'does not allow the use of #update_all' do
|
||||
expect { relation.update_all(share_with_group_lock: false) }
|
||||
.to raise_error(ActiveRecord::ReadOnlyRecord)
|
||||
end
|
||||
|
||||
describe 'hierarchy_order option' do
|
||||
let(:relation) do
|
||||
described_class.new(Group.where(id: child2.id), options: options).base_and_ancestors(hierarchy_order: hierarchy_order)
|
||||
end
|
||||
|
||||
context ':asc' do
|
||||
let(:hierarchy_order) { :asc }
|
||||
|
||||
it 'orders by child to parent' do
|
||||
expect(relation).to eq([child2, child1, parent])
|
||||
end
|
||||
end
|
||||
|
||||
context ':desc' do
|
||||
let(:hierarchy_order) { :desc }
|
||||
|
||||
it 'orders by parent to child' do
|
||||
expect(relation).to eq([parent, child1, child2])
|
||||
end
|
||||
it 'orders by child to parent' do
|
||||
expect(relation).to eq([child2, child1, parent])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#base_and_descendants' do
|
||||
let(:relation) do
|
||||
described_class.new(Group.where(id: parent.id), options: options).base_and_descendants
|
||||
end
|
||||
context ':desc' do
|
||||
let(:hierarchy_order) { :desc }
|
||||
|
||||
it 'includes the base rows' do
|
||||
expect(relation).to include(parent)
|
||||
end
|
||||
|
||||
it 'includes all the descendants' do
|
||||
expect(relation).to include(child1, child2)
|
||||
end
|
||||
|
||||
it 'uses descendants_base #initialize argument' do
|
||||
relation = described_class.new(Group.none, Group.where(id: parent.id), options: options).base_and_descendants
|
||||
|
||||
expect(relation).to include(parent, child1, child2)
|
||||
end
|
||||
|
||||
it 'does not allow the use of #update_all' do
|
||||
expect { relation.update_all(share_with_group_lock: false) }
|
||||
.to raise_error(ActiveRecord::ReadOnlyRecord)
|
||||
end
|
||||
|
||||
context 'when with_depth is true' do
|
||||
let(:relation) do
|
||||
described_class.new(Group.where(id: parent.id), options: options).base_and_descendants(with_depth: true)
|
||||
end
|
||||
|
||||
it 'includes depth in the results' do
|
||||
object_depths = {
|
||||
parent.id => 1,
|
||||
child1.id => 2,
|
||||
child2.id => 3
|
||||
}
|
||||
|
||||
relation.each do |object|
|
||||
expect(object.depth).to eq(object_depths[object.id])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#descendants' do
|
||||
it 'includes only the descendants' do
|
||||
relation = described_class.new(Group.where(id: parent), options: options).descendants
|
||||
|
||||
expect(relation).to contain_exactly(child1, child2)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#max_descendants_depth' do
|
||||
subject { described_class.new(base_relation, options: options).max_descendants_depth }
|
||||
|
||||
context 'when base relation is empty' do
|
||||
let(:base_relation) { Group.where(id: nil) }
|
||||
|
||||
it { expect(subject).to be_nil }
|
||||
end
|
||||
|
||||
context 'when base has no children' do
|
||||
let(:base_relation) { Group.where(id: child2) }
|
||||
|
||||
it { expect(subject).to eq(1) }
|
||||
end
|
||||
|
||||
context 'when base has grandchildren' do
|
||||
let(:base_relation) { Group.where(id: parent) }
|
||||
|
||||
it { expect(subject).to eq(3) }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#ancestors' do
|
||||
it 'includes only the ancestors' do
|
||||
relation = described_class.new(Group.where(id: child2), options: options).ancestors
|
||||
|
||||
expect(relation).to contain_exactly(child1, parent)
|
||||
end
|
||||
|
||||
it 'can find ancestors upto a certain level' do
|
||||
relation = described_class.new(Group.where(id: child2), options: options).ancestors(upto: child1)
|
||||
|
||||
expect(relation).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
describe '#all_objects' do
|
||||
let(:relation) do
|
||||
described_class.new(Group.where(id: child1.id), options: options).all_objects
|
||||
end
|
||||
|
||||
it 'includes the base rows' do
|
||||
expect(relation).to include(child1)
|
||||
end
|
||||
|
||||
it 'includes the ancestors' do
|
||||
expect(relation).to include(parent)
|
||||
end
|
||||
|
||||
it 'includes the descendants' do
|
||||
expect(relation).to include(child2)
|
||||
end
|
||||
|
||||
it 'uses ancestors_base #initialize argument for ancestors' do
|
||||
relation = described_class.new(Group.where(id: child1.id), Group.where(id: non_existing_record_id), options: options).all_objects
|
||||
|
||||
expect(relation).to include(parent)
|
||||
end
|
||||
|
||||
it 'uses descendants_base #initialize argument for descendants' do
|
||||
relation = described_class.new(Group.where(id: non_existing_record_id), Group.where(id: child1.id), options: options).all_objects
|
||||
|
||||
expect(relation).to include(child2)
|
||||
end
|
||||
|
||||
it 'does not allow the use of #update_all' do
|
||||
expect { relation.update_all(share_with_group_lock: false) }
|
||||
.to raise_error(ActiveRecord::ReadOnlyRecord)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the use_distinct_in_object_hierarchy feature flag is enabled' do
|
||||
before do
|
||||
stub_feature_flags(use_distinct_in_object_hierarchy: true)
|
||||
stub_feature_flags(use_distinct_for_all_object_hierarchy: false)
|
||||
end
|
||||
|
||||
it_behaves_like 'Gitlab::ObjectHierarchy test cases'
|
||||
|
||||
it 'calls DISTINCT' do
|
||||
expect(child2.self_and_ancestors.to_sql).to include("DISTINCT")
|
||||
end
|
||||
|
||||
context 'when use_traversal_ids feature flag is enabled' do
|
||||
it 'does not call DISTINCT' do
|
||||
expect(parent.self_and_descendants.to_sql).not_to include("DISTINCT")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when use_traversal_ids feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(use_traversal_ids: false)
|
||||
end
|
||||
|
||||
it 'calls DISTINCT' do
|
||||
expect(parent.self_and_descendants.to_sql).to include("DISTINCT")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the use_distinct_for_all_object_hierarchy feature flag is enabled' do
|
||||
before do
|
||||
stub_feature_flags(use_distinct_in_object_hierarchy: false)
|
||||
stub_feature_flags(use_distinct_for_all_object_hierarchy: true)
|
||||
end
|
||||
|
||||
it_behaves_like 'Gitlab::ObjectHierarchy test cases'
|
||||
|
||||
it 'calls DISTINCT' do
|
||||
expect(child2.self_and_ancestors.to_sql).to include("DISTINCT")
|
||||
end
|
||||
|
||||
context 'when use_traversal_ids feature flag is enabled' do
|
||||
it 'does not call DISTINCT' do
|
||||
expect(parent.self_and_descendants.to_sql).not_to include("DISTINCT")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when use_traversal_ids feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(use_traversal_ids: false)
|
||||
end
|
||||
|
||||
it 'calls DISTINCT' do
|
||||
expect(parent.self_and_descendants.to_sql).to include("DISTINCT")
|
||||
end
|
||||
|
||||
context 'when the skip_ordering option is set' do
|
||||
let(:options) { { skip_ordering: true } }
|
||||
|
||||
it_behaves_like 'Gitlab::ObjectHierarchy test cases'
|
||||
|
||||
it 'does not include ROW_NUMBER()' do
|
||||
query = described_class.new(Group.where(id: parent.id), options: options).base_and_descendants.to_sql
|
||||
|
||||
expect(query).to include("DISTINCT")
|
||||
expect(query).not_to include("ROW_NUMBER()")
|
||||
it 'orders by parent to child' do
|
||||
expect(relation).to eq([parent, child1, child2])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the use_distinct_in_object_hierarchy feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(use_distinct_in_object_hierarchy: false)
|
||||
stub_feature_flags(use_distinct_for_all_object_hierarchy: false)
|
||||
describe '#base_and_descendants' do
|
||||
let(:relation) do
|
||||
described_class.new(Group.where(id: parent.id), options: options).base_and_descendants
|
||||
end
|
||||
|
||||
it_behaves_like 'Gitlab::ObjectHierarchy test cases'
|
||||
it 'includes the base rows' do
|
||||
expect(relation).to include(parent)
|
||||
end
|
||||
|
||||
it 'does not call DISTINCT' do
|
||||
expect(parent.self_and_descendants.to_sql).not_to include("DISTINCT")
|
||||
expect(child2.self_and_ancestors.to_sql).not_to include("DISTINCT")
|
||||
it 'includes all the descendants' do
|
||||
expect(relation).to include(child1, child2)
|
||||
end
|
||||
|
||||
it 'uses descendants_base #initialize argument' do
|
||||
relation = described_class.new(Group.none, Group.where(id: parent.id), options: options).base_and_descendants
|
||||
|
||||
expect(relation).to include(parent, child1, child2)
|
||||
end
|
||||
|
||||
it 'does not allow the use of #update_all' do
|
||||
expect { relation.update_all(share_with_group_lock: false) }
|
||||
.to raise_error(ActiveRecord::ReadOnlyRecord)
|
||||
end
|
||||
|
||||
context 'when with_depth is true' do
|
||||
let(:relation) do
|
||||
described_class.new(Group.where(id: parent.id), options: options).base_and_descendants(with_depth: true)
|
||||
end
|
||||
|
||||
it 'includes depth in the results' do
|
||||
object_depths = {
|
||||
parent.id => 1,
|
||||
child1.id => 2,
|
||||
child2.id => 3
|
||||
}
|
||||
|
||||
relation.each do |object|
|
||||
expect(object.depth).to eq(object_depths[object.id])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#descendants' do
|
||||
it 'includes only the descendants' do
|
||||
relation = described_class.new(Group.where(id: parent), options: options).descendants
|
||||
|
||||
expect(relation).to contain_exactly(child1, child2)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#max_descendants_depth' do
|
||||
subject { described_class.new(base_relation, options: options).max_descendants_depth }
|
||||
|
||||
context 'when base relation is empty' do
|
||||
let(:base_relation) { Group.where(id: nil) }
|
||||
|
||||
it { expect(subject).to be_nil }
|
||||
end
|
||||
|
||||
context 'when base has no children' do
|
||||
let(:base_relation) { Group.where(id: child2) }
|
||||
|
||||
it { expect(subject).to eq(1) }
|
||||
end
|
||||
|
||||
context 'when base has grandchildren' do
|
||||
let(:base_relation) { Group.where(id: parent) }
|
||||
|
||||
it { expect(subject).to eq(3) }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#ancestors' do
|
||||
it 'includes only the ancestors' do
|
||||
relation = described_class.new(Group.where(id: child2), options: options).ancestors
|
||||
|
||||
expect(relation).to contain_exactly(child1, parent)
|
||||
end
|
||||
|
||||
it 'can find ancestors upto a certain level' do
|
||||
relation = described_class.new(Group.where(id: child2), options: options).ancestors(upto: child1)
|
||||
|
||||
expect(relation).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
describe '#all_objects' do
|
||||
let(:relation) do
|
||||
described_class.new(Group.where(id: child1.id), options: options).all_objects
|
||||
end
|
||||
|
||||
it 'includes the base rows' do
|
||||
expect(relation).to include(child1)
|
||||
end
|
||||
|
||||
it 'includes the ancestors' do
|
||||
expect(relation).to include(parent)
|
||||
end
|
||||
|
||||
it 'includes the descendants' do
|
||||
expect(relation).to include(child2)
|
||||
end
|
||||
|
||||
it 'uses ancestors_base #initialize argument for ancestors' do
|
||||
relation = described_class.new(Group.where(id: child1.id), Group.where(id: non_existing_record_id), options: options).all_objects
|
||||
|
||||
expect(relation).to include(parent)
|
||||
end
|
||||
|
||||
it 'uses descendants_base #initialize argument for descendants' do
|
||||
relation = described_class.new(Group.where(id: non_existing_record_id), Group.where(id: child1.id), options: options).all_objects
|
||||
|
||||
expect(relation).to include(child2)
|
||||
end
|
||||
|
||||
it 'does not allow the use of #update_all' do
|
||||
expect { relation.update_all(share_with_group_lock: false) }
|
||||
.to raise_error(ActiveRecord::ReadOnlyRecord)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
11
spec/mailers/emails/admin_notification_spec.rb
Normal file
11
spec/mailers/emails/admin_notification_spec.rb
Normal file
|
@ -0,0 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Emails::AdminNotification do
|
||||
it 'adds email methods to Notify' do
|
||||
subject.instance_methods.each do |email_method|
|
||||
expect(Notify).to be_respond_to(email_method)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1609,6 +1609,32 @@ RSpec.describe Notify do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'admin notification' do
|
||||
let(:example_site_path) { root_path }
|
||||
let(:user) { create(:user) }
|
||||
|
||||
subject { @email = described_class.send_admin_notification(user.id, 'Admin announcement', 'Text') }
|
||||
|
||||
it 'is sent as the author' do
|
||||
sender = subject.header[:from].addrs[0]
|
||||
expect(sender.display_name).to eq("GitLab")
|
||||
expect(sender.address).to eq(gitlab_sender)
|
||||
end
|
||||
|
||||
it 'is sent to recipient' do
|
||||
is_expected.to deliver_to user.email
|
||||
end
|
||||
|
||||
it 'has the correct subject' do
|
||||
is_expected.to have_subject 'Admin announcement'
|
||||
end
|
||||
|
||||
it 'includes unsubscribe link' do
|
||||
unsubscribe_link = "http://localhost/unsubscribes/#{Base64.urlsafe_encode64(user.email)}"
|
||||
is_expected.to have_body_text(unsubscribe_link)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'confirmation if email changed' do
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
require_migration! 'schedule_merge_request_diff_users_background_migration'
|
||||
require_migration! 'reschedule_merge_request_diff_users_background_migration'
|
||||
|
||||
RSpec.describe ScheduleMergeRequestDiffUsersBackgroundMigration, :migration do
|
||||
RSpec.describe RescheduleMergeRequestDiffUsersBackgroundMigration, :migration do
|
||||
let(:migration) { described_class.new }
|
||||
|
||||
describe '#up' do
|
||||
|
@ -19,6 +19,21 @@ RSpec.describe ScheduleMergeRequestDiffUsersBackgroundMigration, :migration do
|
|||
.and_return(85_123)
|
||||
end
|
||||
|
||||
it 'deletes existing background migration job records' do
|
||||
args = [150_000, 300_000]
|
||||
|
||||
Gitlab::Database::BackgroundMigrationJob
|
||||
.create!(class_name: described_class::MIGRATION_NAME, arguments: args)
|
||||
|
||||
migration.up
|
||||
|
||||
found = Gitlab::Database::BackgroundMigrationJob
|
||||
.where(class_name: described_class::MIGRATION_NAME, arguments: args)
|
||||
.count
|
||||
|
||||
expect(found).to eq(0)
|
||||
end
|
||||
|
||||
it 'schedules the migrations in batches' do
|
||||
expect(migration)
|
||||
.to receive(:migrate_in)
|
25
spec/migrations/reset_job_token_scope_enabled_spec.rb
Normal file
25
spec/migrations/reset_job_token_scope_enabled_spec.rb
Normal file
|
@ -0,0 +1,25 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
require_migration!
|
||||
|
||||
RSpec.describe ResetJobTokenScopeEnabled do
|
||||
let(:settings) { table(:project_ci_cd_settings) }
|
||||
let(:projects) { table(:projects) }
|
||||
let(:namespaces) { table(:namespaces) }
|
||||
let(:namespace) { namespaces.create!(name: 'gitlab', path: 'gitlab-org') }
|
||||
let(:project_1) { projects.create!(name: 'proj-1', path: 'gitlab-org', namespace_id: namespace.id)}
|
||||
let(:project_2) { projects.create!(name: 'proj-2', path: 'gitlab-org', namespace_id: namespace.id)}
|
||||
|
||||
before do
|
||||
settings.create!(id: 1, project_id: project_1.id, job_token_scope_enabled: true)
|
||||
settings.create!(id: 2, project_id: project_2.id, job_token_scope_enabled: false)
|
||||
end
|
||||
|
||||
it 'migrates job_token_scope_enabled to be always false' do
|
||||
expect { migrate! }
|
||||
.to change { settings.where(job_token_scope_enabled: false).count }
|
||||
.from(1).to(2)
|
||||
end
|
||||
end
|
|
@ -3811,16 +3811,6 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
|
|||
it 'can generate a codequality report' do
|
||||
expect(subject).to be_truthy
|
||||
end
|
||||
|
||||
context 'when feature is disabled' do
|
||||
before do
|
||||
stub_feature_flags(codequality_mr_diff: false)
|
||||
end
|
||||
|
||||
it 'can not generate a codequality report' do
|
||||
expect(subject).to be_falsey
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -2083,14 +2083,6 @@ RSpec.describe MergeRequest, factory_default: :keep do
|
|||
let(:merge_request) { create(:merge_request, :with_codequality_mr_diff_reports) }
|
||||
|
||||
it { is_expected.to be_truthy }
|
||||
|
||||
context 'when feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(codequality_mr_diff: false)
|
||||
end
|
||||
|
||||
it { is_expected.to be_falsey }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when head pipeline does not have codeqquality mr diff report' do
|
||||
|
|
|
@ -294,32 +294,6 @@ module Ci
|
|||
end
|
||||
end
|
||||
|
||||
context 'when the use_distinct_in_register_job_object_hierarchy feature flag is enabled' do
|
||||
before do
|
||||
stub_feature_flags(use_distinct_in_register_job_object_hierarchy: true)
|
||||
stub_feature_flags(use_distinct_for_all_object_hierarchy: true)
|
||||
end
|
||||
|
||||
it 'calls DISTINCT' do
|
||||
queue = ::Ci::Queue::BuildQueueService.new(group_runner)
|
||||
|
||||
expect(queue.builds_for_group_runner.to_sql).to include("DISTINCT")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the use_distinct_in_register_job_object_hierarchy feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(use_distinct_in_register_job_object_hierarchy: false)
|
||||
stub_feature_flags(use_distinct_for_all_object_hierarchy: false)
|
||||
end
|
||||
|
||||
it 'does not call DISTINCT' do
|
||||
queue = ::Ci::Queue::BuildQueueService.new(group_runner)
|
||||
|
||||
expect(queue.builds_for_group_runner.to_sql).not_to include("DISTINCT")
|
||||
end
|
||||
end
|
||||
|
||||
context 'group runner' do
|
||||
let(:build) { execute(group_runner) }
|
||||
|
||||
|
|
Loading…
Reference in a new issue