Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2023-09-28 06:10:23 +00:00
parent 26169f534c
commit b491556459
18 changed files with 614 additions and 285 deletions

View File

@ -160,7 +160,7 @@ gdk-qa-reliable:
QA_RUN_TYPE: gdk-qa-blocking
parallel: 10
rules:
- if: $CI_MERGE_REQUEST_LABELS =~ /devops::govern|devops::create/
- if: $CI_MERGE_REQUEST_LABELS =~ /devops::govern|devops::create|devops::verify/
- when: on_success
allow_failure: true

View File

@ -3148,7 +3148,6 @@ RSpec/MissingFeatureCategory:
- 'spec/lib/gitlab/ci/pipeline/expression/lexeme/not_equals_spec.rb'
- 'spec/lib/gitlab/ci/pipeline/expression/lexeme/null_spec.rb'
- 'spec/lib/gitlab/ci/pipeline/expression/lexeme/or_spec.rb'
- 'spec/lib/gitlab/ci/pipeline/expression/lexeme/pattern_spec.rb'
- 'spec/lib/gitlab/ci/pipeline/expression/lexeme/string_spec.rb'
- 'spec/lib/gitlab/ci/pipeline/expression/lexeme/variable_spec.rb'
- 'spec/lib/gitlab/ci/pipeline/expression/lexer_spec.rb'

View File

@ -301,6 +301,8 @@ class GraphqlController < ApplicationController
end
def introspection_query_can_use_cache?
return false if Gitlab.dev_or_test_env?
CACHED_INTROSPECTION_QUERY_STRING == graphql_query_object.query_string.squish
end

View File

@ -4,6 +4,7 @@ module Analytics
module CycleAnalytics
class IssueStageEvent < ApplicationRecord
include StageEventModel
include Awardable
extend SuppressCompositePrimaryKeyWarning
validates(*%i[stage_event_hash_id issue_id group_id project_id start_event_timestamp], presence: true)
@ -11,6 +12,8 @@ module Analytics
alias_attribute :state, :state_id
enum state: Issue.available_states, _suffix: true
has_one :epic_issue, primary_key: 'issue_id', foreign_key: 'issue_id' # rubocop: disable Rails/InverseOf
scope :assigned_to, ->(user) do
assignees_class = IssueAssignee
condition = assignees_class.where(user_id: user).where(arel_table[:issue_id].eq(assignees_class.arel_table[:issue_id]))

View File

@ -803,7 +803,7 @@ class ApplicationSetting < MainClusterwide::ApplicationRecord
# Restricting the validation to `on: :update` only to avoid cyclical dependencies with
# License <--> ApplicationSetting. This method calls a license check when we create
# ApplicationSetting from defaults which in turn depends on ApplicationSetting record.
# The currect default is defined in the `defaults` method so we don't need to validate
# The correct default is defined in the `defaults` method so we don't need to validate
# it here.
validates :disable_feed_token,
inclusion: { in: [true, false], message: N_('must be a boolean value') }, on: :update

View File

