Commit 074d013e authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent 8f9beefa
...@@ -163,7 +163,7 @@ frontend-fixtures: ...@@ -163,7 +163,7 @@ frontend-fixtures:
frontend-fixtures-as-if-foss: frontend-fixtures-as-if-foss:
extends: extends:
- .frontend-fixtures-base - .frontend-fixtures-base
- .frontend:rules:default-frontend-jobs-as-if-foss - .frontend:rules:default-frontend-jobs-no-foss
- .as-if-foss - .as-if-foss
.frontend-job-base: .frontend-job-base:
...@@ -206,7 +206,7 @@ karma: ...@@ -206,7 +206,7 @@ karma:
karma-as-if-foss: karma-as-if-foss:
extends: extends:
- .karma-base - .karma-base
- .frontend:rules:default-frontend-jobs-as-if-foss - .frontend:rules:default-frontend-jobs-no-foss
- .as-if-foss - .as-if-foss
needs: ["frontend-fixtures-as-if-foss"] needs: ["frontend-fixtures-as-if-foss"]
...@@ -241,7 +241,7 @@ jest: ...@@ -241,7 +241,7 @@ jest:
jest-as-if-foss: jest-as-if-foss:
extends: extends:
- .jest-base - .jest-base
- .frontend:rules:default-frontend-jobs-as-if-foss - .frontend:rules:default-frontend-jobs-no-foss
- .as-if-foss - .as-if-foss
needs: ["frontend-fixtures-as-if-foss"] needs: ["frontend-fixtures-as-if-foss"]
cache: cache:
...@@ -250,7 +250,7 @@ jest-as-if-foss: ...@@ -250,7 +250,7 @@ jest-as-if-foss:
coverage-frontend: coverage-frontend:
extends: extends:
- .default-retry - .default-retry
- .frontend:rules:default-frontend-jobs - .frontend:rules:default-frontend-jobs-no-foss
needs: ["jest"] needs: ["jest"]
stage: post-test stage: post-test
before_script: before_script:
......
...@@ -273,7 +273,7 @@ ...@@ -273,7 +273,7 @@
changes: *code-backstage-patterns changes: *code-backstage-patterns
when: on_success when: on_success
.frontend:rules:default-frontend-jobs-as-if-foss: .frontend:rules:default-frontend-jobs-no-foss:
rules: rules:
- <<: *if-not-ee - <<: *if-not-ee
when: never when: never
......
...@@ -200,6 +200,35 @@ GitlabSecurity/PublicSend: ...@@ -200,6 +200,35 @@ GitlabSecurity/PublicSend:
- 'ee/lib/**/*.rake' - 'ee/lib/**/*.rake'
- 'ee/spec/**/*' - 'ee/spec/**/*'
Gitlab/DuplicateSpecLocation:
Exclude:
- ee/spec/controllers/groups_controller_spec.rb
- ee/spec/controllers/projects/jobs_controller_spec.rb
- ee/spec/helpers/auth_helper_spec.rb
- ee/spec/lib/gitlab/gl_repository_spec.rb
- ee/spec/lib/gitlab/usage_data_spec.rb
- ee/spec/models/namespace_spec.rb
- ee/spec/models/note_spec.rb
- ee/spec/serializers/environment_entity_spec.rb
- ee/spec/services/issues/create_service_spec.rb
- ee/spec/services/merge_requests/create_service_spec.rb
- ee/spec/services/merge_requests/refresh_service_spec.rb
- ee/spec/services/merge_requests/update_service_spec.rb
- ee/spec/services/system_hooks_service_spec.rb
- ee/spec/controllers/ee/groups_controller_spec.rb
- ee/spec/controllers/ee/projects/jobs_controller_spec.rb
- ee/spec/helpers/ee/auth_helper_spec.rb
- ee/spec/lib/ee/gitlab/gl_repository_spec.rb
- ee/spec/lib/ee/gitlab/usage_data_spec.rb
- ee/spec/models/ee/namespace_spec.rb
- ee/spec/models/ee/note_spec.rb
- ee/spec/serializers/ee/environment_entity_spec.rb
- ee/spec/services/ee/issues/create_service_spec.rb
- ee/spec/services/ee/merge_requests/create_service_spec.rb
- ee/spec/services/ee/merge_requests/refresh_service_spec.rb
- ee/spec/services/ee/merge_requests/update_service_spec.rb
- ee/spec/services/ee/system_hooks_service_spec.rb
Cop/InjectEnterpriseEditionModule: Cop/InjectEnterpriseEditionModule:
Enabled: true Enabled: true
Exclude: Exclude:
......
<script> <script>
import { escapeRegExp } from 'lodash'; import { escapeRegExp } from 'lodash';
import { GlBadge, GlLink, GlSkeletonLoading, GlTooltipDirective, GlLoadingIcon } from '@gitlab/ui'; import { GlBadge, GlLink, GlSkeletonLoading, GlTooltipDirective, GlLoadingIcon } from '@gitlab/ui';
import { visitUrl, escapeFileUrl } from '~/lib/utils/url_utility'; import { escapeFileUrl } from '~/lib/utils/url_utility';
import TimeagoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; import TimeagoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
import { getIconName } from '../../utils/icon'; import { getIconName } from '../../utils/icon';
...@@ -117,39 +117,37 @@ export default { ...@@ -117,39 +117,37 @@ export default {
return this.commit && this.commit.lockLabel; return this.commit && this.commit.lockLabel;
}, },
}, },
methods: {
openRow(e) {
if (e.target.tagName === 'A') return;
if (this.isFolder && !e.metaKey) {
this.$router.push(this.routerLinkTo);
} else {
visitUrl(this.url, e.metaKey);
}
},
},
}; };
</script> </script>
<template> <template>
<tr :class="`file_${id}`" class="tree-item" @click="openRow"> <tr class="tree-item">
<td class="tree-item-file-name"> <td class="tree-item-file-name cursor-default position-relative">
<gl-loading-icon <gl-loading-icon
v-if="path === loadingPath" v-if="path === loadingPath"
size="sm" size="sm"
inline inline
class="d-inline-block align-text-bottom fa-fw" class="d-inline-block align-text-bottom fa-fw"
/> />
<i v-else :aria-label="type" role="img" :class="iconName" class="fa fa-fw"></i>
<component <component
:is="linkComponent" :is="linkComponent"
ref="link" ref="link"
:to="routerLinkTo" :to="routerLinkTo"
:href="url" :href="url"
class="str-truncated" :class="{
'is-submodule': isSubmodule,
}"
class="tree-item-link str-truncated"
data-qa-selector="file_name_link" data-qa-selector="file_name_link"
> >
{{ fullPath }} <i
v-if="!loadingPath"
:aria-label="type"
role="img"
:class="iconName"
class="fa fa-fw mr-1"
></i
><span class="position-relative">{{ fullPath }}</span>
</component> </component>
<!-- eslint-disable-next-line @gitlab/vue-require-i18n-strings --> <!-- eslint-disable-next-line @gitlab/vue-require-i18n-strings -->
<gl-badge v-if="lfsOid" variant="default" class="label-lfs ml-1">LFS</gl-badge> <gl-badge v-if="lfsOid" variant="default" class="label-lfs ml-1">LFS</gl-badge>
...@@ -165,7 +163,7 @@ export default { ...@@ -165,7 +163,7 @@ export default {
class="ml-2 vertical-align-middle" class="ml-2 vertical-align-middle"
/> />
</td> </td>
<td class="d-none d-sm-table-cell tree-commit"> <td class="d-none d-sm-table-cell tree-commit cursor-default">
<gl-link <gl-link
v-if="commit" v-if="commit"
:href="commit.commitPath" :href="commit.commitPath"
...@@ -176,7 +174,7 @@ export default { ...@@ -176,7 +174,7 @@ export default {
</gl-link> </gl-link>
<gl-skeleton-loading v-else :lines="1" class="h-auto" /> <gl-skeleton-loading v-else :lines="1" class="h-auto" />
</td> </td>
<td class="tree-time-ago text-right"> <td class="tree-time-ago text-right cursor-default">
<timeago-tooltip v-if="commit" :time="commit.committedDate" /> <timeago-tooltip v-if="commit" :time="commit.committedDate" />
<gl-skeleton-loading v-else :lines="1" class="ml-auto h-auto w-50" /> <gl-skeleton-loading v-else :lines="1" class="ml-auto h-auto w-50" />
</td> </td>
......
...@@ -511,3 +511,21 @@ span.idiff { ...@@ -511,3 +511,21 @@ span.idiff {
.code-navigation-popover { .code-navigation-popover {
max-width: 450px; max-width: 450px;
} }
.tree-item-link {
&:not(.is-submodule) {
span {
z-index: 2;
}
&::before {
content: '';
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
z-index: 1;
}
}
}
...@@ -65,7 +65,7 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic ...@@ -65,7 +65,7 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic
options = additional_attributes.merge(diff_view: diff_view) options = additional_attributes.merge(diff_view: diff_view)
if @merge_request.project.context_commits_enabled? if @merge_request.project.context_commits_enabled?
options[:context_commits] = @merge_request.context_commits options[:context_commits] = @merge_request.recent_context_commits
end end
render json: DiffsSerializer.new(request).represent(diffs, options) render json: DiffsSerializer.new(request).represent(diffs, options)
......
...@@ -9,7 +9,7 @@ module Resolvers ...@@ -9,7 +9,7 @@ module Resolvers
def resolve(**args) def resolve(**args)
current_user = context[:current_user] current_user = context[:current_user]
issue_id = GlobalID.parse(args[:id]).model_id issue_id = GlobalID.parse(args[:id])&.model_id
# Get data from Sentry # Get data from Sentry
response = ::ErrorTracking::IssueDetailsService.new( response = ::ErrorTracking::IssueDetailsService.new(
......
...@@ -8,7 +8,7 @@ module Resolvers ...@@ -8,7 +8,7 @@ module Resolvers
description: 'ID of the Sentry issue' description: 'ID of the Sentry issue'
def resolve(**args) def resolve(**args)
issue_id = GlobalID.parse(args[:id]).model_id issue_id = GlobalID.parse(args[:id])&.model_id
# Get data from Sentry # Get data from Sentry
response = ::ErrorTracking::IssueLatestEventService.new( response = ::ErrorTracking::IssueLatestEventService.new(
......
...@@ -410,8 +410,16 @@ class MergeRequest < ApplicationRecord ...@@ -410,8 +410,16 @@ class MergeRequest < ApplicationRecord
"#{project.to_reference_base(from, full: full)}#{reference}" "#{project.to_reference_base(from, full: full)}#{reference}"
end end
def context_commits def context_commits(limit: nil)
@context_commits ||= merge_request_context_commits.map(&:to_commit) @context_commits ||= merge_request_context_commits.limit(limit).map(&:to_commit)
end
def recent_context_commits
context_commits(limit: MergeRequestDiff::COMMITS_SAFE_SIZE)
end
def context_commits_count
context_commits.count
end end
def commits(limit: nil) def commits(limit: nil)
......
# frozen_string_literal: true # frozen_string_literal: true
class ResourceMilestoneEvent < ResourceEvent class ResourceMilestoneEvent < ResourceEvent
include IgnorableColumns
belongs_to :issue belongs_to :issue
belongs_to :merge_request belongs_to :merge_request
belongs_to :milestone belongs_to :milestone
...@@ -18,6 +20,8 @@ class ResourceMilestoneEvent < ResourceEvent ...@@ -18,6 +20,8 @@ class ResourceMilestoneEvent < ResourceEvent
# state is used for issue and merge request states. # state is used for issue and merge request states.
enum state: Issue.available_states.merge(MergeRequest.available_states) enum state: Issue.available_states.merge(MergeRequest.available_states)
ignore_columns %i[reference reference_html cached_markdown_version], remove_with: '13.1', remove_after: '2020-06-22'
def self.issuable_attrs def self.issuable_attrs
%i(issue merge_request).freeze %i(issue merge_request).freeze
end end
......
# frozen_string_literal: true # frozen_string_literal: true
class CommitEntity < API::Entities::Commit class CommitEntity < API::Entities::CommitWithLink
include MarkupHelper
include RequestAwareEntity
expose :author, using: UserEntity
expose :author_gravatar_url do |commit|
GravatarService.new.execute(commit.author_email) # rubocop: disable CodeReuse/ServiceClass
end
expose :commit_url do |commit, options|
project_commit_url(request.project, commit, params: options.fetch(:commit_url_params, {}))
end
expose :commit_path do |commit, options|
project_commit_path(request.project, commit, params: options.fetch(:commit_url_params, {}))
end
expose :description_html, if: { type: :full } do |commit|
markdown_field(commit, :description)
end
expose :title_html, if: { type: :full } do |commit|
markdown_field(commit, :title)
end
expose :signature_html, if: { type: :full } do |commit|
render('projects/commit/_signature', signature: commit.signature) if commit.has_signature?
end
expose :pipeline_status_path, if: { type: :full } do |commit, options|
pipeline_ref = options[:pipeline_ref]
pipeline_project = options[:pipeline_project] || commit.project
next unless pipeline_ref && pipeline_project
pipeline = commit.latest_pipeline_for_project(pipeline_ref, pipeline_project)
next unless pipeline&.status
pipelines_project_commit_path(pipeline_project, commit.id, ref: pipeline_ref)
end
def render(*args)
return unless request.respond_to?(:render) && request.render.respond_to?(:call)
request.render.call(*args)
end
end end
# frozen_string_literal: true # frozen_string_literal: true
class UserEntity < API::Entities::UserBasic class UserEntity < API::Entities::UserPath
include RequestAwareEntity
include UserStatusTooltip
expose :path do |user|
user_path(user)
end
end end
...@@ -74,7 +74,7 @@ module Issuable ...@@ -74,7 +74,7 @@ module Issuable
if matching_destination_milestone.present? if matching_destination_milestone.present?
event.attributes event.attributes
.except('id', 'reference', 'reference_html') .except('id')
.merge(entity_key => new_entity.id, .merge(entity_key => new_entity.id,
'milestone_id' => matching_destination_milestone.id, 'milestone_id' => matching_destination_milestone.id,
'action' => ResourceMilestoneEvent.actions[event.action], 'action' => ResourceMilestoneEvent.actions[event.action],
......
...@@ -6,7 +6,7 @@ module Metrics ...@@ -6,7 +6,7 @@ module Metrics
module Dashboard module Dashboard
class CloneDashboardService < ::BaseService class CloneDashboardService < ::BaseService
ALLOWED_FILE_TYPE = '.yml' ALLOWED_FILE_TYPE = '.yml'
USER_DASHBOARDS_DIR = ::Metrics::Dashboard::ProjectDashboardService::DASHBOARD_ROOT USER_DASHBOARDS_DIR = ::Metrics::Dashboard::CustomDashboardService::DASHBOARD_ROOT
class << self class << self
def allowed_dashboard_templates def allowed_dashboard_templates
...@@ -52,7 +52,7 @@ module Metrics ...@@ -52,7 +52,7 @@ module Metrics
def dashboard_details def dashboard_details
{ {
path: new_dashboard_path, path: new_dashboard_path,
display_name: ::Metrics::Dashboard::ProjectDashboardService.name_for_path(new_dashboard_path), display_name: ::Metrics::Dashboard::CustomDashboardService.name_for_path(new_dashboard_path),
default: false, default: false,
system_dashboard: false system_dashboard: false
} }
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
# Use Gitlab::Metrics::Dashboard::Finder to retrive dashboards. # Use Gitlab::Metrics::Dashboard::Finder to retrive dashboards.
module Metrics module Metrics
module Dashboard module Dashboard
class ProjectDashboardService < ::Metrics::Dashboard::BaseService class CustomDashboardService < ::Metrics::Dashboard::BaseService
DASHBOARD_ROOT = ".gitlab/dashboards" DASHBOARD_ROOT = ".gitlab/dashboards"
class << self class << self
......
...@@ -7,7 +7,7 @@ module Metrics ...@@ -7,7 +7,7 @@ module Metrics
include Stepable include Stepable
ALLOWED_FILE_TYPE = '.yml' ALLOWED_FILE_TYPE = '.yml'
USER_DASHBOARDS_DIR = ::Metrics::Dashboard::ProjectDashboardService::DASHBOARD_ROOT USER_DASHBOARDS_DIR = ::Metrics::Dashboard::CustomDashboardService::DASHBOARD_ROOT
steps :check_push_authorized, steps :check_push_authorized,
:check_branch_name, :check_branch_name,
...@@ -117,7 +117,7 @@ module Metrics ...@@ -117,7 +117,7 @@ module Metrics
def dashboard_details def dashboard_details
{ {
path: update_dashboard_path, path: update_dashboard_path,
display_name: ::Metrics::Dashboard::ProjectDashboardService.name_for_path(update_dashboard_path), display_name: ::Metrics::Dashboard::CustomDashboardService.name_for_path(update_dashboard_path),
default: false, default: false,
system_dashboard: false system_dashboard: false
} }
......
...@@ -17,5 +17,6 @@ ...@@ -17,5 +17,6 @@
%ul.wiki-pages %ul.wiki-pages
= render @sidebar_wiki_entries, context: 'sidebar' = render @sidebar_wiki_entries, context: 'sidebar'
.block.w-100 .block.w-100
= link_to project_wikis_pages_path(@project), class: 'btn btn-block' do - if @sidebar_wiki_entries&.length.to_i >= 15
= s_("Wiki|More Pages") = link_to project_wikis_pages_path(@project), class: 'btn btn-block' do
= s_("Wiki|View All Pages")
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
.card-header .card-header
%h5 %h5
= hook_class.underscore.humanize.titleize.pluralize = hook_class.underscore.humanize.titleize.pluralize
(#{hooks.count}) (#{hooks.load.size})
- if hooks.any? - if hooks.any?
%ul.content-list %ul.content-list
......
...@@ -891,7 +891,7 @@ ...@@ -891,7 +891,7 @@
:urgency: :low :urgency: :low
:resource_boundary: :unknown :resource_boundary: :unknown
:weight: 1 :weight: 1
:idempotent: :idempotent: true
- :name: update_namespace_statistics:namespaces_schedule_aggregation - :name: update_namespace_statistics:namespaces_schedule_aggregation
:feature_category: :source_code_management :feature_category: :source_code_management
:has_external_dependencies: :has_external_dependencies:
......
# frozen_string_literal: true # frozen_string_literal: true
module Namespaces module Namespaces
class RootStatisticsWorker # rubocop:disable Scalability/IdempotentWorker class RootStatisticsWorker
include ApplicationWorker include ApplicationWorker
queue_namespace :update_namespace_statistics queue_namespace :update_namespace_statistics
feature_category :source_code_management feature_category :source_code_management
idempotent!
def perform(namespace_id) def perform(namespace_id)
namespace = Namespace.find(namespace_id) namespace = Namespace.find(namespace_id)
......
---
title: Update More Pages button on Wiki Page
merge_request: 27499
author:
type: changed
---
title: Fix OpenAPI file detector
merge_request: 27321
author: Roger Meier
type: fixed
---
title: Reduce SQL queries when rendering webhook settings
merge_request: 27359
author:
type: performance
...@@ -3,9 +3,9 @@ ...@@ -3,9 +3,9 @@
require 'yaml' require 'yaml'
SEE_DOC = "See [the documentation](https://docs.gitlab.com/ce/development/changelog.html)." SEE_DOC = "See [the documentation](https://docs.gitlab.com/ee/development/changelog.html)."
CREATE_CHANGELOG_MESSAGE = <<~MSG CREATE_CHANGELOG_MESSAGE = <<~MSG
You can create one with: If you want to create a changelog entry for GitLab FOSS, run the following:
``` ```
bin/changelog -m %<mr_iid>s "%<mr_title>s" bin/changelog -m %<mr_iid>s "%<mr_title>s"
...@@ -20,7 +20,7 @@ bin/changelog --ee -m %<mr_iid>s "%<mr_title>s" ...@@ -20,7 +20,7 @@ bin/changelog --ee -m %<mr_iid>s "%<mr_title>s"
Note: Merge requests with %<labels>s do not trigger this check. Note: Merge requests with %<labels>s do not trigger this check.
MSG MSG
def check_changelog(path) def check_changelog_yaml(path)
yaml = YAML.safe_load(File.read(path)) yaml = YAML.safe_load(File.read(path))
fail "`title` should be set, in #{gitlab.html_link(path)}! #{SEE_DOC}" if yaml["title"].nil? fail "`title` should be set, in #{gitlab.html_link(path)}! #{SEE_DOC}" if yaml["title"].nil?
...@@ -28,8 +28,6 @@ def check_changelog(path) ...@@ -28,8 +28,6 @@ def check_changelog(path)
if yaml["merge_request"].nil? && !helper.security_mr? if yaml["merge_request"].nil? && !helper.security_mr?
message "Consider setting `merge_request` to #{gitlab.mr_json["iid"]} in #{gitlab.html_link(path)}. #{SEE_DOC}" message "Consider setting `merge_request` to #{gitlab.mr_json["iid"]} in #{gitlab.html_link(path)}. #{SEE_DOC}"
elsif yaml["merge_request"] != gitlab.mr_json["iid"] && !changelog.ce_port_changelog?(path)
fail "Merge request ID was not set to #{gitlab.mr_json["iid"]}! #{SEE_DOC}"
end end
rescue Psych::SyntaxError, Psych::DisallowedClass, Psych::BadAlias rescue Psych::SyntaxError, Psych::DisallowedClass, Psych::BadAlias
# YAML could not be parsed, fail the build. # YAML could not be parsed, fail the build.
...@@ -38,6 +36,19 @@ rescue StandardError => e ...@@ -38,6 +36,19 @@ rescue StandardError => e
warn "There was a problem trying to check the Changelog. Exception: #{e.name} - #{e.message}" warn "There was a problem trying to check the Changelog. Exception: #{e.name} - #{e.message}"
end end
def check_changelog_path(path)
ee_changes = helper.all_ee_changes.dup
ee_changes.delete(path)
if ee_changes.any? && !changelog.ee_changelog?
warn "This MR has a Changelog file outside `ee/`, but code changes in `ee/`. Consider moving the Changelog file into `ee/`."
end
if ee_changes.empty? && changelog.ee_changelog?
warn "This MR has a Changelog file in `ee/`, but no code changes in `ee/`. Consider moving the Changelog file outside `ee/`."
end
end
def sanitized_mr_title def sanitized_mr_title
helper.sanitize_mr_title(gitlab.mr_json["title"]) helper.sanitize_mr_title(gitlab.mr_json["title"])
end end
...@@ -49,11 +60,10 @@ end ...@@ -49,11 +60,10 @@ end
changelog_found = changelog.found changelog_found = changelog.found
if changelog.needed? if changelog_found
if changelog_found check_changelog_yaml(changelog_found)
check_changelog(changelog_found) check_changelog_path(changelog_found)
else elsif changelog.needed?
message "**[CHANGELOG missing](https://docs.gitlab.com/ce/development/changelog.html)**: If this merge request [doesn't need a CHANGELOG entry](https://docs.gitlab.com/ee/development/changelog.html#what-warrants-a-changelog-entry), feel free to ignore this message.\n\n" + message "**[CHANGELOG missing](https://docs.gitlab.com/ee/development/changelog.html)**: If this merge request [doesn't need a CHANGELOG entry](https://docs.gitlab.com/ee/development/changelog.html#what-warrants-a-changelog-entry), feel free to ignore this message.\n\n" +
format(CREATE_CHANGELOG_MESSAGE, mr_iid: gitlab.mr_json["iid"], mr_title: sanitized_mr_title, labels: changelog.presented_no_changelog_labels) format(CREATE_CHANGELOG_MESSAGE, mr_iid: gitlab.mr_json["iid"], mr_title: sanitized_mr_title, labels: changelog.presented_no_changelog_labels)
end
end end
# frozen_string_literal: true
module API
module Entities
class CommitWithLink < Commit
include MarkupHelper
include RequestAwareEntity
expose :author, using: Entities::UserPath
expose :author_gravatar_url do |commit|
GravatarService.new.execute(commit.author_email)
end
expose :commit_url do |commit, options|
project_commit_url(request.project, commit, params: options.fetch(:commit_url_params, {}))
end
expose :commit_path do |commit, options|
project_commit_path(request.project, commit, params: options.fetch(:commit_url_params, {}))
end
expose :description_html, if: { type: :full } do |commit|
markdown_field(commit, :description)
end
expose :title_html, if: { type: :full } do |commit|
markdown_field(commit, :title)
end
expose :signature_html, if: { type: :full } do |commit|
render('projects/commit/_signature', signature: commit.signature) if commit.has_signature?
end
expose :pipeline_status_path, if: { type: :full } do |commit, options|
pipeline_ref = options[:pipeline_ref]
pipeline_project = options[:pipeline_project] || commit.project
next unless pipeline_ref && pipeline_project
pipeline = commit.latest_pipeline_for_project(pipeline_ref, pipeline_project)
next unless pipeline&.status
pipelines_project_commit_path(pipeline_project, commit.id, ref: pipeline_ref)
end
def render(*args)
return unless request.respond_to?(:render) && request.render.respond_to?(:call)
request.render.call(*args)
end
end
end
end
# frozen_string_literal: true
module API
module Entities
class UserPath < UserBasic
include RequestAwareEntity
include UserStatusTooltip
expose :path do |user|
user_path(user)
end
end
end
end
...@@ -306,7 +306,7 @@ module API ...@@ -306,7 +306,7 @@ module API
context_commits = context_commits =
paginate(merge_request.merge_request_context_commits).map(&:to_commit) paginate(merge_request.merge_request_context_commits).map(&:to_commit)
present context_commits, with: Entities::Commit present context_commits, with: Entities::CommitWithLink, type: :full, request: merge_request
end end
params do params do
......
...@@ -11,7 +11,7 @@ module Gitlab ...@@ -11,7 +11,7 @@ module Gitlab
end end
def found def found
git.added_files.find { |path| path =~ %r{\A(ee/)?(changelogs/unreleased)(-ee)?/} } @found ||= git.added_files.find { |path| path =~ %r{\A(ee/)?(changelogs/unreleased)(-ee)?/} }
end end
def presented_no_changelog_labels def presented_no_changelog_labels
...@@ -22,12 +22,8 @@ module Gitlab ...@@ -22,12 +22,8 @@ module Gitlab
gitlab.mr_json["title"].gsub(/^WIP: */, '').gsub(/`/, '\\\`') gitlab.mr_json["title"].gsub(/^WIP: */, '').gsub(/`/, '\\\`')
end end
def ee_changelog?(changelog_path) def ee_changelog?
changelog_path =~ /unreleased-ee/ found.start_with?('ee/')
end
def ce_port_changelog?(changelog_path)
helper.ee? && !ee_changelog?(changelog_path)
end end
private private
......
...@@ -34,6 +34,10 @@ module Gitlab ...@@ -34,6 +34,10 @@ module Gitlab
.sort .sort
end end
def all_ee_changes
all_changed_files.grep(%r{\Aee/})
end
def ee? def ee?
# Support former project name for `dev` and support local Danger run # Support former project name for `dev` and support local Danger run
%w[gitlab gitlab-ee].include?(ENV['CI_PROJECT_NAME']) || Dir.exist?('../../ee') %w[gitlab gitlab-ee].include?(ENV['CI_PROJECT_NAME']) || Dir.exist?('../../ee')
......
...@@ -40,7 +40,7 @@ module Gitlab ...@@ -40,7 +40,7 @@ module Gitlab
yarn_lock: 'yarn.lock', yarn_lock: 'yarn.lock',
# OpenAPI Specification files # OpenAPI Specification files
openapi: %r{.*(openapi|swagger).*\.(yaml|yml|json)\z}i openapi: %r{[^/]*(openapi|swagger)[^/]*\.(yaml|yml|json)\z}i
}.freeze }.freeze
# Returns an Array of file types based on the given paths. # Returns an Array of file types based on the given paths.
......
...@@ -78,7 +78,7 @@ module Gitlab ...@@ -78,7 +78,7 @@ module Gitlab
end end
def project_service def project_service
::Metrics::Dashboard::ProjectDashboardService ::Metrics::Dashboard::CustomDashboardService
end end
def self_monitoring_service def self_monitoring_service
......
...@@ -20,7 +20,7 @@ module Gitlab ...@@ -20,7 +20,7 @@ module Gitlab
::Metrics::Dashboard::SystemDashboardService, ::Metrics::Dashboard::SystemDashboardService,
::Metrics::Dashboard::PodDashboardService, ::Metrics::Dashboard::PodDashboardService,
::Metrics::Dashboard::SelfMonitoringDashboardService, ::Metrics::Dashboard::SelfMonitoringDashboardService,
::Metrics::Dashboard::ProjectDashboardService ::Metrics::Dashboard::CustomDashboardService
].freeze ].freeze
# Returns a class which inherits from the BaseService # Returns a class which inherits from the BaseService
......
...@@ -5,8 +5,18 @@ require 'digest' ...@@ -5,8 +5,18 @@ require 'digest'
module Gitlab module Gitlab
module SidekiqMiddleware module SidekiqMiddleware
module DuplicateJobs module DuplicateJobs
def self.drop_duplicates? DROPPABLE_QUEUES = Set.new([
Feature.enabled?(:drop_duplicate_sidekiq_jobs) Namespaces::RootStatisticsWorker.queue
]).freeze
def self.drop_duplicates?(queue_name)
Feature.enabled?(:drop_duplicate_sidekiq_jobs) ||
drop_duplicates_for_queue?(queue_name)
end
private_class_method def self.drop_duplicates_for_queue?(queue_name)
DROPPABLE_QUEUES.include?(queue_name) &&
Feature.enabled?(:drop_duplicate_sidekiq_jobs_for_queue)
end end
end end
end end
......
...@@ -67,7 +67,7 @@ module Gitlab ...@@ -67,7 +67,7 @@ module Gitlab
end end
def droppable? def droppable?
idempotent? && duplicate? && DuplicateJobs.drop_duplicates? idempotent? && duplicate? && DuplicateJobs.drop_duplicates?(queue_name)
end end
private private
......
...@@ -9166,18 +9166,6 @@ msgstr "" ...@@ -9166,18 +9166,6 @@ msgstr ""
msgid "GeoNodes|Last event ID seen from primary" msgid "GeoNodes|Last event ID seen from primary"
msgstr "" msgstr ""
msgid "GeoNodes|Learn more about Repository checksum progress"
msgstr ""
msgid "GeoNodes|Learn more about Repository verification"
msgstr ""
msgid "GeoNodes|Learn more about Wiki checksum progress"
msgstr ""
msgid "GeoNodes|Learn more about Wiki verification"
msgstr ""
msgid "GeoNodes|Loading nodes" msgid "GeoNodes|Loading nodes"
msgstr "" msgstr ""
...@@ -9208,6 +9196,9 @@ msgstr "" ...@@ -9208,6 +9196,9 @@ msgstr ""
msgid "GeoNodes|Removing a Geo secondary node stops the synchronization to that node. Are you sure?" msgid "GeoNodes|Removing a Geo secondary node stops the synchronization to that node. Are you sure?"
msgstr "" msgstr ""
msgid "GeoNodes|Replicated data is verified with the %{nodeText} using checksums"
msgstr ""
msgid "GeoNodes|Replication slot WAL" msgid "GeoNodes|Replication slot WAL"
msgstr "" msgstr ""
...@@ -9217,12 +9208,6 @@ msgstr "" ...@@ -9217,12 +9208,6 @@ msgstr ""
msgid "GeoNodes|Repositories" msgid "GeoNodes|Repositories"
msgstr "" msgstr ""
msgid "GeoNodes|Repositories checksummed for verification with their counterparts on Secondary nodes"
msgstr ""
msgid "GeoNodes|Repositories verified with their counterparts on the Primary node"
msgstr ""
msgid "GeoNodes|Repository checksum progress" msgid "GeoNodes|Repository checksum progress"
msgstr "" msgstr ""
...@@ -9274,16 +9259,16 @@ msgstr "" ...@@ -9274,16 +9259,16 @@ msgstr ""
msgid "GeoNodes|Wikis" msgid "GeoNodes|Wikis"
msgstr "" msgstr ""
msgid "GeoNodes|Wikis checksummed for verification with their counterparts on Secondary nodes" msgid "GeoNodes|With %{geo} you can install a special read-only and replicated instance anywhere. Before you add nodes, follow the %{instructions} in the exact order they appear."
msgstr "" msgstr ""
msgid "GeoNodes|Wikis verified with their counterparts on the Primary node" msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
msgstr "" msgstr ""
msgid "GeoNodes|With %{geo} you can install a special read-only and replicated instance anywhere. Before you add nodes, follow the %{instructions} in the exact order they appear." msgid "GeoNodes|primary node"
msgstr "" msgstr ""
msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS." msgid "GeoNodes|secondary nodes"
msgstr "" msgstr ""
msgid "Geo|%{name} is scheduled for forced re-download" msgid "Geo|%{name} is scheduled for forced re-download"
...@@ -22762,9 +22747,6 @@ msgstr "" ...@@ -22762,9 +22747,6 @@ msgstr ""
msgid "Wiki|Edit Page" msgid "Wiki|Edit Page"
msgstr "" msgstr ""
msgid "Wiki|More Pages"
msgstr ""
msgid "Wiki|New page" msgid "Wiki|New page"
msgstr "" msgstr ""
...@@ -22783,6 +22765,9 @@ msgstr "" ...@@ -22783,6 +22765,9 @@ msgstr ""
msgid "Wiki|Title" msgid "Wiki|Title"
msgstr "" msgstr ""
msgid "Wiki|View All Pages"
msgstr ""
msgid "Wiki|Wiki Pages" msgid "Wiki|Wiki Pages"
msgstr "" msgstr ""
......
...@@ -33,9 +33,9 @@ module QA ...@@ -33,9 +33,9 @@ module QA
create_many_branches create_many_branches
create_many_new_files create_many_new_files
create_mr_with_many_commits create_mr_with_many_commits
create_many_issues
methods_arr = [ methods_arr = [
method(:create_many_issues),
method(:create_many_labels), method(:create_many_labels),
method(:create_many_todos), method(:create_many_todos),
method(:create_many_merge_requests), method(:create_many_merge_requests),
...@@ -104,6 +104,7 @@ module QA ...@@ -104,6 +104,7 @@ module QA
def create_many_new_files def create_many_new_files
create_a_new_file_api_req("hello.txt", "master", "#{@group_name}%2F#{@project_name}", "hello", "my new content") create_a_new_file_api_req("hello.txt", "master", "#{@group_name}%2F#{@project_name}", "hello", "my new content")
30.times do |i| 30.times do |i|
create_a_new_file_api_req("hello#{i}.txt", "master", "#{@group_name}%2F#{@project_name}", "hello", "my new content")
create_a_new_file_api_req("hello#{i}.txt", "branch#{i}", "#{@group_name}%2F#{@project_name}", "hello", "my new content") create_a_new_file_api_req("hello#{i}.txt", "branch#{i}", "#{@group_name}%2F#{@project_name}", "hello", "my new content")
end end
...@@ -137,7 +138,7 @@ module QA ...@@ -137,7 +138,7 @@ module QA
16.times do |i| 16.times do |i|
faker_line_arr = Faker::Lorem.sentences(1500) faker_line_arr = Faker::Lorem.sentences(1500)
content = faker_line_arr.join("\n\r") content = faker_line_arr.join("\n\r")
create_a_new_file_api_req("hello#{i}.txt", "master", "#{@group_name}%2F#{@project_name}", "Add hello#{i}.txt", content) create_a_new_file_api_req("hello#{i + 100}.txt", "master", "#{@group_name}%2F#{@project_name}", "Add hello#{i + 100}.txt", content)
content_arr[i] = faker_line_arr content_arr[i] = faker_line_arr
end end
...@@ -147,7 +148,7 @@ module QA ...@@ -147,7 +148,7 @@ module QA
missed_line_array = content_arr[i].each_slice(2).map(&:first) missed_line_array = content_arr[i].each_slice(2).map(&:first)
content = missed_line_array.join("\n\rIm new!:D \n\r ") content = missed_line_array.join("\n\rIm new!:D \n\r ")
update_file_api_req("hello#{i}.txt", "performance", "#{@group_name}%2F#{@project_name}", "Update hello#{i}.txt", content) update_file_api_req("hello#{i + 100}.txt", "performance", "#{@group_name}%2F#{@project_name}", "Update hello#{i + 100}.txt", content)
end end
create_mr_response = create_a_merge_request_api_req("#{@group_name}%2F#{@project_name}", "performance", "master", "Large_MR") create_mr_response = create_a_merge_request_api_req("#{@group_name}%2F#{@project_name}", "performance", "master", "Large_MR")
...@@ -182,7 +183,7 @@ module QA ...@@ -182,7 +183,7 @@ module QA
def create_diff_note(iid, file_count, line_count, head_sha, start_sha, base_sha, line_type) def create_diff_note(iid, file_count, line_count, head_sha, start_sha, base_sha, line_type)
post Runtime::API::Request.new(@api_client, "/projects/#{@group_name}%2F#{@project_name}/merge_requests/#{iid}/discussions").url, post Runtime::API::Request.new(@api_client, "/projects/#{@group_name}%2F#{@project_name}/merge_requests/#{iid}/discussions").url,
"" "body=\"Let us discuss\"& "" "body=\"Let us discuss\"&
position[position_type]=text& position[position_type]=text&
position[new_path]=hello#{file_count}.txt& position[new_path]=hello#{file_count}.txt&
position[old_path]=hello#{file_count}.txt& position[old_path]=hello#{file_count}.txt&
......
# frozen_string_literal: true
require 'rubocop/rspec/top_level_describe'
module RuboCop
module Cop
module Gitlab
# Cop that detects duplicate EE spec files
#
# There should not be files in both ee/spec/*/ee/my_spec.rb and ee/spec/*/my_spec.rb
#
# # bad
# ee/spec/controllers/my_spec.rb # describe MyClass
# ee/spec/controllers/ee/my_spec.rb # describe MyClass
#
# # good, spec for EE extension code
# ee/spec/controllers/ee/my_spec.rb # describe MyClass
#
# # good, spec for EE only code
# ee/spec/controllers/my_spec.rb # describe MyClass
#
class DuplicateSpecLocation < RuboCop::Cop::Cop
include RuboCop::RSpec::TopLevelDescribe
MSG = 'Duplicate spec location in `%<path>s`.'
def on_top_level_describe(node, _args)
path = file_path_for_node(node).sub(/\A#{rails_root}\//, '')
duplicate_path = find_duplicate_path(path)
if duplicate_path && File.exist?(File.join(rails_root, duplicate_path))
add_offense(node, message: format(MSG, path: duplicate_path))
end
end
private
def ee_spec?(path)
File.fnmatch?('ee/spec/**/*.rb', path, File::FNM_PATHNAME)
end
def find_duplicate_path(path)
return unless ee_spec?(path)
if File.fnmatch?('ee/spec/**/ee/**', path)
path.match('\A(ee/spec/[^/]+)/ee/(.+)') do |match|
File.join(match[1], match[2])
end
else
path.match('\A(ee/spec/[^/]+)/(.+)') do |match|
File.join(match[1], 'ee', match[2])
end
end
end
def file_path_for_node(node)
node.location.expression.source_buffer.name
end
def rails_root
File.expand_path('../../..', __dir__)
end
end
end
end
end
...@@ -21,7 +21,13 @@ GITLAB_DOCS_REPO = 'gitlab-org/gitlab-docs'.freeze ...@@ -21,7 +21,13 @@ GITLAB_DOCS_REPO = 'gitlab-org/gitlab-docs'.freeze
# kicked the review app. # kicked the review app.
# #
def docs_branch def docs_branch
"docs-preview-#{slug}-#{ENV["CI_MERGE_REQUEST_IID"]}" # Check if CI_MERGE_REQUEST_IID is present. This requires pipelines
# for merge requests to be enabled.
if ENV["CI_MERGE_REQUEST_IID"].nil?
"docs-preview-#{slug}-#{ENV["CI_COMMIT_REF_SLUG"]}"
else
"docs-preview-#{slug}-#{ENV["CI_MERGE_REQUEST_IID"]}"
end
end end
# #
......
...@@ -312,7 +312,6 @@ describe "User creates wiki page" do ...@@ -312,7 +312,6 @@ describe "User creates wiki page" do
visit(project_wikis_path(project)) visit(project_wikis_path(project))
expect(page).to have_content('another') expect(page).to have_content('another')
expect(page).to have_content('More Pages')
end end
context 'when there is a customized sidebar' do context 'when there is a customized sidebar' do
...@@ -324,10 +323,23 @@ describe "User creates wiki page" do ...@@ -324,10 +323,23 @@ describe "User creates wiki page" do
visit(project_wikis_path(project)) visit(project_wikis_path(project))
expect(page).to have_content('My customized sidebar') expect(page).to have_content('My customized sidebar')
expect(page).to have_content('More Pages')
expect(page).not_to have_content('Another') expect(page).not_to have_content('Another')
end end
end end
end end
context 'when there are more than 15 existing pages' do
before do
create(:wiki_page, wiki: wiki, attrs: { title: 'home', content: 'home' })
(1..14).each { |i| create(:wiki_page, wiki: wiki, attrs: { title: "page-#{i}", content: "page #{i}" }) }
end
it 'renders a default sidebar when there is no customized sidebar' do
visit(project_wikis_path(project))
expect(page).to have_content('View All Pages')
expect(page).to have_content('page 1')
end
end
end end
end end
Element.prototype.scrollBy = jest.fn();
import './element_scroll_into_view'; import './element_scroll_into_view';
import './element_scroll_by';
import './form_element'; import './form_element';
import './get_client_rects'; import './get_client_rects';
import './inner_text'; import './inner_text';
......
...@@ -2,25 +2,28 @@ ...@@ -2,25 +2,28 @@
exports[`Repository table row component renders table row 1`] = ` exports[`Repository table row component renders table row 1`] = `
<tr <tr
class="tree-item file_1" class="tree-item"
> >
<td <td
class="tree-item-file-name" class="tree-item-file-name cursor-default position-relative"
> >
<i <!---->
aria-label="file"
class="fa fa-fw fa-file-text-o"
role="img"
/>
<a <a
class="str-truncated" class="tree-item-link str-truncated"
data-qa-selector="file_name_link" data-qa-selector="file_name_link"
href="https://test.com" href="https://test.com"
> >
<i
test aria-label="file"
class="fa fa-fw mr-1 fa-file-text-o"
role="img"
/>
<span
class="position-relative"
>
test
</span>
</a> </a>
<!----> <!---->
...@@ -31,7 +34,7 @@ exports[`Repository table row component renders table row 1`] = ` ...@@ -31,7 +34,7 @@ exports[`Repository table row component renders table row 1`] = `
</td> </td>
<td <td
class="d-none d-sm-table-cell tree-commit" class="d-none d-sm-table-cell tree-commit cursor-default"
> >
<gl-skeleton-loading-stub <gl-skeleton-loading-stub
class="h-auto" class="h-auto"
...@@ -40,7 +43,7 @@ exports[`Repository table row component renders table row 1`] = ` ...@@ -40,7 +43,7 @@ exports[`Repository table row component renders table row 1`] = `
</td> </td>
<td <td
class="tree-time-ago text-right" class="tree-time-ago text-right cursor-default"
> >
<gl-skeleton-loading-stub <gl-skeleton-loading-stub
class="ml-auto h-auto w-50" class="ml-auto h-auto w-50"
...@@ -52,25 +55,28 @@ exports[`Repository table row component renders table row 1`] = ` ...@@ -52,25 +55,28 @@ exports[`Repository table row component renders table row 1`] = `
exports[`Repository table row component renders table row for path with special character 1`] = ` exports[`Repository table row component renders table row for path with special character 1`] = `
<tr <tr
class="tree-item file_1" class="tree-item"
> >
<td <td
class="tree-item-file-name" class="tree-item-file-name cursor-default position-relative"
> >
<i <!---->
aria-label="file"
class="fa fa-fw fa-file-text-o"
role="img"
/>
<a <a
class="str-truncated" class="tree-item-link str-truncated"
data-qa-selector="file_name_link" data-qa-selector="file_name_link"
href="https://test.com" href="https://test.com"
> >
<i
test aria-label="file"
class="fa fa-fw mr-1 fa-file-text-o"
role="img"
/>
<span
class="position-relative"
>
test
</span>
</a> </a>
<!----> <!---->
...@@ -81,7 +87,7 @@ exports[`Repository table row component renders table row for path with special ...@@ -81,7 +87,7 @@ exports[`Repository table row component renders table row for path with special
</td> </td>
<td <td
class="d-none d-sm-table-cell tree-commit" class="d-none d-sm-table-cell tree-commit cursor-default"
> >
<gl-skeleton-loading-stub <gl-skeleton-loading-stub
class="h-auto" class="h-auto"
...@@ -90,7 +96,7 @@ exports[`Repository table row component renders table row for path with special ...@@ -90,7 +96,7 @@ exports[`Repository table row component renders table row for path with special
</td> </td>
<td <td
class="tree-time-ago text-right" class="tree-time-ago text-right cursor-default"
> >
<gl-skeleton-loading-stub <gl-skeleton-loading-stub
class="ml-auto h-auto w-50" class="ml-auto h-auto w-50"
......
import { shallowMount, RouterLinkStub } from '@vue/test-utils'; import { shallowMount, RouterLinkStub } from '@vue/test-utils';
import { GlBadge, GlLink, GlLoadingIcon } from '@gitlab/ui'; import { GlBadge, GlLink, GlLoadingIcon } from '@gitlab/ui';
import { visitUrl } from '~/lib/utils/url_utility';
import TableRow from '~/repository/components/table/row.vue'; import TableRow from '~/repository/components/table/row.vue';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
jest.mock('~/lib/utils/url_utility', () => ({
...jest.requireActual('~/lib/utils/url_utility'),
visitUrl: jest.fn(),
}));
let vm; let vm;
let $router; let $router;
...@@ -87,31 +81,6 @@ describe('Repository table row component', () => { ...@@ -87,31 +81,6 @@ describe('Repository table row component', () => {
}); });
}); });
it.each`
type | pushes
${'tree'} | ${true}
${'file'} | ${false}
${'commit'} | ${false}
`('pushes new router if type $type is tree', ({ type, pushes }) => {
factory({
id: '1',
sha: '123',
path: 'test',
type,
currentPath: '/',
});
return vm.vm.$nextTick().then(() => {
vm.trigger('click');
if (pushes) {
expect($router.push).toHaveBeenCalledWith({ path: '/-/tree/master/test' });
} else {
expect($router.push).not.toHaveBeenCalled();
}
});
});
it.each` it.each`
path path
${'test#'} ${'test#'}
...@@ -132,7 +101,7 @@ describe('Repository table row component', () => { ...@@ -132,7 +101,7 @@ describe('Repository table row component', () => {
}); });
}); });
it('pushes new route for directory with hash', () => { it('renders link for directory with hash', () => {
factory({ factory({
id: '1', id: '1',
sha: '123', sha: '123',
...@@ -142,36 +111,7 @@ describe('Repository table row component', () => { ...@@ -142,36 +111,7 @@ describe('Repository table row component', () => {
}); });
return vm.vm.$nextTick().then(() => { return vm.vm.$nextTick().then(() => {
vm.trigger('click'); expect(vm.find('.tree-item-link').props('to')).toEqual({ path: '/-/tree/master/test%23' });
expect($router.push).toHaveBeenCalledWith({ path: '/-/tree/master/test%23' });
});
});
it.each`
type | pushes
${'tree'} | ${true}
${'file'} | ${false}
${'commit'} | ${false}
`('calls visitUrl if $type is not tree', ({ type, pushes }) => {
factory({
id: '1',
sha: '123',
path: 'test',
type,
currentPath: '/',
});
return vm.vm.$nextTick().then(() => {
vm.trigger('click');
if (pushes) {
expect(visitUrl).not.toHaveBeenCalled();
} else {
const [url, external] = visitUrl.mock.calls[0];
expect(url).toBe('https://test.com');
expect(external).toBeFalsy();
}
}); });
}); });
......
...@@ -18,6 +18,16 @@ describe Resolvers::ErrorTracking::SentryDetailedErrorResolver do ...@@ -18,6 +18,16 @@ describe Resolvers::ErrorTracking::SentryDetailedErrorResolver do
.and_return issue_details_service .and_return issue_details_service
end end
shared_examples 'it resolves to nil' do
it 'resolves to nil' do
allow(issue_details_service).to receive(:execute)
.and_return(issue: nil)
result = resolve_error(args)
expect(result).to be_nil
end
end
describe '#resolve' do describe '#resolve' do
let(:args) { { id: issue_global_id(1234) } } let(:args) { { id: issue_global_id(1234) } }
...@@ -32,7 +42,7 @@ describe Resolvers::ErrorTracking::SentryDetailedErrorResolver do ...@@ -32,7 +42,7 @@ describe Resolvers::ErrorTracking::SentryDetailedErrorResolver do
before do before do
allow(issue_details_service).to receive(:execute) allow(issue_details_service).to receive(:execute)
.and_return({ issue: detailed_error }) .and_return(issue: detailed_error)
end end
it 'resolves to a detailed error' do it 'resolves to a detailed error' do
...@@ -44,12 +54,14 @@ describe Resolvers::ErrorTracking::SentryDetailedErrorResolver do ...@@ -44,12 +54,14 @@ describe Resolvers::ErrorTracking::SentryDetailedErrorResolver do
end end
end end
it 'resolves to nil if no match' do context 'if id does not match issue' do
allow(issue_details_service).to receive(:execute) it_behaves_like 'it resolves to nil'
.and_return({ issue: nil }) end
result = resolve_error(args) context 'blank id' do
expect(result).to eq nil let(:args) { { id: '' } }
it_behaves_like 'it resolves to nil'
end end
end end
......
...@@ -76,10 +76,10 @@ describe Gitlab::Danger::Changelog do ...@@ -76,10 +76,10 @@ describe Gitlab::Danger::Changelog do
context 'added files contain a changelog' do context 'added files contain a changelog' do
[ [
'changelogs/unreleased/entry.md', 'changelogs/unreleased/entry.yml',
'ee/changelogs/unreleased/entry.md', 'ee/changelogs/unreleased/entry.yml',
'changelogs/unreleased-ee/entry.md', 'changelogs/unreleased-ee/entry.yml',
'ee/changelogs/unreleased-ee/entry.md' 'ee/changelogs/unreleased-ee/entry.yml'
].each do |file_path| ].each do |file_path|
let(:added_files) { [file_path] } let(:added_files) { [file_path] }
...@@ -107,46 +107,22 @@ describe Gitlab::Danger::Changelog do ...@@ -107,46 +107,22 @@ describe Gitlab::Danger::Changelog do
end end
describe '#ee_changelog?' do describe '#ee_changelog?' do
context 'is ee changelog' do subject { changelog.ee_changelog? }
[
'changelogs/unreleased-ee/entry.md',
'ee/changelogs/unreleased-ee/entry.md'
].each do |file_path|
subject { changelog.ee_changelog?(file_path) }
it { is_expected.to be_truthy } before do
end allow(changelog).to receive(:found).and_return(file_path)
end end
context 'is not ee changelog' do context 'is ee changelog' do
[ let(:file_path) { 'ee/changelogs/unreleased/entry.yml' }
'changelogs/unreleased/entry.md',
'ee/changelogs/unreleased/entry.md'
].each do |file_path|
subject { changelog.ee_changelog?(file_path) }
it { is_expected.to be_falsy }
end
end
end
describe '#ce_port_changelog?' do it { is_expected.to be_truthy }
where(:helper_ee?, :file_path, :expected) do
true | 'changelogs/unreleased-ee/entry.md' | false
true | 'ee/changelogs/unreleased-ee/entry.md' | false
false | 'changelogs/unreleased-ee/entry.md' | false
false | 'ee/changelogs/unreleased-ee/entry.md' | false
true | 'changelogs/unreleased/entry.md' | true
true | 'ee/changelogs/unreleased/entry.md' | true
false | 'changelogs/unreleased/entry.md' | false
false | 'ee/changelogs/unreleased/entry.md' | false
end end
with_them do context 'is not ee changelog' do
let(:ee?) { helper_ee? } let(:file_path) { 'changelogs/unreleased/entry.yml' }
subject { changelog.ce_port_changelog?(file_path) }
it { is_expected.to eq(expected) } it { is_expected.to be_falsy }
end end
end end
end end
...@@ -76,6 +76,16 @@ describe Gitlab::Danger::Helper do ...@@ -76,6 +76,16 @@ describe Gitlab::Danger::Helper do
end end
end end
describe '#all_ee_changes' do
subject { helper.all_ee_changes }
it 'returns all changed files starting with ee/' do
expect(helper).to receive(:all_changed_files).and_return(%w[fr/ee/beer.rb ee/wine.rb ee/lib/ido.rb ee.k])
is_expected.to match_array(%w[ee/wine.rb ee/lib/ido.rb])
end
end
describe '#ee?' do describe '#ee?' do
subject { helper.ee? } subject { helper.ee? }
......
...@@ -96,14 +96,25 @@ describe Gitlab::FileDetector do ...@@ -96,14 +96,25 @@ describe Gitlab::FileDetector do
'swagger.yml', 'swagger.yaml', 'swagger.json', 'swagger.yml', 'swagger.yaml', 'swagger.json',
'gitlab_swagger.yml', 'openapi_gitlab.yml', 'gitlab_swagger.yml', 'openapi_gitlab.yml',
'OpenAPI.YML', 'openapi.Yaml', 'openapi.JSON', 'OpenAPI.YML', 'openapi.Yaml', 'openapi.JSON',
'openapi.gitlab.yml', 'gitlab.openapi.yml' 'openapi.gitlab.yml', 'gitlab.openapi.yml',
'attention/openapi.yml', 'attention/swagger.yml',
'attention/gitlab_swagger.yml', 'attention/openapi_gitlab.yml',
'openapi/openapi.yml', 'openapi/swagger.yml',
'openapi/my_openapi.yml', 'openapi/swagger_one.yml'
] ]
openapi_types.each do |type_name| openapi_types.each do |type_name|
expect(described_class.type_of(type_name)).to eq(:openapi) expect(described_class.type_of(type_name)).to eq(:openapi)
end end
expect(described_class.type_of('openapiyml')).to be_nil openapi_bad_types = [
'openapiyml',
'openapi/attention.yaml', 'swagger/attention.yaml'
]
openapi_bad_types.each do |type_name|
expect(described_class.type_of(type_name)).to be_nil
end
end end
end end
end end
...@@ -15,7 +15,7 @@ describe Gitlab::Metrics::Dashboard::ServiceSelector do ...@@ -15,7 +15,7 @@ describe Gitlab::Metrics::Dashboard::ServiceSelector do
context 'when just the dashboard path is provided' do context 'when just the dashboard path is provided' do
let(:arguments) { { dashboard_path: '.gitlab/dashboards/test.yml' } } let(:arguments) { { dashboard_path: '.gitlab/dashboards/test.yml' } }
it { is_expected.to be Metrics::Dashboard::ProjectDashboardService } it { is_expected.to be Metrics::Dashboard::CustomDashboardService }
context 'when the path is for the system dashboard' do context 'when the path is for the system dashboard' do
let(:arguments) { { dashboard_path: system_dashboard_path } } let(:arguments) { { dashboard_path: system_dashboard_path } }
......
...@@ -129,7 +129,8 @@ describe Gitlab::SidekiqMiddleware::DuplicateJobs::DuplicateJob, :clean_gitlab_r ...@@ -129,7 +129,8 @@ describe Gitlab::SidekiqMiddleware::DuplicateJobs::DuplicateJob, :clean_gitlab_r
before do before do
allow(AuthorizedProjectsWorker).to receive(:idempotent?).and_return(idempotent) allow(AuthorizedProjectsWorker).to receive(:idempotent?).and_return(idempotent)
allow(duplicate_job).to receive(:duplicate?).and_return(duplicate) allow(duplicate_job).to receive(:duplicate?).and_return(duplicate)
stub_feature_flags(drop_duplicate_sidekiq_jobs: feature_enabled) allow(Gitlab::SidekiqMiddleware::DuplicateJobs)
.to receive(:drop_duplicates?).with(queue).and_return(feature_enabled)
end end
it 'is droppable when all conditions are met' do it 'is droppable when all conditions are met' do
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::SidekiqMiddleware::DuplicateJobs do
using RSpec::Parameterized::TableSyntax
describe '.drop_duplicates?' do
where(:global_feature_enabled, :selected_queue_enabled, :queue, :expected) do
true | true | described_class::DROPPABLE_QUEUES.first | true
true | true | "other_queue" | true
true | false | described_class::DROPPABLE_QUEUES.first | true
true | false | "other_queue" | true
false | true | described_class::DROPPABLE_QUEUES.first | true
false | true | "other_queue" | false
false | false | described_class::DROPPABLE_QUEUES.first | false
false | false | "other_queue" | false
end
with_them do
before do
stub_feature_flags(drop_duplicate_sidekiq_jobs: global_feature_enabled,
drop_duplicate_sidekiq_jobs_for_queue: selected_queue_enabled)
end
it "allows dropping jobs when expected" do
expect(described_class.drop_duplicates?(queue)).to be(expected)
end
end
end
end
# frozen_string_literal: true
require 'fast_spec_helper'
require 'rubocop'
require 'rubocop/rspec/support'
require_relative '../../../../rubocop/cop/gitlab/duplicate_spec_location'
describe RuboCop::Cop::Gitlab::DuplicateSpecLocation do
include RuboCop::RSpec::ExpectOffense
include CopHelper
subject(:cop) { described_class.new }
let(:rails_root) { '../../../../' }
def full_path(path)
File.expand_path(File.join(rails_root, path), __dir__)
end
context 'Non-EE spec file' do
it 'registers no offenses' do
expect_no_offenses(<<~SOURCE.strip_indent, full_path('spec/foo_spec.rb'))
describe 'Foo' do
end
SOURCE
end
end
context 'Non-EE application file' do
it 'registers no offenses' do
expect_no_offenses(<<~SOURCE.strip_indent, full_path('app/models/blog_post.rb'))
class BlogPost
end
SOURCE
end
end
context 'EE application file' do
it 'registers no offenses' do
expect_no_offenses(<<~SOURCE.strip_indent, full_path('ee/app/models/blog_post.rb'))
class BlogPost
end
SOURCE
end
end
context 'EE spec file for EE only code' do
let(:spec_file_path) { full_path('ee/spec/controllers/foo_spec.rb') }
it 'registers no offenses' do
expect_no_offenses(<<~SOURCE.strip_indent, spec_file_path)
describe 'Foo' do
end
SOURCE
end
context 'there is a duplicate file' do
before do
allow(File).to receive(:exist?).and_call_original
allow(File).to receive(:exist?)
.with(full_path('ee/spec/controllers/ee/foo_spec.rb'))
.and_return(true)
end
it 'marks the describe as offending' do
expect_offense(<<~SOURCE.strip_indent, spec_file_path)
describe 'Foo' do
^^^^^^^^^^^^^^ Duplicate spec location in `ee/spec/controllers/ee/foo_spec.rb`.
end
SOURCE
end
end
end
context 'EE spec file for EE extension' do
let(:spec_file_path) { full_path('ee/spec/controllers/ee/foo_spec.rb') }
it 'registers no offenses' do
expect_no_offenses(<<~SOURCE.strip_indent, spec_file_path)
describe 'Foo' do
end
SOURCE
end
context 'there is a duplicate file' do
before do
allow(File).to receive(:exist?).and_call_original
allow(File).to receive(:exist?)
.with(full_path('ee/spec/controllers/foo_spec.rb'))
.and_return(true)
end
it 'marks the describe as offending' do
expect_offense(<<~SOURCE.strip_indent, spec_file_path)
describe 'Foo' do
^^^^^^^^^^^^^^ Duplicate spec location in `ee/spec/controllers/foo_spec.rb`.
end
SOURCE
end
end
end
end
...@@ -112,9 +112,6 @@ describe Issuable::Clone::AttributesRewriter do ...@@ -112,9 +112,6 @@ describe Issuable::Clone::AttributesRewriter do
expect(event.milestone_id).to eq(expected_attrs[:milestone].id) expect(event.milestone_id).to eq(expected_attrs[:milestone].id)
expect(event.action).to eq(expected_attrs[:action]) expect(event.action).to eq(expected_attrs[:action])
expect(event.state).to eq(expected_attrs[:state]) expect(event.state).to eq(expected_attrs[:state])
expect(event.reference).to be_nil
expect(event.reference_html).to be_nil
end end
end end
end end
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
require 'spec_helper' require 'spec_helper'
describe Metrics::Dashboard::ProjectDashboardService, :use_clean_rails_memory_store_caching do describe Metrics::Dashboard::CustomDashboardService, :use_clean_rails_memory_store_caching do
include MetricsDashboardHelpers include MetricsDashboardHelpers
let_it_be(:user) { create(:user) } let_it_be(:user) { create(:user) }
......
...@@ -74,4 +74,19 @@ describe Namespaces::RootStatisticsWorker, '#perform' do ...@@ -74,4 +74,19 @@ describe Namespaces::RootStatisticsWorker, '#perform' do
worker.perform(group.id) worker.perform(group.id)
end end
end end
it_behaves_like 'an idempotent worker' do
let(:job_args) { [group.id] }
it 'deletes one aggregation schedule' do
# Make sure the group and it's aggregation schedule are created before
# counting
group
expect { worker.perform(*job_args) }
.to change { Namespace::AggregationSchedule.count }.by(-1)
expect { worker.perform(*job_args) }
.not_to change { Namespace::AggregationSchedule.count }
end
end
end end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment