Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-07-12 15:09:19 +00:00
parent f81141c25d
commit d9b3f39aca
49 changed files with 555 additions and 561 deletions

View file

@ -1 +1 @@
c8a29dc9fd507cab8835b2e1152b94a6ac96de35
a8d42fb628ab8d4cbde0481d25724963f73e7931

View file

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

View 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

View file

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

View 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

View file

@ -22,6 +22,7 @@ class Notify < ApplicationMailer
include Emails::Reviews
include Emails::ServiceDesk
include Emails::InProductMarketing
include Emails::AdminNotification
helper TimeboxesHelper
helper MergeRequestsHelper

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,7 @@
= simple_format @body
\----
%p
Don't want to receive updates from GitLab administrators?
= link_to 'Unsubscribe', @unsubscribe_url

View file

@ -0,0 +1,6 @@
= h @body
\-----
Don't want to receive updates from GitLab administrators?
Unsubscribe here: #{@unsubscribe_url}

View file

@ -0,0 +1,2 @@
%p
You have been unsubscribed from receiving GitLab administrator notifications.

View file

@ -0,0 +1 @@
You have been unsubscribed from receiving GitLab administrator notifications.

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1 @@
7add197fec50d8da5bcdbca83115558480668c26ad3a3fefc4ab93c07f34f63a

View file

@ -0,0 +1 @@
8545d6575c9dacec6796882677c4403cf3559430518e8709bf390f20717413d7

View file

@ -14,7 +14,7 @@ Available only in APIv4.
## Render an arbitrary Markdown document
```plaintext
POST /api/v4/markdown
POST /markdown
```
| Attribute | Type | Required | Description |

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View 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

View file

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

View file

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

View file

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

View 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

View file

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

View file

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

View 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

View file

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

View file

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

View file

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