Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
fedf978f9a
commit
6315ed9630
|
|
@ -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"]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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: {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Migrate epic, epic notes mentions to respective DB table
|
||||
merge_request: 22333
|
||||
author:
|
||||
type: changed
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add validation for custom PrometheusDashboard
|
||||
merge_request: 22893
|
||||
author:
|
||||
type: added
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add support for lsif artifact report
|
||||
merge_request: 23672
|
||||
author:
|
||||
type: added
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add iid to operations_feature_flags and backfill
|
||||
merge_request: 22175
|
||||
author:
|
||||
type: added
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Eliminate statement timeouts when namespace is blank
|
||||
merge_request: 23839
|
||||
author:
|
||||
type: fixed
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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 |
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ module RuboCop
|
|||
namespaces
|
||||
notes
|
||||
projects
|
||||
project_ci_cd_settings
|
||||
routes
|
||||
users
|
||||
].freeze
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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') }
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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: {} }
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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")
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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::HighlightCache).to receive(:clear)
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue