summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG-EE.md7
-rw-r--r--CHANGELOG.md14
-rw-r--r--app/assets/javascripts/repository/components/breadcrumbs.vue7
-rw-r--r--app/assets/javascripts/repository/components/last_commit.vue18
-rw-r--r--app/assets/javascripts/repository/components/tree_content.vue10
-rw-r--r--app/assets/javascripts/repository/index.js2
-rw-r--r--app/controllers/admin/runners_controller.rb5
-rw-r--r--app/controllers/import/base_controller.rb4
-rw-r--r--app/controllers/import/bitbucket_server_controller.rb2
-rw-r--r--app/controllers/import/manifest_controller.rb2
-rw-r--r--app/finders/contributed_projects_finder.rb4
-rw-r--r--app/finders/personal_projects_finder.rb4
-rw-r--r--app/helpers/analytics_navbar_helper.rb66
-rw-r--r--app/helpers/projects_helper.rb5
-rw-r--r--app/helpers/tab_helper.rb11
-rw-r--r--app/models/ci/build.rb3
-rw-r--r--app/models/project.rb2
-rw-r--r--app/services/system_note_service.rb21
-rw-r--r--app/services/system_notes/issuables_service.rb6
-rw-r--r--app/views/admin/runners/show.html.haml6
-rw-r--r--app/views/layouts/nav/sidebar/_project.html.haml47
-rw-r--r--app/views/layouts/nav/sidebar/_project_analytics_link.html.haml16
-rw-r--r--app/workers/concerns/application_worker.rb1
-rw-r--r--app/workers/concerns/cronjob_queue.rb1
-rw-r--r--app/workers/concerns/worker_context.rb27
-rw-r--r--changelogs/unreleased/197925-setting-minimum-password-length-breaks-sign-up-with-saml.yml5
-rw-r--r--changelogs/unreleased/198030-fix-wrong-data.yml5
-rw-r--r--changelogs/unreleased/198289-unable-to-sign-out-from-secondary-geo-node.yml5
-rw-r--r--changelogs/unreleased/33257-prevent-accidental-deletions-via-soft-delete-for-groups-api.yml5
-rw-r--r--changelogs/unreleased/39825-close-issue-after-error-resolve.yml5
-rw-r--r--changelogs/unreleased/jprovazn-fix-group-preloader.yml5
-rw-r--r--changelogs/unreleased/revert-22364.yml6
-rw-r--r--changelogs/unreleased/sh-add-missing-backtrace-define.yml5
-rw-r--r--changelogs/unreleased/sh-optimize-runners-admin-show.yml5
-rw-r--r--doc/administration/audit_events.md1
-rw-r--r--doc/api/groups.md30
-rw-r--r--doc/api/projects.md4
-rw-r--r--doc/api/settings.md2
-rw-r--r--doc/user/admin_area/settings/visibility_and_access_controls.md6
-rw-r--r--doc/user/project/operations/error_tracking.md4
-rw-r--r--lib/api/groups.rb16
-rw-r--r--lib/gitlab/application_context.rb6
-rw-r--r--lib/gitlab/sidekiq_middleware.rb1
-rw-r--r--lib/gitlab/sidekiq_middleware/worker_context/server.rb27
-rw-r--r--locale/gitlab.pot9
-rw-r--r--spec/controllers/admin/runners_controller_spec.rb27
-rw-r--r--spec/features/projects/active_tabs_spec.rb (renamed from spec/features/projects/actve_tabs_spec.rb)48
-rw-r--r--spec/features/projects/user_uses_shortcuts_spec.rb16
-rw-r--r--spec/lib/gitlab/application_context_spec.rb14
-rw-r--r--spec/lib/gitlab/sidekiq_middleware/worker_context/server_spec.rb72
-rw-r--r--spec/lib/gitlab/sidekiq_middleware_spec.rb3
-rw-r--r--spec/services/system_note_service_spec.rb10
-rw-r--r--spec/services/system_notes/issuables_service_spec.rb13
-rw-r--r--spec/support/shared_examples/nav_sidebar_shared_examples.rb26
-rw-r--r--spec/support/shared_examples/views/nav_sidebar_shared_examples.rb11
-rw-r--r--spec/workers/concerns/cronjob_queue_spec.rb8
-rw-r--r--spec/workers/concerns/worker_context_spec.rb34
57 files changed, 574 insertions, 151 deletions
diff --git a/CHANGELOG-EE.md b/CHANGELOG-EE.md
index 7bea16da4e6..4ec5e9c98fd 100644
--- a/CHANGELOG-EE.md
+++ b/CHANGELOG-EE.md
@@ -1,5 +1,12 @@
Please view this file on the master branch, on stable branches it's out of date.
+## 12.7.1
+
+### Fixed (1 change)
+
+- Fix create/delete API calls for approval rules. !23107
+
+
## 12.7.0
### Removed (2 changes)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 896b3a68bad..a147bd438b4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,7 +4,19 @@ entry.
## 12.7.1
-- No changes.
+### Fixed (6 changes)
+
+- Fix loading of sub-epics caused by wrong subscription check. !23184
+- Fix Bitbucket Server importer error handler. !23310
+- Fixes random passwords generated not conforming to minimum_password_length setting. !23387
+- Reverts MR diff redesign which fixes Web IDE visual bugs including file dropdown not showing up. !23428
+- Allow users to sign out on a read-only instance. !23545
+- Remove invalid data from jira_tracker_data table. !23621
+
+### Added (1 change)
+
+- Close Issue when resolving corresponding Sentry error. !22744
+
## 12.7.0
diff --git a/app/assets/javascripts/repository/components/breadcrumbs.vue b/app/assets/javascripts/repository/components/breadcrumbs.vue
index f6b9ea5d30d..5917c96990e 100644
--- a/app/assets/javascripts/repository/components/breadcrumbs.vue
+++ b/app/assets/javascripts/repository/components/breadcrumbs.vue
@@ -34,7 +34,10 @@ export default {
projectPath: this.projectPath,
};
},
- update: data => data.project.userPermissions,
+ update: data => data.project?.userPermissions,
+ error(error) {
+ throw error;
+ },
},
},
mixins: [getRefMixin],
@@ -172,7 +175,7 @@ export default {
);
}
- if (this.userPermissions.pushCode) {
+ if (this.userPermissions?.pushCode) {
items.push(
{
type: ROW_TYPES.divider,
diff --git a/app/assets/javascripts/repository/components/last_commit.vue b/app/assets/javascripts/repository/components/last_commit.vue
index fe1724acf89..573b0c4963e 100644
--- a/app/assets/javascripts/repository/components/last_commit.vue
+++ b/app/assets/javascripts/repository/components/last_commit.vue
@@ -40,16 +40,19 @@ export default {
};
},
update: data => {
- const pipelines = data.project.repository.tree.lastCommit.pipelines.edges;
+ const pipelines = data.project?.repository?.tree?.lastCommit?.pipelines?.edges;
return {
- ...data.project.repository.tree.lastCommit,
- pipeline: pipelines.length && pipelines[0].node,
+ ...data.project?.repository?.tree?.lastCommit,
+ pipeline: pipelines?.length && pipelines[0].node,
};
},
context: {
isSingleRequest: true,
},
+ error(error) {
+ throw error;
+ },
},
},
props: {
@@ -62,7 +65,7 @@ export default {
data() {
return {
projectPath: '',
- commit: {},
+ commit: null,
showDescription: false,
};
},
@@ -79,6 +82,11 @@ export default {
return this.commit.sha.substr(0, 8);
},
},
+ watch: {
+ currentPath() {
+ this.commit = null;
+ },
+ },
methods: {
toggleShowDescription() {
this.showDescription = !this.showDescription;
@@ -91,7 +99,7 @@ export default {
<template>
<div class="info-well d-none d-sm-flex project-last-commit commit p-3">
<gl-loading-icon v-if="isLoading" size="md" color="dark" class="m-auto" />
- <template v-else>
+ <template v-else-if="commit">
<user-avatar-link
v-if="commit.author"
:link-href="commit.author.webUrl"
diff --git a/app/assets/javascripts/repository/components/tree_content.vue b/app/assets/javascripts/repository/components/tree_content.vue
index 92e33b013c3..7b34e9ef60d 100644
--- a/app/assets/javascripts/repository/components/tree_content.vue
+++ b/app/assets/javascripts/repository/components/tree_content.vue
@@ -86,7 +86,8 @@ export default {
},
})
.then(({ data }) => {
- if (!data) return;
+ if (data.errors) throw data.errors;
+ if (!data?.project?.repository) return;
const pageInfo = this.hasNextPage(data.project.repository.tree);
@@ -99,12 +100,15 @@ export default {
{},
);
- if (pageInfo && pageInfo.hasNextPage) {
+ if (pageInfo?.hasNextPage) {
this.nextPageCursor = pageInfo.endCursor;
this.fetchFiles();
}
})
- .catch(() => createFlash(__('An error occurred while fetching folder content.')));
+ .catch(error => {
+ createFlash(__('An error occurred while fetching folder content.'));
+ throw error;
+ });
},
normalizeData(key, data) {
return this.entries[key].concat(data.map(({ node }) => node));
diff --git a/app/assets/javascripts/repository/index.js b/app/assets/javascripts/repository/index.js
index 2ef0c078f13..a26acbbe301 100644
--- a/app/assets/javascripts/repository/index.js
+++ b/app/assets/javascripts/repository/index.js
@@ -23,7 +23,7 @@ export default function setupVueRepositoryList() {
projectPath,
projectShortPath,
ref,
- vueFileListLfsBadge: gon?.features?.vueFileListLfsBadge,
+ vueFileListLfsBadge: gon.features?.vueFileListLfsBadge || false,
commits: [],
},
});
diff --git a/app/controllers/admin/runners_controller.rb b/app/controllers/admin/runners_controller.rb
index 783c59822f1..9eaa55039c8 100644
--- a/app/controllers/admin/runners_controller.rb
+++ b/app/controllers/admin/runners_controller.rb
@@ -66,7 +66,7 @@ class Admin::RunnersController < Admin::ApplicationController
# rubocop: disable CodeReuse/ActiveRecord
def assign_builds_and_projects
- @builds = runner.builds.order('id DESC').first(30)
+ @builds = runner.builds.order('id DESC').preload_project_and_pipeline_project.first(30)
@projects =
if params[:search].present?
::Project.search(params[:search])
@@ -75,7 +75,8 @@ class Admin::RunnersController < Admin::ApplicationController
end
@projects = @projects.where.not(id: runner.projects.select(:id)) if runner.projects.any?
- @projects = @projects.page(params[:page]).per(30)
+ @projects = @projects.inc_routes
+ @projects = @projects.page(params[:page]).per(30).without_count
end
# rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/controllers/import/base_controller.rb b/app/controllers/import/base_controller.rb
index f16f752f85b..04919a4b9d0 100644
--- a/app/controllers/import/base_controller.rb
+++ b/app/controllers/import/base_controller.rb
@@ -7,14 +7,14 @@ class Import::BaseController < ApplicationController
# rubocop: disable CodeReuse/ActiveRecord
def find_already_added_projects(import_type)
- current_user.created_projects.where(import_type: import_type).includes(:import_state)
+ current_user.created_projects.where(import_type: import_type).with_import_state
end
# rubocop: enable CodeReuse/ActiveRecord
# rubocop: disable CodeReuse/ActiveRecord
def find_jobs(import_type)
current_user.created_projects
- .includes(:import_state)
+ .with_import_state
.where(import_type: import_type)
.to_json(only: [:id], methods: [:import_status])
end
diff --git a/app/controllers/import/bitbucket_server_controller.rb b/app/controllers/import/bitbucket_server_controller.rb
index dc72a4e4fd9..5fb7b5dccc5 100644
--- a/app/controllers/import/bitbucket_server_controller.rb
+++ b/app/controllers/import/bitbucket_server_controller.rb
@@ -82,7 +82,7 @@ class Import::BitbucketServerController < Import::BaseController
# rubocop: disable CodeReuse/ActiveRecord
def filter_added_projects(import_type, import_sources)
- current_user.created_projects.where(import_type: import_type, import_source: import_sources).includes(:import_state)
+ current_user.created_projects.where(import_type: import_type, import_source: import_sources).with_import_state
end
# rubocop: enable CodeReuse/ActiveRecord
diff --git a/app/controllers/import/manifest_controller.rb b/app/controllers/import/manifest_controller.rb
index 7ba8b3ce938..9aec870c6ea 100644
--- a/app/controllers/import/manifest_controller.rb
+++ b/app/controllers/import/manifest_controller.rb
@@ -87,7 +87,7 @@ class Import::ManifestController < Import::BaseController
group.all_projects
.where(import_type: 'manifest')
.where(creator_id: current_user)
- .includes(:import_state)
+ .with_import_state
end
# rubocop: enable CodeReuse/ActiveRecord
diff --git a/app/finders/contributed_projects_finder.rb b/app/finders/contributed_projects_finder.rb
index f8c7f0c3167..a351d30229e 100644
--- a/app/finders/contributed_projects_finder.rb
+++ b/app/finders/contributed_projects_finder.rb
@@ -12,16 +12,14 @@ class ContributedProjectsFinder < UnionFinder
# visible by this user.
#
# Returns an ActiveRecord::Relation.
- # rubocop: disable CodeReuse/ActiveRecord
def execute(current_user = nil)
# Do not show contributed projects if the user profile is private.
return Project.none unless can_read_profile?(current_user)
segments = all_projects(current_user)
- find_union(segments, Project).includes(:namespace).order_id_desc
+ find_union(segments, Project).with_namespace.order_id_desc
end
- # rubocop: enable CodeReuse/ActiveRecord
private
diff --git a/app/finders/personal_projects_finder.rb b/app/finders/personal_projects_finder.rb
index 20f5b221a89..e7094d73905 100644
--- a/app/finders/personal_projects_finder.rb
+++ b/app/finders/personal_projects_finder.rb
@@ -17,15 +17,13 @@ class PersonalProjectsFinder < UnionFinder
# min_access_level: integer
#
# Returns an ActiveRecord::Relation.
- # rubocop: disable CodeReuse/ActiveRecord
def execute(current_user = nil)
return Project.none unless can?(current_user, :read_user_profile, @user)
segments = all_projects(current_user)
- find_union(segments, Project).includes(:namespace).order_updated_desc
+ find_union(segments, Project).with_namespace.order_updated_desc
end
- # rubocop: enable CodeReuse/ActiveRecord
private
diff --git a/app/helpers/analytics_navbar_helper.rb b/app/helpers/analytics_navbar_helper.rb
new file mode 100644
index 00000000000..d9aadfaeb15
--- /dev/null
+++ b/app/helpers/analytics_navbar_helper.rb
@@ -0,0 +1,66 @@
+# frozen_string_literal: true
+
+module AnalyticsNavbarHelper
+ class NavbarSubItem
+ attr_reader :title, :path, :link, :link_to_options
+
+ def initialize(title:, path:, link:, link_to_options: {})
+ @title = title
+ @path = path
+ @link = link
+ @link_to_options = link_to_options.merge(title: title)
+ end
+ end
+
+ def project_analytics_navbar_links(project, current_user)
+ [
+ cycle_analytics_navbar_link(project, current_user),
+ repository_analytics_navbar_link(project, current_user),
+ ci_cd_analytics_navbar_link(project, current_user)
+ ].compact
+ end
+
+ private
+
+ def navbar_sub_item(args)
+ NavbarSubItem.new(args)
+ end
+
+ def cycle_analytics_navbar_link(project, current_user)
+ return unless Feature.enabled?(:analytics_pages_under_project_analytics_sidebar, project)
+ return unless project_nav_tab?(:cycle_analytics)
+
+ navbar_sub_item(
+ title: _('Cycle Analytics'),
+ path: 'cycle_analytics#show',
+ link: project_cycle_analytics_path(project),
+ link_to_options: { class: 'shortcuts-project-cycle-analytics' }
+ )
+ end
+
+ def repository_analytics_navbar_link(project, current_user)
+ return if Feature.disabled?(:analytics_pages_under_project_analytics_sidebar, project)
+ return if project.empty_repo?
+
+ navbar_sub_item(
+ title: _('Repository Analytics'),
+ path: 'graphs#charts',
+ link: charts_project_graph_path(project, current_ref),
+ link_to_options: { class: 'shortcuts-repository-charts' }
+ )
+ end
+
+ def ci_cd_analytics_navbar_link(project, current_user)
+ return unless Feature.enabled?(:analytics_pages_under_project_analytics_sidebar, project)
+ return unless project_nav_tab?(:pipelines)
+ return unless project.feature_available?(:builds, current_user) || !project.empty_repo?
+
+ navbar_sub_item(
+ title: _('CI / CD Analytics'),
+ path: 'pipelines#charts',
+ link: charts_project_pipelines_path(project)
+ )
+ end
+end
+
+AnalyticsNavbarHelper.prepend_if_ee('EE::AnalyticsNavbarHelper')
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index 339d68871ae..3000cfb678e 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -403,6 +403,10 @@ module ProjectsHelper
nav_tabs << :operations
end
+ if can?(current_user, :read_cycle_analytics, project)
+ nav_tabs << :cycle_analytics
+ end
+
tab_ability_map.each do |tab, ability|
if can?(current_user, ability, project)
nav_tabs << tab
@@ -643,7 +647,6 @@ module ProjectsHelper
projects#show
projects#activity
releases#index
- cycle_analytics#show
]
end
diff --git a/app/helpers/tab_helper.rb b/app/helpers/tab_helper.rb
index 58edb327be0..0f156003a01 100644
--- a/app/helpers/tab_helper.rb
+++ b/app/helpers/tab_helper.rb
@@ -12,6 +12,7 @@ module TabHelper
# :action - One or more action names to check (optional).
# :path - A shorthand path, such as 'dashboard#index', to check (optional).
# :html_options - Extra options to be passed to the list element (optional).
+ # :unless - Callable object to skip rendering the 'active' class on `li` element (optional).
# block - An optional block that will become the contents of the returned
# `li` element.
#
@@ -56,6 +57,14 @@ module TabHelper
# nav_link(path: 'admin/appearances#show') { "Hello"}
# # => '<li class="active">Hello</li>'
#
+ # # Shorthand path + unless
+ # # Add `active` class when TreeController is requested, except the `index` action.
+ # nav_link(controller: 'tree', unless: -> { action_name?('index') }) { "Hello" }
+ # # => '<li class="active">Hello</li>'
+ #
+ # # When `TreeController#index` is requested
+ # # => '<li>Hello</li>'
+ #
# Returns a list item element String
def nav_link(options = {}, &block)
klass = active_nav_link?(options) ? 'active' : ''
@@ -73,6 +82,8 @@ module TabHelper
end
def active_nav_link?(options)
+ return false if options[:unless]&.call
+
if path = options.delete(:path)
unless path.respond_to?(:each)
path = [path]
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 369a793f3d5..d1edf3e9c03 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -172,6 +172,9 @@ module Ci
scope :queued_before, ->(time) { where(arel_table[:queued_at].lt(time)) }
scope :order_id_desc, -> { order('ci_builds.id DESC') }
+ PROJECT_ROUTE_AND_NAMESPACE_ROUTE = { project: [:project_feature, :route, { namespace: :route }] }.freeze
+ scope :preload_project_and_pipeline_project, -> { preload(PROJECT_ROUTE_AND_NAMESPACE_ROUTE, pipeline: PROJECT_ROUTE_AND_NAMESPACE_ROUTE) }
+
acts_as_taggable
add_authentication_token_field :token, encrypted: :optional
diff --git a/app/models/project.rb b/app/models/project.rb
index 236111cba94..cd191589351 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -411,6 +411,8 @@ class Project < ApplicationRecord
scope :with_project_feature, -> { joins('LEFT JOIN project_features ON projects.id = project_features.project_id') }
scope :inc_routes, -> { includes(:route, namespace: :route) }
scope :with_statistics, -> { includes(:statistics) }
+ scope :with_namespace, -> { includes(:namespace) }
+ scope :with_import_state, -> { includes(:import_state) }
scope :with_service, ->(service) { joins(service).eager_load(service) }
scope :with_shared_runners, -> { where(shared_runners_enabled: true) }
scope :with_container_registry, -> { where(container_registry_enabled: true) }
diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb
index 033c80fd8ed..8a0f44b4e93 100644
--- a/app/services/system_note_service.rb
+++ b/app/services/system_note_service.rb
@@ -100,9 +100,7 @@ module SystemNoteService
end
def close_after_error_tracking_resolve(issue, project, author)
- body = _('resolved the corresponding error and closed the issue.')
-
- create_note(NoteSummary.new(issue, project, author, body, action: 'closed'))
+ ::SystemNotes::IssuablesService.new(noteable: issue, project: project, author: author).close_after_error_tracking_resolve
end
def change_status(noteable, project, author, status, source = nil)
@@ -243,23 +241,6 @@ module SystemNoteService
def zoom_link_removed(issue, project, author)
::SystemNotes::ZoomService.new(noteable: issue, project: project, author: author).zoom_link_removed
end
-
- private
-
- def create_note(note_summary)
- note = Note.create(note_summary.note.merge(system: true))
- note.system_note_metadata = SystemNoteMetadata.new(note_summary.metadata) if note_summary.metadata?
-
- note
- end
-
- def url_helpers
- @url_helpers ||= Gitlab::Routing.url_helpers
- end
-
- def content_tag(*args)
- ActionController::Base.helpers.content_tag(*args)
- end
end
SystemNoteService.prepend_if_ee('EE::SystemNoteService')
diff --git a/app/services/system_notes/issuables_service.rb b/app/services/system_notes/issuables_service.rb
index 6fffd2ed4bf..d7787dac4b8 100644
--- a/app/services/system_notes/issuables_service.rb
+++ b/app/services/system_notes/issuables_service.rb
@@ -282,6 +282,12 @@ module SystemNotes
create_note(NoteSummary.new(noteable, project, author, body, action: action))
end
+ def close_after_error_tracking_resolve
+ body = _('resolved the corresponding error and closed the issue.')
+
+ create_note(NoteSummary.new(noteable, project, author, body, action: 'closed'))
+ end
+
private
def cross_reference_note_content(gfm_reference)
diff --git a/app/views/admin/runners/show.html.haml b/app/views/admin/runners/show.html.haml
index 62be38e9dd2..f860b7a61a2 100644
--- a/app/views/admin/runners/show.html.haml
+++ b/app/views/admin/runners/show.html.haml
@@ -48,7 +48,7 @@
= project.full_name
%td
.float-right
- = link_to 'Disable', [:admin, project.namespace.becomes(Namespace), project, runner_project], method: :delete, class: 'btn btn-danger btn-sm'
+ = link_to 'Disable', admin_namespace_project_runner_project_path(project.namespace, project, runner_project), method: :delete, class: 'btn btn-danger btn-sm'
%table.table.unassigned-projects
%thead
@@ -70,10 +70,10 @@
= project.full_name
%td
.float-right
- = form_for [:admin, project.namespace.becomes(Namespace), project, project.runner_projects.new] do |f|
+ = form_for project.runner_projects.new, url: admin_namespace_project_runner_projects_path(project.namespace, project), method: :post do |f|
= f.hidden_field :runner_id, value: @runner.id
= f.submit 'Enable', class: 'btn btn-sm'
- = paginate @projects, theme: "gitlab"
+ = paginate_without_count @projects
.col-md-6
%h4 Recent jobs served by this Runner
diff --git a/app/views/layouts/nav/sidebar/_project.html.haml b/app/views/layouts/nav/sidebar/_project.html.haml
index 3464cc1ea07..3096b2e43fe 100644
--- a/app/views/layouts/nav/sidebar/_project.html.haml
+++ b/app/views/layouts/nav/sidebar/_project.html.haml
@@ -1,3 +1,5 @@
+- should_display_analytics_pages_in_sidebar = Feature.enabled?(:analytics_pages_under_project_analytics_sidebar, @project)
+
.nav-sidebar{ class: ("sidebar-collapsed-desktop" if collapsed_sidebar?) }
.nav-sidebar-inner-scroll
- can_edit = can?(current_user, :admin_project, @project)
@@ -8,7 +10,9 @@
.sidebar-context-title
= @project.name
%ul.sidebar-top-level-items
- = nav_link(path: sidebar_projects_paths, html_options: { class: 'home' }) do
+ - paths = sidebar_projects_paths
+ - paths << 'cycle_analytics#show' unless should_display_analytics_pages_in_sidebar
+ = nav_link(path: paths, html_options: { class: 'home' }) do
= link_to project_path(@project), class: 'shortcuts-project rspec-project-link', data: { qa_selector: 'project_link' } do
.nav-icon-container
= sprite_icon('home')
@@ -34,15 +38,18 @@
= link_to project_releases_path(@project), title: _('Releases'), class: 'shortcuts-project-releases' do
%span= _('Releases')
- - if can?(current_user, :read_cycle_analytics, @project)
- = nav_link(path: 'cycle_analytics#show') do
- = link_to project_cycle_analytics_path(@project), title: _('Cycle Analytics'), class: 'shortcuts-project-cycle-analytics' do
- %span= _('Cycle Analytics')
- = render_if_exists 'layouts/nav/project_insights_link'
+ - unless should_display_analytics_pages_in_sidebar
+ - if can?(current_user, :read_cycle_analytics, @project)
+ = nav_link(path: 'cycle_analytics#show') do
+ = link_to project_cycle_analytics_path(@project), title: _('Cycle Analytics'), class: 'shortcuts-project-cycle-analytics' do
+ %span= _('Cycle Analytics')
+
+ = render_if_exists 'layouts/nav/project_insights_link'
+
- if project_nav_tab? :files
- = nav_link(controller: sidebar_repository_paths) do
+ = nav_link(controller: sidebar_repository_paths, unless: -> { should_display_analytics_pages_in_sidebar && current_path?('projects/graphs#charts') }) do
= link_to project_tree_path(@project), class: 'shortcuts-tree qa-project-menu-repo' do
.nav-icon-container
= sprite_icon('doc-text')
@@ -83,9 +90,10 @@
= link_to project_compare_index_path(@project, from: @repository.root_ref, to: current_ref) do
= _('Compare')
- = nav_link(path: 'graphs#charts') do
- = link_to charts_project_graph_path(@project, current_ref) do
- = _('Charts')
+ - unless should_display_analytics_pages_in_sidebar
+ = nav_link(path: 'graphs#charts') do
+ = link_to charts_project_graph_path(@project, current_ref) do
+ = _('Charts')
= render_if_exists 'projects/sidebar/repository_locked_files'
@@ -170,7 +178,7 @@
= number_with_delimiter(@project.open_merge_requests_count)
- if project_nav_tab? :pipelines
- = nav_link(controller: [:pipelines, :builds, :jobs, :pipeline_schedules, :artifacts]) do
+ = nav_link(controller: [:pipelines, :builds, :jobs, :pipeline_schedules, :artifacts], unless: -> { should_display_analytics_pages_in_sidebar && current_path?('projects/pipelines#charts') }) do
= link_to project_pipelines_path(@project), class: 'shortcuts-pipelines qa-link-pipelines rspec-link-pipelines', data: { qa_selector: 'ci_cd_link' } do
.nav-icon-container
= sprite_icon('rocket')
@@ -201,13 +209,13 @@
%span
= _('Artifacts')
- - if project_nav_tab? :pipelines
+ - if !should_display_analytics_pages_in_sidebar && project_nav_tab?(:pipelines)
= nav_link(controller: :pipeline_schedules) do
= link_to pipeline_schedules_path(@project), title: _('Schedules'), class: 'shortcuts-builds' do
%span
= _('Schedules')
- - if @project.feature_available?(:builds, current_user) && !@project.empty_repo?
+ - if !should_display_analytics_pages_in_sidebar && @project.feature_available?(:builds, current_user) && !@project.empty_repo?
= nav_link(path: 'pipelines#charts') do
= link_to charts_project_pipelines_path(@project), title: _('Charts'), class: 'shortcuts-pipelines-charts' do
%span
@@ -290,7 +298,7 @@
= render_if_exists 'layouts/nav/sidebar/project_packages_link'
- = render_if_exists 'layouts/nav/sidebar/project_analytics_link' # EE-specific
+ = render 'layouts/nav/sidebar/project_analytics_link'
- if project_nav_tab? :wiki
- wiki_url = project_wiki_path(@project, :home)
@@ -410,11 +418,12 @@
= link_to project_network_path(@project, current_ref), title: _('Network'), class: 'shortcuts-network' do
= _('Graph')
- -# Shortcut to Repository > Charts (formerly, top-nav item "Graphs")
- - unless @project.empty_repo?
- %li.hidden
- = link_to charts_project_graph_path(@project, current_ref), title: _('Charts'), class: 'shortcuts-repository-charts' do
- = _('Charts')
+ - unless should_display_analytics_pages_in_sidebar
+ -# Shortcut to Repository > Charts (formerly, top-nav item "Graphs")
+ - unless @project.empty_repo?
+ %li.hidden
+ = link_to charts_project_graph_path(@project, current_ref), title: _('Charts'), class: 'shortcuts-repository-charts' do
+ = _('Charts')
-# Shortcut to Issues > New Issue
- if project_nav_tab?(:issues)
diff --git a/app/views/layouts/nav/sidebar/_project_analytics_link.html.haml b/app/views/layouts/nav/sidebar/_project_analytics_link.html.haml
new file mode 100644
index 00000000000..3eb8c024588
--- /dev/null
+++ b/app/views/layouts/nav/sidebar/_project_analytics_link.html.haml
@@ -0,0 +1,16 @@
+- navbar_sub_item = project_analytics_navbar_links(@project, current_user).sort_by(&:title)
+- all_paths = navbar_sub_item.map(&:path)
+
+- if navbar_sub_item.any?
+ = nav_link(path: all_paths) do
+ = link_to navbar_sub_item.first.link, data: { qa_selector: 'project_analytics_link' } do
+ .nav-icon-container
+ = sprite_icon('chart')
+ %span.nav-item-name
+ = _('Analytics')
+
+ %ul.sidebar-sub-level-items
+ - navbar_sub_item.each do |menu_item|
+ = nav_link(path: menu_item.path) do
+ = link_to(menu_item.link, menu_item.link_to_options) do
+ %span= menu_item.title
diff --git a/app/workers/concerns/application_worker.rb b/app/workers/concerns/application_worker.rb
index 62748808ff1..733156ab758 100644
--- a/app/workers/concerns/application_worker.rb
+++ b/app/workers/concerns/application_worker.rb
@@ -9,6 +9,7 @@ module ApplicationWorker
include Sidekiq::Worker # rubocop:disable Cop/IncludeSidekiqWorker
include WorkerAttributes
+ include WorkerContext
included do
set_queue
diff --git a/app/workers/concerns/cronjob_queue.rb b/app/workers/concerns/cronjob_queue.rb
index 0683b229381..25ee4539cab 100644
--- a/app/workers/concerns/cronjob_queue.rb
+++ b/app/workers/concerns/cronjob_queue.rb
@@ -8,5 +8,6 @@ module CronjobQueue
included do
queue_namespace :cronjob
sidekiq_options retry: false
+ worker_context project: nil, namespace: nil, user: nil
end
end
diff --git a/app/workers/concerns/worker_context.rb b/app/workers/concerns/worker_context.rb
new file mode 100644
index 00000000000..d85565e3446
--- /dev/null
+++ b/app/workers/concerns/worker_context.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+module WorkerContext
+ extend ActiveSupport::Concern
+
+ class_methods do
+ def worker_context(attributes)
+ @worker_context = Gitlab::ApplicationContext.new(attributes)
+ end
+
+ def get_worker_context
+ @worker_context || superclass_context
+ end
+
+ private
+
+ def superclass_context
+ return unless superclass.include?(WorkerContext)
+
+ superclass.get_worker_context
+ end
+ end
+
+ def with_context(context, &block)
+ Gitlab::ApplicationContext.new(context).use(&block)
+ end
+end
diff --git a/changelogs/unreleased/197925-setting-minimum-password-length-breaks-sign-up-with-saml.yml b/changelogs/unreleased/197925-setting-minimum-password-length-breaks-sign-up-with-saml.yml
deleted file mode 100644
index 0530fd88749..00000000000
--- a/changelogs/unreleased/197925-setting-minimum-password-length-breaks-sign-up-with-saml.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fixes random passwords generated not conforming to minimum_password_length setting
-merge_request: 23387
-author:
-type: fixed
diff --git a/changelogs/unreleased/198030-fix-wrong-data.yml b/changelogs/unreleased/198030-fix-wrong-data.yml
deleted file mode 100644
index d18203cdfef..00000000000
--- a/changelogs/unreleased/198030-fix-wrong-data.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Remove invalid data from jira_tracker_data table
-merge_request: 23621
-author:
-type: fixed
diff --git a/changelogs/unreleased/198289-unable-to-sign-out-from-secondary-geo-node.yml b/changelogs/unreleased/198289-unable-to-sign-out-from-secondary-geo-node.yml
deleted file mode 100644
index ac8db19b2c4..00000000000
--- a/changelogs/unreleased/198289-unable-to-sign-out-from-secondary-geo-node.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Allow users to sign out on a read-only instance
-merge_request: 23545
-author:
-type: fixed
diff --git a/changelogs/unreleased/33257-prevent-accidental-deletions-via-soft-delete-for-groups-api.yml b/changelogs/unreleased/33257-prevent-accidental-deletions-via-soft-delete-for-groups-api.yml
new file mode 100644
index 00000000000..769d1192f6d
--- /dev/null
+++ b/changelogs/unreleased/33257-prevent-accidental-deletions-via-soft-delete-for-groups-api.yml
@@ -0,0 +1,5 @@
+---
+title: Add API endpoints for 'soft-delete for groups' feature
+merge_request: 19430
+author:
+type: added
diff --git a/changelogs/unreleased/39825-close-issue-after-error-resolve.yml b/changelogs/unreleased/39825-close-issue-after-error-resolve.yml
deleted file mode 100644
index 9a59c812290..00000000000
--- a/changelogs/unreleased/39825-close-issue-after-error-resolve.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Close Issue when resolving corresponding Sentry error
-merge_request: 22744
-author:
-type: added
diff --git a/changelogs/unreleased/jprovazn-fix-group-preloader.yml b/changelogs/unreleased/jprovazn-fix-group-preloader.yml
deleted file mode 100644
index b2211fb81af..00000000000
--- a/changelogs/unreleased/jprovazn-fix-group-preloader.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix loading of sub-epics caused by wrong subscription check.
-merge_request: 23184
-author:
-type: fixed
diff --git a/changelogs/unreleased/revert-22364.yml b/changelogs/unreleased/revert-22364.yml
deleted file mode 100644
index 2b2d066adab..00000000000
--- a/changelogs/unreleased/revert-22364.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: Reverts MR diff redesign which fixes Web IDE visual bugs including file dropdown
- not showing up
-merge_request: 23428
-author:
-type: fixed
diff --git a/changelogs/unreleased/sh-add-missing-backtrace-define.yml b/changelogs/unreleased/sh-add-missing-backtrace-define.yml
deleted file mode 100644
index fd07321d012..00000000000
--- a/changelogs/unreleased/sh-add-missing-backtrace-define.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix Bitbucket Server importer error handler
-merge_request: 23310
-author:
-type: fixed
diff --git a/changelogs/unreleased/sh-optimize-runners-admin-show.yml b/changelogs/unreleased/sh-optimize-runners-admin-show.yml
new file mode 100644
index 00000000000..2222f622a6f
--- /dev/null
+++ b/changelogs/unreleased/sh-optimize-runners-admin-show.yml
@@ -0,0 +1,5 @@
+---
+title: Optimize page loading of Admin::RunnersController#show
+merge_request: 23309
+author:
+type: performance
diff --git a/doc/administration/audit_events.md b/doc/administration/audit_events.md
index 7d3be9e1bd3..6366ed21865 100644
--- a/doc/administration/audit_events.md
+++ b/doc/administration/audit_events.md
@@ -105,6 +105,7 @@ recorded:
- Ask for password reset
- Grant OAuth access
- Started/stopped user impersonation
+- Changed username
It is possible to filter particular actions by choosing an audit data type from
the filter dropdown box. You can further filter by specific group, project or user
diff --git a/doc/api/groups.md b/doc/api/groups.md
index de8490fa1f4..156dda52ab0 100644
--- a/doc/api/groups.md
+++ b/doc/api/groups.md
@@ -628,7 +628,12 @@ Feature.disable(:limit_projects_in_groups_api)
## Remove group
-Removes group with all projects inside. Only available to group owners and administrators.
+Only available to group owners and administrators.
+
+This endpoint either:
+
+- Removes group, and queues a background job to delete all projects in the group as well.
+- Since GitLab 12.8, on [Premium](https://about.gitlab.com/pricing/premium/) or higher tiers, marks a group for deletion. The deletion will happen 7 days later by default, but this can be changed in the [instance settings](../user/admin_area/settings/visibility_and_access_controls.md#default-deletion-adjourned-period-premium-only).
```
DELETE /groups/:id
@@ -636,10 +641,27 @@ DELETE /groups/:id
Parameters:
-- `id` (required) - The ID or path of a user group
+| Attribute | Type | Required | Description |
+| --------------- | -------------- | -------- | ----------- |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) |
+
+The response will be `202 Accepted` if the user has authorization.
+
+## Restore group marked for deletion **(PREMIUM)**
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/33257) in GitLab 12.8.
+
+Restores a group marked for deletion.
+
+```plaintext
+POST /groups/:id/restore
+```
+
+Parameters:
-This will queue a background job to delete all projects in the group. The
-response will be a 202 Accepted if the user has authorization.
+| Attribute | Type | Required | Description |
+| --------------- | -------------- | -------- | ----------- |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) |
## Search for group
diff --git a/doc/api/projects.md b/doc/api/projects.md
index c950ec9384d..6467f2626de 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -1769,7 +1769,7 @@ This endpoint either:
- Removes a project including all associated resources (issues, merge requests etc).
- From GitLab 12.6 on Premium or higher tiers, marks a project for deletion. Actual
deletion happens after number of days specified in
- [instance settings](../user/admin_area/settings/visibility_and_access_controls.md#project-deletion-adjourned-period-premium-only).
+ [instance settings](../user/admin_area/settings/visibility_and_access_controls.md#default-deletion-adjourned-period-premium-only).
```
DELETE /projects/:id
@@ -1781,6 +1781,8 @@ DELETE /projects/:id
## Restore project marked for deletion **(PREMIUM)**
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/32935) in GitLab 12.6.
+
Restores project marked for deletion.
```
diff --git a/doc/api/settings.md b/doc/api/settings.md
index 316e5bb0109..ed07bbc575d 100644
--- a/doc/api/settings.md
+++ b/doc/api/settings.md
@@ -294,7 +294,7 @@ are listed in the descriptions of the relevant settings.
| `plantuml_enabled` | boolean | no | (**If enabled, requires:** `plantuml_url`) Enable PlantUML integration. Default is `false`. |
| `plantuml_url` | string | required by: `plantuml_enabled` | The PlantUML instance URL for integration. |
| `polling_interval_multiplier` | decimal | no | Interval multiplier used by endpoints that perform polling. Set to `0` to disable polling. |
-| `deletion_adjourned_period` | integer | no | **(PREMIUM ONLY)** How many days after marking project for deletion it is actually removed. Value between 0 and 90.
+| `deletion_adjourned_period` | integer | no | **(PREMIUM ONLY)** The number of days to wait before removing a project or group that is marked for deletion. Value must be between 0 and 90.
| `project_export_enabled` | boolean | no | Enable project export. |
| `prometheus_metrics_enabled` | boolean | no | Enable Prometheus metrics. |
| `protected_ci_variables` | boolean | no | Environment variables are protected by default. |
diff --git a/doc/user/admin_area/settings/visibility_and_access_controls.md b/doc/user/admin_area/settings/visibility_and_access_controls.md
index b5d708b5e04..6a02f15ff43 100644
--- a/doc/user/admin_area/settings/visibility_and_access_controls.md
+++ b/doc/user/admin_area/settings/visibility_and_access_controls.md
@@ -47,11 +47,13 @@ To ensure only admin users can delete projects:
1. Check the **Default project deletion protection** checkbox.
1. Click **Save changes**.
-## Project deletion adjourned period **(PREMIUM ONLY)**
+## Default deletion adjourned period **(PREMIUM ONLY)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/32935) in GitLab 12.6.
-By default, project marked for deletion will be permanently removed after 7 days. This period may be changed.
+By default, a project or group marked for removal will be permanently removed after 7 days.
+This period may be changed, and setting this period to 0 will enable immediate removal
+of projects or groups.
To change this period:
diff --git a/doc/user/project/operations/error_tracking.md b/doc/user/project/operations/error_tracking.md
index 3fcf6e39ee0..47b153ddd7e 100644
--- a/doc/user/project/operations/error_tracking.md
+++ b/doc/user/project/operations/error_tracking.md
@@ -57,11 +57,11 @@ This page has:
- Other details about the issue, including a full stack trace.
- In [GitLab 12.7 and newer](https://gitlab.com/gitlab-org/gitlab/issues/36246), language and urgency are displayed.
-By default, a **Create issue** button is displayed. Once you have used it to create an issue, the button is hidden.
+By default, a **Create issue** button is displayed:
![Error Details without Issue Link](img/error_details_v12_7.png)
-If a link does exist, it will be shown in the details and the 'Create issue' button will change to a 'View issue' button:
+If you create a GitLab issue from the error, the **Create issue** button will change to a **View issue** button:
![Error Details with Issue Link](img/error_details_with_issue_v12_7.png)
diff --git a/lib/api/groups.rb b/lib/api/groups.rb
index 52fa3f8a68e..d375c35e8c0 100644
--- a/lib/api/groups.rb
+++ b/lib/api/groups.rb
@@ -92,6 +92,15 @@ module API
present paginate(groups), options
end
+
+ def delete_group(group)
+ Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/46285')
+ destroy_conditionally!(group) do |group|
+ ::Groups::DestroyService.new(group, current_user).async_execute
+ end
+
+ accepted!
+ end
end
resource :groups do
@@ -187,12 +196,7 @@ module API
group = find_group!(params[:id])
authorize! :admin_group, group
- Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/46285')
- destroy_conditionally!(group) do |group|
- ::Groups::DestroyService.new(group, current_user).async_execute
- end
-
- accepted!
+ delete_group(group)
end
desc 'Get a list of projects in this group.' do
diff --git a/lib/gitlab/application_context.rb b/lib/gitlab/application_context.rb
index 5a9a99423c4..b950bfb0f3a 100644
--- a/lib/gitlab/application_context.rb
+++ b/lib/gitlab/application_context.rb
@@ -16,7 +16,7 @@ module Gitlab
def self.with_context(args, &block)
application_context = new(**args)
- Labkit::Context.with_context(application_context.to_lazy_hash, &block)
+ application_context.use(&block)
end
def self.push(args)
@@ -42,6 +42,10 @@ module Gitlab
end
end
+ def use
+ Labkit::Context.with_context(to_lazy_hash) { yield }
+ end
+
private
attr_reader :set_values
diff --git a/lib/gitlab/sidekiq_middleware.rb b/lib/gitlab/sidekiq_middleware.rb
index 3dda244233f..b19853a1702 100644
--- a/lib/gitlab/sidekiq_middleware.rb
+++ b/lib/gitlab/sidekiq_middleware.rb
@@ -18,6 +18,7 @@ module Gitlab
chain.add Labkit::Middleware::Sidekiq::Server
chain.add Gitlab::SidekiqMiddleware::InstrumentationLogger
chain.add Gitlab::SidekiqStatus::ServerMiddleware
+ chain.add Gitlab::SidekiqMiddleware::WorkerContext::Server
end
end
diff --git a/lib/gitlab/sidekiq_middleware/worker_context/server.rb b/lib/gitlab/sidekiq_middleware/worker_context/server.rb
new file mode 100644
index 00000000000..29d98ad16a9
--- /dev/null
+++ b/lib/gitlab/sidekiq_middleware/worker_context/server.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module SidekiqMiddleware
+ module WorkerContext
+ class Server
+ def call(worker, job, _queue, &block)
+ worker_class = worker.class
+
+ # This is not a worker we know about, perhaps from a gem
+ return yield unless worker_class.respond_to?(:get_worker_context)
+
+ # Use the context defined on the class level as a base context
+ wrap_in_optional_context(worker_class.get_worker_context, &block)
+ end
+
+ private
+
+ def wrap_in_optional_context(context, &block)
+ return yield unless context
+
+ context.use(&block)
+ end
+ end
+ end
+ end
+end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 9a840aa11bc..c51b29d24dc 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -3014,6 +3014,9 @@ msgstr ""
msgid "CI / CD"
msgstr ""
+msgid "CI / CD Analytics"
+msgstr ""
+
msgid "CI / CD Charts"
msgstr ""
@@ -14310,9 +14313,6 @@ msgstr ""
msgid "Project '%{project_name}' will be deleted on %{date}"
msgstr ""
-msgid "Project Analytics"
-msgstr ""
-
msgid "Project Badges"
msgstr ""
@@ -15769,6 +15769,9 @@ msgstr ""
msgid "Repository"
msgstr ""
+msgid "Repository Analytics"
+msgstr ""
+
msgid "Repository Graph"
msgstr ""
diff --git a/spec/controllers/admin/runners_controller_spec.rb b/spec/controllers/admin/runners_controller_spec.rb
index bbeda7dae0f..a1d346d088d 100644
--- a/spec/controllers/admin/runners_controller_spec.rb
+++ b/spec/controllers/admin/runners_controller_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
describe Admin::RunnersController do
- let!(:runner) { create(:ci_runner) }
+ let_it_be(:runner) { create(:ci_runner) }
before do
sign_in(create(:admin))
@@ -36,6 +36,16 @@ describe Admin::RunnersController do
end
describe '#show' do
+ render_views
+
+ let_it_be(:project) { create(:project) }
+ let_it_be(:project_two) { create(:project) }
+
+ before_all do
+ create(:ci_build, runner: runner, project: project)
+ create(:ci_build, runner: runner, project: project_two)
+ end
+
it 'shows a particular runner' do
get :show, params: { id: runner.id }
@@ -47,6 +57,21 @@ describe Admin::RunnersController do
expect(response).to have_gitlab_http_status(404)
end
+
+ it 'avoids N+1 queries', :request_store do
+ get :show, params: { id: runner.id }
+
+ control_count = ActiveRecord::QueryRecorder.new { get :show, params: { id: runner.id } }.count
+
+ new_project = create(:project)
+ create(:ci_build, runner: runner, project: new_project)
+
+ # There is one additional query looking up subject.group in ProjectPolicy for the
+ # needs_new_sso_session permission
+ expect { get :show, params: { id: runner.id } }.not_to exceed_query_limit(control_count + 1)
+
+ expect(response).to have_gitlab_http_status(200)
+ end
end
describe '#update' do
diff --git a/spec/features/projects/actve_tabs_spec.rb b/spec/features/projects/active_tabs_spec.rb
index 56f587f23ee..41c0e583815 100644
--- a/spec/features/projects/actve_tabs_spec.rb
+++ b/spec/features/projects/active_tabs_spec.rb
@@ -7,6 +7,8 @@ describe 'Project active tab' do
let(:project) { create(:project, :repository) }
before do
+ stub_feature_flags(analytics_pages_under_project_analytics_sidebar: { enabled: false, thing: project })
+
project.add_maintainer(user)
sign_in(user)
end
@@ -17,21 +19,6 @@ describe 'Project active tab' do
end
end
- shared_examples 'page has active tab' do |title|
- it "activates #{title} tab" do
- expect(page).to have_selector('.sidebar-top-level-items > li.active', count: 1)
- expect(find('.sidebar-top-level-items > li.active')).to have_content(title)
- end
- end
-
- shared_examples 'page has active sub tab' do |title|
- it "activates #{title} sub tab" do
- expect(page).to have_selector('.sidebar-sub-level-items > li.active:not(.fly-out-top-item)', count: 1)
- expect(find('.sidebar-sub-level-items > li.active:not(.fly-out-top-item)'))
- .to have_content(title)
- end
- end
-
context 'on project Home' do
before do
visit project_path(project)
@@ -136,4 +123,35 @@ describe 'Project active tab' do
it_behaves_like 'page has active sub tab', 'Repository'
end
end
+
+ context 'when `analytics_pages_under_project_analytics_sidebar` feature flag is enabled' do
+ before do
+ stub_feature_flags(analytics_pages_under_project_analytics_sidebar: { enabled: true, thing: project })
+ end
+
+ context 'on project Analytics' do
+ before do
+ visit charts_project_graph_path(project, 'master')
+ end
+
+ context 'on project Analytics/Repository Analytics' do
+ it_behaves_like 'page has active tab', _('Analytics')
+ it_behaves_like 'page has active sub tab', _('Repository Analytics')
+ end
+
+ context 'on project Analytics/Repository Analytics' do
+ it_behaves_like 'page has active tab', _('Analytics')
+ it_behaves_like 'page has active sub tab', _('Repository Analytics')
+ end
+
+ context 'on project Analytics/Cycle Analytics' do
+ before do
+ click_tab(_('CI / CD Analytics'))
+ end
+
+ it_behaves_like 'page has active tab', _('Analytics')
+ it_behaves_like 'page has active sub tab', _('CI / CD Analytics')
+ end
+ end
+ end
end
diff --git a/spec/features/projects/user_uses_shortcuts_spec.rb b/spec/features/projects/user_uses_shortcuts_spec.rb
index ff24730acef..c6efe1f1896 100644
--- a/spec/features/projects/user_uses_shortcuts_spec.rb
+++ b/spec/features/projects/user_uses_shortcuts_spec.rb
@@ -7,6 +7,8 @@ describe 'User uses shortcuts', :js do
let(:user) { create(:user) }
before do
+ stub_feature_flags(analytics_pages_under_project_analytics_sidebar: { enabled: false, thing: project })
+
project.add_maintainer(user)
sign_in(user)
@@ -156,4 +158,18 @@ describe 'User uses shortcuts', :js do
expect(page).to have_active_navigation('Wiki')
end
end
+
+ context 'when `analytics_pages_under_project_analytics_sidebar` feature flag is enabled' do
+ before do
+ stub_feature_flags(analytics_pages_under_project_analytics_sidebar: { enabled: true, thing: project })
+ end
+
+ it 'redirects to the repository charts page' do
+ find('body').native.send_key('g')
+ find('body').native.send_key('d')
+
+ expect(page).to have_active_navigation(_('Analytics'))
+ expect(page).to have_active_sub_navigation(_('Repository Analytics'))
+ end
+ end
end
diff --git a/spec/lib/gitlab/application_context_spec.rb b/spec/lib/gitlab/application_context_spec.rb
index 1fd400294b7..c6bc3c945a8 100644
--- a/spec/lib/gitlab/application_context_spec.rb
+++ b/spec/lib/gitlab/application_context_spec.rb
@@ -79,4 +79,18 @@ describe Gitlab::ApplicationContext do
.to include(project: project.full_path, root_namespace: project.full_path_components.first)
end
end
+
+ describe '#use' do
+ let(:context) { described_class.new(user: build(:user)) }
+
+ it 'yields control' do
+ expect { |b| context.use(&b) }.to yield_control
+ end
+
+ it 'passes the expected context on to labkit' do
+ expect(Labkit::Context).to receive(:with_context).with(a_hash_including(user: duck_type(:call)))
+
+ context.use {}
+ end
+ end
end
diff --git a/spec/lib/gitlab/sidekiq_middleware/worker_context/server_spec.rb b/spec/lib/gitlab/sidekiq_middleware/worker_context/server_spec.rb
new file mode 100644
index 00000000000..f64ebece930
--- /dev/null
+++ b/spec/lib/gitlab/sidekiq_middleware/worker_context/server_spec.rb
@@ -0,0 +1,72 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::SidekiqMiddleware::WorkerContext::Server do
+ let(:worker_class) do
+ Class.new do
+ def self.name
+ "TestWorker"
+ end
+
+ # To keep track of the context that was active for certain arguments
+ cattr_accessor(:contexts) { {} }
+
+ include ApplicationWorker
+
+ worker_context user: nil
+
+ def perform(identifier, *args)
+ self.class.contexts.merge!(identifier => Labkit::Context.current.to_h)
+ end
+ end
+ end
+
+ let(:other_worker) do
+ Class.new do
+ def self.name
+ "OtherWorker"
+ end
+
+ include Sidekiq::Worker
+
+ def perform
+ end
+ end
+ end
+
+ before do
+ stub_const("TestWorker", worker_class)
+ stub_const("OtherWorker", other_worker)
+ end
+
+ around do |example|
+ Sidekiq::Testing.inline! { example.run }
+ end
+
+ before(:context) do
+ Sidekiq::Testing.server_middleware do |chain|
+ chain.add described_class
+ end
+ end
+
+ after(:context) do
+ Sidekiq::Testing.server_middleware do |chain|
+ chain.remove described_class
+ end
+ end
+
+ describe "#call" do
+ it 'applies a class context' do
+ Gitlab::ApplicationContext.with_context(user: build_stubbed(:user)) do
+ TestWorker.perform_async("identifier", 1)
+ end
+
+ expect(TestWorker.contexts['identifier'].keys).not_to include('meta.user')
+ end
+
+ it "doesn't fail for unknown workers" do
+ expect { OtherWorker.perform_async }.not_to raise_error
+ end
+ end
+end
diff --git a/spec/lib/gitlab/sidekiq_middleware_spec.rb b/spec/lib/gitlab/sidekiq_middleware_spec.rb
index 473d85c0143..b3c0a5b04f0 100644
--- a/spec/lib/gitlab/sidekiq_middleware_spec.rb
+++ b/spec/lib/gitlab/sidekiq_middleware_spec.rb
@@ -44,7 +44,8 @@ describe Gitlab::SidekiqMiddleware do
Gitlab::SidekiqMiddleware::ServerMetrics,
Gitlab::SidekiqMiddleware::ArgumentsLogger,
Gitlab::SidekiqMiddleware::MemoryKiller,
- Gitlab::SidekiqMiddleware::RequestStoreMiddleware
+ Gitlab::SidekiqMiddleware::RequestStoreMiddleware,
+ Gitlab::SidekiqMiddleware::WorkerContext::Server
]
end
let(:enabled_sidekiq_middlewares) { all_sidekiq_middlewares - disabled_sidekiq_middlewares }
diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb
index 40772297cce..9ef8b390b92 100644
--- a/spec/services/system_note_service_spec.rb
+++ b/spec/services/system_note_service_spec.rb
@@ -63,6 +63,16 @@ describe SystemNoteService do
end
end
+ describe '.close_after_error_tracking_resolve' do
+ it 'calls IssuableService' do
+ expect_next_instance_of(::SystemNotes::IssuablesService) do |service|
+ expect(service).to receive(:close_after_error_tracking_resolve)
+ end
+
+ described_class.close_after_error_tracking_resolve(noteable, project, author)
+ end
+ end
+
describe '.change_milestone' do
let(:milestone) { double }
diff --git a/spec/services/system_notes/issuables_service_spec.rb b/spec/services/system_notes/issuables_service_spec.rb
index 56ef0039b63..228d69fda4e 100644
--- a/spec/services/system_notes/issuables_service_spec.rb
+++ b/spec/services/system_notes/issuables_service_spec.rb
@@ -630,4 +630,17 @@ describe ::SystemNotes::IssuablesService do
end
end
end
+
+ describe '#close_after_error_tracking_resolve' do
+ subject { service.close_after_error_tracking_resolve }
+
+ it_behaves_like 'a system note' do
+ let(:action) { 'closed' }
+ end
+
+ it 'creates the expected system note' do
+ expect(subject.note)
+ .to eq('resolved the corresponding error and closed the issue.')
+ end
+ end
end
diff --git a/spec/support/shared_examples/nav_sidebar_shared_examples.rb b/spec/support/shared_examples/nav_sidebar_shared_examples.rb
new file mode 100644
index 00000000000..e084a957785
--- /dev/null
+++ b/spec/support/shared_examples/nav_sidebar_shared_examples.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'has nav sidebar' do
+ it 'has collapsed nav sidebar on mobile' do
+ render
+
+ expect(rendered).to have_selector('.nav-sidebar')
+ expect(rendered).not_to have_selector('.sidebar-collapsed-desktop')
+ expect(rendered).not_to have_selector('.sidebar-expanded-mobile')
+ end
+end
+
+RSpec.shared_examples 'page has active tab' do |title|
+ it "activates #{title} tab" do
+ expect(page).to have_selector('.sidebar-top-level-items > li.active', count: 1)
+ expect(find('.sidebar-top-level-items > li.active')).to have_content(title)
+ end
+end
+
+RSpec.shared_examples 'page has active sub tab' do |title|
+ it "activates #{title} sub tab" do
+ expect(page).to have_selector('.sidebar-sub-level-items > li.active:not(.fly-out-top-item)', count: 1)
+ expect(find('.sidebar-sub-level-items > li.active:not(.fly-out-top-item)'))
+ .to have_content(title)
+ end
+end
diff --git a/spec/support/shared_examples/views/nav_sidebar_shared_examples.rb b/spec/support/shared_examples/views/nav_sidebar_shared_examples.rb
deleted file mode 100644
index fe9a681377c..00000000000
--- a/spec/support/shared_examples/views/nav_sidebar_shared_examples.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-# frozen_string_literal: true
-
-RSpec.shared_examples 'has nav sidebar' do
- it 'has collapsed nav sidebar on mobile' do
- render
-
- expect(rendered).to have_selector('.nav-sidebar')
- expect(rendered).not_to have_selector('.sidebar-collapsed-desktop')
- expect(rendered).not_to have_selector('.sidebar-expanded-mobile')
- end
-end
diff --git a/spec/workers/concerns/cronjob_queue_spec.rb b/spec/workers/concerns/cronjob_queue_spec.rb
index cf4d47b7500..21483d0e4e3 100644
--- a/spec/workers/concerns/cronjob_queue_spec.rb
+++ b/spec/workers/concerns/cronjob_queue_spec.rb
@@ -21,4 +21,12 @@ describe CronjobQueue do
it 'disables retrying of failed jobs' do
expect(worker.sidekiq_options['retry']).to eq(false)
end
+
+ it 'automatically clears project, user and namespace from the context', :aggregate_failues do
+ worker_context = worker.get_worker_context.to_lazy_hash.transform_values(&:call)
+
+ expect(worker_context[:user]).to be_nil
+ expect(worker_context[:root_namespace]).to be_nil
+ expect(worker_context[:project]).to be_nil
+ end
end
diff --git a/spec/workers/concerns/worker_context_spec.rb b/spec/workers/concerns/worker_context_spec.rb
new file mode 100644
index 00000000000..a7d0ba2b8bd
--- /dev/null
+++ b/spec/workers/concerns/worker_context_spec.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe WorkerContext do
+ let(:worker) do
+ Class.new do
+ include WorkerContext
+ end
+ end
+
+ describe '.worker_context' do
+ it 'allows modifying the context for the entire worker' do
+ worker.worker_context(user: build_stubbed(:user))
+
+ expect(worker.get_worker_context).to be_a(Gitlab::ApplicationContext)
+ end
+
+ it 'allows fetches the context from a superclass if none was defined' do
+ worker.worker_context(user: build_stubbed(:user))
+ subclass = Class.new(worker)
+
+ expect(subclass.get_worker_context).to eq(worker.get_worker_context)
+ end
+ end
+
+ describe '#with_context' do
+ it 'allows modifying context when the job is running' do
+ worker.new.with_context(user: build_stubbed(:user, username: 'jane-doe')) do
+ expect(Labkit::Context.current.to_h).to include('meta.user' => 'jane-doe')
+ end
+ end
+ end
+end