summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-01-28 18:08:35 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-01-28 18:08:35 +0000
commit6315ed9630fb1c6ade3114beb762cd1568d79219 (patch)
tree2a5d31936d09c14420c8f4c8bd752e268f0eb19f
parentfedf978f9aa1909ed7bb3fad767ad120a1c6bd7b (diff)
downloadgitlab-ce-6315ed9630fb1c6ade3114beb762cd1568d79219.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--.gitlab/ci/frontend.gitlab-ci.yml101
-rw-r--r--.gitlab/ci/global.gitlab-ci.yml8
-rw-r--r--app/assets/javascripts/broadcast_notification.js21
-rw-r--r--app/assets/javascripts/main.js2
-rw-r--r--app/helpers/broadcast_messages_helper.rb13
-rw-r--r--app/models/ci/job_artifact.rb9
-rw-r--r--app/models/concerns/atomic_internal_id.rb4
-rw-r--r--app/models/internal_id.rb2
-rw-r--r--app/models/performance_monitoring/prometheus_dashboard.rb31
-rw-r--r--app/models/performance_monitoring/prometheus_metric.rb25
-rw-r--r--app/models/performance_monitoring/prometheus_panel.rb24
-rw-r--r--app/models/performance_monitoring/prometheus_panel_group.rb22
-rw-r--r--app/models/repository.rb12
-rw-r--r--app/views/shared/_broadcast_message.html.haml8
-rw-r--r--changelogs/unreleased/21801-migrate-epic-and-epic-notes-mentions-to-epic-user-mentions-table.yml5
-rw-r--r--changelogs/unreleased/custom-dashboard-validation.yml5
-rw-r--r--changelogs/unreleased/id-upload-lsif-dump.yml5
-rw-r--r--changelogs/unreleased/ops-ff-iid.yml5
-rw-r--r--changelogs/unreleased/sh-fix-empty-namespace-git-access.yml5
-rw-r--r--config/routes.rb26
-rw-r--r--config/routes/group.rb6
-rw-r--r--config/routes/project.rb8
-rw-r--r--db/migrate/20190416185130_add_merge_train_enabled_to_ci_cd_settings.rb6
-rw-r--r--db/migrate/20200117194830_add_iid_to_operations_feature_flags.rb13
-rw-r--r--db/migrate/20200117194840_add_index_on_operations_feature_flags_iid.rb17
-rw-r--r--db/post_migrate/20191115115043_migrate_epic_mentions_to_db.rb36
-rw-r--r--db/post_migrate/20191115115522_migrate_epic_notes_mentions_to_db.rb45
-rw-r--r--db/post_migrate/20191128162854_drop_project_ci_cd_settings_merge_trains_enabled.rb2
-rw-r--r--db/post_migrate/20200117194850_backfill_operations_feature_flags_iid.rb24
-rw-r--r--db/post_migrate/20200117194900_delete_internal_ids_where_feature_flags_usage.rb19
-rw-r--r--db/schema.rb3
-rw-r--r--doc/administration/high_availability/README.md2
-rw-r--r--lib/gitlab/background_migration/user_mentions/create_resource_user_mention.rb42
-rw-r--r--lib/gitlab/background_migration/user_mentions/models/epic.rb50
-rw-r--r--lib/gitlab/background_migration/user_mentions/models/epic_user_mention.rb18
-rw-r--r--lib/gitlab/background_migration/user_mentions/models/isolated_mentionable.rb95
-rw-r--r--lib/gitlab/background_migration/user_mentions/models/note.rb65
-rw-r--r--lib/gitlab/ci/config/entry/reports.rb3
-rw-r--r--lib/gitlab/database/migration_helpers.rb14
-rw-r--r--lib/gitlab/diff/file_collection/merge_request_diff_base.rb6
-rw-r--r--lib/gitlab/git_access.rb11
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/login/login_via_oauth_spec.rb5
-rw-r--r--rubocop/cop/migration/update_large_table.rb1
-rw-r--r--spec/controllers/projects/issues_controller_spec.rb3
-rw-r--r--spec/features/broadcast_messages_spec.rb30
-rw-r--r--spec/helpers/broadcast_messages_helper_spec.rb23
-rw-r--r--spec/lib/gitlab/ci/config/entry/reports_spec.rb1
-rw-r--r--spec/lib/gitlab/database/migration_helpers_spec.rb362
-rw-r--r--spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb15
-rw-r--r--spec/lib/gitlab/git_access_spec.rb26
-rw-r--r--spec/migrations/backfill_operations_feature_flags_iid_spec.rb34
-rw-r--r--spec/migrations/delete_internal_ids_where_feature_flags_usage_spec.rb44
-rw-r--r--spec/models/repository_spec.rb9
-rw-r--r--spec/services/ci/retry_build_service_spec.rb2
-rw-r--r--spec/services/merge_requests/reload_diffs_service_spec.rb31
-rw-r--r--spec/services/notes/create_service_spec.rb24
-rw-r--r--spec/support/shared_examples/models/mentionable_shared_examples.rb10
57 files changed, 1276 insertions, 162 deletions
diff --git a/.gitlab/ci/frontend.gitlab-ci.yml b/.gitlab/ci/frontend.gitlab-ci.yml
index 076de55014e..b1699ce147f 100644
--- a/.gitlab/ci/frontend.gitlab-ci.yml
+++ b/.gitlab/ci/frontend.gitlab-ci.yml
@@ -1,15 +1,19 @@
# Make sure to update all the similar conditions in other CI config files if you modify these conditions
-.if-default: &if-default
- if: '$CI_COMMIT_REF_NAME == "master" || $CI_COMMIT_REF_NAME =~ /^[\d-]+-stable(-ee)?$/ || $CI_COMMIT_REF_NAME =~ /^\d+-\d+-auto-deploy-\d+$/ || $CI_COMMIT_REF_NAME =~ /^security\// || $CI_MERGE_REQUEST_IID || $CI_COMMIT_TAG'
+.if-not-canonical-namespace: &if-not-canonical-namespace
+ if: '$CI_PROJECT_NAMESPACE !~ /^gitlab(-org)?($|\/)/'
# Make sure to update all the similar conditions in other CI config files if you modify these conditions
-.if-default-ee: &if-default-ee
- if: '($CI_COMMIT_REF_NAME == "master" || $CI_COMMIT_REF_NAME =~ /^[\d-]+-stable(-ee)?$/ || $CI_COMMIT_REF_NAME =~ /^\d+-\d+-auto-deploy-\d+$/ || $CI_COMMIT_REF_NAME =~ /^security\// || $CI_MERGE_REQUEST_IID || $CI_COMMIT_TAG) && $CI_PROJECT_NAME =~ /^gitlab(-ee)?$/'
+.if-not-ee: &if-not-ee
+ if: '$CI_PROJECT_NAME !~ /^gitlab(-ee)?$/'
# Make sure to update all the similar conditions in other CI config files if you modify these conditions
-.if-master: &if-master
+.if-master-refs: &if-master-refs
if: '$CI_COMMIT_REF_NAME == "master"'
+# Make sure to update all the similar conditions in other CI config files if you modify these conditions
+.if-default-refs: &if-default-refs
+ if: '$CI_COMMIT_REF_NAME == "master" || $CI_COMMIT_REF_NAME =~ /^[\d-]+-stable(-ee)?$/ || $CI_COMMIT_REF_NAME =~ /^\d+-\d+-auto-deploy-\d+$/ || $CI_COMMIT_REF_NAME =~ /^security\// || $CI_MERGE_REQUEST_IID || $CI_COMMIT_TAG'
+
# Make sure to update all the similar patterns in other CI config files if you modify these patterns
.code-backstage-patterns: &code-backstage-patterns
- ".gitlab/ci/**/*"
@@ -33,6 +37,32 @@
- "{,ee/}spec/**/*"
- "doc/README.md" # Some RSpec test rely on this file
+# Make sure to update all the similar patterns in other CI config files if you modify these patterns
+.code-backstage-patterns-qa: &code-backstage-patterns-qa
+ - ".gitlab/ci/**/*"
+ - ".{eslintignore,gitattributes,nvmrc,prettierrc,stylelintrc,yamllint}"
+ - ".{codeclimate,eslintrc,gitlab-ci,haml-lint,haml-lint_todo,rubocop,rubocop_todo,scss-lint}.yml"
+ - ".csscomb.json"
+ - "Dockerfile.assets"
+ - "*_VERSION"
+ - "Gemfile{,.lock}"
+ - "Rakefile"
+ - "{babel.config,jest.config}.js"
+ - "config.ru"
+ - "{package.json,yarn.lock}"
+ - "{,ee/}{app,bin,config,db,haml_lint,lib,locale,public,scripts,symbol,vendor}/**/*"
+ - "doc/api/graphql/reference/*" # Files in this folder are auto-generated
+ # Backstage changes
+ - "Dangerfile"
+ - "danger/**/*"
+ - "{,ee/}fixtures/**/*"
+ - "{,ee/}rubocop/**/*"
+ - "{,ee/}spec/**/*"
+ - "doc/README.md" # Some RSpec test rely on this file
+ # QA changes
+ - ".dockerignore"
+ - "qa/**/*"
+
.assets-compile-cache:
cache:
paths:
@@ -46,10 +76,8 @@
extends:
- .default-tags
- .default-retry
- - .default-only
- .default-before_script
- .assets-compile-cache
- - .only:changes-code-backstage-qa
image: registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.5-git-2.24-lfs-2.9-chrome-73.0-node-12.x-yarn-1.16-graphicsmagick-1.3.33-docker-19.03.1
stage: prepare
services:
@@ -80,24 +108,29 @@
- time scripts/build_assets_image
- scripts/clean-old-cached-assets
- rm -f /etc/apt/sources.list.d/google*.list # We don't need to update Chrome here
- only:
- variables:
- - $CI_SERVER_HOST == "gitlab.com" && $CI_PROJECT_NAMESPACE =~ /^gitlab-org($|\/)/ # Matches the gitlab-org group and its subgroups
- - $CI_SERVER_HOST == "dev.gitlab.org"
tags:
- gitlab-org
- docker
gitlab:assets:compile pull-push-cache:
extends: .gitlab:assets:compile-metadata
- only:
- refs:
- - master
+ rules:
+ - <<: *if-not-canonical-namespace
+ when: never
+ - <<: *if-master-refs
+ changes: *code-backstage-patterns-qa
+ when: on_success
cache:
policy: pull-push
gitlab:assets:compile pull-cache:
extends: .gitlab:assets:compile-metadata
+ rules:
+ - <<: *if-not-canonical-namespace
+ when: never
+ - <<: *if-default-refs
+ changes: *code-backstage-patterns-qa
+ when: on_success
cache:
policy: pull
@@ -105,10 +138,8 @@ gitlab:assets:compile pull-cache:
extends:
- .default-tags
- .default-retry
- - .default-only
- .default-before_script
- .assets-compile-cache
- - .only:changes-code-backstage-qa
stage: prepare
script:
- node --version
@@ -130,28 +161,46 @@ gitlab:assets:compile pull-cache:
compile-assets pull-push-cache:
extends: .compile-assets-metadata
- only:
- refs:
- - master
+ rules:
+ - <<: *if-master-refs
+ changes: *code-backstage-patterns-qa
+ when: on_success
cache:
policy: pull-push
compile-assets pull-push-cache foss:
- extends: [".compile-assets-metadata", ".only-ee-as-if-foss"]
- only:
- refs:
- - master
+ extends:
+ - .compile-assets-metadata
+ - .as-if-foss
+ rules:
+ - <<: *if-not-ee
+ when: never
+ - <<: *if-master-refs
+ changes: *code-backstage-patterns-qa
+ when: on_success
cache:
policy: pull-push
key: "assets-compile:v8:foss"
compile-assets pull-cache:
extends: .compile-assets-metadata
+ rules:
+ - <<: *if-default-refs
+ changes: *code-backstage-patterns-qa
+ when: on_success
cache:
policy: pull
compile-assets pull-cache foss:
- extends: [".compile-assets-metadata", ".only-ee-as-if-foss"]
+ extends:
+ - .compile-assets-metadata
+ - .as-if-foss
+ rules:
+ - <<: *if-not-ee
+ when: never
+ - <<: *if-default-refs
+ changes: *code-backstage-patterns-qa
+ when: on_success
cache:
policy: pull
key: "assets-compile:v8:foss"
@@ -240,7 +289,7 @@ jest-foss:
- .default-cache
stage: test
rules:
- - <<: *if-master
+ - <<: *if-master-refs
when: on_success
dependencies: []
cache:
@@ -274,7 +323,7 @@ webpack-dev-server:
- .default-cache
stage: test
rules:
- - <<: *if-default
+ - <<: *if-default-refs
changes: *code-backstage-patterns
when: on_success
needs: ["setup-test-env", "compile-assets pull-cache"]
diff --git a/.gitlab/ci/global.gitlab-ci.yml b/.gitlab/ci/global.gitlab-ci.yml
index 6943f51047c..d6fcddee6e8 100644
--- a/.gitlab/ci/global.gitlab-ci.yml
+++ b/.gitlab/ci/global.gitlab-ci.yml
@@ -230,7 +230,11 @@
- $CI_PROJECT_NAME == "gitlab"
- $CI_PROJECT_NAME == "gitlab-ee" # Support former project name for forks/mirrors
-.only-ee-as-if-foss:
- extends: .only-ee
+.as-if-foss:
variables:
FOSS_ONLY: '1'
+
+.only-ee-as-if-foss:
+ extends:
+ - .only-ee
+ - .as-if-foss
diff --git a/app/assets/javascripts/broadcast_notification.js b/app/assets/javascripts/broadcast_notification.js
new file mode 100644
index 00000000000..b124502506a
--- /dev/null
+++ b/app/assets/javascripts/broadcast_notification.js
@@ -0,0 +1,21 @@
+import Cookies from 'js-cookie';
+
+const handleOnDismiss = ({ currentTarget }) => {
+ currentTarget.removeEventListener('click', handleOnDismiss);
+ const {
+ dataset: { id },
+ } = currentTarget;
+
+ Cookies.set(`hide_broadcast_notification_message_${id}`, true);
+
+ const notification = document.querySelector(`.js-broadcast-notification-${id}`);
+ notification.parentNode.removeChild(notification);
+};
+
+export default () => {
+ const dismissButton = document.querySelector('.js-dismiss-current-broadcast-notification');
+
+ if (dismissButton) {
+ dismissButton.addEventListener('click', handleOnDismiss);
+ }
+};
diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js
index d755e7e8cdb..1e07469bd7a 100644
--- a/app/assets/javascripts/main.js
+++ b/app/assets/javascripts/main.js
@@ -35,6 +35,7 @@ import initPerformanceBar from './performance_bar';
import initSearchAutocomplete from './search_autocomplete';
import GlFieldErrors from './gl_field_errors';
import initUserPopovers from './user_popovers';
+import initBroadcastNotifications from './broadcast_notification';
import { initUserTracking } from './tracking';
import { __ } from './locale';
@@ -105,6 +106,7 @@ function deferredInitialisation() {
initUsagePingConsent();
initUserPopovers();
initUserTracking();
+ initBroadcastNotifications();
if (document.querySelector('.search')) initSearchAutocomplete();
diff --git a/app/helpers/broadcast_messages_helper.rb b/app/helpers/broadcast_messages_helper.rb
index b95fd8800c0..34e65c322c6 100644
--- a/app/helpers/broadcast_messages_helper.rb
+++ b/app/helpers/broadcast_messages_helper.rb
@@ -6,19 +6,16 @@ module BroadcastMessagesHelper
end
def current_broadcast_notification_message
- BroadcastMessage.current_notification_messages(request.path).last
+ not_hidden_messages = BroadcastMessage.current_notification_messages(request.path).select do |message|
+ cookies["hide_broadcast_notification_message_#{message.id}"].blank?
+ end
+ not_hidden_messages.last
end
def broadcast_message(message, opts = {})
return unless message.present?
- classes = "broadcast-#{message.broadcast_type}-message #{opts[:preview] && 'preview'}"
-
- content_tag :div, dir: 'auto', class: classes, style: broadcast_message_style(message) do
- concat sprite_icon('bullhorn', size: 16, css_class: 'vertical-align-text-top')
- concat ' '
- concat render_broadcast_message(message)
- end
+ render "shared/broadcast_message", { message: message, opts: opts }
end
def broadcast_message_style(broadcast_message)
diff --git a/app/models/ci/job_artifact.rb b/app/models/ci/job_artifact.rb
index 9eca324f0fc..4b205cbe67a 100644
--- a/app/models/ci/job_artifact.rb
+++ b/app/models/ci/job_artifact.rb
@@ -27,7 +27,8 @@ module Ci
license_management: 'gl-license-management-report.json',
license_scanning: 'gl-license-scanning-report.json',
performance: 'performance.json',
- metrics: 'metrics.txt'
+ metrics: 'metrics.txt',
+ lsif: 'lsif.sqlite3'
}.freeze
INTERNAL_TYPES = {
@@ -52,7 +53,8 @@ module Ci
dast: :raw,
license_management: :raw,
license_scanning: :raw,
- performance: :raw
+ performance: :raw,
+ lsif: :raw
}.freeze
TYPE_AND_FORMAT_PAIRS = INTERNAL_TYPES.merge(REPORT_TYPES).freeze
@@ -114,7 +116,8 @@ module Ci
performance: 11, ## EE-specific
metrics: 12, ## EE-specific
metrics_referee: 13, ## runner referees
- network_referee: 14 ## runner referees
+ network_referee: 14, ## runner referees
+ lsif: 15 # LSIF dump for code navigation
}
enum file_format: {
diff --git a/app/models/concerns/atomic_internal_id.rb b/app/models/concerns/atomic_internal_id.rb
index 3e9b084e784..4a632e8cd0c 100644
--- a/app/models/concerns/atomic_internal_id.rb
+++ b/app/models/concerns/atomic_internal_id.rb
@@ -27,7 +27,7 @@ module AtomicInternalId
extend ActiveSupport::Concern
class_methods do
- def has_internal_id(column, scope:, init:, ensure_if: nil, track_if: nil, presence: true) # rubocop:disable Naming/PredicateName
+ def has_internal_id(column, scope:, init:, ensure_if: nil, track_if: nil, presence: true, backfill: false) # rubocop:disable Naming/PredicateName
# We require init here to retain the ability to recalculate in the absence of a
# InternalId record (we may delete records in `internal_ids` for example).
raise "has_internal_id requires a init block, none given." unless init
@@ -38,6 +38,8 @@ module AtomicInternalId
validates column, presence: presence
define_method("ensure_#{scope}_#{column}!") do
+ return if backfill && self.class.where(column => nil).exists?
+
scope_value = internal_id_read_scope(scope)
value = read_attribute(column)
return value unless scope_value
diff --git a/app/models/internal_id.rb b/app/models/internal_id.rb
index 8d3eeaf2461..3e8d0c6a778 100644
--- a/app/models/internal_id.rb
+++ b/app/models/internal_id.rb
@@ -21,7 +21,7 @@ class InternalId < ApplicationRecord
belongs_to :project
belongs_to :namespace
- enum usage: { issues: 0, merge_requests: 1, deployments: 2, milestones: 3, epics: 4, ci_pipelines: 5 }
+ enum usage: { issues: 0, merge_requests: 1, deployments: 2, milestones: 3, epics: 4, ci_pipelines: 5, operations_feature_flags: 6 }
validates :usage, presence: true
diff --git a/app/models/performance_monitoring/prometheus_dashboard.rb b/app/models/performance_monitoring/prometheus_dashboard.rb
new file mode 100644
index 00000000000..5f2df444fd0
--- /dev/null
+++ b/app/models/performance_monitoring/prometheus_dashboard.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+module PerformanceMonitoring
+ class PrometheusDashboard
+ include ActiveModel::Model
+
+ attr_accessor :dashboard, :panel_groups
+
+ validates :dashboard, presence: true
+ validates :panel_groups, presence: true
+
+ def self.from_json(json_content)
+ dashboard = new(
+ dashboard: json_content['dashboard'],
+ panel_groups: json_content['panel_groups'].map { |group| PrometheusPanelGroup.from_json(group) }
+ )
+
+ dashboard.tap(&:validate!)
+ end
+
+ def to_yaml
+ self.as_json(only: valid_attributes).to_yaml
+ end
+
+ private
+
+ def valid_attributes
+ %w(panel_groups panels metrics group priority type title y_label weight id unit label query query_range dashboard)
+ end
+ end
+end
diff --git a/app/models/performance_monitoring/prometheus_metric.rb b/app/models/performance_monitoring/prometheus_metric.rb
new file mode 100644
index 00000000000..7b8bef906fa
--- /dev/null
+++ b/app/models/performance_monitoring/prometheus_metric.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module PerformanceMonitoring
+ class PrometheusMetric
+ include ActiveModel::Model
+
+ attr_accessor :id, :unit, :label, :query, :query_range
+
+ validates :unit, presence: true
+ validates :query, presence: true, unless: :query_range
+ validates :query_range, presence: true, unless: :query
+
+ def self.from_json(json_content)
+ metric = PrometheusMetric.new(
+ id: json_content['id'],
+ unit: json_content['unit'],
+ label: json_content['label'],
+ query: json_content['query'],
+ query_range: json_content['query_range']
+ )
+
+ metric.tap(&:validate!)
+ end
+ end
+end
diff --git a/app/models/performance_monitoring/prometheus_panel.rb b/app/models/performance_monitoring/prometheus_panel.rb
new file mode 100644
index 00000000000..c03218b4219
--- /dev/null
+++ b/app/models/performance_monitoring/prometheus_panel.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+module PerformanceMonitoring
+ class PrometheusPanel
+ include ActiveModel::Model
+
+ attr_accessor :type, :title, :y_label, :weight, :metrics
+
+ validates :title, presence: true
+ validates :metrics, presence: true
+
+ def self.from_json(json_content)
+ panel = new(
+ type: json_content['type'],
+ title: json_content['title'],
+ y_label: json_content['y_label'],
+ weight: json_content['weight'],
+ metrics: json_content['metrics'].map { |metric| PrometheusMetric.from_json(metric) }
+ )
+
+ panel.tap(&:validate!)
+ end
+ end
+end
diff --git a/app/models/performance_monitoring/prometheus_panel_group.rb b/app/models/performance_monitoring/prometheus_panel_group.rb
new file mode 100644
index 00000000000..e672545fce3
--- /dev/null
+++ b/app/models/performance_monitoring/prometheus_panel_group.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+module PerformanceMonitoring
+ class PrometheusPanelGroup
+ include ActiveModel::Model
+
+ attr_accessor :group, :priority, :panels
+
+ validates :group, presence: true
+ validates :panels, presence: true
+
+ def self.from_json(json_content)
+ panel_group = new(
+ group: json_content['group'],
+ priority: json_content['priority'],
+ panels: json_content['panels'].map { |panel| PrometheusPanel.from_json(panel) }
+ )
+
+ panel_group.tap(&:validate!)
+ end
+ end
+end
diff --git a/app/models/repository.rb b/app/models/repository.rb
index c0d1750fe42..ee919f844dc 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -927,22 +927,12 @@ class Repository
def ancestor?(ancestor_id, descendant_id)
return false if ancestor_id.nil? || descendant_id.nil?
- counter = Gitlab::Metrics.counter(
- :repository_ancestor_calls_total,
- 'The number of times we call Repository#ancestor with valid arguments')
- cache_hit = true
-
cache_key = "ancestor:#{ancestor_id}:#{descendant_id}"
- result = request_store_cache.fetch(cache_key) do
+ request_store_cache.fetch(cache_key) do
cache.fetch(cache_key) do
- cache_hit = false
raw_repository.ancestor?(ancestor_id, descendant_id)
end
end
-
- counter.increment(cache_hit: cache_hit.to_s)
-
- result
end
def fetch_as_mirror(url, forced: false, refmap: :all_refs, remote_name: nil, prune: true)
diff --git a/app/views/shared/_broadcast_message.html.haml b/app/views/shared/_broadcast_message.html.haml
new file mode 100644
index 00000000000..c058b210688
--- /dev/null
+++ b/app/views/shared/_broadcast_message.html.haml
@@ -0,0 +1,8 @@
+%div{ class: "broadcast-#{message.broadcast_type}-message #{opts[:preview] && 'preview'} js-broadcast-notification-#{message.id} d-flex",
+ style: broadcast_message_style(message), dir: 'auto' }
+ %div
+ = sprite_icon('bullhorn', size: 16, css_class: 'vertical-align-text-top')
+ = render_broadcast_message(message)
+ - if message.notification? && opts[:preview].blank?
+ %button.js-dismiss-current-broadcast-notification.btn.btn-link.text-dark.pl-2.pr-2{ 'aria-label' => _('Close'), :type => 'button', data: { id: message.id } }
+ %i.fa.fa-times
diff --git a/changelogs/unreleased/21801-migrate-epic-and-epic-notes-mentions-to-epic-user-mentions-table.yml b/changelogs/unreleased/21801-migrate-epic-and-epic-notes-mentions-to-epic-user-mentions-table.yml
new file mode 100644
index 00000000000..168a8c7ac12
--- /dev/null
+++ b/changelogs/unreleased/21801-migrate-epic-and-epic-notes-mentions-to-epic-user-mentions-table.yml
@@ -0,0 +1,5 @@
+---
+title: Migrate epic, epic notes mentions to respective DB table
+merge_request: 22333
+author:
+type: changed
diff --git a/changelogs/unreleased/custom-dashboard-validation.yml b/changelogs/unreleased/custom-dashboard-validation.yml
new file mode 100644
index 00000000000..cd425e6dcf7
--- /dev/null
+++ b/changelogs/unreleased/custom-dashboard-validation.yml
@@ -0,0 +1,5 @@
+---
+title: Add validation for custom PrometheusDashboard
+merge_request: 22893
+author:
+type: added
diff --git a/changelogs/unreleased/id-upload-lsif-dump.yml b/changelogs/unreleased/id-upload-lsif-dump.yml
new file mode 100644
index 00000000000..62ba8f650ca
--- /dev/null
+++ b/changelogs/unreleased/id-upload-lsif-dump.yml
@@ -0,0 +1,5 @@
+---
+title: Add support for lsif artifact report
+merge_request: 23672
+author:
+type: added
diff --git a/changelogs/unreleased/ops-ff-iid.yml b/changelogs/unreleased/ops-ff-iid.yml
new file mode 100644
index 00000000000..8c17eb23520
--- /dev/null
+++ b/changelogs/unreleased/ops-ff-iid.yml
@@ -0,0 +1,5 @@
+---
+title: Add iid to operations_feature_flags and backfill
+merge_request: 22175
+author:
+type: added
diff --git a/changelogs/unreleased/sh-fix-empty-namespace-git-access.yml b/changelogs/unreleased/sh-fix-empty-namespace-git-access.yml
new file mode 100644
index 00000000000..a44a7d36e7f
--- /dev/null
+++ b/changelogs/unreleased/sh-fix-empty-namespace-git-access.yml
@@ -0,0 +1,5 @@
+---
+title: Eliminate statement timeouts when namespace is blank
+merge_request: 23839
+author:
+type: fixed
diff --git a/config/routes.rb b/config/routes.rb
index 518cf985718..be0ef0106f4 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -120,9 +120,7 @@ Rails.application.routes.draw do
draw :country
draw :country_state
draw :subscription
- end
- Gitlab.ee do
constraints(-> (*) { Gitlab::Analytics.any_features_enabled? }) do
draw :analytics
end
@@ -168,11 +166,6 @@ Rails.application.routes.draw do
end
end
- draw :api
- draw :sidekiq
- draw :help
- draw :snippets
-
# Invites
resources :invites, only: [:show], constraints: { id: /[A-Za-z0-9_-]+/ } do
member do
@@ -193,6 +186,25 @@ Rails.application.routes.draw do
# Notification settings
resources :notification_settings, only: [:create, :update]
+ resources :groups, only: [:index, :new, :create] do
+ post :preview_markdown
+ end
+
+ resources :projects, only: [:index, :new, :create]
+
+ get '/projects/:id' => 'projects#resolve'
+
+ Gitlab.ee do
+ scope '/-/push_from_secondary/:geo_node_id' do
+ draw :git_http
+ end
+ end
+
+ draw :git_http
+ draw :api
+ draw :sidekiq
+ draw :help
+ draw :snippets
draw :google_api
draw :import
draw :uploads
diff --git a/config/routes/group.rb b/config/routes/group.rb
index 24957d5ecef..8f572a685d8 100644
--- a/config/routes/group.rb
+++ b/config/routes/group.rb
@@ -1,11 +1,5 @@
# frozen_string_literal: true
-# rubocop: disable Cop/PutGroupRoutesUnderScope
-resources :groups, only: [:index, :new, :create] do
- post :preview_markdown
-end
-# rubocop: enable Cop/PutGroupRoutesUnderScope
-
constraints(::Constraints::GroupUrlConstrainer.new) do
scope(path: 'groups/*id',
controller: :groups,
diff --git a/config/routes/project.rb b/config/routes/project.rb
index df505f96c9d..e1950724386 100644
--- a/config/routes/project.rb
+++ b/config/routes/project.rb
@@ -1,13 +1,5 @@
# frozen_string_literal: true
-# rubocop: disable Cop/PutProjectRoutesUnderScope
-resources :projects, only: [:index, :new, :create]
-
-draw :git_http
-
-get '/projects/:id' => 'projects#resolve'
-# rubocop: enable Cop/PutProjectRoutesUnderScope
-
constraints(::Constraints::ProjectUrlConstrainer.new) do
# If the route has a wildcard segment, the segment has a regex constraint,
# the segment is potentially followed by _another_ wildcard segment, and
diff --git a/db/migrate/20190416185130_add_merge_train_enabled_to_ci_cd_settings.rb b/db/migrate/20190416185130_add_merge_train_enabled_to_ci_cd_settings.rb
index e6427534310..55ef3c79f3f 100644
--- a/db/migrate/20190416185130_add_merge_train_enabled_to_ci_cd_settings.rb
+++ b/db/migrate/20190416185130_add_merge_train_enabled_to_ci_cd_settings.rb
@@ -7,9 +7,13 @@ class AddMergeTrainEnabledToCiCdSettings < ActiveRecord::Migration[5.1]
disable_ddl_transaction!
+ # rubocop:disable Migration/UpdateLargeTable
+ # rubocop:disable Migration/AddColumnWithDefault
def up
- add_column_with_default :project_ci_cd_settings, :merge_trains_enabled, :boolean, default: false, allow_null: false # rubocop:disable Migration/AddColumnWithDefault
+ add_column_with_default :project_ci_cd_settings, :merge_trains_enabled, :boolean, default: false, allow_null: false
end
+ # rubocop:enable Migration/UpdateLargeTable
+ # rubocop:enable Migration/AddColumnWithDefault
def down
remove_column :project_ci_cd_settings, :merge_trains_enabled
diff --git a/db/migrate/20200117194830_add_iid_to_operations_feature_flags.rb b/db/migrate/20200117194830_add_iid_to_operations_feature_flags.rb
new file mode 100644
index 00000000000..b18d9788b9f
--- /dev/null
+++ b/db/migrate/20200117194830_add_iid_to_operations_feature_flags.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+class AddIidToOperationsFeatureFlags < ActiveRecord::Migration[5.2]
+ DOWNTIME = false
+
+ def up
+ add_column :operations_feature_flags, :iid, :integer
+ end
+
+ def down
+ remove_column :operations_feature_flags, :iid
+ end
+end
diff --git a/db/migrate/20200117194840_add_index_on_operations_feature_flags_iid.rb b/db/migrate/20200117194840_add_index_on_operations_feature_flags_iid.rb
new file mode 100644
index 00000000000..67dedb56ec1
--- /dev/null
+++ b/db/migrate/20200117194840_add_index_on_operations_feature_flags_iid.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class AddIndexOnOperationsFeatureFlagsIid < ActiveRecord::Migration[5.2]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :operations_feature_flags, [:project_id, :iid], unique: true
+ end
+
+ def down
+ remove_concurrent_index :operations_feature_flags, [:project_id, :iid]
+ end
+end
diff --git a/db/post_migrate/20191115115043_migrate_epic_mentions_to_db.rb b/db/post_migrate/20191115115043_migrate_epic_mentions_to_db.rb
new file mode 100644
index 00000000000..97f2e568a7e
--- /dev/null
+++ b/db/post_migrate/20191115115043_migrate_epic_mentions_to_db.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+class MigrateEpicMentionsToDb < ActiveRecord::Migration[5.2]
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ DELAY = 2.minutes.to_i
+ BATCH_SIZE = 10000
+ MIGRATION = 'UserMentions::CreateResourceUserMention'
+
+ JOIN = "LEFT JOIN epic_user_mentions on epics.id = epic_user_mentions.epic_id"
+ QUERY_CONDITIONS = "(description like '%@%' OR title like '%@%') AND epic_user_mentions.epic_id is null"
+
+ class Epic < ActiveRecord::Base
+ include EachBatch
+
+ self.table_name = 'epics'
+ end
+
+ def up
+ return unless Gitlab.ee?
+
+ Epic
+ .joins(JOIN)
+ .where(QUERY_CONDITIONS)
+ .each_batch(of: BATCH_SIZE) do |batch, index|
+ range = batch.pluck(Arel.sql('MIN(epics.id)'), Arel.sql('MAX(epics.id)')).first
+ BackgroundMigrationWorker.perform_in(index * DELAY, MIGRATION, ['Epic', JOIN, QUERY_CONDITIONS, false, *range])
+ end
+ end
+
+ def down
+ # no-op
+ end
+end
diff --git a/db/post_migrate/20191115115522_migrate_epic_notes_mentions_to_db.rb b/db/post_migrate/20191115115522_migrate_epic_notes_mentions_to_db.rb
new file mode 100644
index 00000000000..e0b3c36b57d
--- /dev/null
+++ b/db/post_migrate/20191115115522_migrate_epic_notes_mentions_to_db.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+class MigrateEpicNotesMentionsToDb < ActiveRecord::Migration[5.2]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ DELAY = 2.minutes.to_i
+ BATCH_SIZE = 10000
+ MIGRATION = 'UserMentions::CreateResourceUserMention'
+
+ INDEX_NAME = 'epic_mentions_temp_index'
+ INDEX_CONDITION = "note LIKE '%@%'::text AND notes.noteable_type = 'Epic'"
+ QUERY_CONDITIONS = "#{INDEX_CONDITION} AND epic_user_mentions.epic_id IS NULL"
+ JOIN = 'LEFT JOIN epic_user_mentions ON notes.id = epic_user_mentions.note_id'
+
+ class Note < ActiveRecord::Base
+ include EachBatch
+
+ self.table_name = 'notes'
+ end
+
+ def up
+ return unless Gitlab.ee?
+
+ # create temporary index for notes with mentions, may take well over 1h
+ add_concurrent_index(:notes, :id, where: INDEX_CONDITION, name: INDEX_NAME)
+
+ Note
+ .joins(JOIN)
+ .where(QUERY_CONDITIONS)
+ .each_batch(of: BATCH_SIZE) do |batch, index|
+ range = batch.pluck(Arel.sql('MIN(notes.id)'), Arel.sql('MAX(notes.id)')).first
+ BackgroundMigrationWorker.perform_in(index * DELAY, MIGRATION, ['Epic', JOIN, QUERY_CONDITIONS, true, *range])
+ end
+ end
+
+ def down
+ # no-op
+ # temporary index is to be dropped in a different migration in an upcoming release:
+ # https://gitlab.com/gitlab-org/gitlab/issues/196842
+ end
+end
diff --git a/db/post_migrate/20191128162854_drop_project_ci_cd_settings_merge_trains_enabled.rb b/db/post_migrate/20191128162854_drop_project_ci_cd_settings_merge_trains_enabled.rb
index df5c6c8f6cc..c2e6792e611 100644
--- a/db/post_migrate/20191128162854_drop_project_ci_cd_settings_merge_trains_enabled.rb
+++ b/db/post_migrate/20191128162854_drop_project_ci_cd_settings_merge_trains_enabled.rb
@@ -12,6 +12,6 @@ class DropProjectCiCdSettingsMergeTrainsEnabled < ActiveRecord::Migration[5.2]
end
def down
- add_column_with_default :project_ci_cd_settings, :merge_trains_enabled, :boolean, default: false, allow_null: true
+ add_column_with_default :project_ci_cd_settings, :merge_trains_enabled, :boolean, default: false, allow_null: true # rubocop:disable Migration/UpdateLargeTable
end
end
diff --git a/db/post_migrate/20200117194850_backfill_operations_feature_flags_iid.rb b/db/post_migrate/20200117194850_backfill_operations_feature_flags_iid.rb
new file mode 100644
index 00000000000..bc97bd6062d
--- /dev/null
+++ b/db/post_migrate/20200117194850_backfill_operations_feature_flags_iid.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+class BackfillOperationsFeatureFlagsIid < ActiveRecord::Migration[5.2]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ ###
+ # This should update about 700 rows on gitlab.com
+ # Execution time is predicted to take less than a second based on #database-lab results
+ # https://gitlab.com/gitlab-org/gitlab/merge_requests/22175#migration-performance
+ ###
+ def up
+ execute('LOCK operations_feature_flags IN ACCESS EXCLUSIVE MODE')
+
+ backfill_iids('operations_feature_flags')
+
+ change_column_null :operations_feature_flags, :iid, false
+ end
+
+ def down
+ change_column_null :operations_feature_flags, :iid, true
+ end
+end
diff --git a/db/post_migrate/20200117194900_delete_internal_ids_where_feature_flags_usage.rb b/db/post_migrate/20200117194900_delete_internal_ids_where_feature_flags_usage.rb
new file mode 100644
index 00000000000..0cf1ab03622
--- /dev/null
+++ b/db/post_migrate/20200117194900_delete_internal_ids_where_feature_flags_usage.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+class DeleteInternalIdsWhereFeatureFlagsUsage < ActiveRecord::Migration[5.2]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def up
+ sql = <<~SQL
+ DELETE FROM internal_ids WHERE usage = 6
+ SQL
+
+ execute(sql)
+ end
+
+ def down
+ # no-op
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index e4ec6e55fa6..f48ead215bc 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -2778,6 +2778,7 @@ ActiveRecord::Schema.define(version: 2020_01_27_090233) do
t.index ["commit_id"], name: "index_notes_on_commit_id"
t.index ["created_at"], name: "index_notes_on_created_at"
t.index ["discussion_id"], name: "index_notes_on_discussion_id"
+ t.index ["id"], name: "epic_mentions_temp_index", where: "((note ~~ '%@%'::text) AND ((noteable_type)::text = 'Epic'::text))"
t.index ["line_code"], name: "index_notes_on_line_code"
t.index ["note"], name: "index_notes_on_note_trigram", opclass: :gin_trgm_ops, using: :gin
t.index ["noteable_id", "noteable_type"], name: "index_notes_on_noteable_id_and_noteable_type"
@@ -2881,6 +2882,8 @@ ActiveRecord::Schema.define(version: 2020_01_27_090233) do
t.datetime_with_timezone "updated_at", null: false
t.string "name", null: false
t.text "description"
+ t.integer "iid", null: false
+ t.index ["project_id", "iid"], name: "index_operations_feature_flags_on_project_id_and_iid", unique: true
t.index ["project_id", "name"], name: "index_operations_feature_flags_on_project_id_and_name", unique: true
end
diff --git a/doc/administration/high_availability/README.md b/doc/administration/high_availability/README.md
index 2c506898aeb..09747595fc0 100644
--- a/doc/administration/high_availability/README.md
+++ b/doc/administration/high_availability/README.md
@@ -327,7 +327,7 @@ On different cloud vendors a best effort like for like can be used.
| Service | Nodes | Configuration | GCP type |
| ----------------------------|-------|-----------------------|---------------|
| GitLab Rails[^1] | 15 | 32 vCPU, 28.8GB Memory | n1-highcpu-32 |
-| PostgreSQL | 3 | 8 vCPU, 30GB Memory | n1-standard-8 |
+| PostgreSQL | 3 | 16 vCPU, 60GB Memory | n1-standard-16 |
| PgBouncer | 3 | 2 vCPU, 1.8GB Memory | n1-highcpu-2 |
| Gitaly[^2] [^7] | X | 64 vCPU, 240GB Memory | n1-standard-64 |
| Redis[^3] - Cache | 3 | 4 vCPU, 15GB Memory | n1-standard-4 |
diff --git a/lib/gitlab/background_migration/user_mentions/create_resource_user_mention.rb b/lib/gitlab/background_migration/user_mentions/create_resource_user_mention.rb
new file mode 100644
index 00000000000..e951b44b036
--- /dev/null
+++ b/lib/gitlab/background_migration/user_mentions/create_resource_user_mention.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+# rubocop:disable Style/Documentation
+
+module Gitlab
+ module BackgroundMigration
+ module UserMentions
+ class CreateResourceUserMention
+ # Resources that have mentions to be migrated:
+ # issue, merge_request, epic, commit, snippet, design
+
+ BULK_INSERT_SIZE = 5000
+ ISOLATION_MODULE = 'Gitlab::BackgroundMigration::UserMentions::Models'
+
+ def perform(resource_model, join, conditions, with_notes, start_id, end_id)
+ resource_model = "#{ISOLATION_MODULE}::#{resource_model}".constantize if resource_model.is_a?(String)
+ model = with_notes ? "#{ISOLATION_MODULE}::Note".constantize : resource_model
+ resource_user_mention_model = resource_model.user_mention_model
+
+ records = model.joins(join).where(conditions).where(id: start_id..end_id)
+
+ records.in_groups_of(BULK_INSERT_SIZE, false).each do |records|
+ mentions = []
+ records.each do |record|
+ mentions << record.build_mention_values
+ end
+
+ no_quote_columns = [:note_id]
+ no_quote_columns << resource_user_mention_model.resource_foreign_key
+
+ Gitlab::Database.bulk_insert(
+ resource_user_mention_model.table_name,
+ mentions,
+ return_ids: true,
+ disable_quote: no_quote_columns,
+ on_conflict: :do_nothing
+ )
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/background_migration/user_mentions/models/epic.rb b/lib/gitlab/background_migration/user_mentions/models/epic.rb
new file mode 100644
index 00000000000..019d8f0ea8b
--- /dev/null
+++ b/lib/gitlab/background_migration/user_mentions/models/epic.rb
@@ -0,0 +1,50 @@
+# frozen_string_literal: true
+# rubocop:disable Style/Documentation
+
+module Gitlab
+ module BackgroundMigration
+ module UserMentions
+ module Models
+ class Epic < ActiveRecord::Base
+ include IsolatedMentionable
+ include CacheMarkdownField
+
+ attr_mentionable :title, pipeline: :single_line
+ attr_mentionable :description
+ cache_markdown_field :title, pipeline: :single_line
+ cache_markdown_field :description, issuable_state_filter_enabled: true
+
+ self.table_name = 'epics'
+
+ belongs_to :author, class_name: "User"
+ belongs_to :project
+ belongs_to :group
+
+ def self.user_mention_model
+ Gitlab::BackgroundMigration::UserMentions::Models::EpicUserMention
+ end
+
+ def user_mention_model
+ self.class.user_mention_model
+ end
+
+ def project
+ nil
+ end
+
+ def mentionable_params
+ { group: group, label_url_method: :group_epics_url }
+ end
+
+ def user_mention_resource_id
+ id
+ end
+
+ def user_mention_note_id
+ 'NULL'
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/background_migration/user_mentions/models/epic_user_mention.rb b/lib/gitlab/background_migration/user_mentions/models/epic_user_mention.rb
new file mode 100644
index 00000000000..4e3ce9bf3a7
--- /dev/null
+++ b/lib/gitlab/background_migration/user_mentions/models/epic_user_mention.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+# rubocop:disable Style/Documentation
+
+module Gitlab
+ module BackgroundMigration
+ module UserMentions
+ module Models
+ class EpicUserMention < ActiveRecord::Base
+ self.table_name = 'epic_user_mentions'
+
+ def self.resource_foreign_key
+ :epic_id
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/background_migration/user_mentions/models/isolated_mentionable.rb b/lib/gitlab/background_migration/user_mentions/models/isolated_mentionable.rb
new file mode 100644
index 00000000000..40aab896212
--- /dev/null
+++ b/lib/gitlab/background_migration/user_mentions/models/isolated_mentionable.rb
@@ -0,0 +1,95 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module BackgroundMigration
+ module UserMentions
+ module Models
+ # == IsolatedMentionable concern
+ #
+ # Shortcutted for isolation version of Mentionable to be used in mentions migrations
+ #
+ module IsolatedMentionable
+ extend ::ActiveSupport::Concern
+
+ class_methods do
+ # Indicate which attributes of the Mentionable to search for GFM references.
+ def attr_mentionable(attr, options = {})
+ attr = attr.to_s
+ mentionable_attrs << [attr, options]
+ end
+ end
+
+ included do
+ # Accessor for attributes marked mentionable.
+ cattr_accessor :mentionable_attrs, instance_accessor: false do
+ []
+ end
+
+ if self < Participable
+ participant -> (user, ext) { all_references(user, extractor: ext) }
+ end
+ end
+
+ def all_references(current_user = nil, extractor: nil)
+ # Use custom extractor if it's passed in the function parameters.
+ if extractor
+ extractors[current_user] = extractor
+ else
+ extractor = extractors[current_user] ||= ::Gitlab::ReferenceExtractor.new(project, current_user)
+
+ extractor.reset_memoized_values
+ end
+
+ self.class.mentionable_attrs.each do |attr, options|
+ text = __send__(attr) # rubocop:disable GitlabSecurity/PublicSend
+ options = options.merge(
+ cache_key: [self, attr],
+ author: author,
+ skip_project_check: skip_project_check?
+ ).merge(mentionable_params)
+
+ cached_html = self.try(:updated_cached_html_for, attr.to_sym)
+ options[:rendered] = cached_html if cached_html
+
+ extractor.analyze(text, options)
+ end
+
+ extractor
+ end
+
+ def extractors
+ @extractors ||= {}
+ end
+
+ def skip_project_check?
+ false
+ end
+
+ def build_mention_values
+ refs = all_references(author)
+
+ {
+ "#{self.user_mention_model.resource_foreign_key}": user_mention_resource_id,
+ note_id: user_mention_note_id,
+ mentioned_users_ids: array_to_sql(refs.mentioned_users.pluck(:id)),
+ mentioned_projects_ids: array_to_sql(refs.mentioned_projects.pluck(:id)),
+ mentioned_groups_ids: array_to_sql(refs.mentioned_groups.pluck(:id))
+ }
+ end
+
+ def array_to_sql(ids_array)
+ return unless ids_array.present?
+
+ '{' + ids_array.join(", ") + '}'
+ end
+
+ private
+
+ def mentionable_params
+ {}
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/background_migration/user_mentions/models/note.rb b/lib/gitlab/background_migration/user_mentions/models/note.rb
new file mode 100644
index 00000000000..c2828202907
--- /dev/null
+++ b/lib/gitlab/background_migration/user_mentions/models/note.rb
@@ -0,0 +1,65 @@
+# frozen_string_literal: true
+# rubocop:disable Style/Documentation
+
+module Gitlab
+ module BackgroundMigration
+ module UserMentions
+ module Models
+ class Note < ActiveRecord::Base
+ include IsolatedMentionable
+ include CacheMarkdownField
+
+ self.table_name = 'notes'
+ self.inheritance_column = :_type_disabled
+
+ attr_mentionable :note, pipeline: :note
+ cache_markdown_field :note, pipeline: :note, issuable_state_filter_enabled: true
+
+ belongs_to :author, class_name: "User"
+ belongs_to :noteable, polymorphic: true
+ belongs_to :project
+
+ def user_mention_model
+ "#{CreateResourceUserMention::ISOLATION_MODULE}::#{noteable.class}".constantize.user_mention_model
+ end
+
+ def for_personal_snippet?
+ noteable.class.name == 'PersonalSnippet'
+ end
+
+ def for_project_noteable?
+ !for_personal_snippet?
+ end
+
+ def skip_project_check?
+ !for_project_noteable?
+ end
+
+ def for_epic?
+ noteable.class.name == 'Epic'
+ end
+
+ def user_mention_resource_id
+ noteable_id || commit_id
+ end
+
+ def user_mention_note_id
+ id
+ end
+
+ private
+
+ def mentionable_params
+ return super unless for_epic?
+
+ super.merge(banzai_context_params)
+ end
+
+ def banzai_context_params
+ { group: noteable.group, label_url_method: :group_epics_url }
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/config/entry/reports.rb b/lib/gitlab/ci/config/entry/reports.rb
index f984d7d397a..571e056e096 100644
--- a/lib/gitlab/ci/config/entry/reports.rb
+++ b/lib/gitlab/ci/config/entry/reports.rb
@@ -11,7 +11,7 @@ module Gitlab
include ::Gitlab::Config::Entry::Validatable
include ::Gitlab::Config::Entry::Attributable
- ALLOWED_KEYS = %i[junit codequality sast dependency_scanning container_scanning dast performance license_management license_scanning metrics].freeze
+ ALLOWED_KEYS = %i[junit codequality sast dependency_scanning container_scanning dast performance license_management license_scanning metrics lsif].freeze
attributes ALLOWED_KEYS
@@ -30,6 +30,7 @@ module Gitlab
validates :license_management, array_of_strings_or_string: true
validates :license_scanning, array_of_strings_or_string: true
validates :metrics, array_of_strings_or_string: true
+ validates :lsif, array_of_strings_or_string: true
end
end
diff --git a/lib/gitlab/database/migration_helpers.rb b/lib/gitlab/database/migration_helpers.rb
index 5077143e15e..b53e01e6ff2 100644
--- a/lib/gitlab/database/migration_helpers.rb
+++ b/lib/gitlab/database/migration_helpers.rb
@@ -1119,6 +1119,20 @@ into similar problems in the future (e.g. when new tables are created).
SQL
end
+ # Note this should only be used with very small tables
+ def backfill_iids(table)
+ sql = <<-END
+ UPDATE #{table}
+ SET iid = #{table}_with_calculated_iid.iid_num
+ FROM (
+ SELECT id, ROW_NUMBER() OVER (PARTITION BY project_id ORDER BY id ASC) AS iid_num FROM #{table}
+ ) AS #{table}_with_calculated_iid
+ WHERE #{table}.id = #{table}_with_calculated_iid.id
+ END
+
+ execute(sql)
+ end
+
private
def tables_match?(target_table, foreign_key_table)
diff --git a/lib/gitlab/diff/file_collection/merge_request_diff_base.rb b/lib/gitlab/diff/file_collection/merge_request_diff_base.rb
index d27da186de0..d126fdb2be2 100644
--- a/lib/gitlab/diff/file_collection/merge_request_diff_base.rb
+++ b/lib/gitlab/diff/file_collection/merge_request_diff_base.rb
@@ -47,11 +47,7 @@ module Gitlab
private
def cache
- @cache ||= if Feature.enabled?(:hset_redis_diff_caching, project, default_enabled: true)
- Gitlab::Diff::HighlightCache.new(self)
- else
- Gitlab::Diff::DeprecatedHighlightCache.new(self)
- end
+ @cache ||= Gitlab::Diff::HighlightCache.new(self)
end
end
end
diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb
index 7e9ec097ef7..906350e57c5 100644
--- a/lib/gitlab/git_access.rb
+++ b/lib/gitlab/git_access.rb
@@ -50,8 +50,8 @@ module Gitlab
@project = project
@protocol = protocol
@authentication_abilities = authentication_abilities
- @namespace_path = namespace_path
- @project_path = project_path
+ @namespace_path = namespace_path || project&.namespace&.full_path
+ @project_path = project_path || project&.path
@redirected_path = redirected_path
@auth_result_type = auth_result_type
end
@@ -60,6 +60,7 @@ module Gitlab
@logger = Checks::TimedLogger.new(timeout: INTERNAL_TIMEOUT, header: LOG_HEADER)
@changes = changes
+ check_namespace!
check_protocol!
check_valid_actor!
check_active_user!
@@ -136,6 +137,12 @@ module Gitlab
end
end
+ def check_namespace!
+ return if namespace_path.present?
+
+ raise NotFoundError, ERROR_MESSAGES[:project_not_found]
+ end
+
def check_active_user!
return unless user
diff --git a/qa/qa/specs/features/browser_ui/1_manage/login/login_via_oauth_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/login/login_via_oauth_spec.rb
index 70303a30153..dea85f68be2 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/login/login_via_oauth_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/login/login_via_oauth_spec.rb
@@ -1,7 +1,10 @@
# frozen_string_literal: true
module QA
- context 'Manage', :orchestrated, :oauth, quarantine: 'https://gitlab.com/gitlab-org/gitlab/issues/196517' do
+ # This test is skipped instead of quarantine because continuously running
+ # this test may cause the user to hit GitHub's rate limits thus blocking the user.
+ # Related issue: https://gitlab.com/gitlab-org/gitlab/issues/196517
+ context 'Manage', :orchestrated, :oauth, :skip do
describe 'OAuth login' do
it 'User logs in to GitLab with GitHub OAuth' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
diff --git a/rubocop/cop/migration/update_large_table.rb b/rubocop/cop/migration/update_large_table.rb
index c15eec22d04..94bba31c249 100644
--- a/rubocop/cop/migration/update_large_table.rb
+++ b/rubocop/cop/migration/update_large_table.rb
@@ -34,6 +34,7 @@ module RuboCop
namespaces
notes
projects
+ project_ci_cd_settings
routes
users
].freeze
diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb
index 7cdb8989692..e2ffc595c44 100644
--- a/spec/controllers/projects/issues_controller_spec.rb
+++ b/spec/controllers/projects/issues_controller_spec.rb
@@ -1129,8 +1129,9 @@ describe Projects::IssuesController do
sign_in(user)
end
- it "rejects a developer to destroy an issue" do
+ it "does not delete the issue, returning :not_found" do
delete :destroy, params: { namespace_id: project.namespace, project_id: project, id: issue.iid }
+
expect(response).to have_gitlab_http_status(:not_found)
end
end
diff --git a/spec/features/broadcast_messages_spec.rb b/spec/features/broadcast_messages_spec.rb
new file mode 100644
index 00000000000..43fbf1010c9
--- /dev/null
+++ b/spec/features/broadcast_messages_spec.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Broadcast Messages' do
+ let!(:broadcast_message) { create(:broadcast_message, broadcast_type: 'notification', message: 'SampleMessage') }
+
+ it 'shows broadcast message' do
+ visit root_path
+
+ expect(page).to have_content 'SampleMessage'
+ end
+
+ it 'hides broadcast message after dismiss', :js do
+ visit root_path
+
+ find('.js-dismiss-current-broadcast-notification').click
+
+ expect(page).not_to have_content 'SampleMessage'
+ end
+
+ it 'broadcast message is still hidden after refresh', :js do
+ visit root_path
+
+ find('.js-dismiss-current-broadcast-notification').click
+ visit root_path
+
+ expect(page).not_to have_content 'SampleMessage'
+ end
+end
diff --git a/spec/helpers/broadcast_messages_helper_spec.rb b/spec/helpers/broadcast_messages_helper_spec.rb
index a0682c0e278..7e181e429d7 100644
--- a/spec/helpers/broadcast_messages_helper_spec.rb
+++ b/spec/helpers/broadcast_messages_helper_spec.rb
@@ -3,6 +3,29 @@
require 'spec_helper'
describe BroadcastMessagesHelper do
+ describe 'current_broadcast_notification_message' do
+ subject { helper.current_broadcast_notification_message }
+
+ context 'with available broadcast notification messages' do
+ let!(:broadcast_message_1) { create(:broadcast_message, broadcast_type: 'notification', starts_at: Time.now - 1.day) }
+ let!(:broadcast_message_2) { create(:broadcast_message, broadcast_type: 'notification', starts_at: Time.now) }
+
+ it { is_expected.to eq broadcast_message_2 }
+
+ context 'when last broadcast message is hidden' do
+ before do
+ helper.request.cookies["hide_broadcast_notification_message_#{broadcast_message_2.id}"] = 'true'
+ end
+
+ it { is_expected.to eq broadcast_message_1 }
+ end
+ end
+
+ context 'without broadcast notification messages' do
+ it { is_expected.to be_nil }
+ end
+ end
+
describe 'broadcast_message' do
let(:current_broadcast_message) { BroadcastMessage.new(message: 'Current Message') }
diff --git a/spec/lib/gitlab/ci/config/entry/reports_spec.rb b/spec/lib/gitlab/ci/config/entry/reports_spec.rb
index 8562885c90c..1ec30976284 100644
--- a/spec/lib/gitlab/ci/config/entry/reports_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/reports_spec.rb
@@ -43,6 +43,7 @@ describe Gitlab::Ci::Config::Entry::Reports do
:license_management | 'gl-license-management-report.json'
:license_scanning | 'gl-license-scanning-report.json'
:performance | 'performance.json'
+ :lsif | 'lsif.sqlite3'
end
with_them do
diff --git a/spec/lib/gitlab/database/migration_helpers_spec.rb b/spec/lib/gitlab/database/migration_helpers_spec.rb
index f71d3a67eb9..3797e794985 100644
--- a/spec/lib/gitlab/database/migration_helpers_spec.rb
+++ b/spec/lib/gitlab/database/migration_helpers_spec.rb
@@ -1531,4 +1531,366 @@ describe Gitlab::Database::MigrationHelpers do
expect(buffer.read).to include("\"class\":\"#{model.class}\"")
end
end
+
+ describe '#backfill_iids' do
+ include MigrationsHelpers
+
+ class self::Issue < ActiveRecord::Base
+ include AtomicInternalId
+
+ self.table_name = 'issues'
+ self.inheritance_column = :_type_disabled
+
+ belongs_to :project
+
+ has_internal_id :iid,
+ scope: :project,
+ init: ->(s) { s&.project&.issues&.maximum(:iid) },
+ backfill: true,
+ presence: false
+ end
+
+ let(:namespaces) { table(:namespaces) }
+ let(:projects) { table(:projects) }
+ let(:issues) { table(:issues) }
+
+ def setup
+ namespace = namespaces.create!(name: 'foo', path: 'foo')
+ project = projects.create!(namespace_id: namespace.id)
+
+ project
+ end
+
+ it 'generates iids properly for models created after the migration' do
+ project = setup
+
+ model.backfill_iids('issues')
+
+ issue = self.class::Issue.create!(project_id: project.id)
+
+ expect(issue.iid).to eq(1)
+ end
+
+ it 'generates iids properly for models created after the migration when iids are backfilled' do
+ project = setup
+ issue_a = issues.create!(project_id: project.id)
+
+ model.backfill_iids('issues')
+
+ issue_b = self.class::Issue.create!(project_id: project.id)
+
+ expect(issue_a.reload.iid).to eq(1)
+ expect(issue_b.iid).to eq(2)
+ end
+
+ it 'generates iids properly for models created after the migration across multiple projects' do
+ project_a = setup
+ project_b = setup
+ issues.create!(project_id: project_a.id)
+ issues.create!(project_id: project_b.id)
+ issues.create!(project_id: project_b.id)
+
+ model.backfill_iids('issues')
+
+ issue_a = self.class::Issue.create!(project_id: project_a.id)
+ issue_b = self.class::Issue.create!(project_id: project_b.id)
+
+ expect(issue_a.iid).to eq(2)
+ expect(issue_b.iid).to eq(3)
+ end
+
+ context 'when the new code creates a row post deploy but before the migration runs' do
+ it 'does not change the row iid' do
+ project = setup
+ issue = self.class::Issue.create!(project_id: project.id)
+
+ model.backfill_iids('issues')
+
+ expect(issue.reload.iid).to eq(1)
+ end
+
+ it 'backfills iids for rows already in the database' do
+ project = setup
+ issue_a = issues.create!(project_id: project.id)
+ issue_b = issues.create!(project_id: project.id)
+ issue_c = self.class::Issue.create!(project_id: project.id)
+
+ model.backfill_iids('issues')
+
+ expect(issue_a.reload.iid).to eq(1)
+ expect(issue_b.reload.iid).to eq(2)
+ expect(issue_c.reload.iid).to eq(3)
+ end
+
+ it 'backfills iids across multiple projects' do
+ project_a = setup
+ project_b = setup
+ issue_a = issues.create!(project_id: project_a.id)
+ issue_b = issues.create!(project_id: project_b.id)
+ issue_c = self.class::Issue.create!(project_id: project_a.id)
+ issue_d = self.class::Issue.create!(project_id: project_b.id)
+
+ model.backfill_iids('issues')
+
+ expect(issue_a.reload.iid).to eq(1)
+ expect(issue_b.reload.iid).to eq(1)
+ expect(issue_c.reload.iid).to eq(2)
+ expect(issue_d.reload.iid).to eq(2)
+ end
+
+ it 'generates iids properly for models created after the migration' do
+ project = setup
+ issue_a = issues.create!(project_id: project.id)
+ issue_b = issues.create!(project_id: project.id)
+ issue_c = self.class::Issue.create!(project_id: project.id)
+
+ model.backfill_iids('issues')
+
+ issue_d = self.class::Issue.create!(project_id: project.id)
+ issue_e = self.class::Issue.create!(project_id: project.id)
+
+ expect(issue_a.reload.iid).to eq(1)
+ expect(issue_b.reload.iid).to eq(2)
+ expect(issue_c.reload.iid).to eq(3)
+ expect(issue_d.iid).to eq(4)
+ expect(issue_e.iid).to eq(5)
+ end
+
+ it 'backfills iids and properly generates iids for new models across multiple projects' do
+ project_a = setup
+ project_b = setup
+ issue_a = issues.create!(project_id: project_a.id)
+ issue_b = issues.create!(project_id: project_b.id)
+ issue_c = self.class::Issue.create!(project_id: project_a.id)
+ issue_d = self.class::Issue.create!(project_id: project_b.id)
+
+ model.backfill_iids('issues')
+
+ issue_e = self.class::Issue.create!(project_id: project_a.id)
+ issue_f = self.class::Issue.create!(project_id: project_b.id)
+ issue_g = self.class::Issue.create!(project_id: project_a.id)
+
+ expect(issue_a.reload.iid).to eq(1)
+ expect(issue_b.reload.iid).to eq(1)
+ expect(issue_c.reload.iid).to eq(2)
+ expect(issue_d.reload.iid).to eq(2)
+ expect(issue_e.iid).to eq(3)
+ expect(issue_f.iid).to eq(3)
+ expect(issue_g.iid).to eq(4)
+ end
+ end
+
+ context 'when the new code creates a model and then old code creates a model post deploy but before the migration runs' do
+ it 'backfills iids' do
+ project = setup
+ issue_a = issues.create!(project_id: project.id)
+ issue_b = self.class::Issue.create!(project_id: project.id)
+ issue_c = issues.create!(project_id: project.id)
+
+ model.backfill_iids('issues')
+
+ expect(issue_a.reload.iid).to eq(1)
+ expect(issue_b.reload.iid).to eq(2)
+ expect(issue_c.reload.iid).to eq(3)
+ end
+
+ it 'generates an iid for a new model after the migration' do
+ project = setup
+ issue_a = issues.create!(project_id: project.id)
+ issue_b = issues.create!(project_id: project.id)
+ issue_c = self.class::Issue.create!(project_id: project.id)
+ issue_d = issues.create!(project_id: project.id)
+
+ model.backfill_iids('issues')
+
+ issue_e = self.class::Issue.create!(project_id: project.id)
+
+ expect(issue_a.reload.iid).to eq(1)
+ expect(issue_b.reload.iid).to eq(2)
+ expect(issue_c.reload.iid).to eq(3)
+ expect(issue_d.reload.iid).to eq(4)
+ expect(issue_e.iid).to eq(5)
+ end
+ end
+
+ context 'when the new code and old code alternate creating models post deploy but before the migration runs' do
+ it 'backfills iids' do
+ project = setup
+ issue_a = issues.create!(project_id: project.id)
+ issue_b = self.class::Issue.create!(project_id: project.id)
+ issue_c = issues.create!(project_id: project.id)
+ issue_d = self.class::Issue.create!(project_id: project.id)
+
+ model.backfill_iids('issues')
+
+ expect(issue_a.reload.iid).to eq(1)
+ expect(issue_b.reload.iid).to eq(2)
+ expect(issue_c.reload.iid).to eq(3)
+ expect(issue_d.reload.iid).to eq(4)
+ end
+
+ it 'generates an iid for a new model after the migration' do
+ project = setup
+ issue_a = issues.create!(project_id: project.id)
+ issue_b = issues.create!(project_id: project.id)
+ issue_c = self.class::Issue.create!(project_id: project.id)
+ issue_d = issues.create!(project_id: project.id)
+ issue_e = self.class::Issue.create!(project_id: project.id)
+
+ model.backfill_iids('issues')
+
+ issue_f = self.class::Issue.create!(project_id: project.id)
+
+ expect(issue_a.reload.iid).to eq(1)
+ expect(issue_b.reload.iid).to eq(2)
+ expect(issue_c.reload.iid).to eq(3)
+ expect(issue_d.reload.iid).to eq(4)
+ expect(issue_e.reload.iid).to eq(5)
+ expect(issue_f.iid).to eq(6)
+ end
+ end
+
+ context 'when the new code creates and deletes a model post deploy but before the migration runs' do
+ it 'backfills iids for rows already in the database' do
+ project = setup
+ issue_a = issues.create!(project_id: project.id)
+ issue_b = issues.create!(project_id: project.id)
+ issue_c = self.class::Issue.create!(project_id: project.id)
+ issue_c.delete
+
+ model.backfill_iids('issues')
+
+ expect(issue_a.reload.iid).to eq(1)
+ expect(issue_b.reload.iid).to eq(2)
+ end
+
+ it 'successfully creates a new model after the migration' do
+ project = setup
+ issue_a = issues.create!(project_id: project.id)
+ issue_b = issues.create!(project_id: project.id)
+ issue_c = self.class::Issue.create!(project_id: project.id)
+ issue_c.delete
+
+ model.backfill_iids('issues')
+
+ issue_d = self.class::Issue.create!(project_id: project.id)
+
+ expect(issue_a.reload.iid).to eq(1)
+ expect(issue_b.reload.iid).to eq(2)
+ expect(issue_d.iid).to eq(3)
+ end
+ end
+
+ context 'when the new code creates and deletes a model and old code creates a model post deploy but before the migration runs' do
+ it 'backfills iids' do
+ project = setup
+ issue_a = issues.create!(project_id: project.id)
+ issue_b = issues.create!(project_id: project.id)
+ issue_c = self.class::Issue.create!(project_id: project.id)
+ issue_c.delete
+ issue_d = issues.create!(project_id: project.id)
+
+ model.backfill_iids('issues')
+
+ expect(issue_a.reload.iid).to eq(1)
+ expect(issue_b.reload.iid).to eq(2)
+ expect(issue_d.reload.iid).to eq(3)
+ end
+
+ it 'successfully creates a new model after the migration' do
+ project = setup
+ issue_a = issues.create!(project_id: project.id)
+ issue_b = issues.create!(project_id: project.id)
+ issue_c = self.class::Issue.create!(project_id: project.id)
+ issue_c.delete
+ issue_d = issues.create!(project_id: project.id)
+
+ model.backfill_iids('issues')
+
+ issue_e = self.class::Issue.create!(project_id: project.id)
+
+ expect(issue_a.reload.iid).to eq(1)
+ expect(issue_b.reload.iid).to eq(2)
+ expect(issue_d.reload.iid).to eq(3)
+ expect(issue_e.iid).to eq(4)
+ end
+ end
+
+ context 'when the new code creates and deletes a model and then creates another model post deploy but before the migration runs' do
+ it 'successfully generates an iid for a new model after the migration' do
+ project = setup
+ issue_a = issues.create!(project_id: project.id)
+ issue_b = issues.create!(project_id: project.id)
+ issue_c = self.class::Issue.create!(project_id: project.id)
+ issue_c.delete
+ issue_d = self.class::Issue.create!(project_id: project.id)
+
+ model.backfill_iids('issues')
+
+ expect(issue_a.reload.iid).to eq(1)
+ expect(issue_b.reload.iid).to eq(2)
+ expect(issue_d.reload.iid).to eq(3)
+ end
+
+ it 'successfully generates an iid for a new model after the migration' do
+ project = setup
+ issue_a = issues.create!(project_id: project.id)
+ issue_b = issues.create!(project_id: project.id)
+ issue_c = self.class::Issue.create!(project_id: project.id)
+ issue_c.delete
+ issue_d = self.class::Issue.create!(project_id: project.id)
+
+ model.backfill_iids('issues')
+
+ issue_e = self.class::Issue.create!(project_id: project.id)
+
+ expect(issue_a.reload.iid).to eq(1)
+ expect(issue_b.reload.iid).to eq(2)
+ expect(issue_d.reload.iid).to eq(3)
+ expect(issue_e.iid).to eq(4)
+ end
+ end
+
+ context 'when the first model is created for a project after the migration' do
+ it 'generates an iid' do
+ project_a = setup
+ project_b = setup
+ issue_a = issues.create!(project_id: project_a.id)
+
+ model.backfill_iids('issues')
+
+ issue_b = self.class::Issue.create!(project_id: project_b.id)
+
+ expect(issue_a.reload.iid).to eq(1)
+ expect(issue_b.reload.iid).to eq(1)
+ end
+ end
+
+ context 'when a row already has an iid set in the database' do
+ it 'backfills iids' do
+ project = setup
+ issue_a = issues.create!(project_id: project.id, iid: 1)
+ issue_b = issues.create!(project_id: project.id, iid: 2)
+
+ model.backfill_iids('issues')
+
+ expect(issue_a.reload.iid).to eq(1)
+ expect(issue_b.reload.iid).to eq(2)
+ end
+
+ it 'backfills for multiple projects' do
+ project_a = setup
+ project_b = setup
+ issue_a = issues.create!(project_id: project_a.id, iid: 1)
+ issue_b = issues.create!(project_id: project_b.id, iid: 1)
+ issue_c = issues.create!(project_id: project_a.id, iid: 2)
+
+ model.backfill_iids('issues')
+
+ expect(issue_a.reload.iid).to eq(1)
+ expect(issue_b.reload.iid).to eq(1)
+ expect(issue_c.reload.iid).to eq(2)
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb b/spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb
index a4f74ddc8c2..c2b6ca4164c 100644
--- a/spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb
+++ b/spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb
@@ -38,21 +38,6 @@ describe Gitlab::Diff::FileCollection::MergeRequestDiff do
let(:diffable) { merge_request.merge_request_diff }
end
- context 'using Gitlab::Diff::DeprecatedHighlightCache' do
- before do
- stub_feature_flags(hset_redis_diff_caching: false)
- end
-
- it 'uses a different cache key if diff line keys change' do
- mr_diff = described_class.new(merge_request.merge_request_diff, diff_options: nil)
- key = mr_diff.cache_key
-
- stub_const('Gitlab::Diff::Line::SERIALIZE_KEYS', [:foo])
-
- expect(mr_diff.cache_key).not_to eq(key)
- end
- end
-
it_behaves_like 'diff statistics' do
let(:collection_default_args) do
{ diff_options: {} }
diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb
index 36bde9de12d..231bcb4150c 100644
--- a/spec/lib/gitlab/git_access_spec.rb
+++ b/spec/lib/gitlab/git_access_spec.rb
@@ -75,6 +75,32 @@ describe Gitlab::GitAccess do
end
end
+ describe '#check_namespace!' do
+ context 'when namespace exists' do
+ before do
+ project.add_maintainer(user)
+ end
+
+ it 'allows push and pull access' do
+ aggregate_failures do
+ expect { push_access_check }.not_to raise_error
+ expect { pull_access_check }.not_to raise_error
+ end
+ end
+ end
+
+ context 'when namespace does not exist' do
+ let(:namespace_path) { nil }
+
+ it 'does not allow push and pull access' do
+ aggregate_failures do
+ expect { push_access_check }.to raise_not_found
+ expect { pull_access_check }.to raise_not_found
+ end
+ end
+ end
+ end
+
describe '#check_project_accessibility!' do
context 'when the project exists' do
context 'when actor exists' do
diff --git a/spec/migrations/backfill_operations_feature_flags_iid_spec.rb b/spec/migrations/backfill_operations_feature_flags_iid_spec.rb
new file mode 100644
index 00000000000..f7a223e794a
--- /dev/null
+++ b/spec/migrations/backfill_operations_feature_flags_iid_spec.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require Rails.root.join('db', 'post_migrate', '20200117194850_backfill_operations_feature_flags_iid.rb')
+
+describe BackfillOperationsFeatureFlagsIid, :migration do
+ let(:namespaces) { table(:namespaces) }
+ let(:projects) { table(:projects) }
+ let(:flags) { table(:operations_feature_flags) }
+
+ def setup
+ namespace = namespaces.create!(name: 'foo', path: 'foo')
+ project = projects.create!(namespace_id: namespace.id)
+
+ project
+ end
+
+ it 'migrates successfully when there are no flags in the database' do
+ setup
+
+ disable_migrations_output { migrate! }
+
+ expect(flags.count).to eq(0)
+ end
+
+ it 'migrates successfully with a row in the table in both FOSS and EE' do
+ project = setup
+ flags.create!(project_id: project.id, active: true, name: 'test_flag')
+
+ disable_migrations_output { migrate! }
+
+ expect(flags.count).to eq(1)
+ end
+end
diff --git a/spec/migrations/delete_internal_ids_where_feature_flags_usage_spec.rb b/spec/migrations/delete_internal_ids_where_feature_flags_usage_spec.rb
new file mode 100644
index 00000000000..b9c6b489aca
--- /dev/null
+++ b/spec/migrations/delete_internal_ids_where_feature_flags_usage_spec.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require Rails.root.join('db', 'post_migrate', '20200117194900_delete_internal_ids_where_feature_flags_usage')
+
+describe DeleteInternalIdsWhereFeatureFlagsUsage, :migration do
+ let(:namespaces) { table(:namespaces) }
+ let(:projects) { table(:projects) }
+ let(:internal_ids) { table(:internal_ids) }
+
+ def setup
+ namespace = namespaces.create!(name: 'foo', path: 'foo')
+ project = projects.create!(namespace_id: namespace.id)
+
+ project
+ end
+
+ it 'deletes feature flag rows from the internal_ids table' do
+ project = setup
+ internal_ids.create!(project_id: project.id, usage: 6, last_value: 1)
+
+ disable_migrations_output { migrate! }
+
+ expect(internal_ids.count).to eq(0)
+ end
+
+ it 'does not delete issue rows from the internal_ids table' do
+ project = setup
+ internal_ids.create!(project_id: project.id, usage: 0, last_value: 1)
+
+ disable_migrations_output { migrate! }
+
+ expect(internal_ids.count).to eq(1)
+ end
+
+ it 'does not delete merge request rows from the internal_ids table' do
+ project = setup
+ internal_ids.create!(project_id: project.id, usage: 1, last_value: 1)
+
+ disable_migrations_output { migrate! }
+
+ expect(internal_ids.count).to eq(1)
+ end
+end
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index 845c906129f..19a45ce5f88 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -2450,15 +2450,6 @@ describe Repository do
2.times { repository.ancestor?(commit.id, ancestor.id) }
end
- it 'increments a counter with cache hits' do
- counter = Gitlab::Metrics.counter(:repository_ancestor_calls_total, 'Repository ancestor calls')
-
- expect do
- 2.times { repository.ancestor?(commit.id, ancestor.id) }
- end.to change { counter.get(cache_hit: 'true') }.by(1)
- .and change { counter.get(cache_hit: 'false') }.by(1)
- end
-
it 'returns the value from the request store' do
repository.__send__(:request_store_cache).write(cache_key, "it's apparent")
diff --git a/spec/services/ci/retry_build_service_spec.rb b/spec/services/ci/retry_build_service_spec.rb
index b3189974440..83df54bc2ff 100644
--- a/spec/services/ci/retry_build_service_spec.rb
+++ b/spec/services/ci/retry_build_service_spec.rb
@@ -33,7 +33,7 @@ describe Ci::RetryBuildService do
job_artifacts_sast job_artifacts_dependency_scanning
job_artifacts_container_scanning job_artifacts_dast
job_artifacts_license_management job_artifacts_license_scanning
- job_artifacts_performance
+ job_artifacts_performance job_artifacts_lsif
job_artifacts_codequality job_artifacts_metrics scheduled_at
job_variables waiting_for_resource_at job_artifacts_metrics_referee
job_artifacts_network_referee].freeze
diff --git a/spec/services/merge_requests/reload_diffs_service_spec.rb b/spec/services/merge_requests/reload_diffs_service_spec.rb
index c450fc0a7dc..d2444af1b0f 100644
--- a/spec/services/merge_requests/reload_diffs_service_spec.rb
+++ b/spec/services/merge_requests/reload_diffs_service_spec.rb
@@ -33,34 +33,13 @@ describe MergeRequests::ReloadDiffsService, :use_clean_rails_memory_store_cachin
end
context 'cache clearing' do
- context 'using Gitlab::Diff::DeprecatedHighlightCache' do
- before do
- stub_feature_flags(hset_redis_diff_caching: false)
- end
+ it 'clears the cache for older diffs on the merge request' do
+ old_diff = merge_request.merge_request_diff
+ old_cache_key = old_diff.diffs_collection.cache_key
- it 'clears the cache for older diffs on the merge request' do
- old_diff = merge_request.merge_request_diff
- old_cache_key = old_diff.diffs_collection.cache_key
+ expect_any_instance_of(Redis).to receive(:del).with(old_cache_key).and_call_original
- expect(Rails.cache).to receive(:delete).with(old_cache_key).and_call_original
-
- subject.execute
- end
- end
-
- context 'using Gitlab::Diff::HighlightCache' do
- before do
- stub_feature_flags(hset_redis_diff_caching: true)
- end
-
- it 'clears the cache for older diffs on the merge request' do
- old_diff = merge_request.merge_request_diff
- old_cache_key = old_diff.diffs_collection.cache_key
-
- expect_any_instance_of(Redis).to receive(:del).with(old_cache_key).and_call_original
-
- subject.execute
- end
+ subject.execute
end
it 'avoids N+1 queries', :request_store do
diff --git a/spec/services/notes/create_service_spec.rb b/spec/services/notes/create_service_spec.rb
index c5e2fe8de12..7ba069d1e39 100644
--- a/spec/services/notes/create_service_spec.rb
+++ b/spec/services/notes/create_service_spec.rb
@@ -87,28 +87,10 @@ describe Notes::CreateService do
.to receive(:unfolded_diff?) { true }
end
- context 'using Gitlab::Diff::DeprecatedHighlightCache' do
- before do
- stub_feature_flags(hset_redis_diff_caching: false)
- end
-
- it 'clears noteable diff cache when it was unfolded for the note position' do
- expect_any_instance_of(Gitlab::Diff::DeprecatedHighlightCache).to receive(:clear)
-
- described_class.new(project_with_repo, user, new_opts).execute
- end
- end
+ it 'clears noteable diff cache when it was unfolded for the note position' do
+ expect_any_instance_of(Gitlab::Diff::HighlightCache).to receive(:clear)
- context 'using Gitlab::Diff::HighlightCache' do
- before do
- stub_feature_flags(hset_redis_diff_caching: true)
- end
-
- it 'clears noteable diff cache when it was unfolded for the note position' do
- expect_any_instance_of(Gitlab::Diff::HighlightCache).to receive(:clear)
-
- described_class.new(project_with_repo, user, new_opts).execute
- end
+ described_class.new(project_with_repo, user, new_opts).execute
end
it 'does not clear cache when note is not the first of the discussion' do
diff --git a/spec/support/shared_examples/models/mentionable_shared_examples.rb b/spec/support/shared_examples/models/mentionable_shared_examples.rb
index 0c55e9de045..0e8ee6f66f5 100644
--- a/spec/support/shared_examples/models/mentionable_shared_examples.rb
+++ b/spec/support/shared_examples/models/mentionable_shared_examples.rb
@@ -229,16 +229,17 @@ RSpec.shared_examples 'mentions in description' do |mentionable_type|
context 'when mentionable description contains mentions' do
let(:user) { create(:user) }
+ let(:user2) { create(:user) }
let(:group) { create(:group) }
- let(:mentionable_desc) { "#{user.to_reference} some description #{group.to_reference(full: true)} and @all" }
+ let(:mentionable_desc) { "#{user.to_reference} #{user2.to_reference} #{user.to_reference} some description #{group.to_reference(full: true)} and #{user2.to_reference} @all" }
let(:mentionable) { create(mentionable_type, description: mentionable_desc) }
it 'stores mentions' do
add_member(user)
expect(mentionable.user_mentions.count).to eq 1
- expect(mentionable.referenced_users).to match_array([user])
+ expect(mentionable.referenced_users).to match_array([user, user2])
expect(mentionable.referenced_projects(user)).to match_array([mentionable.project].compact) # epic.project is nil, and we want empty []
expect(mentionable.referenced_groups(user)).to match_array([group])
end
@@ -249,8 +250,9 @@ end
RSpec.shared_examples 'mentions in notes' do |mentionable_type|
context 'when mentionable notes contain mentions' do
let(:user) { create(:user) }
+ let(:user2) { create(:user) }
let(:group) { create(:group) }
- let(:note_desc) { "#{user.to_reference} and #{group.to_reference(full: true)} and @all" }
+ let(:note_desc) { "#{user.to_reference} #{user2.to_reference} #{user.to_reference} and #{group.to_reference(full: true)} and #{user2.to_reference} @all" }
let!(:mentionable) { note.noteable }
before do
@@ -261,7 +263,7 @@ RSpec.shared_examples 'mentions in notes' do |mentionable_type|
it 'returns all mentionable mentions' do
expect(mentionable.user_mentions.count).to eq 1
- expect(mentionable.referenced_users).to eq [user]
+ expect(mentionable.referenced_users).to eq [user, user2]
expect(mentionable.referenced_projects(user)).to eq [mentionable.project].compact # epic.project is nil, and we want empty []
expect(mentionable.referenced_groups(user)).to eq [group]
end