Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
0b789f95a3
commit
a297076878
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 386 KiB After Width: | Height: | Size: 387 KiB |
|
@ -44,6 +44,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
|
|||
push_frontend_feature_flag(:issue_assignees_widget, @project, default_enabled: :yaml)
|
||||
push_frontend_feature_flag(:realtime_labels, project, default_enabled: :yaml)
|
||||
push_frontend_feature_flag(:updated_diff_expansion_buttons, project, default_enabled: :yaml)
|
||||
push_frontend_feature_flag(:mr_attention_requests, current_user, default_enabled: :yaml)
|
||||
end
|
||||
|
||||
before_action do
|
||||
|
|
|
@ -13,6 +13,8 @@ module Mutations
|
|||
DESC
|
||||
|
||||
def resolve(project_path:, iid:, user:)
|
||||
raise Gitlab::Graphql::Errors::ResourceNotAvailable, 'Feature disabled' unless feature_enabled?
|
||||
|
||||
merge_request = authorized_find!(project_path: project_path, iid: iid)
|
||||
|
||||
result = ::MergeRequests::RemoveAttentionRequestedService.new(
|
||||
|
@ -27,6 +29,12 @@ module Mutations
|
|||
errors: Array(result[:message])
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def feature_enabled?
|
||||
current_user&.mr_attention_requests_enabled?
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -13,6 +13,8 @@ module Mutations
|
|||
DESC
|
||||
|
||||
def resolve(project_path:, iid:, user:)
|
||||
raise Gitlab::Graphql::Errors::ResourceNotAvailable, 'Feature disabled' unless feature_enabled?
|
||||
|
||||
merge_request = authorized_find!(project_path: project_path, iid: iid)
|
||||
|
||||
result = ::MergeRequests::RequestAttentionService.new(
|
||||
|
@ -27,6 +29,12 @@ module Mutations
|
|||
errors: Array(result[:message])
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def feature_enabled?
|
||||
current_user&.mr_attention_requests_enabled?
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -13,6 +13,8 @@ module Mutations
|
|||
DESC
|
||||
|
||||
def resolve(project_path:, iid:, user:)
|
||||
raise Gitlab::Graphql::Errors::ResourceNotAvailable, 'Feature disabled' unless current_user&.mr_attention_requests_enabled?
|
||||
|
||||
merge_request = authorized_find!(project_path: project_path, iid: iid)
|
||||
|
||||
result = ::MergeRequests::ToggleAttentionRequestedService.new(project: merge_request.project, current_user: current_user, merge_request: merge_request, user: user).execute
|
||||
|
|
|
@ -69,9 +69,9 @@ module Types
|
|||
mount_mutation Mutations::MergeRequests::SetDraft, calls_gitaly: true
|
||||
mount_mutation Mutations::MergeRequests::SetAssignees
|
||||
mount_mutation Mutations::MergeRequests::ReviewerRereview
|
||||
mount_mutation Mutations::MergeRequests::RequestAttention, feature_flag: :mr_attention_requests
|
||||
mount_mutation Mutations::MergeRequests::RemoveAttentionRequest, feature_flag: :mr_attention_requests
|
||||
mount_mutation Mutations::MergeRequests::ToggleAttentionRequested, feature_flag: :mr_attention_requests
|
||||
mount_mutation Mutations::MergeRequests::RequestAttention
|
||||
mount_mutation Mutations::MergeRequests::RemoveAttentionRequest
|
||||
mount_mutation Mutations::MergeRequests::ToggleAttentionRequested
|
||||
mount_mutation Mutations::Metrics::Dashboard::Annotations::Create
|
||||
mount_mutation Mutations::Metrics::Dashboard::Annotations::Delete
|
||||
mount_mutation Mutations::Notes::Create::Note, calls_gitaly: true
|
||||
|
|
|
@ -156,7 +156,7 @@ module MergeRequestsHelper
|
|||
total: total_count
|
||||
}
|
||||
|
||||
if Feature.enabled?(:mr_attention_requests, default_enabled: :yaml)
|
||||
if current_user&.mr_attention_requests_enabled?
|
||||
attention_requested_count = attention_requested_merge_requests_count
|
||||
|
||||
counts[:attention_requested_count] = attention_requested_count
|
||||
|
|
|
@ -119,6 +119,10 @@ module AlertManagement
|
|||
end
|
||||
end
|
||||
|
||||
def self.find_ongoing_alert(project, fingerprint)
|
||||
for_fingerprint(project, fingerprint).not_resolved.take
|
||||
end
|
||||
|
||||
def self.last_prometheus_alert_by_project_id
|
||||
ids = select(arel_table[:id].maximum).group(:project_id)
|
||||
with_prometheus_alert.where(id: ids)
|
||||
|
|
|
@ -16,8 +16,6 @@ module MergeRequestReviewerState
|
|||
|
||||
belongs_to :updated_state_by, class_name: 'User', foreign_key: :updated_state_by_user_id
|
||||
|
||||
after_initialize :set_state, unless: :persisted?
|
||||
|
||||
def attention_requested_by
|
||||
return unless attention_requested?
|
||||
|
||||
|
|
|
@ -1977,10 +1977,6 @@ class MergeRequest < ApplicationRecord
|
|||
end
|
||||
end
|
||||
|
||||
def attention_requested_enabled?
|
||||
Feature.enabled?(:mr_attention_requests, project, default_enabled: :yaml)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_accessor :skip_fetch_ref
|
||||
|
|
|
@ -10,12 +10,6 @@ class MergeRequestAssignee < ApplicationRecord
|
|||
|
||||
scope :in_projects, ->(project_ids) { joins(:merge_request).where(merge_requests: { target_project_id: project_ids }) }
|
||||
|
||||
def set_state
|
||||
if Feature.enabled?(:mr_attention_requests, self.merge_request&.project, default_enabled: :yaml)
|
||||
self.state = MergeRequestReviewer.find_by(user_id: self.user_id, merge_request_id: self.merge_request_id)&.state || :attention_requested
|
||||
end
|
||||
end
|
||||
|
||||
def cache_key
|
||||
[model_name.cache_key, id, state, assignee.cache_key]
|
||||
end
|
||||
|
|
|
@ -6,12 +6,6 @@ class MergeRequestReviewer < ApplicationRecord
|
|||
belongs_to :merge_request
|
||||
belongs_to :reviewer, class_name: 'User', foreign_key: :user_id, inverse_of: :merge_request_reviewers
|
||||
|
||||
def set_state
|
||||
if Feature.enabled?(:mr_attention_requests, self.merge_request&.project, default_enabled: :yaml)
|
||||
self.state = MergeRequestAssignee.find_by(user_id: self.user_id, merge_request_id: self.merge_request_id)&.state || :attention_requested
|
||||
end
|
||||
end
|
||||
|
||||
def cache_key
|
||||
[model_name.cache_key, id, state, reviewer.cache_key]
|
||||
end
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ProjectSetting < ApplicationRecord
|
||||
ALLOWED_TARGET_PLATFORMS = %w(ios osx tvos watchos).freeze
|
||||
ALLOWED_TARGET_PLATFORMS = %w(ios osx tvos watchos android).freeze
|
||||
|
||||
belongs_to :project, inverse_of: :project_setting
|
||||
|
||||
|
|
|
@ -2070,6 +2070,10 @@ class User < ApplicationRecord
|
|||
end
|
||||
end
|
||||
|
||||
def mr_attention_requests_enabled?
|
||||
Feature.enabled?(:mr_attention_requests, self, default_enabled: :yaml)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# override, from Devise::Validatable
|
||||
|
|
|
@ -20,7 +20,7 @@ class MergeRequestUserEntity < ::API::Entities::UserBasic
|
|||
find_reviewer_or_assignee(user, options)&.reviewed?
|
||||
end
|
||||
|
||||
expose :attention_requested, if: satisfies(:present?, :allows_reviewers?, :attention_requested_enabled?) do |user, options|
|
||||
expose :attention_requested, if: ->(_, options) { options[:merge_request].present? && options[:merge_request].allows_reviewers? && request.current_user&.mr_attention_requests_enabled? } do |user, options|
|
||||
find_reviewer_or_assignee(user, options)&.attention_requested?
|
||||
end
|
||||
|
||||
|
|
|
@ -104,7 +104,7 @@ module AlertManagement
|
|||
def find_existing_alert
|
||||
return unless incoming_payload.gitlab_fingerprint
|
||||
|
||||
AlertManagement::Alert.not_resolved.for_fingerprint(project, incoming_payload.gitlab_fingerprint).first
|
||||
AlertManagement::Alert.find_ongoing_alert(project, incoming_payload.gitlab_fingerprint)
|
||||
end
|
||||
|
||||
def build_new_alert
|
||||
|
|
|
@ -43,6 +43,8 @@ module MergeRequests
|
|||
end
|
||||
|
||||
def handle_assignees_change(merge_request, old_assignees)
|
||||
bulk_update_assignees_state(merge_request, merge_request.assignees - old_assignees)
|
||||
|
||||
MergeRequests::HandleAssigneesChangeService
|
||||
.new(project: project, current_user: current_user)
|
||||
.async_execute(merge_request, old_assignees)
|
||||
|
@ -58,11 +60,10 @@ module MergeRequests
|
|||
new_reviewers = merge_request.reviewers - old_reviewers
|
||||
merge_request_activity_counter.track_users_review_requested(users: new_reviewers)
|
||||
merge_request_activity_counter.track_reviewers_changed_action(user: current_user)
|
||||
bulk_update_reviewers_state(merge_request, new_reviewers)
|
||||
|
||||
unless new_reviewers.include?(current_user)
|
||||
remove_attention_requested(merge_request)
|
||||
|
||||
merge_request.merge_request_reviewers_with(new_reviewers).update_all(updated_state_by_user_id: current_user.id)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -246,7 +247,7 @@ module MergeRequests
|
|||
end
|
||||
|
||||
def remove_all_attention_requests(merge_request)
|
||||
return unless merge_request.attention_requested_enabled?
|
||||
return unless current_user.mr_attention_requests_enabled?
|
||||
|
||||
users = merge_request.reviewers + merge_request.assignees
|
||||
|
||||
|
@ -254,10 +255,42 @@ module MergeRequests
|
|||
end
|
||||
|
||||
def remove_attention_requested(merge_request)
|
||||
return unless merge_request.attention_requested_enabled?
|
||||
return unless current_user.mr_attention_requests_enabled?
|
||||
|
||||
::MergeRequests::RemoveAttentionRequestedService.new(project: merge_request.project, current_user: current_user, merge_request: merge_request, user: current_user).execute
|
||||
end
|
||||
|
||||
def bulk_update_assignees_state(merge_request, new_assignees)
|
||||
return unless current_user.mr_attention_requests_enabled?
|
||||
return if new_assignees.empty?
|
||||
|
||||
assignees_map = merge_request.merge_request_assignees_with(new_assignees).to_h do |assignee|
|
||||
state = merge_request.find_reviewer(assignee.assignee)&.state || :attention_requested
|
||||
|
||||
[
|
||||
assignee,
|
||||
{ state: MergeRequestAssignee.states[state], updated_state_by_user_id: current_user.id }
|
||||
]
|
||||
end
|
||||
|
||||
::Gitlab::Database::BulkUpdate.execute(%i[state updated_state_by_user_id], assignees_map)
|
||||
end
|
||||
|
||||
def bulk_update_reviewers_state(merge_request, new_reviewers)
|
||||
return unless current_user.mr_attention_requests_enabled?
|
||||
return if new_reviewers.empty?
|
||||
|
||||
reviewers_map = merge_request.merge_request_reviewers_with(new_reviewers).to_h do |reviewer|
|
||||
state = merge_request.find_assignee(reviewer.reviewer)&.state || :attention_requested
|
||||
|
||||
[
|
||||
reviewer,
|
||||
{ state: MergeRequestReviewer.states[state], updated_state_by_user_id: current_user.id }
|
||||
]
|
||||
end
|
||||
|
||||
::Gitlab::Database::BulkUpdate.execute(%i[state updated_state_by_user_id], reviewers_map)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -21,8 +21,6 @@ module MergeRequests
|
|||
merge_request_activity_counter.track_users_assigned_to_mr(users: new_assignees)
|
||||
merge_request_activity_counter.track_assignees_changed_action(user: current_user)
|
||||
|
||||
merge_request.merge_request_assignees_with(new_assignees).update_all(updated_state_by_user_id: current_user.id)
|
||||
|
||||
execute_assignees_hooks(merge_request, old_assignees) if options[:execute_hooks]
|
||||
|
||||
unless new_assignees.include?(current_user)
|
||||
|
|
|
@ -20,6 +20,8 @@ module MergeRequests
|
|||
attrs = update_attrs.merge(assignee_ids: new_ids)
|
||||
merge_request.update!(**attrs)
|
||||
|
||||
bulk_update_assignees_state(merge_request, merge_request.assignees - old_assignees)
|
||||
|
||||
# Defer the more expensive operations (handle_assignee_changes) to the background
|
||||
MergeRequests::HandleAssigneesChangeService
|
||||
.new(project: project, current_user: current_user)
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Projects
|
||||
# Service class to detect if a project is made to run on the Android platform.
|
||||
#
|
||||
# This service searches for an AndroidManifest.xml file which all Android app
|
||||
# project must have. It returns the symbol :android if the given project is an
|
||||
# Android app project.
|
||||
#
|
||||
# Ref: https://developer.android.com/guide/topics/manifest/manifest-intro
|
||||
#
|
||||
# Example usage:
|
||||
# > AndroidTargetPlatformDetectorService.new(a_project).execute
|
||||
# => nil
|
||||
# > AndroidTargetPlatformDetectorService.new(an_android_project).execute
|
||||
# => :android
|
||||
class AndroidTargetPlatformDetectorService < BaseService
|
||||
# <manifest> element is required and must occur once inside AndroidManifest.xml
|
||||
MANIFEST_FILE_SEARCH_QUERY = '<manifest filename:AndroidManifest.xml'
|
||||
|
||||
def execute
|
||||
detect
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def file_finder
|
||||
@file_finder ||= ::Gitlab::FileFinder.new(project, project.default_branch)
|
||||
end
|
||||
|
||||
def detect
|
||||
return :android if file_finder.find(MANIFEST_FILE_SEARCH_QUERY).present?
|
||||
end
|
||||
end
|
||||
end
|
|
@ -4,15 +4,22 @@ module Projects
|
|||
class RecordTargetPlatformsService < BaseService
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
|
||||
def initialize(project, detector_service)
|
||||
@project = project
|
||||
@detector_service = detector_service
|
||||
end
|
||||
|
||||
def execute
|
||||
record_target_platforms
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :project, :detector_service
|
||||
|
||||
def target_platforms
|
||||
strong_memoize(:target_platforms) do
|
||||
AppleTargetPlatformDetectorService.new(project).execute
|
||||
Array(detector_service.new(project).execute)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -36,6 +43,7 @@ module Projects
|
|||
end
|
||||
|
||||
def send_build_ios_app_guide_email
|
||||
return unless target_platforms.include? :ios
|
||||
return unless experiment_candidate?
|
||||
|
||||
campaign = Users::InProductMarketingEmail::BUILD_IOS_APP_GUIDE
|
||||
|
|
|
@ -60,7 +60,7 @@
|
|||
= number_with_delimiter(issues_count)
|
||||
- if header_link?(:merge_requests)
|
||||
= nav_link(path: 'dashboard#merge_requests', html_options: { class: "user-counter dropdown" }) do
|
||||
- top_level_link = Feature.enabled?(:mr_attention_requests, default_enabled: :yaml) ? attention_requested_mrs_dashboard_path : assigned_mrs_dashboard_path
|
||||
- top_level_link = current_user.mr_attention_requests_enabled? ? attention_requested_mrs_dashboard_path : assigned_mrs_dashboard_path
|
||||
= link_to top_level_link, class: 'dashboard-shortcuts-merge_requests', title: _('Merge requests'), aria: { label: _('Merge requests') },
|
||||
data: { qa_selector: 'merge_requests_shortcut_button',
|
||||
toggle: "dropdown",
|
||||
|
@ -77,7 +77,7 @@
|
|||
%ul
|
||||
%li.dropdown-header
|
||||
= _('Merge requests')
|
||||
- if Feature.enabled?(:mr_attention_requests, default_enabled: :yaml)
|
||||
- if current_user.mr_attention_requests_enabled?
|
||||
%li#js-need-attention-nav
|
||||
#js-need-attention-nav-onboarding
|
||||
= link_to attention_requested_mrs_dashboard_path, class: 'gl-display-flex! gl-align-items-center js-prefetch-document' do
|
||||
|
|
|
@ -92,7 +92,7 @@
|
|||
|
||||
#js-review-bar
|
||||
|
||||
- if Feature.enabled?(:mr_attention_requests, default_enabled: :yaml)
|
||||
- if current_user&.mr_attention_requests_enabled?
|
||||
#js-need-attention-sidebar-onboarding
|
||||
|
||||
= render 'projects/invite_members_modal', project: @project
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
- render_count = assignees_rendering_overflow ? max_render - 1 : max_render
|
||||
- more_assignees_count = issuable.assignees.size - render_count
|
||||
|
||||
- if issuable.instance_of?(MergeRequest) && Feature.enabled?(:mr_attention_requests, default_enabled: :yaml)
|
||||
- if issuable.instance_of?(MergeRequest) && current_user&.mr_attention_requests_enabled?
|
||||
= render 'shared/issuable/merge_request_assignees', issuable: issuable, count: render_count
|
||||
- else
|
||||
- issuable.assignees.take(render_count).each do |assignee| # rubocop: disable CodeReuse/ActiveRecord
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
- render_count = reviewers_rendering_overflow ? max_render - 1 : max_render
|
||||
- more_reviewers_count = issuable.reviewers.size - render_count
|
||||
|
||||
- if issuable.instance_of?(MergeRequest) && Feature.enabled?(:mr_attention_requests, default_enabled: :yaml)
|
||||
- if issuable.instance_of?(MergeRequest) && current_user&.mr_attention_requests_enabled?
|
||||
= render 'shared/issuable/merge_request_reviewers', issuable: issuable, count: render_count
|
||||
- else
|
||||
- issuable.reviewers.take(render_count).each do |reviewer| # rubocop: disable CodeReuse/ActiveRecord
|
||||
|
|
|
@ -88,7 +88,7 @@
|
|||
= render 'shared/issuable/user_dropdown_item',
|
||||
user: User.new(username: '{{username}}', name: '{{name}}'),
|
||||
avatar: { lazy: true, url: '{{avatar_url}}' }
|
||||
- if Feature.enabled?(:mr_attention_requests, default_enabled: :yaml)
|
||||
- if current_user&.mr_attention_requests_enabled?
|
||||
#js-dropdown-attention-requested.filtered-search-input-dropdown-menu.dropdown-menu
|
||||
- if current_user
|
||||
%ul{ data: { dropdown: true } }
|
||||
|
|
|
@ -7,6 +7,7 @@ module Projects
|
|||
|
||||
LEASE_TIMEOUT = 1.hour.to_i
|
||||
APPLE_PLATFORM_LANGUAGES = %w(swift objective-c).freeze
|
||||
ANDROID_PLATFORM_LANGUAGES = %w(java kotlin).freeze
|
||||
|
||||
feature_category :experimentation_activation
|
||||
data_consistency :always
|
||||
|
@ -18,10 +19,10 @@ module Projects
|
|||
@project = Project.find_by_id(project_id)
|
||||
|
||||
return unless project
|
||||
return unless uses_apple_platform_languages?
|
||||
return unless detector_service
|
||||
|
||||
try_obtain_lease do
|
||||
@target_platforms = Projects::RecordTargetPlatformsService.new(project).execute
|
||||
@target_platforms = Projects::RecordTargetPlatformsService.new(project, detector_service).execute
|
||||
log_target_platforms_metadata
|
||||
end
|
||||
end
|
||||
|
@ -30,8 +31,29 @@ module Projects
|
|||
|
||||
attr_reader :target_platforms, :project
|
||||
|
||||
def detector_service
|
||||
if uses_apple_platform_languages?
|
||||
AppleTargetPlatformDetectorService
|
||||
elsif uses_android_platform_languages? && detect_android_projects_enabled?
|
||||
AndroidTargetPlatformDetectorService
|
||||
end
|
||||
end
|
||||
|
||||
def detect_android_projects_enabled?
|
||||
Feature.enabled?(:detect_android_projects, project)
|
||||
end
|
||||
|
||||
def uses_apple_platform_languages?
|
||||
project.repository_languages.with_programming_language(*APPLE_PLATFORM_LANGUAGES).present?
|
||||
target_languages.with_programming_language(*APPLE_PLATFORM_LANGUAGES).present?
|
||||
end
|
||||
|
||||
def uses_android_platform_languages?
|
||||
target_languages.with_programming_language(*ANDROID_PLATFORM_LANGUAGES).present?
|
||||
end
|
||||
|
||||
def target_languages
|
||||
languages = APPLE_PLATFORM_LANGUAGES + ANDROID_PLATFORM_LANGUAGES
|
||||
@target_languages ||= project.repository_languages.with_programming_language(*languages)
|
||||
end
|
||||
|
||||
def log_target_platforms_metadata
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: detect_android_projects
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85681
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/360902
|
||||
milestone: '15.0'
|
||||
type: development
|
||||
group: group::activation
|
||||
default_enabled: false
|
|
@ -0,0 +1,15 @@
|
|||
- name: "`artifacts:report:cobertura` keyword"
|
||||
announcement_milestone: "14.8"
|
||||
announcement_date: "2022-02-22"
|
||||
removal_milestone: "15.0"
|
||||
removal_date: "2022-05-22"
|
||||
breaking_change: false
|
||||
body: |
|
||||
As of GitLab 15.0, the `artifacts:report:cobertura` keyword has been replaced by
|
||||
[`artifacts:reports:coverage_report`](https://gitlab.com/gitlab-org/gitlab/-/issues/344533). Cobertura is the only
|
||||
supported report file, but this is the first step towards GitLab supporting other report types.
|
||||
|
||||
# The following items are not published on the docs page, but may be used in the future.
|
||||
stage: Verify
|
||||
issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/348980
|
||||
documentation_url: https://docs.gitlab.com/ee/ci/yaml/artifacts_reports.html#artifactsreportscobertura
|
|
@ -12,6 +12,7 @@ level: warning
|
|||
ignorecase: true
|
||||
swap:
|
||||
air(?:-| )?gapped: offline environment
|
||||
bullet: list item
|
||||
click: select
|
||||
code base: codebase
|
||||
config: configuration
|
||||
|
|
|
@ -3441,8 +3441,6 @@ Input type: `MergeRequestCreateInput`
|
|||
|
||||
### `Mutation.mergeRequestRemoveAttentionRequest`
|
||||
|
||||
Available only when feature flag `mr_attention_requests` is enabled. This flag is disabled by default, because the feature is experimental and is subject to change without notice.
|
||||
|
||||
Input type: `MergeRequestRemoveAttentionRequestInput`
|
||||
|
||||
#### Arguments
|
||||
|
@ -3464,8 +3462,6 @@ Input type: `MergeRequestRemoveAttentionRequestInput`
|
|||
|
||||
### `Mutation.mergeRequestRequestAttention`
|
||||
|
||||
Available only when feature flag `mr_attention_requests` is enabled. This flag is disabled by default, because the feature is experimental and is subject to change without notice.
|
||||
|
||||
Input type: `MergeRequestRequestAttentionInput`
|
||||
|
||||
#### Arguments
|
||||
|
@ -3636,8 +3632,6 @@ Input type: `MergeRequestSetSubscriptionInput`
|
|||
|
||||
### `Mutation.mergeRequestToggleAttentionRequested`
|
||||
|
||||
Available only when feature flag `mr_attention_requests` is enabled. This flag is disabled by default, because the feature is experimental and is subject to change without notice.
|
||||
|
||||
Input type: `MergeRequestToggleAttentionRequestedInput`
|
||||
|
||||
#### Arguments
|
||||
|
|
|
@ -13,7 +13,7 @@ Generally, if a style is not covered by [existing Rubocop rules or style guides]
|
|||
Before adding a new cop to enforce a given style, make sure to discuss it with your team.
|
||||
When the style is approved by a backend EM or by a BE staff eng, add a new section to this page to
|
||||
document the new rule. For every new guideline, add it in a new section and link the discussion from the section's
|
||||
[version history note](../documentation/versions.md#add-a-version-history-bullet)
|
||||
[version history note](../documentation/versions.md#add-a-version-history-item)
|
||||
to provide context and serve as a reference.
|
||||
|
||||
Just because something is listed here does not mean it cannot be reopened for discussion.
|
||||
|
|
|
@ -75,7 +75,7 @@ Example response:
|
|||
```
|
||||
````
|
||||
|
||||
Adjust the [version history note accordingly](versions.md#add-a-version-history-bullet)
|
||||
Adjust the [version history note accordingly](versions.md#add-a-version-history-item)
|
||||
to describe the GitLab release that introduced the API call.
|
||||
|
||||
## Method description
|
||||
|
|
|
@ -237,7 +237,7 @@ consider using subsections for each distinct task.
|
|||
### Related topics
|
||||
|
||||
If inline links are not sufficient, you can create a topic called **Related topics**
|
||||
and include a bulleted list of related topics. This topic should be above the Troubleshooting section.
|
||||
and include an unordered list of related topics. This topic should be above the Troubleshooting section.
|
||||
|
||||
```markdown
|
||||
# Related topics
|
||||
|
|
|
@ -980,7 +980,7 @@ If you are documenting multiple fields and only one field needs explanation, do
|
|||
1. Expand **Push rules**.
|
||||
1. Complete the fields. **Branch name** must be a regular expression.
|
||||
|
||||
To describe multiple fields, use bullets:
|
||||
To describe multiple fields, use unordered list items:
|
||||
|
||||
1. Expand **General pipelines**.
|
||||
1. Complete the fields.
|
||||
|
|
|
@ -166,6 +166,14 @@ Use **text box** to refer to the UI field. Do not use **field** or **box**. For
|
|||
|
||||
- In the **Variable name** text box, enter a value.
|
||||
|
||||
|
||||
## bullet
|
||||
|
||||
Don't refer to individual items in an ordered or unordered list as **bullets**. Use **list item** instead. If you need to be less ambiguous, you can use:
|
||||
|
||||
- **Ordered list item** for items in an ordered list.
|
||||
- **Unordered list item** for items in an unordered list.
|
||||
|
||||
## button
|
||||
|
||||
Don't use a descriptor with **button**.
|
||||
|
|
|
@ -29,9 +29,9 @@ either as a **Version history** bullet or as an inline text reference.
|
|||
|
||||
You do not need to add version information on the pages in the `/development` directory.
|
||||
|
||||
### Add a **Version history** bullet
|
||||
### Add a **Version history** item
|
||||
|
||||
If all content in a topic is related, add a version history bullet after the topic heading.
|
||||
If all content in a topic is related, add a version history item after the topic heading.
|
||||
For example:
|
||||
|
||||
```markdown
|
||||
|
@ -42,7 +42,7 @@ For example:
|
|||
This feature does something.
|
||||
```
|
||||
|
||||
The bullet text must include these words in order. Capitalization doesn't matter.
|
||||
The item text must include these words in order. Capitalization doesn't matter.
|
||||
|
||||
- `introduced`, `enabled`, `deprecated`, `changed`, `moved`, `recommended`, `removed`, or `renamed`
|
||||
- `in` or `to`
|
||||
|
@ -122,7 +122,7 @@ The title and a removed indicator remains until three months after the removal.
|
|||
|
||||
To remove a page:
|
||||
|
||||
1. Leave the page title. Remove all other content, including the version history bullets and the word `WARNING:`.
|
||||
1. Leave the page title. Remove all other content, including the version history items and the word `WARNING:`.
|
||||
1. After the title, change `(deprecated)` to `(removed)`.
|
||||
1. Update the YAML metadata:
|
||||
- For `remove_date`, set the value to a date three months after
|
||||
|
@ -156,7 +156,7 @@ This content is removed from the documentation as part of the Technical Writing
|
|||
To remove a topic:
|
||||
|
||||
1. Leave the title and the details of the deprecation and removal. Remove all other content,
|
||||
including the version history bullets and the word `WARNING:`.
|
||||
including the version history items and the word `WARNING:`.
|
||||
1. Add `(removed)` after the title.
|
||||
1. Add the following HTML comments above and below the topic.
|
||||
For the `remove_date`, set a date three months after the release where it was removed.
|
||||
|
@ -184,7 +184,7 @@ GitLab 14.0, 13.0 and 12.0 are supported.
|
|||
|
||||
[View the list of supported versions](https://about.gitlab.com/support/statement-of-support.html#version-support).
|
||||
|
||||
If you see version history bullets or inline text that refers to unsupported versions, you can remove it.
|
||||
If you see version history items or inline text that refers to unsupported versions, you can remove it.
|
||||
|
||||
Historical feature information is available in [release posts](https://about.gitlab.com/releases/)
|
||||
or by searching for the issue or merge request where the work was done.
|
||||
|
|
|
@ -188,6 +188,12 @@ The new security approvals feature is similar to vulnerability check. For exampl
|
|||
- A two-step approval process can be enforced for any desired changes to security approval rules.
|
||||
- A single set of security policies can be applied to multiple development projects to allow for ease in maintaining a single, centralized ruleset.
|
||||
|
||||
### `artifacts:report:cobertura` keyword
|
||||
|
||||
As of GitLab 15.0, the `artifacts:report:cobertura` keyword has been replaced by
|
||||
[`artifacts:reports:coverage_report`](https://gitlab.com/gitlab-org/gitlab/-/issues/344533). Cobertura is the only
|
||||
supported report file, but this is the first step towards GitLab supporting other report types.
|
||||
|
||||
## 14.10
|
||||
|
||||
### Permissions change for downloading Composer dependencies
|
||||
|
|
|
@ -19,7 +19,7 @@ module API
|
|||
todos: current_user.todos_pending_count
|
||||
}
|
||||
|
||||
if Feature.enabled?(:mr_attention_requests, default_enabled: :yaml)
|
||||
if current_user&.mr_attention_requests_enabled?
|
||||
counts[:attention_requests] = current_user.attention_requested_open_merge_requests_count
|
||||
end
|
||||
|
||||
|
|
|
@ -58,7 +58,7 @@ module Gitlab
|
|||
push_frontend_feature_flag(:sandboxed_mermaid, default_enabled: :yaml)
|
||||
push_frontend_feature_flag(:source_editor_toolbar, default_enabled: :yaml)
|
||||
push_frontend_feature_flag(:gl_avatar_for_all_user_avatars, default_enabled: :yaml)
|
||||
push_frontend_feature_flag(:mr_attention_requests, default_enabled: :yaml)
|
||||
push_frontend_feature_flag(:mr_attention_requests, current_user, default_enabled: :yaml)
|
||||
push_frontend_feature_flag(:follow_in_user_popover, current_user, default_enabled: :yaml)
|
||||
end
|
||||
|
||||
|
|
|
@ -285,7 +285,7 @@ module Gitlab
|
|||
end
|
||||
types MergeRequest
|
||||
condition do
|
||||
Feature.enabled?(:mr_attention_requests, project, default_enabled: :yaml) &&
|
||||
current_user.mr_attention_requests_enabled? &&
|
||||
current_user.can?(:"admin_#{quick_action_target.to_ability_name}", project)
|
||||
end
|
||||
parse_params do |attention_param|
|
||||
|
@ -321,7 +321,7 @@ module Gitlab
|
|||
end
|
||||
types MergeRequest
|
||||
condition do
|
||||
Feature.enabled?(:mr_attention_requests, project, default_enabled: :yaml) &&
|
||||
current_user.mr_attention_requests_enabled? &&
|
||||
current_user.can?(:"admin_#{quick_action_target.to_ability_name}", project)
|
||||
end
|
||||
parse_params do |attention_param|
|
||||
|
|
|
@ -53,6 +53,11 @@ RSpec.describe 'Navigation bar counter', :use_clean_rails_memory_store_caching d
|
|||
describe 'feature flag mr_attention_requests is enabled' do
|
||||
before do
|
||||
merge_request.update!(assignees: [user])
|
||||
|
||||
merge_request.find_assignee(user).update!(state: :attention_requested)
|
||||
|
||||
user.invalidate_attention_requested_count
|
||||
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
|
|
|
@ -112,8 +112,8 @@ RSpec.describe 'Dashboard Merge Requests' do
|
|||
end
|
||||
|
||||
it 'includes assigned and reviewers in badge' do
|
||||
within("span[aria-label='#{n_("%d merge request", "%d merge requests", 3) % 3}']") do
|
||||
expect(page).to have_content('3')
|
||||
within("span[aria-label='#{n_("%d merge request", "%d merge requests", 0) % 0}']") do
|
||||
expect(page).to have_content('0')
|
||||
end
|
||||
expect(find('.js-assigned-mr-count')).to have_content('2')
|
||||
expect(find('.js-reviewer-mr-count')).to have_content('1')
|
||||
|
|
|
@ -414,6 +414,9 @@ RSpec.describe MergeRequestsFinder do
|
|||
before do
|
||||
reviewer = merge_request1.find_reviewer(user2)
|
||||
reviewer.update!(state: :reviewed)
|
||||
|
||||
merge_request2.find_reviewer(user2).update!(state: :attention_requested)
|
||||
merge_request3.find_assignee(user2).update!(state: :attention_requested)
|
||||
end
|
||||
|
||||
context 'by username' do
|
||||
|
|
|
@ -76,6 +76,7 @@ RSpec.describe GitlabSchema.types['UserMergeRequestInteraction'] do
|
|||
context 'when the user has been asked to review the MR' do
|
||||
before do
|
||||
merge_request.reviewers << user
|
||||
merge_request.find_reviewer(user).update!(state: :attention_requested)
|
||||
end
|
||||
|
||||
it { is_expected.to eq(Types::MergeRequestReviewStateEnum.values['ATTENTION_REQUESTED'].value) }
|
||||
|
|
|
@ -84,7 +84,7 @@ RSpec.describe MergeRequestsHelper do
|
|||
|
||||
describe 'mr_attention_requests disabled' do
|
||||
before do
|
||||
stub_feature_flags(mr_attention_requests: false)
|
||||
allow(user).to receive(:mr_attention_requests_enabled?).and_return(false)
|
||||
end
|
||||
|
||||
it "returns assigned, review requested and total merge request counts" do
|
||||
|
@ -97,6 +97,10 @@ RSpec.describe MergeRequestsHelper do
|
|||
end
|
||||
|
||||
describe 'mr_attention_requests enabled' do
|
||||
before do
|
||||
allow(user).to receive(:mr_attention_requests_enabled?).and_return(true)
|
||||
end
|
||||
|
||||
it "returns assigned, review requested, attention requests and total merge request counts" do
|
||||
expect(subject).to eq(
|
||||
assigned: user.assigned_open_merge_requests_count,
|
||||
|
|
|
@ -233,6 +233,17 @@ RSpec.describe AlertManagement::Alert do
|
|||
end
|
||||
end
|
||||
|
||||
describe '.find_ongoing_alert' do
|
||||
let_it_be(:fingerprint) { SecureRandom.hex }
|
||||
let_it_be(:resolved_alert_with_fingerprint) { create(:alert_management_alert, :resolved, project: project, fingerprint: fingerprint) }
|
||||
let_it_be(:alert_with_fingerprint_in_other_project) { create(:alert_management_alert, project: project2, fingerprint: fingerprint) }
|
||||
let_it_be(:alert_with_fingerprint) { create(:alert_management_alert, project: project, fingerprint: fingerprint) }
|
||||
|
||||
subject { described_class.find_ongoing_alert(project, fingerprint) }
|
||||
|
||||
it { is_expected.to eq(alert_with_fingerprint) }
|
||||
end
|
||||
|
||||
describe '.last_prometheus_alert_by_project_id' do
|
||||
subject { described_class.last_prometheus_alert_by_project_id }
|
||||
|
||||
|
|
|
@ -41,22 +41,11 @@ RSpec.describe MergeRequestAssignee do
|
|||
|
||||
it_behaves_like 'having unique enum values'
|
||||
|
||||
it_behaves_like 'having reviewer state'
|
||||
|
||||
describe 'syncs to reviewer state' do
|
||||
before do
|
||||
reviewer = merge_request.merge_request_reviewers.build(reviewer: assignee)
|
||||
reviewer.update!(state: :reviewed)
|
||||
end
|
||||
|
||||
it { is_expected.to have_attributes(state: 'reviewed') }
|
||||
end
|
||||
|
||||
describe '#attention_requested_by' do
|
||||
let(:current_user) { create(:user) }
|
||||
|
||||
before do
|
||||
subject.update!(updated_state_by: current_user)
|
||||
subject.update!(updated_state_by: current_user, state: :attention_requested)
|
||||
end
|
||||
|
||||
context 'attention requested' do
|
||||
|
|
|
@ -10,17 +10,6 @@ RSpec.describe MergeRequestReviewer do
|
|||
|
||||
it_behaves_like 'having unique enum values'
|
||||
|
||||
it_behaves_like 'having reviewer state'
|
||||
|
||||
describe 'syncs to assignee state' do
|
||||
before do
|
||||
assignee = merge_request.merge_request_assignees.build(assignee: reviewer)
|
||||
assignee.update!(state: :reviewed)
|
||||
end
|
||||
|
||||
it { is_expected.to have_attributes(state: 'reviewed') }
|
||||
end
|
||||
|
||||
describe 'associations' do
|
||||
it { is_expected.to belong_to(:merge_request).class_name('MergeRequest') }
|
||||
it { is_expected.to belong_to(:reviewer).class_name('User').inverse_of(:merge_request_reviewers) }
|
||||
|
@ -30,7 +19,7 @@ RSpec.describe MergeRequestReviewer do
|
|||
let(:current_user) { create(:user) }
|
||||
|
||||
before do
|
||||
subject.update!(updated_state_by: current_user)
|
||||
subject.update!(updated_state_by: current_user, state: :attention_requested)
|
||||
end
|
||||
|
||||
context 'attention requested' do
|
||||
|
|
|
@ -151,6 +151,8 @@ RSpec.describe MergeRequest, factory_default: :keep do
|
|||
before do
|
||||
assignee = merge_request6.find_assignee(user2)
|
||||
assignee.update!(state: :reviewed)
|
||||
merge_request2.find_reviewer(user2).update!(state: :attention_requested)
|
||||
merge_request5.find_assignee(user2).update!(state: :attention_requested)
|
||||
end
|
||||
|
||||
it 'returns MRs that have any attention requests' do
|
||||
|
|
|
@ -5006,9 +5006,13 @@ RSpec.describe User do
|
|||
let(:archived_project) { create(:project, :public, :archived) }
|
||||
|
||||
before do
|
||||
create(:merge_request, source_project: project, author: user, reviewers: [user])
|
||||
create(:merge_request, :closed, source_project: project, author: user, reviewers: [user])
|
||||
create(:merge_request, source_project: archived_project, author: user, reviewers: [user])
|
||||
mr1 = create(:merge_request, source_project: project, author: user, reviewers: [user])
|
||||
mr2 = create(:merge_request, :closed, source_project: project, author: user, reviewers: [user])
|
||||
mr3 = create(:merge_request, source_project: archived_project, author: user, reviewers: [user])
|
||||
|
||||
mr1.find_reviewer(user).update!(state: :attention_requested)
|
||||
mr2.find_reviewer(user).update!(state: :attention_requested)
|
||||
mr3.find_reviewer(user).update!(state: :attention_requested)
|
||||
end
|
||||
|
||||
it 'returns number of open merge requests from non-archived projects' do
|
||||
|
@ -6817,4 +6821,23 @@ RSpec.describe User do
|
|||
it_behaves_like 'it has loose foreign keys' do
|
||||
let(:factory_name) { :user }
|
||||
end
|
||||
|
||||
describe 'mr_attention_requests_enabled?' do
|
||||
let(:user) { create(:user) }
|
||||
|
||||
before do
|
||||
stub_feature_flags(mr_attention_requests: false)
|
||||
end
|
||||
|
||||
it { expect(user.mr_attention_requests_enabled?).to be(false) }
|
||||
|
||||
it 'feature flag is enabled for user' do
|
||||
stub_feature_flags(mr_attention_requests: user)
|
||||
|
||||
another_user = create(:user)
|
||||
|
||||
expect(user.mr_attention_requests_enabled?).to be(true)
|
||||
expect(another_user.mr_attention_requests_enabled?).to be(false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -59,6 +59,7 @@ RSpec.describe ::Users::MergeRequestInteraction do
|
|||
context 'when the user has been asked to review the MR' do
|
||||
before do
|
||||
merge_request.reviewers << user
|
||||
merge_request.find_reviewer(user).update!(state: :attention_requested)
|
||||
end
|
||||
|
||||
it { is_expected.to eq(Types::MergeRequestReviewStateEnum.values['ATTENTION_REQUESTED'].value) }
|
||||
|
|
|
@ -63,4 +63,17 @@ RSpec.describe 'Request attention' do
|
|||
expect(mutation_errors).not_to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
context 'feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(mr_attention_requests: false)
|
||||
end
|
||||
|
||||
it 'returns an error' do
|
||||
post_graphql_mutation(mutation, current_user: current_user)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:success)
|
||||
expect(graphql_errors[0]["message"]).to eq "Feature disabled"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -63,4 +63,17 @@ RSpec.describe 'Remove attention request' do
|
|||
expect(mutation_errors).not_to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
context 'feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(mr_attention_requests: false)
|
||||
end
|
||||
|
||||
it 'returns an error' do
|
||||
post_graphql_mutation(mutation, current_user: current_user)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:success)
|
||||
expect(graphql_errors[0]["message"]).to eq "Feature disabled"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -425,7 +425,7 @@ RSpec.describe 'getting merge request information nested in a project' do
|
|||
|
||||
other_users.each do |user|
|
||||
assign_user(user)
|
||||
merge_request.merge_request_reviewers.find_or_create_by!(reviewer: user)
|
||||
merge_request.merge_request_reviewers.find_or_create_by!(reviewer: user, state: :attention_requested)
|
||||
end
|
||||
|
||||
expect { post_graphql(query) }.not_to exceed_query_limit(baseline)
|
||||
|
@ -466,7 +466,7 @@ RSpec.describe 'getting merge request information nested in a project' do
|
|||
let(:can_update) { false }
|
||||
|
||||
def assign_user(user)
|
||||
merge_request.merge_request_reviewers.create!(reviewer: user)
|
||||
merge_request.merge_request_reviewers.create!(reviewer: user, state: :attention_requested)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@ RSpec.describe API::UserCounts do
|
|||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(json_response).to be_a Hash
|
||||
expect(json_response['merge_requests']).to eq(2)
|
||||
expect(json_response['attention_requests']).to eq(2)
|
||||
expect(json_response['attention_requests']).to eq(0)
|
||||
end
|
||||
|
||||
describe 'mr_attention_requests is disabled' do
|
||||
|
|
|
@ -58,6 +58,10 @@ RSpec.describe MergeRequestUserEntity do
|
|||
end
|
||||
|
||||
context 'attention_requested' do
|
||||
before do
|
||||
merge_request.find_assignee(user).update!(state: :attention_requested)
|
||||
end
|
||||
|
||||
it { is_expected.to include(attention_requested: true ) }
|
||||
end
|
||||
|
||||
|
|
|
@ -95,12 +95,6 @@ RSpec.describe MergeRequests::HandleAssigneesChangeService do
|
|||
execute
|
||||
end
|
||||
|
||||
it 'updates attention requested by of assignee' do
|
||||
execute
|
||||
|
||||
expect(merge_request.find_assignee(assignee).updated_state_by).to eq(user)
|
||||
end
|
||||
|
||||
it 'tracks users assigned event' do
|
||||
expect(Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter)
|
||||
.to receive(:track_users_assigned_to_mr).once.with(users: [assignee])
|
||||
|
|
|
@ -113,6 +113,49 @@ RSpec.describe MergeRequests::UpdateAssigneesService do
|
|||
expect { service.execute(merge_request) }
|
||||
.to issue_fewer_queries_than { update_service.execute(other_mr) }
|
||||
end
|
||||
|
||||
context 'setting state of assignees' do
|
||||
before do
|
||||
stub_feature_flags(mr_attention_requests: false)
|
||||
end
|
||||
|
||||
it 'does not set state as attention_requested if feature flag is disabled' do
|
||||
update_merge_request
|
||||
|
||||
expect(merge_request.merge_request_assignees[0].state).not_to eq('attention_requested')
|
||||
end
|
||||
|
||||
context 'feature flag is enabled for current_user' do
|
||||
before do
|
||||
stub_feature_flags(mr_attention_requests: user)
|
||||
end
|
||||
|
||||
it 'sets state as attention_requested' do
|
||||
update_merge_request
|
||||
|
||||
expect(merge_request.merge_request_assignees[0].state).to eq('attention_requested')
|
||||
expect(merge_request.merge_request_assignees[0].updated_state_by).to eq(user)
|
||||
end
|
||||
|
||||
it 'uses reviewers state if it is same user as new assignee' do
|
||||
merge_request.reviewers << user2
|
||||
|
||||
update_merge_request
|
||||
|
||||
expect(merge_request.merge_request_assignees[0].state).to eq('unreviewed')
|
||||
end
|
||||
|
||||
context 'when assignee_ids matches existing assignee' do
|
||||
let(:opts) { { assignee_ids: [user3.id] } }
|
||||
|
||||
it 'keeps original assignees state' do
|
||||
update_merge_request
|
||||
|
||||
expect(merge_request.find_assignee(user3).state).to eq('unreviewed')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -328,6 +328,49 @@ RSpec.describe MergeRequests::UpdateService, :mailer do
|
|||
|
||||
update_merge_request(reviewer_ids: [user.id])
|
||||
end
|
||||
|
||||
context 'setting state of reviewers' do
|
||||
before do
|
||||
stub_feature_flags(mr_attention_requests: false)
|
||||
end
|
||||
|
||||
it 'does not set state as attention_requested if feature flag is disabled' do
|
||||
update_merge_request(reviewer_ids: [user.id])
|
||||
|
||||
expect(merge_request.merge_request_reviewers[0].state).not_to eq('attention_requested')
|
||||
end
|
||||
|
||||
context 'feature flag is enabled for current_user' do
|
||||
before do
|
||||
stub_feature_flags(mr_attention_requests: user)
|
||||
end
|
||||
|
||||
it 'sets state as attention_requested' do
|
||||
update_merge_request(reviewer_ids: [user.id])
|
||||
|
||||
expect(merge_request.merge_request_reviewers[0].state).to eq('attention_requested')
|
||||
expect(merge_request.merge_request_reviewers[0].updated_state_by).to eq(user)
|
||||
end
|
||||
|
||||
it 'keeps original reviewers state' do
|
||||
merge_request.find_reviewer(user2).update!(state: :unreviewed)
|
||||
|
||||
update_merge_request({
|
||||
reviewer_ids: [user2.id]
|
||||
})
|
||||
|
||||
expect(merge_request.find_reviewer(user2).state).to eq('unreviewed')
|
||||
end
|
||||
|
||||
it 'uses reviewers state if it is same user as new assignee' do
|
||||
merge_request.assignees << user
|
||||
|
||||
update_merge_request(reviewer_ids: [user.id])
|
||||
|
||||
expect(merge_request.merge_request_reviewers[0].state).to eq('unreviewed')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it 'creates a resource label event' do
|
||||
|
@ -1066,6 +1109,53 @@ RSpec.describe MergeRequests::UpdateService, :mailer do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'setting state of assignees' do
|
||||
before do
|
||||
stub_feature_flags(mr_attention_requests: false)
|
||||
end
|
||||
|
||||
it 'does not set state as attention_requested if feature flag is disabled' do
|
||||
update_merge_request({
|
||||
assignee_ids: [user2.id]
|
||||
})
|
||||
|
||||
expect(merge_request.merge_request_assignees[0].state).not_to eq('attention_requested')
|
||||
end
|
||||
|
||||
context 'feature flag is enabled for current_user' do
|
||||
before do
|
||||
stub_feature_flags(mr_attention_requests: user)
|
||||
end
|
||||
|
||||
it 'sets state as attention_requested' do
|
||||
update_merge_request({
|
||||
assignee_ids: [user2.id]
|
||||
})
|
||||
|
||||
expect(merge_request.merge_request_assignees[0].state).to eq('attention_requested')
|
||||
expect(merge_request.merge_request_assignees[0].updated_state_by).to eq(user)
|
||||
end
|
||||
|
||||
it 'keeps original assignees state' do
|
||||
update_merge_request({
|
||||
assignee_ids: [user3.id]
|
||||
})
|
||||
|
||||
expect(merge_request.find_assignee(user3).state).to eq('unreviewed')
|
||||
end
|
||||
|
||||
it 'uses reviewers state if it is same user as new assignee' do
|
||||
merge_request.reviewers << user2
|
||||
|
||||
update_merge_request({
|
||||
assignee_ids: [user2.id]
|
||||
})
|
||||
|
||||
expect(merge_request.merge_request_assignees[0].state).to eq('unreviewed')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when adding time spent' do
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Projects::AndroidTargetPlatformDetectorService do
|
||||
let_it_be(:project) { build(:project) }
|
||||
|
||||
subject { described_class.new(project).execute }
|
||||
|
||||
before do
|
||||
allow(Gitlab::FileFinder).to receive(:new) { finder }
|
||||
end
|
||||
|
||||
context 'when project is not an Android project' do
|
||||
let(:finder) { instance_double(Gitlab::FileFinder, find: []) }
|
||||
|
||||
it { is_expected.to be_nil }
|
||||
end
|
||||
|
||||
context 'when project is an Android project' do
|
||||
let(:finder) { instance_double(Gitlab::FileFinder) }
|
||||
|
||||
before do
|
||||
query = described_class::MANIFEST_FILE_SEARCH_QUERY
|
||||
allow(finder).to receive(:find).with(query) { [instance_double(Gitlab::Search::FoundBlob)] }
|
||||
end
|
||||
|
||||
it { is_expected.to eq :android }
|
||||
end
|
||||
end
|
|
@ -5,25 +5,38 @@ require 'spec_helper'
|
|||
RSpec.describe Projects::RecordTargetPlatformsService, '#execute' do
|
||||
let_it_be(:project) { create(:project) }
|
||||
|
||||
subject(:execute) { described_class.new(project).execute }
|
||||
let(:detector_service) { Projects::AppleTargetPlatformDetectorService }
|
||||
|
||||
context 'when project is an XCode project' do
|
||||
def project_setting
|
||||
ProjectSetting.find_by_project_id(project.id)
|
||||
end
|
||||
subject(:execute) { described_class.new(project, detector_service).execute }
|
||||
|
||||
context 'when detector returns target platform values' do
|
||||
let(:detector_result) { [:ios, :osx] }
|
||||
let(:service_result) { detector_result.map(&:to_s) }
|
||||
|
||||
before do
|
||||
double = instance_double(Projects::AppleTargetPlatformDetectorService, execute: [:ios, :osx])
|
||||
allow(Projects::AppleTargetPlatformDetectorService).to receive(:new) { double }
|
||||
double = instance_double(detector_service, execute: detector_result)
|
||||
allow(detector_service).to receive(:new) { double }
|
||||
end
|
||||
|
||||
it 'creates a new setting record for the project', :aggregate_failures do
|
||||
expect { execute }.to change { ProjectSetting.count }.from(0).to(1)
|
||||
expect(ProjectSetting.last.target_platforms).to match_array(%w(ios osx))
|
||||
shared_examples 'saves and returns detected target platforms' do
|
||||
it 'creates a new setting record for the project', :aggregate_failures do
|
||||
expect { execute }.to change { ProjectSetting.count }.from(0).to(1)
|
||||
expect(ProjectSetting.last.target_platforms).to match_array(service_result)
|
||||
end
|
||||
|
||||
it 'returns the array of stored target platforms' do
|
||||
expect(execute).to match_array service_result
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns array of detected target platforms' do
|
||||
expect(execute).to match_array %w(ios osx)
|
||||
it_behaves_like 'saves and returns detected target platforms'
|
||||
|
||||
context 'when detector returns a non-array value' do
|
||||
let(:detector_service) { Projects::AndroidTargetPlatformDetectorService }
|
||||
let(:detector_result) { :android }
|
||||
let(:service_result) { [detector_result.to_s] }
|
||||
|
||||
it_behaves_like 'saves and returns detected target platforms'
|
||||
end
|
||||
|
||||
context 'when a project has an existing setting record' do
|
||||
|
@ -31,6 +44,10 @@ RSpec.describe Projects::RecordTargetPlatformsService, '#execute' do
|
|||
create(:project_setting, project: project, target_platforms: saved_target_platforms)
|
||||
end
|
||||
|
||||
def project_setting
|
||||
ProjectSetting.find_by_project_id(project.id)
|
||||
end
|
||||
|
||||
context 'when target platforms changed' do
|
||||
let(:saved_target_platforms) { %w(tvos) }
|
||||
|
||||
|
@ -81,23 +98,44 @@ RSpec.describe Projects::RecordTargetPlatformsService, '#execute' do
|
|||
it_behaves_like 'tracks experiment assignment event'
|
||||
end
|
||||
|
||||
context 'experiment control' do
|
||||
before do
|
||||
stub_experiments(build_ios_app_guide_email: :control)
|
||||
end
|
||||
|
||||
shared_examples 'does not send email' do
|
||||
it 'does not execute a Projects::InProductMarketingCampaignEmailsService' do
|
||||
expect(Projects::InProductMarketingCampaignEmailsService).not_to receive(:new)
|
||||
|
||||
execute
|
||||
end
|
||||
end
|
||||
|
||||
context 'experiment control' do
|
||||
before do
|
||||
stub_experiments(build_ios_app_guide_email: :control)
|
||||
end
|
||||
|
||||
it_behaves_like 'does not send email'
|
||||
it_behaves_like 'tracks experiment assignment event'
|
||||
end
|
||||
|
||||
context 'when project is not an iOS project' do
|
||||
let(:detector_service) { Projects::AppleTargetPlatformDetectorService }
|
||||
let(:detector_result) { :android }
|
||||
|
||||
before do
|
||||
stub_experiments(build_ios_app_guide_email: :candidate)
|
||||
end
|
||||
|
||||
it_behaves_like 'does not send email'
|
||||
|
||||
it 'does not track experiment assignment event', :experiment do
|
||||
expect(experiment(:build_ios_app_guide_email))
|
||||
.not_to track(:assignment)
|
||||
|
||||
execute
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when project is not an XCode project' do
|
||||
context 'when detector does not return any target platform values' do
|
||||
before do
|
||||
double = instance_double(Projects::AppleTargetPlatformDetectorService, execute: [])
|
||||
allow(Projects::AppleTargetPlatformDetectorService).to receive(:new).with(project) { double }
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.shared_examples 'having reviewer state' do
|
||||
describe 'mr_attention_requests feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(mr_attention_requests: false)
|
||||
end
|
||||
|
||||
it { is_expected.to have_attributes(state: 'unreviewed') }
|
||||
end
|
||||
|
||||
describe 'mr_attention_requests feature flag is enabled' do
|
||||
it { is_expected.to have_attributes(state: 'attention_requested') }
|
||||
end
|
||||
end
|
|
@ -7,11 +7,11 @@ RSpec.describe Projects::RecordTargetPlatformsWorker do
|
|||
|
||||
let_it_be(:swift) { create(:programming_language, name: 'Swift') }
|
||||
let_it_be(:objective_c) { create(:programming_language, name: 'Objective-C') }
|
||||
let_it_be(:java) { create(:programming_language, name: 'Java') }
|
||||
let_it_be(:kotlin) { create(:programming_language, name: 'Kotlin') }
|
||||
let_it_be(:project) { create(:project, :repository, detected_repository_languages: true) }
|
||||
|
||||
let(:worker) { described_class.new }
|
||||
let(:service_result) { %w(ios osx watchos) }
|
||||
let(:service_double) { instance_double(Projects::RecordTargetPlatformsService, execute: service_result) }
|
||||
let(:lease_key) { "#{described_class.name.underscore}:#{project.id}" }
|
||||
let(:lease_timeout) { described_class::LEASE_TIMEOUT }
|
||||
|
||||
|
@ -21,16 +21,20 @@ RSpec.describe Projects::RecordTargetPlatformsWorker do
|
|||
stub_exclusive_lease(lease_key, timeout: lease_timeout)
|
||||
end
|
||||
|
||||
shared_examples 'performs detection' do
|
||||
it 'creates and executes a Projects::RecordTargetPlatformService instance for the project', :aggregate_failures do
|
||||
expect(Projects::RecordTargetPlatformsService).to receive(:new).with(project) { service_double }
|
||||
shared_examples 'performs detection' do |detector_service_class|
|
||||
let(:service_double) { instance_double(detector_service_class, execute: service_result) }
|
||||
|
||||
it "creates and executes a #{detector_service_class} instance for the project", :aggregate_failures do
|
||||
expect(Projects::RecordTargetPlatformsService).to receive(:new)
|
||||
.with(project, detector_service_class) { service_double }
|
||||
expect(service_double).to receive(:execute)
|
||||
|
||||
perform
|
||||
end
|
||||
|
||||
it 'logs extra metadata on done', :aggregate_failures do
|
||||
expect(Projects::RecordTargetPlatformsService).to receive(:new).with(project) { service_double }
|
||||
expect(Projects::RecordTargetPlatformsService).to receive(:new)
|
||||
.with(project, detector_service_class) { service_double }
|
||||
expect(worker).to receive(:log_extra_metadata_on_done).with(:target_platforms, service_result)
|
||||
|
||||
perform
|
||||
|
@ -45,19 +49,68 @@ RSpec.describe Projects::RecordTargetPlatformsWorker do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when project uses Swift programming language' do
|
||||
let!(:repository_language) { create(:repository_language, project: project, programming_language: swift) }
|
||||
|
||||
include_examples 'performs detection'
|
||||
def create_language(language)
|
||||
create(:repository_language, project: project, programming_language: language)
|
||||
end
|
||||
|
||||
context 'when project uses Objective-C programming language' do
|
||||
let!(:repository_language) { create(:repository_language, project: project, programming_language: objective_c) }
|
||||
context 'when project uses programming language for Apple platform' do
|
||||
let(:service_result) { %w(ios osx watchos) }
|
||||
|
||||
include_examples 'performs detection'
|
||||
context 'when project uses Swift programming language' do
|
||||
before do
|
||||
create_language(swift)
|
||||
end
|
||||
|
||||
it_behaves_like 'performs detection', Projects::AppleTargetPlatformDetectorService
|
||||
end
|
||||
|
||||
context 'when project uses Objective-C programming language' do
|
||||
before do
|
||||
create_language(objective_c)
|
||||
end
|
||||
|
||||
it_behaves_like 'performs detection', Projects::AppleTargetPlatformDetectorService
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the project does not contain programming languages for Apple platforms' do
|
||||
context 'when project uses programming language for Android platform' do
|
||||
let(:feature_enabled) { true }
|
||||
let(:service_result) { %w(android) }
|
||||
|
||||
before do
|
||||
stub_feature_flags(detect_android_projects: feature_enabled)
|
||||
end
|
||||
|
||||
context 'when project uses Java' do
|
||||
before do
|
||||
create_language(java)
|
||||
end
|
||||
|
||||
it_behaves_like 'performs detection', Projects::AndroidTargetPlatformDetectorService
|
||||
|
||||
context 'when feature flag is disabled' do
|
||||
let(:feature_enabled) { false }
|
||||
|
||||
it_behaves_like 'does nothing'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when project uses Kotlin' do
|
||||
before do
|
||||
create_language(kotlin)
|
||||
end
|
||||
|
||||
it_behaves_like 'performs detection', Projects::AndroidTargetPlatformDetectorService
|
||||
|
||||
context 'when feature flag is disabled' do
|
||||
let(:feature_enabled) { false }
|
||||
|
||||
it_behaves_like 'does nothing'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the project does not use programming languages for Apple or Android platforms' do
|
||||
it_behaves_like 'does nothing'
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in New Issue