@ -13,26 +13,26 @@ module Awardable
end
class_methods do
def awarded(user, name = nil)
def awarded(user, name = nil, base_class_name = base_class.name, awardable_id_column = :id)
award_emoji_table = Arel::Table.new('award_emoji')
inner_query = award_emoji_table
.project('true')
.where(award_emoji_table[:user_id].eq(user.id))
.where(award_emoji_table[:awardable_type].eq(base_class.name))
.where(award_emoji_table[:awardable_id].eq(self.arel_table[:id]))
.where(award_emoji_table[:awardable_type].eq(base_class_name))
.where(award_emoji_table[:awardable_id].eq(self.arel_table[awardable_id_column]))
inner_query = inner_query.where(award_emoji_table[:name].eq(name)) if name.present?
where(inner_query.exists)
end
def not_awarded(user, name = nil)
def not_awarded(user, name = nil, base_class_name = base_class.name, awardable_id_column = :id)
award_emoji_table = Arel::Table.new('award_emoji')
inner_query = award_emoji_table
.project('true')
.where(award_emoji_table[:user_id].eq(user.id))
.where(award_emoji_table[:awardable_type].eq(base_class.name))
.where(award_emoji_table[:awardable_id].eq(self.arel_table[:id]))
.where(award_emoji_table[:awardable_type].eq(base_class_name))
.where(award_emoji_table[:awardable_id].eq(self.arel_table[awardable_id_column]))
inner_query = inner_query.where(award_emoji_table[:name].eq(name)) if name.present?
@ -52,14 +52,14 @@ module Awardable
end
# Order votes by emoji, optional sort order param `descending` defaults to true
def order_votes(emoji_name, direction)
def order_votes(emoji_name, direction, base_class_name = base_class.name, awardable_id_column = :id)
awardable_table = self.arel_table
awards_table = AwardEmoji.arel_table
join_clause = awardable_table
.join(awards_table, Arel::Nodes::OuterJoin)
.on(awards_table[:awardable_id].eq(awardable_table[:id])
.and(awards_table[:awardable_type].eq(base_class.name).and(awards_table[:name].eq(emoji_name))))
.on(awards_table[:awardable_id].eq(awardable_table[awardable_id_column])
.and(awards_table[:awardable_type].eq(base_class_name).and(awards_table[:name].eq(emoji_name))))
.join_sources
joins(join_clause).group(awardable_table[:id]).reorder(

View File

@ -28,6 +28,10 @@ Rails.application.configure do
# Only use best-standards-support built into browsers
config.action_dispatch.best_standards_support = :builtin
# There is no need to check if assets are precompiled locally
# To debug AssetNotPrecompiled errors locally, set CHECK_PRECOMPILED_ASSETS to true
config.assets.check_precompiled_asset = Gitlab::Utils.to_boolean(ENV['CHECK_PRECOMPILED_ASSETS'], default: false)
# Do not compress assets
config.assets.compress = false

View File

@ -1,8 +0,0 @@
---
name: enable_hamilton_in_usage_quotas_ui
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/123480
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/411855
milestone: '16.1'
type: development
group: group::purchase
default_enabled: false

File diff suppressed because it is too large Load Diff

View File

@ -22,6 +22,10 @@ module Gitlab
:page,
:stage_id,
:end_event_filter,
:weight,
:epic_id,
:my_reaction_emoji,
:iteration_id,
label_name: [].freeze,
assignee_username: [].freeze,
project_ids: [].freeze
@ -46,6 +50,10 @@ module Gitlab
attribute :page
attribute :stage_id
attribute :end_event_filter
attribute :weight
attribute :epic_id
attribute :my_reaction_emoji
attribute :iteration_id
FINDER_PARAM_NAMES.each do |param_name|
attribute param_name

View File

@ -5,7 +5,6 @@ module Gitlab
module Pipeline
module Expression
ExpressionError = Class.new(StandardError)
RuntimeError = Class.new(ExpressionError)
end
end
end

View File

@ -5,23 +5,17 @@ module Gitlab
module Pipeline
module Expression
module Lexeme
require_dependency 're2'
class Pattern < Lexeme::Value
PATTERN = %r{^\/([^\/]|\\/)+[^\\]\/[ismU]*}
def initialize(regexp)
super(regexp.gsub(%r{\\/}, '/'))
unless Gitlab::UntrustedRegexp::RubySyntax.valid?(@value)
raise Lexer::SyntaxError, 'Invalid regular expression!'
end
raise Lexer::SyntaxError, 'Invalid regular expression!' unless cached_regexp.valid?
end
def evaluate(variables = {})
Gitlab::UntrustedRegexp::RubySyntax.fabricate!(@value)
rescue RegexpError
raise Expression::RuntimeError, 'Invalid regular expression!'
cached_regexp.expression
end
def inspect
@ -47,6 +41,12 @@ module Gitlab
new_pattern.evaluate(variables)
end
private
def cached_regexp
@cached_regexp ||= RegularExpression.new(@value)
end
end
end
end

View File

@ -0,0 +1,37 @@
# frozen_string_literal: true
module Gitlab
module Ci
module Pipeline
module Expression
module Lexeme
class Pattern
require_dependency 're2'
class RegularExpression
include Gitlab::Utils::StrongMemoize
attr_reader :value
def initialize(value)
@value = value
end
def expression
Gitlab::SafeRequestStore.fetch("#{self.class}#unsafe_regexp:#{value}") do
Gitlab::UntrustedRegexp::RubySyntax.fabricate!(value)
end
end
strong_memoize_attr :expression
def valid?
!!expression
rescue RegexpError
false
end
end
end
end
end
end
end
end

View File

@ -471,62 +471,81 @@ RSpec.describe GraphqlController, feature_category: :integrations do
context 'when querying an IntrospectionQuery', :use_clean_rails_memory_store_caching do
let_it_be(:query) { File.read(Rails.root.join('spec/fixtures/api/graphql/introspection.graphql')) }
it 'caches IntrospectionQuery even when operationName is not given' do
expect(GitlabSchema).to receive(:execute).exactly(:once)
context 'in dev or test env' do
before do
allow(Gitlab).to receive(:dev_or_test_env?).and_return(true)
end
post :execute, params: { query: query }
post :execute, params: { query: query }
end
it 'caches the IntrospectionQuery' do
expect(GitlabSchema).to receive(:execute).exactly(:once)
post :execute, params: { query: query, operationName: 'IntrospectionQuery' }
post :execute, params: { query: query, operationName: 'IntrospectionQuery' }
end
it 'caches separately for both remove_deprecated set to true and false' do
expect(GitlabSchema).to receive(:execute).exactly(:twice)
post :execute, params: { query: query, operationName: 'IntrospectionQuery', remove_deprecated: true }
post :execute, params: { query: query, operationName: 'IntrospectionQuery', remove_deprecated: true }
# We clear this instance variable to reset remove_deprecated
subject.remove_instance_variable(:@context) if subject.instance_variable_defined?(:@context)
post :execute, params: { query: query, operationName: 'IntrospectionQuery', remove_deprecated: false }
post :execute, params: { query: query, operationName: 'IntrospectionQuery', remove_deprecated: false }
end
it 'has a different cache for each Gitlab.revision' do
expect(GitlabSchema).to receive(:execute).exactly(:twice)
post :execute, params: { query: query, operationName: 'IntrospectionQuery' }
allow(Gitlab).to receive(:revision).and_return('new random value')
post :execute, params: { query: query, operationName: 'IntrospectionQuery' }
end
context 'when there is an unknown introspection query' do
let(:query) { File.read(Rails.root.join('spec/fixtures/api/graphql/fake_introspection.graphql')) }
it 'does not cache an unknown introspection query' do
it 'does not cache IntrospectionQuery' do
expect(GitlabSchema).to receive(:execute).exactly(:twice)
post :execute, params: { query: query }
post :execute, params: { query: query }
end
end
context 'in env different from dev or test' do
before do
allow(Gitlab).to receive(:dev_or_test_env?).and_return(false)
end
it 'caches IntrospectionQuery even when operationName is not given' do
expect(GitlabSchema).to receive(:execute).exactly(:once)
post :execute, params: { query: query }
post :execute, params: { query: query }
end
it 'caches the IntrospectionQuery' do
expect(GitlabSchema).to receive(:execute).exactly(:once)
post :execute, params: { query: query, operationName: 'IntrospectionQuery' }
post :execute, params: { query: query, operationName: 'IntrospectionQuery' }
end
end
it 'hits the cache even if the whitespace in the query differs' do
query_1 = File.read(Rails.root.join('spec/fixtures/api/graphql/introspection.graphql'))
query_2 = "#{query_1} " # add a couple of spaces to change the fingerprint
it 'caches separately for both remove_deprecated set to true and false' do
expect(GitlabSchema).to receive(:execute).exactly(:twice)
expect(GitlabSchema).to receive(:execute).exactly(:once)
post :execute, params: { query: query, operationName: 'IntrospectionQuery', remove_deprecated: true }
post :execute, params: { query: query, operationName: 'IntrospectionQuery', remove_deprecated: true }
post :execute, params: { query: query_1, operationName: 'IntrospectionQuery' }
post :execute, params: { query: query_2, operationName: 'IntrospectionQuery' }
# We clear this instance variable to reset remove_deprecated
subject.remove_instance_variable(:@context) if subject.instance_variable_defined?(:@context)
post :execute, params: { query: query, operationName: 'IntrospectionQuery', remove_deprecated: false }
post :execute, params: { query: query, operationName: 'IntrospectionQuery', remove_deprecated: false }
end
it 'has a different cache for each Gitlab.revision' do
expect(GitlabSchema).to receive(:execute).exactly(:twice)
post :execute, params: { query: query, operationName: 'IntrospectionQuery' }
allow(Gitlab).to receive(:revision).and_return('new random value')
post :execute, params: { query: query, operationName: 'IntrospectionQuery' }
end
context 'when there is an unknown introspection query' do
let(:query) { File.read(Rails.root.join('spec/fixtures/api/graphql/fake_introspection.graphql')) }
it 'does not cache an unknown introspection query' do
expect(GitlabSchema).to receive(:execute).exactly(:twice)
post :execute, params: { query: query, operationName: 'IntrospectionQuery' }
post :execute, params: { query: query, operationName: 'IntrospectionQuery' }
end
end
it 'hits the cache even if the whitespace in the query differs' do
query_1 = File.read(Rails.root.join('spec/fixtures/api/graphql/introspection.graphql'))
query_2 = "#{query_1} " # add a couple of spaces to change the fingerprint
expect(GitlabSchema).to receive(:execute).exactly(:once)
post :execute, params: { query: query_1, operationName: 'IntrospectionQuery' }
post :execute, params: { query: query_2, operationName: 'IntrospectionQuery' }
end
end
it 'fails if the GraphiQL gem version is not 1.8.0' do

View File

@ -0,0 +1,57 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::Ci::Pipeline::Expression::Lexeme::Pattern::RegularExpression, feature_category: :continuous_integration do
describe '#initialize' do
it 'initializes the pattern' do
pattern = described_class.new('/foo/')
expect(pattern.value).to eq('/foo/')
end
end
describe '#valid?' do
subject { described_class.new(pattern).valid? }
context 'with valid expressions' do
let(:pattern) { '/foo\\/bar/' }
it { is_expected.to be_truthy }
end
context 'when the value is not a valid regular expression' do
let(:pattern) { 'foo' }
it { is_expected.to be_falsey }
end
end
describe '#expression' do
subject { described_class.new(pattern).expression }
context 'with valid expressions' do
let(:pattern) { '/bar/' }
it { is_expected.to eq Gitlab::UntrustedRegexp.new('bar') }
end
context 'when the value is not a valid regular expression' do
let(:pattern) { 'foo' }
it { expect { subject }.to raise_error(RegexpError) }
end
context 'when the request store is activated', :request_store do
let(:pattern) { '/foo\\/bar/' }
it 'fabricates once' do
expect(Gitlab::UntrustedRegexp::RubySyntax).to receive(:fabricate!).once.and_call_original
2.times do
expect(described_class.new(pattern).expression).to be_a(Gitlab::UntrustedRegexp)
end
end
end
end
end

View File

@ -1,8 +1,8 @@
# frozen_string_literal: true
require 'fast_spec_helper'
require 'spec_helper'
RSpec.describe Gitlab::Ci::Pipeline::Expression::Lexeme::Pattern do
RSpec.describe Gitlab::Ci::Pipeline::Expression::Lexeme::Pattern, feature_category: :continuous_integration do
describe '#initialize' do
context 'when the value is a valid regular expression' do
it 'initializes the pattern' do
@ -164,14 +164,5 @@ RSpec.describe Gitlab::Ci::Pipeline::Expression::Lexeme::Pattern do
expect(regexp.evaluate).to eq Gitlab::UntrustedRegexp.new('abc')
end
it 'raises error if evaluated regexp is not valid' do
allow(Gitlab::UntrustedRegexp::RubySyntax).to receive(:valid?).and_return(true)
regexp = described_class.new('/invalid ( .*/')
expect { regexp.evaluate }
.to raise_error(Gitlab::Ci::Pipeline::Expression::RuntimeError)
end
end
end

View File

@ -2240,7 +2240,7 @@ RSpec.describe Project, factory_default: :keep, feature_category: :groups_and_pr
context 'when the Slack app setting is not enabled' do
before do
stub_application_setting(slack_app_enabled: false)
allow(Rails.env).to receive(:test?).and_return(false, true)
allow(Rails.env).to receive(:test?).and_return(false)
end
it 'includes all projects' do

View File

@ -11,7 +11,9 @@ RSpec.shared_examples 'unlicensed cycle analytics request params' do
}
end
subject { described_class.new(params) }
let(:request_params) { described_class.new(params) }
subject { request_params }
before do
root_group.add_owner(user)
@ -114,13 +116,13 @@ RSpec.shared_examples 'unlicensed cycle analytics request params' do
end
describe 'use_aggregated_data_collector param' do
subject(:value) { described_class.new(params).to_data_collector_params[:use_aggregated_data_collector] }
subject(:value) { request_params.to_data_collector_params[:use_aggregated_data_collector] }
it { is_expected.to eq(false) }
end
describe 'feature availablity data attributes' do
subject(:value) { described_class.new(params).to_data_attributes }
subject(:value) { request_params.to_data_attributes }
it 'disables all paid features' do
is_expected.to match(a_hash_including(enable_tasks_by_type_chart: 'false',
@ -128,4 +130,26 @@ RSpec.shared_examples 'unlicensed cycle analytics request params' do
enable_projects_filter: 'false'))
end
end
describe '#to_data_collector_params' do
context 'when adding licensed parameters' do
subject(:data_collector_params) { request_params.to_data_collector_params }
before do
params.merge!(
weight: 1,
epic_id: 2,
iteration_id: 3,
my_reaction_emoji: 'tumbsup'
)
end
it 'excludes the attributes from the data collector params' do
expect(data_collector_params).to exclude(:weight)
expect(data_collector_params).to exclude(:epic_id)
expect(data_collector_params).to exclude(:iteration_id)
expect(data_collector_params).to exclude(:my_reaction_emoji)
end
end
end
end