Commit 6315ed96 authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent fedf978f
# 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
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
......
# 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
# 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
# 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
# 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)
......
%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
---
title: Migrate epic, epic notes mentions to respective DB table
merge_request: 22333
author:
type: changed
---
title: Add validation for custom PrometheusDashboard
merge_request: 22893
author:
type: added
---
title: Add support for lsif artifact report
merge_request: 23672
author:
type: added
---
title: Add iid to operations_feature_flags and backfill
merge_request: 22175
author:
type: added
---
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
......
# 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,
......
# 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
......
# 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
# 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
# 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
# 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
# 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
# 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 |
......
# 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
# 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
# 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
# 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
# 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
......
# 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
......
# 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
......
......@@ -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
......
# 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
# 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,26 +33,6 @@ 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
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
......@@ -61,7 +41,6 @@ describe MergeRequests::ReloadDiffsService, :use_clean_rails_memory_store_cachin
subject.execute
end
end
it 'avoids N+1 queries', :request_store do
current_user
......
......@@ -87,29 +87,11 @@ 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
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
end
it 'does not clear cache when note is not the first of the discussion' do
prev_note =
......
......@@ -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
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment