Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-02-03 18:09:25 +00:00
parent 6cbb93596d
commit 1743439667
79 changed files with 807 additions and 232 deletions

View File

@ -17,7 +17,7 @@ stages:
# in cases where jobs require Docker-in-Docker, the job
# definition must be extended with `.use-docker-in-docker`
default:
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.7.2-golang-1.14-git-2.29-lfs-2.9-chrome-87-node-14.15-yarn-1.22-postgresql-11-graphicsmagick-1.3.34"
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.7.2.patched-golang-1.14-git-2.29-lfs-2.9-chrome-87-node-14.15-yarn-1.22-postgresql-11-graphicsmagick-1.3.34"
tags:
- gitlab-org
# All jobs are interruptible by default

View File

@ -1,6 +1,6 @@
cloud-native-image:
extends: .cng:rules
image: ruby:2.7-alpine
image: ${GITLAB_DEPENDENCY_PROXY}ruby:2.7-alpine
dependencies: []
stage: post-test
variables:

View File

@ -2,7 +2,7 @@
extends:
- .default-retry
- .docs:rules:review-docs
image: ruby:2.7-alpine
image: ${GITLAB_DEPENDENCY_PROXY}ruby:2.7-alpine
stage: review
needs: []
variables:

View File

@ -259,13 +259,13 @@ coverage-frontend:
qa-frontend-node:10:
extends: .qa-frontend-node
image: node:dubnium
image: ${GITLAB_DEPENDENCY_PROXY}node:dubnium
qa-frontend-node:latest:
extends:
- .qa-frontend-node
- .frontend:rules:qa-frontend-node-latest
image: node:latest
image: ${GITLAB_DEPENDENCY_PROXY}node:latest
webpack-dev-server:
extends:

View File

@ -79,7 +79,7 @@
policy: pull
.use-pg11:
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.7.2-golang-1.14-git-2.29-lfs-2.9-chrome-87-node-14.15-yarn-1.22-postgresql-11-graphicsmagick-1.3.34"
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.7.2.patched-golang-1.14-git-2.29-lfs-2.9-chrome-87-node-14.15-yarn-1.22-postgresql-11-graphicsmagick-1.3.34"
services:
- name: postgres:11.6
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
@ -88,7 +88,7 @@
POSTGRES_HOST_AUTH_METHOD: trust
.use-pg12:
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.7.2-golang-1.14-git-2.29-lfs-2.9-chrome-87-node-14.15-yarn-1.22-postgresql-12-graphicsmagick-1.3.34"
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.7.2.patched-golang-1.14-git-2.29-lfs-2.9-chrome-87-node-14.15-yarn-1.22-postgresql-12-graphicsmagick-1.3.34"
services:
- name: postgres:12
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
@ -97,7 +97,7 @@
POSTGRES_HOST_AUTH_METHOD: trust
.use-pg11-ee:
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.7.2-golang-1.14-git-2.29-lfs-2.9-chrome-87-node-14.15-yarn-1.22-postgresql-11-graphicsmagick-1.3.34"
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.7.2.patched-golang-1.14-git-2.29-lfs-2.9-chrome-87-node-14.15-yarn-1.22-postgresql-11-graphicsmagick-1.3.34"
services:
- name: postgres:11.6
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
@ -108,7 +108,7 @@
POSTGRES_HOST_AUTH_METHOD: trust
.use-pg12-ee:
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.7.2-golang-1.14-git-2.29-lfs-2.9-chrome-87-node-14.15-yarn-1.22-postgresql-12-graphicsmagick-1.3.34"
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.7.2.patched-golang-1.14-git-2.29-lfs-2.9-chrome-87-node-14.15-yarn-1.22-postgresql-12-graphicsmagick-1.3.34"
services:
- name: postgres:12
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
@ -132,7 +132,7 @@
FOSS_ONLY: '1'
.use-docker-in-docker:
image: docker:${DOCKER_VERSION}
image: ${GITLAB_DEPENDENCY_PROXY}docker:${DOCKER_VERSION}
services:
- docker:${DOCKER_VERSION}-dind
variables:

View File

@ -1,5 +1,5 @@
.notify-slack:
image: alpine
image: ${GITLAB_DEPENDENCY_PROXY}alpine
stage: notify
dependencies: []
cache: {}

View File

@ -47,7 +47,7 @@ update-qa-cache:
policy: push # We want to rebuild the cache from scratch to ensure stale dependencies are cleaned up.
.package-and-qa-base:
image: ruby:2.7-alpine
image: ${GITLAB_DEPENDENCY_PROXY}ruby:2.7-alpine
stage: qa
retry: 0
script:

View File

@ -4,7 +4,7 @@
.merge-train-sync:
# We don't need/want any global before/after commands, so we overwrite these
# settings.
image: alpine:edge
image: ${GITLAB_DEPENDENCY_PROXY}alpine:edge
stage: sync
before_script:
- apk add --no-cache --update curl bash jq

View File

@ -25,7 +25,7 @@ review-build-cng:
extends:
- .default-retry
- .review:rules:review-build-cng
image: ruby:2.7-alpine
image: ${GITLAB_DEPENDENCY_PROXY}ruby:2.7-alpine
stage: review-prepare
before_script:
- source ./scripts/utils.sh
@ -199,7 +199,7 @@ review-performance:
parallel-spec-reports:
extends:
- .review:rules:mr-only-manual
image: ruby:2.7-alpine
image: ${GITLAB_DEPENDENCY_PROXY}ruby:2.7-alpine
stage: post-qa
dependencies: ["review-qa-all"]
variables:

View File

@ -26,7 +26,7 @@ cache gems:
dont-interrupt-me:
extends: .setup:rules:dont-interrupt-me
stage: sync
image: alpine:edge
image: ${GITLAB_DEPENDENCY_PROXY}alpine:edge
interruptible: false
variables:
GIT_STRATEGY: none
@ -52,7 +52,7 @@ no_ee_check:
verify-tests-yml:
extends:
- .setup:rules:verify-tests-yml
image: ruby:2.7-alpine
image: ${GITLAB_DEPENDENCY_PROXY}ruby:2.7-alpine
stage: test
needs: []
script:
@ -61,7 +61,7 @@ verify-tests-yml:
- scripts/verify-tff-mapping
.detect-test-base:
image: ruby:2.7
image: ${GITLAB_DEPENDENCY_PROXY}ruby:2.7
needs: []
stage: prepare
script:

View File

@ -1,5 +1,5 @@
.tests-metadata-state:
image: ruby:2.7
image: ${GITLAB_DEPENDENCY_PROXY}ruby:2.7
before_script:
- source scripts/utils.sh
artifacts:

View File

@ -1,6 +1,6 @@
workhorse:
extends: .workhorse:rules:workhorse
image: golang:1.14
image: ${GITLAB_DEPENDENCY_PROXY}golang:1.14
stage: test
needs: []
script:

View File

@ -350,7 +350,7 @@ end
group :development, :test do
gem 'deprecation_toolkit', '~> 1.5.1', require: false
gem 'bullet', '~> 6.1.0'
gem 'bullet', '~> 6.1.3'
gem 'gitlab-pry-byebug', platform: :mri, require: ['pry-byebug', 'pry-byebug/pry_remote_ext']
gem 'pry-rails', '~> 0.3.9'
gem 'pry-remote'

View File

@ -147,7 +147,7 @@ GEM
brakeman (4.2.1)
browser (4.2.0)
builder (3.2.4)
bullet (6.1.0)
bullet (6.1.3)
activesupport (>= 3.0.0)
uniform_notifier (~> 1.11)
bundler-audit (0.7.0.1)
@ -1305,7 +1305,7 @@ DEPENDENCIES
bootstrap_form (~> 4.2.0)
brakeman (~> 4.2)
browser (~> 4.2)
bullet (~> 6.1.0)
bullet (~> 6.1.3)
bundler-audit (~> 0.7.0.1)
capybara (~> 3.34.0)
capybara-screenshot (~> 1.0.22)

View File

@ -1,4 +1,4 @@
import { __ } from '~/locale';
import { __, s__ } from '~/locale';
export const GENERIC_ERROR = __('Something went wrong on our end. Please try again!');
@ -9,8 +9,8 @@ export const DIFF_FILE_HEADER = {
};
export const DIFF_FILE = {
tooLarge: __('MRDiffFile|Changes are too large to be shown.'),
blobView: __('MRDiffFile|View file @ %{commitSha}'),
tooLarge: s__('MRDiffFile|Changes are too large to be shown.'),
blobView: s__('MRDiffFile|View file @ %{commitSha}'),
editInFork: __(
"You're not allowed to %{tag_start}edit%{tag_end} files in this project directly. Please fork this project, make your changes there, and submit a merge request.",
),

View File

@ -159,7 +159,12 @@ export default {
[types.SET_LINE_DISCUSSIONS_FOR_FILE](state, { discussion, diffPositionByLineCode, hash }) {
const { latestDiff } = state;
const discussionLineCodes = [discussion.line_code, ...(discussion.line_codes || [])];
const originalStartLineCode = discussion.original_position?.line_range?.start?.line_code;
const discussionLineCodes = [
discussion.line_code,
originalStartLineCode,
...(discussion.line_codes || []),
];
const fileHash = discussion.diff_file.file_hash;
const lineCheck = (line) =>
discussionLineCodes.some(

View File

@ -95,10 +95,7 @@ export default {
</script>
<template>
<div>
<!-- helpHtml is trusted input -->
<div v-if="helpHtml" v-safe-html:[$options.helpHtmlConfig]="helpHtml"></div>
<div class="gl-mb-3">
<override-dropdown
v-if="defaultState !== null"
:inherit-from-id="defaultState.id"
@ -107,80 +104,91 @@ export default {
@change="setOverride"
/>
<active-checkbox v-if="propsSource.showActive" :key="`${currentKey}-active-checkbox`" />
<jira-trigger-fields
v-if="isJira"
:key="`${currentKey}-jira-trigger-fields`"
v-bind="propsSource.triggerFieldsProps"
/>
<trigger-fields
v-else-if="propsSource.triggerEvents.length"
:key="`${currentKey}-trigger-fields`"
:events="propsSource.triggerEvents"
:type="propsSource.type"
/>
<dynamic-field
v-for="field in propsSource.fields"
:key="`${currentKey}-${field.name}`"
v-bind="field"
/>
<jira-issues-fields
v-if="showJiraIssuesFields"
:key="`${currentKey}-jira-issues-fields`"
v-bind="propsSource.jiraIssuesProps"
/>
<div v-if="isEditable" class="footer-block row-content-block">
<template v-if="isInstanceOrGroupLevel">
<gl-button
v-gl-modal.confirmSaveIntegration
category="primary"
variant="success"
:loading="isSaving"
:disabled="isDisabled"
data-qa-selector="save_changes_button"
>
{{ __('Save changes') }}
</gl-button>
<confirmation-modal @submit="onSaveClick" />
</template>
<gl-button
v-else
category="primary"
variant="success"
type="submit"
:loading="isSaving"
:disabled="isDisabled"
data-qa-selector="save_changes_button"
@click.prevent="onSaveClick"
>
{{ __('Save changes') }}
</gl-button>
<div class="row">
<div class="col-lg-4"></div>
<gl-button
v-if="propsSource.canTest"
:loading="isTesting"
:disabled="isDisabled"
:href="propsSource.testPath"
@click.prevent="onTestClick"
>
{{ __('Test settings') }}
</gl-button>
<div class="col-lg-8">
<!-- helpHtml is trusted input -->
<div v-if="helpHtml" v-safe-html:[$options.helpHtmlConfig]="helpHtml"></div>
<template v-if="showReset">
<gl-button
v-gl-modal.confirmResetIntegration
category="secondary"
variant="default"
:loading="isResetting"
:disabled="isDisabled"
data-testid="reset-button"
>
{{ __('Reset') }}
</gl-button>
<reset-confirmation-modal @reset="onResetClick" />
</template>
<active-checkbox v-if="propsSource.showActive" :key="`${currentKey}-active-checkbox`" />
<jira-trigger-fields
v-if="isJira"
:key="`${currentKey}-jira-trigger-fields`"
v-bind="propsSource.triggerFieldsProps"
/>
<trigger-fields
v-else-if="propsSource.triggerEvents.length"
:key="`${currentKey}-trigger-fields`"
:events="propsSource.triggerEvents"
:type="propsSource.type"
/>
<dynamic-field
v-for="field in propsSource.fields"
:key="`${currentKey}-${field.name}`"
v-bind="field"
/>
<jira-issues-fields
v-if="showJiraIssuesFields"
:key="`${currentKey}-jira-issues-fields`"
v-bind="propsSource.jiraIssuesProps"
/>
<div v-if="isEditable" class="footer-block row-content-block">
<template v-if="isInstanceOrGroupLevel">
<gl-button
v-gl-modal.confirmSaveIntegration
category="primary"
variant="success"
:loading="isSaving"
:disabled="isDisabled"
data-qa-selector="save_changes_button"
>
{{ __('Save changes') }}
</gl-button>
<confirmation-modal @submit="onSaveClick" />
</template>
<gl-button
v-else
category="primary"
variant="success"
type="submit"
:loading="isSaving"
:disabled="isDisabled"
data-qa-selector="save_changes_button"
@click.prevent="onSaveClick"
>
{{ __('Save changes') }}
</gl-button>
<gl-button class="btn-cancel" :href="propsSource.cancelPath">{{ __('Cancel') }}</gl-button>
<gl-button
v-if="propsSource.canTest"
:loading="isTesting"
:disabled="isDisabled"
:href="propsSource.testPath"
@click.prevent="onTestClick"
>
{{ __('Test settings') }}
</gl-button>
<template v-if="showReset">
<gl-button
v-gl-modal.confirmResetIntegration
category="secondary"
variant="default"
:loading="isResetting"
:disabled="isDisabled"
data-testid="reset-button"
>
{{ __('Reset') }}
</gl-button>
<reset-confirmation-modal @reset="onResetClick" />
</template>
<gl-button class="btn-cancel" :href="propsSource.cancelPath">{{
__('Cancel')
}}</gl-button>
</div>
</div>
</div>
</div>
</template>

View File

@ -1,5 +1,10 @@
<script>
import { GlBadge } from '@gitlab/ui';
export default {
components: {
GlBadge,
},
props: {
duration: {
type: String,
@ -9,7 +14,7 @@ export default {
};
</script>
<template>
<div class="log-duration-badge rounded align-self-start px-2 ml-2 flex-shrink-0 ws-normal">
<gl-badge>
{{ duration }}
</div>
</gl-badge>
</template>

View File

@ -143,6 +143,9 @@ export default {
trackingLabel() {
return slugifyWithUnderscore(`${this.commentButtonTitle} button`);
},
hasCloseAndCommentButton() {
return !this.glFeatures.removeCommentCloseReopen;
},
},
watch: {
note(newNote) {
@ -405,7 +408,7 @@ export default {
</div>
<gl-button
v-if="canToggleIssueState"
v-if="hasCloseAndCommentButton && canToggleIssueState"
:loading="isToggleStateButtonLoading"
category="secondary"
:variant="buttonVariant"

View File

@ -0,0 +1,11 @@
# frozen_string_literal: true
module CommentAndCloseFlag
extend ActiveSupport::Concern
included do
before_action do
push_frontend_feature_flag(:remove_comment_close_reopen, @group)
end
end
end

View File

@ -9,6 +9,7 @@ class Projects::IssuesController < Projects::ApplicationController
include IssuesCalendar
include SpammableActions
include RecordUserLastActivity
include CommentAndCloseFlag
ISSUES_EXCEPT_ACTIONS = %i[index calendar new create bulk_update import_csv export_csv service_desk].freeze
SET_ISSUEABLES_INDEX_ONLY_ACTIONS = %i[index calendar service_desk].freeze

View File

@ -11,6 +11,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
include RecordUserLastActivity
include SourcegraphDecorator
include DiffHelper
include CommentAndCloseFlag
skip_before_action :merge_request, only: [:index, :bulk_update, :export_csv]
before_action :apply_diff_view_cookie!, only: [:show]
@ -22,7 +23,8 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
:coverage_reports,
:terraform_reports,
:accessibility_reports,
:codequality_reports
:codequality_reports,
:codequality_mr_diff_reports
]
before_action :set_issuables_index, only: [:index]
before_action :authenticate_user!, only: [:assign_related_issues]
@ -66,7 +68,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
:toggle_award_emoji, :toggle_subscription, :update
]
feature_category :code_testing, [:test_reports, :coverage_reports]
feature_category :code_testing, [:test_reports, :coverage_reports, :codequality_mr_diff_reports]
feature_category :accessibility_testing, [:accessibility_reports]
feature_category :infrastructure_as_code, [:terraform_reports]
@ -195,6 +197,10 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
end
end
def codequality_mr_diff_reports
reports_response(@merge_request.find_codequality_mr_diff_reports)
end
def codequality_reports
reports_response(@merge_request.compare_codequality_reports)
end

View File

@ -49,7 +49,7 @@ module Ci
end
def filter_by_scope(builds)
return filter_by_statuses!(params[:scope], builds) if params[:scope].is_a?(Array)
return filter_by_statuses!(builds) if params[:scope].is_a?(Array)
case params[:scope]
when 'pending'
@ -63,7 +63,7 @@ module Ci
end
end
def filter_by_statuses!(statuses, builds)
def filter_by_statuses!(builds)
unknown_statuses = params[:scope] - ::CommitStatus::AVAILABLE_STATUSES
raise ArgumentError, 'Scope contains invalid value(s)' unless unknown_statuses.empty?

View File

@ -1,5 +1,24 @@
# frozen_string_literal: true
# Backing store for GitLab session data.
#
# The raw session information is stored by the Rails session store
# (config/initializers/session_store.rb). These entries are accessible by the
# rack_key_name class method and consistute the base of the session data
# entries. All other entries in the session store can be traced back to these
# entries.
#
# After a user logs in (config/initializers/warden.rb) a further entry is made
# in Redis. This entry holds a record of the user's logged in session. These
# are accessible with the key_name(user_id, session_id) class method. These
# entries will expire. Lookups to these entries are lazilly cleaned on future
# user access.
#
# There is a reference to all sessions that belong to a specific user. A
# user may login through multiple browsers/devices and thus record multiple
# login sessions. These are accessible through the lookup_key_name(user_id)
# class method.
#
class ActiveSession
include ActiveModel::Model
@ -143,6 +162,10 @@ class ActiveSession
list(user).reject(&:is_impersonated)
end
def self.rack_key_name(session_id)
"#{Gitlab::Redis::SharedState::SESSION_NAMESPACE}:#{session_id}"
end
def self.key_name(user_id, session_id = '*')
"#{Gitlab::Redis::SharedState::USER_SESSIONS_NAMESPACE}:#{user_id}:#{session_id}"
end
@ -197,7 +220,7 @@ class ActiveSession
end
def self.rack_session_keys(rack_session_ids)
rack_session_ids.map { |session_id| "#{Gitlab::Redis::SharedState::SESSION_NAMESPACE}:#{session_id}" }
rack_session_ids.map { |session_id| rack_key_name(session_id)}
end
def self.raw_active_session_entries(redis, session_ids, user_id)

View File

@ -44,6 +44,8 @@ class ApplicationSetting < ApplicationRecord
serialize :domain_denylist, Array # rubocop:disable Cop/ActiveRecordSerialize
serialize :repository_storages # rubocop:disable Cop/ActiveRecordSerialize
serialize :asset_proxy_allowlist, Array # rubocop:disable Cop/ActiveRecordSerialize
# See https://gitlab.com/gitlab-org/gitlab/-/issues/300916
serialize :asset_proxy_whitelist, Array # rubocop:disable Cop/ActiveRecordSerialize
cache_markdown_field :sign_in_text
cache_markdown_field :help_page_text

View File

@ -1003,8 +1003,8 @@ module Ci
has_reports?(Ci::JobArtifact.coverage_reports)
end
def has_codequality_reports?
pipeline_artifacts&.has_report?(:code_quality)
def has_codequality_mr_diff_report?
pipeline_artifacts&.has_report?(:code_quality_mr_diff)
end
def can_generate_codequality_reports?

View File

@ -15,7 +15,7 @@ module Ci
DEFAULT_FILE_NAMES = {
code_coverage: 'code_coverage.json',
code_quality: 'code_quality.json'
code_quality_mr_diff: 'code_quality_mr_diff.json'
}.freeze
belongs_to :project, class_name: "Project", inverse_of: :pipeline_artifacts
@ -32,7 +32,7 @@ module Ci
enum file_type: {
code_coverage: 1,
code_quality: 2
code_quality_mr_diff: 2
}
class << self

View File

@ -1484,6 +1484,24 @@ class MergeRequest < ApplicationRecord
compare_reports(Ci::GenerateCoverageReportsService)
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
# TODO: this method and compare_test_reports use the same
# result type, which is handled by the controller's #reports_response.
# we should minimize mistakes by isolating the common parts.
# issue: https://gitlab.com/gitlab-org/gitlab/issues/34224
def find_codequality_mr_diff_reports
unless has_codequality_mr_diff_report?
return { status: :error, status_reason: 'This merge request does not have codequality mr diff reports' }
end
compare_reports(Ci::GenerateCodequalityMrDiffReportService)
end
def has_codequality_reports?
return false unless ::Gitlab::Ci::Features.display_quality_on_mr_diff?(project)

View File

@ -30,8 +30,8 @@ class ConfluenceService < Service
s_('ConfluenceService|Connect a Confluence Cloud Workspace to GitLab')
end
def detailed_description
return unless project.wiki_enabled?
def help
return unless project&.wiki_enabled?
if activated?
wiki_url = project.wiki.web_url

View File

@ -2,7 +2,7 @@
module Ci
module PipelineArtifacts
class CodeCoveragePresenter < ProcessablePresenter
class CodeCoveragePresenter < Gitlab::View::Presenter::Delegated
include Gitlab::Utils::StrongMemoize
def for_files(filenames)

View File

@ -0,0 +1,25 @@
# frozen_string_literal: true
module Ci
module PipelineArtifacts
class CodeQualityMrDiffPresenter < Gitlab::View::Presenter::Delegated
include Gitlab::Utils::StrongMemoize
def for_files(filenames)
quality_files = raw_report["files"].select { |key| filenames.include?(key) }
{ files: quality_files }
end
private
def raw_report
strong_memoize(:raw_report) do
self.each_blob do |blob|
Gitlab::Json.parse(blob).with_indifferent_access
end
end
end
end
end
end

View File

@ -0,0 +1,30 @@
# frozen_string_literal: true
module Ci
# TODO: a couple of points with this approach:
# + reuses existing architecture and reactive caching
# - it's not a report comparison and some comparing features must be turned off.
# see CompareReportsBaseService for more notes.
# issue: https://gitlab.com/gitlab-org/gitlab/issues/34224
class GenerateCodequalityMrDiffReportService < CompareReportsBaseService
def execute(base_pipeline, head_pipeline)
merge_request = MergeRequest.find_by_id(params[:id])
{
status: :parsed,
key: key(base_pipeline, head_pipeline),
data: head_pipeline.pipeline_artifacts.find_by_file_type(:code_quality_mr_diff).present.for_files(merge_request.new_paths)
}
rescue => e
Gitlab::ErrorTracking.track_exception(e, project_id: project.id)
{
status: :error,
key: key(base_pipeline, head_pipeline),
status_reason: _('An error occurred while fetching codequality mr diff reports.')
}
end
def latest?(base_pipeline, head_pipeline, data)
data&.fetch(:key, nil) == key(base_pipeline, head_pipeline)
end
end
end

View File

@ -1,16 +1,16 @@
# frozen_string_literal: true
module Ci
module PipelineArtifacts
class CreateQualityReportService
class CreateCodeQualityMrDiffReportService
def execute(pipeline)
return unless pipeline.can_generate_codequality_reports?
return if pipeline.has_codequality_reports?
return if pipeline.has_codequality_mr_diff_report?
file = build_carrierwave_file(pipeline)
pipeline.pipeline_artifacts.create!(
project_id: pipeline.project_id,
file_type: :code_quality,
file_type: :code_quality_mr_diff,
file_format: :raw,
size: file["tempfile"].size,
file: file,
@ -23,7 +23,7 @@ module Ci
def build_carrierwave_file(pipeline)
CarrierWaveStringFile.new_file(
file_content: build_quality_mr_diff_report(pipeline),
filename: Ci::PipelineArtifact::DEFAULT_FILE_NAMES.fetch(:code_quality),
filename: Ci::PipelineArtifact::DEFAULT_FILE_NAMES.fetch(:code_quality_mr_diff),
content_type: 'application/json'
)
end

View File

@ -126,3 +126,5 @@ module Groups
end
end
end
Groups::ImportExport::ExportService.prepend_if_ee('EE::Groups::ImportExport::ExportService')

View File

@ -123,3 +123,5 @@ module Groups
end
end
end
Groups::ImportExport::ImportService.prepend_if_ee('EE::Groups::ImportExport::ImportService')

View File

@ -3,6 +3,6 @@
.sub-header-block.bg-gray-light.gl-p-5
.tree-ref-holder.inline.vertical-align-middle
= render 'shared/ref_switcher', destination: 'graphs'
= link_to s_('Commits|History'), project_commits_path(@project, current_ref), class: 'btn gl-button'
= link_to s_('Commits|History'), project_commits_path(@project, current_ref), class: 'btn gl-button btn-default'
.js-contributors-graph{ class: container_class, 'data-project-graph-path': project_graph_path(@project, current_ref, format: :json),'data-project-branch': current_ref }

View File

@ -1,19 +1,14 @@
- if lookup_context.template_exists?('top', "projects/services/#{@service.to_param}", true)
= render "projects/services/#{@service.to_param}/top"
.row.gl-mt-3.gl-mb-3
.col-lg-4
%h3.page-title.gl-mt-0
= @service.title
- if @service.operating?
= sprite_icon('check', css_class: 'gl-text-green-500')
%h3.page-title
= @service.title
- if @service.operating?
= sprite_icon('check', css_class: 'gl-text-green-500')
- if @service.respond_to?(:detailed_description)
%p= @service.detailed_description
.col-lg-8
= form_for(@service, as: :service, url: scoped_integration_path(@service), method: :put, html: { class: 'gl-show-field-errors integration-settings-form js-integration-settings-form', data: { 'test-url' => test_project_service_path(@project, @service) } }) do |form|
= render 'shared/service_settings', form: form, integration: @service
%input{ id: 'services_redirect_to', type: 'hidden', name: 'redirect_to', value: request.referrer }
= form_for(@service, as: :service, url: scoped_integration_path(@service), method: :put, html: { class: 'gl-show-field-errors integration-settings-form js-integration-settings-form', data: { 'test-url' => test_project_service_path(@project, @service) } }) do |form|
= render 'shared/service_settings', form: form, integration: @service
%input{ id: 'services_redirect_to', type: 'hidden', name: 'redirect_to', value: request.referrer }
- if lookup_context.template_exists?('show', "projects/services/#{@service.to_param}", true)
%hr

View File

@ -43,10 +43,10 @@
- if current_user
%li.inline.label-subscription
- if label.can_subscribe_to_label_in_different_levels?
%button.js-unsubscribe-button.label-subscribe-button.btn.btn-default{ class: ('hidden' if status.unsubscribed?), data: { url: toggle_subscription_path, toggle: 'tooltip' }, title: tooltip_title }
%button.js-unsubscribe-button.gl-button.label-subscribe-button.btn.btn-default.gl-ml-3{ class: ('hidden' if status.unsubscribed?), data: { url: toggle_subscription_path, toggle: 'tooltip' }, title: tooltip_title }
%span= _('Unsubscribe')
.dropdown.dropdown-group-label{ class: ('hidden' unless status.unsubscribed?) }
%button.gl-button.label-subscribe-button.btn.btn-default{ data: { toggle: 'dropdown' } }
%button.gl-button.label-subscribe-button.btn.btn-default.gl-ml-3{ data: { toggle: 'dropdown' } }
%span
= _('Subscribe')
= sprite_icon('chevron-down')
@ -59,7 +59,7 @@
%button.js-subscribe-button.js-group-level.label-subscribe-button.btn.btn-default{ class: ('hidden' unless status.unsubscribed?), data: { status: status, url: toggle_subscription_group_label_path(label.group, label) } }
%span= _('Subscribe at group level')
- else
%button.gl-button.js-subscribe-button.label-subscribe-button.btn.btn-default{ data: { status: status, url: toggle_subscription_path, toggle: 'tooltip' }, title: tooltip_title }
%button.gl-button.js-subscribe-button.label-subscribe-button.btn.btn-default.gl-ml-3{ data: { status: status, url: toggle_subscription_path, toggle: 'tooltip' }, title: tooltip_title }
%span= label_subscription_toggle_button_text(label, @project)
= render 'shared/delete_label_modal', label: label

View File

@ -4,7 +4,7 @@
- if @default_integration
.js-vue-default-integration-settings{ data: integration_form_data(@default_integration, group: @group) }
.js-vue-integration-settings{ data: integration_form_data(integration, group: @group) }
.js-integration-help-html
.js-integration-help-html.gl-display-none
-# All content below will be repositioned in Vue
- if lookup_context.template_exists?('help', "projects/services/#{integration.to_param}", true)
= render "projects/services/#{integration.to_param}/help", subject: integration

View File

@ -1,10 +1,7 @@
- integration = local_assigns.fetch(:integration)
.row.gl-mt-3
.col-lg-4
%h3.page-title.gl-mt-0
= integration.title
%h3.page-title
= integration.title
.col-lg-8
= form_for integration, as: :service, url: scoped_integration_path(integration), method: :put, html: { class: 'gl-show-field-errors integration-settings-form js-integration-settings-form', data: { 'test-url' => scoped_test_integration_path(integration) } } do |form|
= render 'shared/service_settings', form: form, integration: integration
= form_for integration, as: :service, url: scoped_integration_path(integration), method: :put, html: { class: 'gl-show-field-errors integration-settings-form js-integration-settings-form', data: { 'test-url' => scoped_test_integration_path(integration) } } do |form|
= render 'shared/service_settings', form: form, integration: integration

View File

@ -12,7 +12,7 @@ module Ci
def perform(pipeline_id)
Ci::Pipeline.find_by_id(pipeline_id).try do |pipeline|
Ci::PipelineArtifacts::CreateQualityReportService.new.execute(pipeline)
Ci::PipelineArtifacts::CreateCodeQualityMrDiffReportService.new.execute(pipeline)
end
end
end

View File

@ -0,0 +1,5 @@
---
title: Add btn-default class for history button in the contributors page
merge_request: 52861
author: Yogi (@yo)
type: other

View File

@ -0,0 +1,5 @@
---
title: Fix spacing before toggle subscribe button on labels
merge_request: 52459
author: Yogi (@yo)
type: other

View File

@ -0,0 +1,5 @@
---
title: Update bullet gem version to 6.1.3
merge_request: 53217
author:
type: other

View File

@ -0,0 +1,5 @@
---
title: Change the badge design in the jobs page
merge_request: 53168
author:
type: changed

View File

@ -0,0 +1,5 @@
---
title: Fixed discussions on merge request commits not showing
merge_request: 53143
author:
type: fixed

View File

@ -0,0 +1,5 @@
---
title: Move integration inheritance override dropdown above grid layout
merge_request: 49325
author:
type: changed

View File

@ -0,0 +1,5 @@
---
title: Switch to correct localization function that strips namespaces
merge_request: 53244
author:
type: fixed

View File

@ -0,0 +1,8 @@
---
name: group_wiki_import_export
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/51873
rollout_issue_url:
milestone: '13.9'
type: development
group: group::editor
default_enabled: false

View File

@ -0,0 +1,8 @@
---
name: remove_comment_close_reopen
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/49614
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/198562
milestone: '13.9'
type: development
group: group::code review
default_enabled: false

View File

@ -124,9 +124,12 @@ class ObjectStoreSettings
target_config = common_config.merge(overrides.slice(*ALLOWED_OBJECT_STORE_OVERRIDES))
section = settings.try(store_type)
next unless uses_object_storage?(section)
# Admins can selectively disable object storage for a specific
# type as an override in the consolidated settings.
next unless overrides.fetch('enabled', true)
next unless section
if target_config['bucket'].blank?
if section['enabled'] && target_config['bucket'].blank?
missing_bucket_for(store_type)
next
end
@ -146,20 +149,6 @@ class ObjectStoreSettings
private
# Admins can selectively disable object storage for a specific type. If
# this hasn't been set, we assume that the consolidated settings
# should be used.
def uses_object_storage?(section)
# Use to_h to avoid https://gitlab.com/gitlab-org/gitlab/-/issues/286873
section = section.to_h
enabled_globally = section.fetch('enabled', false)
object_store_settings = section.fetch('object_store', {})
os_enabled = object_store_settings.fetch('enabled', true)
enabled_globally && os_enabled
end
# We only can use the common object storage settings if:
# 1. The common settings are defined
# 2. The legacy settings are not defined

View File

@ -18,6 +18,7 @@ resources :merge_requests, concerns: :awardable, except: [:new, :create, :show],
get :coverage_reports
get :terraform_reports
get :codequality_reports
get :codequality_mr_diff_reports
scope constraints: ->(req) { req.format == :json }, as: :json do
get :commits

View File

@ -160,6 +160,8 @@
- 1
- - group_wikis_git_garbage_collect
- 1
- - groups_schedule_bulk_repository_shard_moves
- 1
- - groups_update_repository_storage
- 1
- - hashed_storage

View File

@ -730,6 +730,13 @@ or report an issue.
### Object storage settings
WARNING:
With the following settings, Pages uses both NFS and Object Storage locations when deploying the
site. **Do not remove the existing NFS mount used by Pages** when applying these settings. For more
information, see the epics
[3901](https://gitlab.com/groups/gitlab-org/-/epics/3901#how-to-test-object-storage-integration-in-beta)
and [3910](https://gitlab.com/groups/gitlab-org/-/epics/3910).
The following settings are:
- Nested under `pages:` and then `object_store:` on source installations.

View File

@ -1,6 +1,6 @@
---
stage: none
group: unassigned
group: Engineering Productivity
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
---
@ -44,7 +44,7 @@ The source of truth for these workflow rules is defined in [`.gitlab-ci.yml`](ht
In general, pipelines for an MR fall into one or more of the following types,
depending on the changes made in the MR:
- [Docs-only MR pipeline](#docs-only-mr-pipeline): This is typically created for an MR that only changes documentation.
- [Documentation only MR pipeline](#documentation-only-mr-pipeline): This is typically created for an MR that only changes documentation.
- [Code-only MR pipeline](#code-only-mr-pipeline): This is typically created for an MR that only changes code, either backend or frontend.
- [Frontend-only MR pipeline](#frontend-only-mr-pipeline): This is typically created for an MR that only changes frontend code.
- [QA-only MR pipeline](#qa-only-mr-pipeline): This is typically created for an MR that only changes end to end tests related code.
@ -53,18 +53,22 @@ We use the [`rules:`](../ci/yaml/README.md#rules) and [`needs:`](../ci/yaml/READ
to determine the jobs that need to be run in a pipeline. Note that an MR that includes multiple types of changes would
have a pipelines that include jobs from multiple types (e.g. a combination of docs-only and code-only pipelines).
#### Docs-only MR pipeline
#### Documentation only MR pipeline
[Reference pipeline](https://gitlab.com/gitlab-org/gitlab/pipelines/135236627):
[Reference pipeline](https://gitlab.com/gitlab-org/gitlab/-/pipelines/250546928):
```mermaid
graph LR
subgraph "No needed jobs";
1-1["danger-review (2.3 minutes)"];
click 1-1 "https://app.periscopedata.com/app/gitlab/652085/Engineering-Productivity---Pipeline-Build-Durations?widget=8100542&udv=0"
1-50["docs lint (9 minutes)"];
click 1-50 "https://app.periscopedata.com/app/gitlab/652085/Engineering-Productivity---Pipeline-Build-Durations?widget=8356757&udv=0"
end
1-2["docs-lint markdown (1.5 minutes)"];
click 1-2 "https://app.periscopedata.com/app/gitlab/652085/Engineering-Productivity---Pipeline-Build-Durations?widget=10224335&udv=0"
1-3["docs-lint links (6 minutes)"];
click 1-3 "https://app.periscopedata.com/app/gitlab/652085/Engineering-Productivity---Pipeline-Build-Durations?widget=8356757&udv=0"
1-4["ui-docs-links lint (2.5 minutes)"];
click 1-4 "https://app.periscopedata.com/app/gitlab/652085/Engineering-Productivity---Pipeline-Build-Durations?widget=10823717&udv=1020379"
end
```
#### Code-only MR pipeline

View File

@ -103,3 +103,7 @@ module Gitlab
end
Gitlab::ImportExport.prepend_if_ee('EE::Gitlab::ImportExport')
# The methods in `Gitlab::ImportExport::GroupHelper` should be available as both
# instance and class methods.
Gitlab::ImportExport.extend_if_ee('Gitlab::ImportExport::GroupHelper')

View File

@ -19,3 +19,5 @@ module Gitlab
end
end
end
Gitlab::ImportExport::WikiRepoSaver.prepend_if_ee('EE::Gitlab::ImportExport::WikiRepoSaver')

View File

@ -3152,6 +3152,9 @@ msgstr ""
msgid "An error occurred while fetching branches. Retry the search."
msgstr ""
msgid "An error occurred while fetching codequality mr diff reports."
msgstr ""
msgid "An error occurred while fetching commits. Retry the search."
msgstr ""
@ -15907,6 +15910,9 @@ msgstr ""
msgid "Integrations|This integration, and inheriting projects were reset."
msgstr ""
msgid "Integrations|This requires mirroring your GitHub repository to this project. %{docs_link}"
msgstr ""
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""

View File

@ -130,13 +130,14 @@ RSpec.describe ObjectStoreSettings do
end
end
context 'when object storage is selectively disabled for artifacts' do
context 'when object storage is disabled for artifacts with no bucket' do
before do
config['artifacts'] = {
'enabled' => true,
'object_store' => {
'enabled' => false
}
'object_store' => {}
}
config['object_store']['objects']['artifacts'] = {
'enabled' => false
}
end

View File

@ -1118,6 +1118,108 @@ RSpec.describe Projects::MergeRequestsController do
end
end
describe 'GET codequality_mr_diff_reports' do
let_it_be(:merge_request) do
create(:merge_request,
:with_merge_request_pipeline,
target_project: project,
source_project: project)
end
let(:pipeline) do
create(:ci_pipeline,
:success,
project: merge_request.source_project,
ref: merge_request.source_branch,
sha: merge_request.diff_head_sha)
end
before do
allow_any_instance_of(MergeRequest)
.to receive(:find_codequality_mr_diff_reports)
.and_return(report)
allow_any_instance_of(MergeRequest)
.to receive(:actual_head_pipeline)
.and_return(pipeline)
end
subject(:get_codequality_mr_diff_reports) do
get :codequality_mr_diff_reports, params: {
namespace_id: project.namespace.to_param,
project_id: project,
id: merge_request.iid
},
format: :json
end
context 'permissions on a public project with private CI/CD' do
let(:project) { create :project, :repository, :public, :builds_private }
let(:report) { { status: :parsed, data: { 'files' => {} } } }
context 'while signed out' do
before do
sign_out(user)
end
it 'responds with a 404' do
get_codequality_mr_diff_reports
expect(response).to have_gitlab_http_status(:not_found)
expect(response.body).to be_blank
end
end
context 'while signed in as an unrelated user' do
before do
sign_in(create(:user))
end
it 'responds with a 404' do
get_codequality_mr_diff_reports
expect(response).to have_gitlab_http_status(:not_found)
expect(response.body).to be_blank
end
end
end
context 'when pipeline has jobs with codequality mr diff report' do
before do
allow_any_instance_of(MergeRequest)
.to receive(:has_codequality_mr_diff_report?)
.and_return(true)
end
context 'when processing codequality mr diff report is in progress' do
let(:report) { { status: :parsing } }
it 'sends polling interval' do
expect(Gitlab::PollingInterval).to receive(:set_header)
get_codequality_mr_diff_reports
end
it 'returns 204 HTTP status' do
get_codequality_mr_diff_reports
expect(response).to have_gitlab_http_status(:no_content)
end
end
context 'when processing codequality mr diff report is completed' do
let(:report) { { status: :parsed, data: { 'files' => {} } } }
it 'returns codequality mr diff report' do
get_codequality_mr_diff_reports
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to eq({ 'files' => {} })
end
end
end
end
describe 'GET terraform_reports' do
let_it_be(:merge_request) do
create(:merge_request,

View File

@ -49,12 +49,12 @@ FactoryBot.define do
size { 1.megabyte }
end
trait :with_codequality_report do
file_type { :code_quality }
trait :with_codequality_mr_diff_report do
file_type { :code_quality_mr_diff }
after(:build) do |artifact, _evaluator|
artifact.file = fixture_file_upload(
Rails.root.join('spec/fixtures/pipeline_artifacts/code_quality.json'), 'application/json')
Rails.root.join('spec/fixtures/pipeline_artifacts/code_quality_mr_diff.json'), 'application/json')
end
size { file.size }

View File

@ -155,9 +155,9 @@ FactoryBot.define do
end
end
trait :with_quality_report_artifact do
trait :with_codequality_mr_diff_report do
after(:build) do |pipeline, evaluator|
pipeline.pipeline_artifacts << build(:ci_pipeline_artifact, :with_codequality_report, pipeline: pipeline, project: pipeline.project)
pipeline.pipeline_artifacts << build(:ci_pipeline_artifact, :with_codequality_mr_diff_report, pipeline: pipeline, project: pipeline.project)
end
end

View File

@ -200,6 +200,18 @@ FactoryBot.define do
end
end
trait :with_codequality_mr_diff_reports do
after(:build) do |merge_request|
merge_request.head_pipeline = build(
:ci_pipeline,
:success,
:with_codequality_mr_diff_report,
project: merge_request.source_project,
ref: merge_request.source_branch,
sha: merge_request.diff_head_sha)
end
end
trait :with_terraform_reports do
after(:build) do |merge_request|
merge_request.head_pipeline = build(

View File

@ -8,6 +8,8 @@ RSpec.describe 'Thread Comments Issue', :js do
let(:issue) { create(:issue, project: project) }
before do
stub_feature_flags(remove_comment_close_reopen: false)
project.add_maintainer(user)
sign_in(user)

View File

@ -9,6 +9,7 @@ RSpec.describe 'Thread Comments Merge Request', :js do
before do
stub_feature_flags(remove_resolve_note: false)
stub_feature_flags(remove_comment_close_reopen: false)
project.add_maintainer(user)
sign_in(user)

View File

@ -42,9 +42,15 @@ RSpec.describe 'issue state', :js do
end
describe 'when open', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/297348' do
context 'when clicking the top `Close issue` button', :aggregate_failures do
let(:open_issue) { create(:issue, project: project) }
let(:open_issue) { create(:issue, project: project) }
it_behaves_like 'page with comment and close button', 'Close issue' do
def setup
visit project_issue_path(project, open_issue)
end
end
context 'when clicking the top `Close issue` button', :aggregate_failures do
before do
visit project_issue_path(project, open_issue)
end
@ -53,9 +59,8 @@ RSpec.describe 'issue state', :js do
end
context 'when clicking the bottom `Close issue` button', :aggregate_failures do
let(:open_issue) { create(:issue, project: project) }
before do
stub_feature_flags(remove_comment_close_reopen: false)
visit project_issue_path(project, open_issue)
end
@ -64,9 +69,15 @@ RSpec.describe 'issue state', :js do
end
describe 'when closed', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/297201' do
context 'when clicking the top `Reopen issue` button', :aggregate_failures do
let(:closed_issue) { create(:issue, project: project, state: 'closed') }
let(:closed_issue) { create(:issue, project: project, state: 'closed') }
it_behaves_like 'page with comment and close button', 'Reopen issue' do
def setup
visit project_issue_path(project, closed_issue)
end
end
context 'when clicking the top `Reopen issue` button', :aggregate_failures do
before do
visit project_issue_path(project, closed_issue)
end
@ -75,9 +86,8 @@ RSpec.describe 'issue state', :js do
end
context 'when clicking the bottom `Reopen issue` button', :aggregate_failures do
let(:closed_issue) { create(:issue, project: project, state: 'closed') }
before do
stub_feature_flags(remove_comment_close_reopen: false)
visit project_issue_path(project, closed_issue)
end

View File

@ -12,9 +12,15 @@ RSpec.describe 'User closes/reopens a merge request', :js, quarantine: 'https://
end
describe 'when open' do
context 'when clicking the top `Close merge request` link', :aggregate_failures do
let(:open_merge_request) { create(:merge_request, source_project: project, target_project: project) }
let(:open_merge_request) { create(:merge_request, source_project: project, target_project: project) }
it_behaves_like 'page with comment and close button', 'Close merge request' do
def setup
visit merge_request_path(open_merge_request)
end
end
context 'when clicking the top `Close merge request` link', :aggregate_failures do
before do
visit merge_request_path(open_merge_request)
end
@ -34,9 +40,8 @@ RSpec.describe 'User closes/reopens a merge request', :js, quarantine: 'https://
end
context 'when clicking the bottom `Close merge request` button', :aggregate_failures do
let(:open_merge_request) { create(:merge_request, source_project: project, target_project: project) }
before do
stub_feature_flags(remove_comment_close_reopen: false)
visit merge_request_path(open_merge_request)
end
@ -56,9 +61,22 @@ RSpec.describe 'User closes/reopens a merge request', :js, quarantine: 'https://
end
describe 'when closed' do
context 'when clicking the top `Reopen merge request` link', :aggregate_failures do
let(:closed_merge_request) { create(:merge_request, source_project: project, target_project: project, state: 'closed') }
let(:closed_merge_request) { create(:merge_request, source_project: project, target_project: project, state: 'closed') }
it_behaves_like 'page with comment and close button', 'Close merge request' do
def setup
visit merge_request_path(closed_merge_request)
within '.detail-page-header' do
click_button 'Toggle dropdown'
click_link 'Reopen merge request'
end
wait_for_requests
end
end
context 'when clicking the top `Reopen merge request` link', :aggregate_failures do
before do
visit merge_request_path(closed_merge_request)
end
@ -78,9 +96,8 @@ RSpec.describe 'User closes/reopens a merge request', :js, quarantine: 'https://
end
context 'when clicking the bottom `Reopen merge request` button', :aggregate_failures do
let(:closed_merge_request) { create(:merge_request, source_project: project, target_project: project, state: 'closed') }
before do
stub_feature_flags(remove_comment_close_reopen: false)
visit merge_request_path(closed_merge_request)
end

View File

@ -69,7 +69,13 @@ RSpec.describe 'Task Lists', :js do
wait_for_requests
expect(page).to have_selector(".md .task-list .task-list-item .task-list-item-checkbox")
expect(page).to have_selector('.btn-close')
end
it_behaves_like 'page with comment and close button', 'Close issue' do
def setup
visit_issue(project, issue)
wait_for_requests
end
end
it 'is only editable by author' do

View File

@ -1,16 +0,0 @@
{
"files": {
"demo.rb": [
{
"line": 5,
"description": "Method `new_array` has 8 arguments (exceeds 4 allowed). Consider refactoring.",
"severity": "major"
},
{
"line": 5,
"description": "Avoid parameter lists longer than 5 parameters. [8/5]",
"severity": "minor"
}
]
}
}

View File

@ -0,0 +1,23 @@
{
"files": {
"file_a.rb": [
{
"line": 10,
"description": "Avoid parameter lists longer than 5 parameters. [12/5]",
"severity": "major"
},
{
"line": 10,
"description": "Method `new_array` has 12 arguments (exceeds 4 allowed). Consider refactoring.",
"severity": "minor"
}
],
"file_b.rb": [
{
"line": 10,
"description": "This cop checks for methods with too many parameters.\nThe maximum number of parameters is configurable.\nKeyword arguments can optionally be excluded from the total count.",
"severity": "minor"
}
]
}
}

View File

@ -97,18 +97,18 @@ RSpec.describe Ci::PipelineArtifact, type: :model do
end
end
context 'when file_type is code_quality' do
let(:file_type) { :code_quality }
context 'when file_type is code_quality_mr_diff' do
let(:file_type) { :code_quality_mr_diff }
context 'when pipeline artifact has a quality report' do
let!(:pipeline_artifact) { create(:ci_pipeline_artifact, :with_codequality_report) }
context 'when pipeline artifact has a codequality mr diff report' do
let!(:pipeline_artifact) { create(:ci_pipeline_artifact, :with_codequality_mr_diff_report) }
it 'returns true' do
expect(pipeline_artifact).to be_truthy
end
end
context 'when pipeline artifact does not have a quality report' do
context 'when pipeline artifact does not have a codequality mr diff report' do
it 'returns false' do
expect(pipeline_artifact).to be_falsey
end
@ -145,14 +145,14 @@ RSpec.describe Ci::PipelineArtifact, type: :model do
end
end
context 'when file_type is code_quality' do
let(:file_type) { :code_quality }
context 'when file_type is code_quality_mr_diff' do
let(:file_type) { :code_quality_mr_diff }
context 'when pipeline artifact has a quality report' do
let!(:coverage_report) { create(:ci_pipeline_artifact, :with_codequality_report) }
let!(:coverage_report) { create(:ci_pipeline_artifact, :with_codequality_mr_diff_report) }
it 'returns a pipeline artifact with a quality report' do
expect(pipeline_artifact.file_type).to eq('code_quality')
expect(pipeline_artifact.file_type).to eq('code_quality_mr_diff')
end
end

View File

@ -3510,16 +3510,16 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
end
describe '#has_codequality_reports?' do
subject { pipeline.has_codequality_reports? }
describe '#has_codequality_mr_diff_report?' do
subject { pipeline.has_codequality_mr_diff_report? }
context 'when pipeline has a codequality artifact' do
let(:pipeline) { create(:ci_pipeline, :with_quality_report_artifact, :running, project: project) }
context 'when pipeline has a codequality mr diff report' do
let(:pipeline) { create(:ci_pipeline, :with_codequality_mr_diff_report, :running, project: project) }
it { expect(subject).to be_truthy }
end
context 'when pipeline does not have a codequality artifact' do
context 'when pipeline does not have a codequality mr diff report' do
let(:pipeline) { create(:ci_pipeline, :success, project: project) }
it { expect(subject).to be_falsey }

View File

@ -1982,6 +1982,30 @@ RSpec.describe MergeRequest, factory_default: :keep do
end
end
describe '#has_codequality_mr_diff_report?' do
subject { merge_request.has_codequality_mr_diff_report? }
context 'when head pipeline has codequality mr diff report' 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
let(:merge_request) { create(:merge_request) }
it { is_expected.to be_falsey }
end
end
describe '#has_codequality_reports?' do
subject { merge_request.has_codequality_reports? }
@ -2155,6 +2179,54 @@ RSpec.describe MergeRequest, factory_default: :keep do
end
end
describe '#find_codequality_mr_diff_reports' do
let(:project) { create(:project, :repository) }
let(:merge_request) { create(:merge_request, :with_codequality_mr_diff_reports, source_project: project) }
let(:pipeline) { merge_request.head_pipeline }
subject(:mr_diff_report) { merge_request.find_codequality_mr_diff_reports }
context 'when head pipeline has coverage reports' do
context 'when reactive cache worker is parsing results asynchronously' do
it 'returns status' do
expect(mr_diff_report[:status]).to eq(:parsing)
end
end
context 'when reactive cache worker is inline' do
before do
synchronous_reactive_cache(merge_request)
end
it 'returns status and data' do
expect(mr_diff_report[:status]).to eq(:parsed)
end
context 'when an error occurrs' do
before do
merge_request.update!(head_pipeline: nil)
end
it 'returns an error message' do
expect(mr_diff_report[:status]).to eq(:error)
end
end
context 'when cached results is not latest' do
before do
allow_next_instance_of(Ci::GenerateCodequalityMrDiffReportService) do |service|
allow(service).to receive(:latest?).and_return(false)
end
end
it 'raises and InvalidateReactiveCache error' do
expect { mr_diff_report }.to raise_error(ReactiveCaching::InvalidateReactiveCache)
end
end
end
end
end
describe '#compare_test_reports' do
subject { merge_request.compare_test_reports }

View File

@ -43,13 +43,13 @@ RSpec.describe ConfluenceService do
end
end
describe '#detailed_description' do
describe '#help' do
it 'can correctly return a link to the project wiki when active' do
project = create(:project)
subject.project = project
subject.active = true
expect(subject.detailed_description).to include(Gitlab::Routing.url_helpers.project_wikis_url(project))
expect(subject.help).to include(Gitlab::Routing.url_helpers.project_wikis_url(project))
end
context 'when the project wiki is not enabled' do
@ -60,7 +60,7 @@ RSpec.describe ConfluenceService do
[true, false].each do |active|
subject.active = active
expect(subject.detailed_description).to be_nil
expect(subject.help).to be_nil
end
end
end

View File

@ -0,0 +1,66 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Ci::PipelineArtifacts::CodeQualityMrDiffPresenter do
let(:pipeline_artifact) { create(:ci_pipeline_artifact, :with_codequality_mr_diff_report) }
subject(:presenter) { described_class.new(pipeline_artifact) }
describe '#for_files' do
subject(:quality_data) { presenter.for_files(filenames) }
context 'when code quality has data' do
context 'when filenames is empty' do
let(:filenames) { %w() }
it 'returns hash without quality' do
expect(quality_data).to match(files: {})
end
end
context 'when filenames do not match code quality data' do
let(:filenames) { %w(demo.rb) }
it 'returns hash without quality' do
expect(quality_data).to match(files: {})
end
end
context 'when filenames matches code quality data' do
context 'when asking for one filename' do
let(:filenames) { %w(file_a.rb) }
it 'returns quality for the given filename' do
expect(quality_data).to match(
files: {
"file_a.rb" => [
{ line: 10, description: "Avoid parameter lists longer than 5 parameters. [12/5]", severity: "major" },
{ line: 10, description: "Method `new_array` has 12 arguments (exceeds 4 allowed). Consider refactoring.", severity: "minor" }
]
}
)
end
end
context 'when asking for multiple filenames' do
let(:filenames) { %w(file_a.rb file_b.rb) }
it 'returns quality for the given filenames' do
expect(quality_data).to match(
files: {
"file_a.rb" => [
{ line: 10, description: "Avoid parameter lists longer than 5 parameters. [12/5]", severity: "major" },
{ line: 10, description: "Method `new_array` has 12 arguments (exceeds 4 allowed). Consider refactoring.", severity: "minor" }
],
"file_b.rb" => [
{ line: 10, description: "This cop checks for methods with too many parameters.\nThe maximum number of parameters is configurable.\nKeyword arguments can optionally be excluded from the total count.", severity: "minor" }
]
}
)
end
end
end
end
end
end

View File

@ -0,0 +1,51 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Ci::GenerateCodequalityMrDiffReportService do
let(:service) { described_class.new(project) }
let(:project) { create(:project, :repository) }
describe '#execute' do
subject { service.execute(base_pipeline, head_pipeline) }
context 'when head pipeline has codequality mr diff report' do
let!(:merge_request) { create(:merge_request, :with_codequality_mr_diff_reports, source_project: project) }
let!(:service) { described_class.new(project, nil, id: merge_request.id) }
let!(:head_pipeline) { merge_request.head_pipeline }
let!(:base_pipeline) { nil }
it 'returns status and data', :aggregate_failures do
expect_any_instance_of(Ci::PipelineArtifact) do |instance|
expect(instance).to receive(:present)
expect(instance).to receive(:for_files).with(merge_request.new_paths).and_call_original
end
expect(subject[:status]).to eq(:parsed)
expect(subject[:data]).to eq(files: {})
end
end
context 'when head pipeline does not have a codequality mr diff report' do
let!(:merge_request) { create(:merge_request, source_project: project) }
let!(:service) { described_class.new(project, nil, id: merge_request.id) }
let!(:head_pipeline) { merge_request.head_pipeline }
let!(:base_pipeline) { nil }
it 'returns status and error message' do
expect(subject[:status]).to eq(:error)
expect(subject[:status_reason]).to include('An error occurred while fetching codequality mr diff reports.')
end
end
context 'when head pipeline has codequality mr diff report and no merge request associated' do
let!(:head_pipeline) { create(:ci_pipeline, :with_codequality_mr_diff_report, project: project) }
let!(:base_pipeline) { nil }
it 'returns status and error message' do
expect(subject[:status]).to eq(:error)
expect(subject[:status_reason]).to include('An error occurred while fetching codequality mr diff reports.')
end
end
end
end

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe ::Ci::PipelineArtifacts::CreateQualityReportService do
RSpec.describe ::Ci::PipelineArtifacts::CreateCodeQualityMrDiffReportService do
describe '#execute' do
subject(:pipeline_artifact) { described_class.new.execute(pipeline) }
@ -27,7 +27,7 @@ RSpec.describe ::Ci::PipelineArtifacts::CreateQualityReportService do
end
it 'persists the default file name' do
expect(pipeline_artifact.file.filename).to eq('code_quality.json')
expect(pipeline_artifact.file.filename).to eq('code_quality_mr_diff.json')
end
it 'sets expire_at to 1 week' do

View File

@ -0,0 +1,29 @@
# frozen_string_literal: true
RSpec.shared_examples 'page with comment and close button' do |button_text|
context 'when remove_comment_close_reopen feature flag is enabled' do
before do
stub_feature_flags(remove_comment_close_reopen: true)
setup
end
it "does not show #{button_text} button" do
within '.note-form-actions' do
expect(page).not_to have_button(button_text)
end
end
end
context 'when remove_comment_close_reopen feature flag is disabled' do
before do
stub_feature_flags(remove_comment_close_reopen: false)
setup
end
it "shows #{button_text} button" do
within '.note-form-actions' do
expect(page).to have_button(button_text)
end
end
end
end

View File

@ -11,7 +11,7 @@ RSpec.describe ::Ci::PipelineArtifacts::CreateQualityReportWorker do
let(:pipeline_id) { pipeline.id }
it 'calls pipeline codequality report service' do
expect_next_instance_of(::Ci::PipelineArtifacts::CreateQualityReportService) do |quality_report_service|
expect_next_instance_of(::Ci::PipelineArtifacts::CreateCodeQualityMrDiffReportService) do |quality_report_service|
expect(quality_report_service).to receive(:execute)
end
@ -31,7 +31,7 @@ RSpec.describe ::Ci::PipelineArtifacts::CreateQualityReportWorker do
let(:pipeline_id) { non_existing_record_id }
it 'does not call pipeline codequality report service' do
expect(Ci::PipelineArtifacts::CreateQualityReportService).not_to receive(:execute)
expect(Ci::PipelineArtifacts::CreateCodeQualityMrDiffReportService).not_to receive(:execute)
subject
end