Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
e9c3815d3d
commit
b286069fdf
|
|
@ -89,10 +89,6 @@ export default {
|
|||
listTitle() {
|
||||
return this.list?.label?.description || this.list?.assignee?.name || this.list.title || '';
|
||||
},
|
||||
listIterationPeriod() {
|
||||
const iteration = this.list?.iteration;
|
||||
return iteration ? this.getIterationPeriod(iteration) : '';
|
||||
},
|
||||
isIterationList() {
|
||||
return this.listType === ListType.iteration;
|
||||
},
|
||||
|
|
@ -108,9 +104,6 @@ export default {
|
|||
showIterationListDetails() {
|
||||
return this.isIterationList && this.showListDetails;
|
||||
},
|
||||
iterationCadencesAvailable() {
|
||||
return this.isIterationList && this.glFeatures.iterationCadences;
|
||||
},
|
||||
showListDetails() {
|
||||
return !this.list.collapsed || !this.isSwimlanesHeader;
|
||||
},
|
||||
|
|
@ -344,13 +337,6 @@ export default {
|
|||
class="board-title-main-text gl-text-truncate"
|
||||
>
|
||||
{{ listTitle }}
|
||||
<span
|
||||
v-if="iterationCadencesAvailable"
|
||||
class="gl-display-inline-block gl-text-gray-400"
|
||||
data-testid="board-list-iteration-period"
|
||||
>
|
||||
{{ listIterationPeriod }}</span
|
||||
>
|
||||
</span>
|
||||
<span
|
||||
v-if="listType === 'assignee'"
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ class Projects::IssuesController < Projects::ApplicationController
|
|||
push_frontend_feature_flag(:issue_assignees_widget, project, default_enabled: :yaml)
|
||||
push_frontend_feature_flag(:paginated_issue_discussions, project, default_enabled: :yaml)
|
||||
push_frontend_feature_flag(:fix_comment_scroll, project, default_enabled: :yaml)
|
||||
push_frontend_feature_flag(:work_items, project, default_enabled: :yaml)
|
||||
push_frontend_feature_flag(:work_items, project&.group, default_enabled: :yaml)
|
||||
end
|
||||
|
||||
around_action :allow_gitaly_ref_name_caching, only: [:discussions]
|
||||
|
|
|
|||
|
|
@ -1,12 +1,15 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module LazyImageTagHelper
|
||||
include PreferencesHelper
|
||||
|
||||
def placeholder_image
|
||||
"data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
|
||||
end
|
||||
|
||||
# Override the default ActionView `image_tag` helper to support lazy-loading
|
||||
def image_tag(source, options = {})
|
||||
source = options[:dark_variant] if options[:dark_variant] && user_application_dark_mode?
|
||||
options = options.symbolize_keys
|
||||
|
||||
unless options.delete(:lazy) == false
|
||||
|
|
|
|||
|
|
@ -62,6 +62,10 @@ module PreferencesHelper
|
|||
@user_application_theme ||= Gitlab::Themes.for_user(current_user).css_class
|
||||
end
|
||||
|
||||
def user_application_dark_mode?
|
||||
user_application_theme == 'gl-dark'
|
||||
end
|
||||
|
||||
def user_application_theme_css_filename
|
||||
@user_application_theme_css_filename ||= Gitlab::Themes.for_user(current_user).css_filename
|
||||
end
|
||||
|
|
|
|||
|
|
@ -18,5 +18,6 @@ module Ci
|
|||
|
||||
scope :unprotected, -> { where(protected: false) }
|
||||
scope :by_environment_scope, -> (environment_scope) { where(environment_scope: environment_scope) }
|
||||
scope :for_groups, ->(group_ids) { where(group_id: group_ids) }
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -341,12 +341,8 @@ class Project < ApplicationRecord
|
|||
has_many :stages, class_name: 'Ci::Stage', inverse_of: :project
|
||||
has_many :ci_refs, class_name: 'Ci::Ref', inverse_of: :project
|
||||
|
||||
# Ci::Build objects store data on the file system such as artifact files and
|
||||
# build traces. Currently there's no efficient way of removing this data in
|
||||
# bulk that doesn't involve loading the rows into memory. As a result we're
|
||||
# still using `dependent: :destroy` here.
|
||||
has_many :pending_builds, class_name: 'Ci::PendingBuild'
|
||||
has_many :builds, class_name: 'Ci::Build', inverse_of: :project, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
|
||||
has_many :builds, class_name: 'Ci::Build', inverse_of: :project
|
||||
has_many :processables, class_name: 'Ci::Processable', inverse_of: :project
|
||||
has_many :build_trace_chunks, class_name: 'Ci::BuildTraceChunk', through: :builds, source: :trace_chunks
|
||||
has_many :build_report_results, class_name: 'Ci::BuildReportResult', inverse_of: :project
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ class MemberEntity < Grape::Entity
|
|||
expose :valid_level_roles, as: :valid_roles
|
||||
|
||||
expose :user, if: -> (member) { member.user.present? } do |member, options|
|
||||
MemberUserEntity.represent(member.user, source: options[:source])
|
||||
MemberUserEntity.represent(member.user, options)
|
||||
end
|
||||
|
||||
expose :state
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@ job1:
|
|||
script:
|
||||
- echo This rule uses parentheses.
|
||||
rules:
|
||||
if: ($CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_BRANCH == "develop") && $MY_VARIABLE
|
||||
- if: ($CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_BRANCH == "develop") && $MY_VARIABLE
|
||||
```
|
||||
|
||||
WARNING:
|
||||
|
|
|
|||
|
|
@ -334,6 +334,14 @@ NOTE:
|
|||
Specific information that follow related to Ruby and Git versions do not apply to [Omnibus installations](https://docs.gitlab.com/omnibus/)
|
||||
and [Helm Chart deployments](https://docs.gitlab.com/charts/). They come with appropriate Ruby and Git versions and are not using system binaries for Ruby and Git. There is no need to install Ruby or Git when utilizing these two approaches.
|
||||
|
||||
### 14.7.0
|
||||
|
||||
- See [LFS objects import and mirror issue in GitLab 14.6.0 to 14.7.2](#lfs-objects-import-and-mirror-issue-in-gitlab-1460-to-1472).
|
||||
|
||||
### 14.6.0
|
||||
|
||||
- See [LFS objects import and mirror issue in GitLab 14.6.0 to 14.7.2](#lfs-objects-import-and-mirror-issue-in-gitlab-1460-to-1472).
|
||||
|
||||
### 14.5.0
|
||||
|
||||
- When `make` is run, Gitaly builds are now created in `_build/bin` and no longer in the root directory of the source directory. If you
|
||||
|
|
@ -691,6 +699,12 @@ Users who were signed in before Maintenance mode was enabled will continue to be
|
|||
|
||||
[This bug](https://gitlab.com/gitlab-org/gitlab/-/issues/329261) was fixed in GitLab 14.5.0 and backported into 14.4.3 and 14.3.5.
|
||||
|
||||
### LFS objects import and mirror issue in GitLab 14.6.0 to 14.7.2
|
||||
|
||||
When Geo is enabled, LFS objects fail to be saved for imported or mirrored projects.
|
||||
|
||||
[This bug](https://gitlab.com/gitlab-org/gitlab/-/issues/352368) was fixed in GitLab 14.8.0 and backported into 14.7.3.
|
||||
|
||||
## Miscellaneous
|
||||
|
||||
- [MySQL to PostgreSQL](mysql_to_postgresql.md) guides you through migrating
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ module Gitlab
|
|||
@pipeline = pipeline
|
||||
@instance_variables_builder = Builder::Instance.new
|
||||
@project_variables_builder = Builder::Project.new(project)
|
||||
@group_variables_builder = Builder::Group.new(project.group)
|
||||
end
|
||||
|
||||
def scoped_variables(job, environment:, dependencies:)
|
||||
|
|
@ -72,9 +73,13 @@ module Gitlab
|
|||
end
|
||||
|
||||
def secret_group_variables(environment:, ref:)
|
||||
return [] unless project.group
|
||||
if memoize_secret_variables?
|
||||
memoized_secret_group_variables(environment: environment)
|
||||
else
|
||||
return [] unless project.group
|
||||
|
||||
project.group.ci_variables_for(ref, project, environment: environment)
|
||||
project.group.ci_variables_for(ref, project, environment: environment)
|
||||
end
|
||||
end
|
||||
|
||||
def secret_project_variables(environment:, ref:)
|
||||
|
|
@ -90,6 +95,7 @@ module Gitlab
|
|||
attr_reader :pipeline
|
||||
attr_reader :instance_variables_builder
|
||||
attr_reader :project_variables_builder
|
||||
attr_reader :group_variables_builder
|
||||
delegate :project, to: :pipeline
|
||||
|
||||
def predefined_variables(job)
|
||||
|
|
@ -119,6 +125,15 @@ module Gitlab
|
|||
end
|
||||
end
|
||||
|
||||
def memoized_secret_group_variables(environment:)
|
||||
strong_memoize_with(:secret_group_variables, environment) do
|
||||
group_variables_builder
|
||||
.secret_variables(
|
||||
environment: environment,
|
||||
protected_ref: protected_ref?)
|
||||
end
|
||||
end
|
||||
|
||||
def ci_node_total_value(job)
|
||||
parallel = job.options&.dig(:parallel)
|
||||
parallel = parallel.dig(:total) if parallel.is_a?(Hash)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,48 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Ci
|
||||
module Variables
|
||||
class Builder
|
||||
class Group
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
|
||||
def initialize(group)
|
||||
@group = group
|
||||
end
|
||||
|
||||
def secret_variables(environment:, protected_ref: false)
|
||||
return [] unless group
|
||||
|
||||
variables = base_scope
|
||||
variables = variables.unprotected unless protected_ref
|
||||
variables = variables.for_environment(environment)
|
||||
variables = variables.group_by(&:group_id)
|
||||
variables = list_of_ids.reverse.flat_map { |group| variables[group.id] }.compact
|
||||
Gitlab::Ci::Variables::Collection.new(variables)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :group
|
||||
|
||||
def base_scope
|
||||
strong_memoize(:base_scope) do
|
||||
::Ci::GroupVariable.for_groups(list_of_ids)
|
||||
end
|
||||
end
|
||||
|
||||
def list_of_ids
|
||||
strong_memoize(:list_of_ids) do
|
||||
if group.root_ancestor.use_traversal_ids?
|
||||
[group] + group.ancestors(hierarchy_order: :asc)
|
||||
else
|
||||
[group] + group.ancestors
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -4,8 +4,11 @@ module Gitlab
|
|||
module Database
|
||||
module EachDatabase
|
||||
class << self
|
||||
def each_database_connection
|
||||
Gitlab::Database.database_base_models.each_pair do |connection_name, model|
|
||||
def each_database_connection(only: nil)
|
||||
selected_names = Array.wrap(only)
|
||||
base_models = select_base_models(selected_names)
|
||||
|
||||
base_models.each_pair do |connection_name, model|
|
||||
connection = model.connection
|
||||
|
||||
with_shared_connection(connection, connection_name) do
|
||||
|
|
@ -28,6 +31,18 @@ module Gitlab
|
|||
|
||||
private
|
||||
|
||||
def select_base_models(names)
|
||||
base_models = Gitlab::Database.database_base_models
|
||||
|
||||
return base_models if names.empty?
|
||||
|
||||
names.each_with_object(HashWithIndifferentAccess.new) do |name, hash|
|
||||
raise ArgumentError, "#{name} is not a valid database name" unless base_models.key?(name)
|
||||
|
||||
hash[name] = base_models[name]
|
||||
end
|
||||
end
|
||||
|
||||
def with_shared_model_connections(shared_model, &blk)
|
||||
Gitlab::Database.database_base_models.each_pair do |connection_name, connection_model|
|
||||
if shared_model.limit_connection_names
|
||||
|
|
|
|||
|
|
@ -8,8 +8,10 @@ namespace :dev do
|
|||
ENV['force'] = 'yes'
|
||||
Rake::Task["gitlab:setup"].invoke
|
||||
|
||||
# Make sure DB statistics are up to date.
|
||||
ActiveRecord::Base.connection.execute('ANALYZE')
|
||||
Gitlab::Database::EachDatabase.each_database_connection do |connection|
|
||||
# Make sure DB statistics are up to date.
|
||||
connection.execute('ANALYZE')
|
||||
end
|
||||
|
||||
Rake::Task["gitlab:shell:setup"].invoke
|
||||
end
|
||||
|
|
|
|||
|
|
@ -4,30 +4,28 @@ databases = ActiveRecord::Tasks::DatabaseTasks.setup_initial_database_yaml
|
|||
|
||||
namespace :gitlab do
|
||||
namespace :db do
|
||||
desc 'GitLab | DB | Manually insert schema migration version'
|
||||
desc 'GitLab | DB | Manually insert schema migration version on all configured databases'
|
||||
task :mark_migration_complete, [:version] => :environment do |_, args|
|
||||
mark_migration_complete(args[:version])
|
||||
end
|
||||
|
||||
namespace :mark_migration_complete do
|
||||
ActiveRecord::Tasks::DatabaseTasks.for_each(databases) do |name|
|
||||
desc "Gitlab | DB | Manually insert schema migration version on #{name} database"
|
||||
task name, [:version] => :environment do |_, args|
|
||||
mark_migration_complete(args[:version], database: name)
|
||||
ActiveRecord::Tasks::DatabaseTasks.for_each(databases) do |database|
|
||||
desc "Gitlab | DB | Manually insert schema migration version on #{database} database"
|
||||
task database, [:version] => :environment do |_, args|
|
||||
mark_migration_complete(args[:version], only_on: database)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def mark_migration_complete(version, database: nil)
|
||||
def mark_migration_complete(version, only_on: nil)
|
||||
if version.to_i == 0
|
||||
puts 'Must give a version argument that is a non-zero integer'.color(:red)
|
||||
exit 1
|
||||
end
|
||||
|
||||
Gitlab::Database.database_base_models.each do |name, model|
|
||||
next if database && database.to_s != name
|
||||
|
||||
model.connection.execute("INSERT INTO schema_migrations (version) VALUES (#{model.connection.quote(version)})")
|
||||
Gitlab::Database::EachDatabase.each_database_connection(only: only_on) do |connection, name|
|
||||
connection.execute("INSERT INTO schema_migrations (version) VALUES (#{connection.quote(version)})")
|
||||
|
||||
puts "Successfully marked '#{version}' as complete on database #{name}".color(:green)
|
||||
rescue ActiveRecord::RecordNotUnique
|
||||
|
|
@ -35,32 +33,44 @@ namespace :gitlab do
|
|||
end
|
||||
end
|
||||
|
||||
desc 'GitLab | DB | Drop all tables'
|
||||
desc 'GitLab | DB | Drop all tables on all configured databases'
|
||||
task drop_tables: :environment do
|
||||
connection = ActiveRecord::Base.connection
|
||||
drop_tables
|
||||
end
|
||||
|
||||
# In PostgreSQLAdapter, data_sources returns both views and tables, so use
|
||||
# #tables instead
|
||||
tables = connection.tables
|
||||
|
||||
# Removes the entry from the array
|
||||
tables.delete 'schema_migrations'
|
||||
# Truncate schema_migrations to ensure migrations re-run
|
||||
connection.execute('TRUNCATE schema_migrations') if connection.table_exists? 'schema_migrations'
|
||||
|
||||
# Drop any views
|
||||
connection.views.each do |view|
|
||||
connection.execute("DROP VIEW IF EXISTS #{connection.quote_table_name(view)} CASCADE")
|
||||
namespace :drop_tables do
|
||||
ActiveRecord::Tasks::DatabaseTasks.for_each(databases) do |database|
|
||||
desc "GitLab | DB | Drop all tables on the #{database} database"
|
||||
task database => :environment do
|
||||
drop_tables(only_on: database)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Drop tables with cascade to avoid dependent table errors
|
||||
# PG: http://www.postgresql.org/docs/current/static/ddl-depend.html
|
||||
# Add `IF EXISTS` because cascade could have already deleted a table.
|
||||
tables.each { |t| connection.execute("DROP TABLE IF EXISTS #{connection.quote_table_name(t)} CASCADE") }
|
||||
def drop_tables(only_on: nil)
|
||||
Gitlab::Database::EachDatabase.each_database_connection(only: only_on) do |connection, name|
|
||||
# In PostgreSQLAdapter, data_sources returns both views and tables, so use tables instead
|
||||
tables = connection.tables
|
||||
|
||||
# Drop all extra schema objects GitLab owns
|
||||
Gitlab::Database::EXTRA_SCHEMAS.each do |schema|
|
||||
connection.execute("DROP SCHEMA IF EXISTS #{connection.quote_table_name(schema)} CASCADE")
|
||||
# Removes the entry from the array
|
||||
tables.delete 'schema_migrations'
|
||||
# Truncate schema_migrations to ensure migrations re-run
|
||||
connection.execute('TRUNCATE schema_migrations') if connection.table_exists? 'schema_migrations'
|
||||
|
||||
# Drop any views
|
||||
connection.views.each do |view|
|
||||
connection.execute("DROP VIEW IF EXISTS #{connection.quote_table_name(view)} CASCADE")
|
||||
end
|
||||
|
||||
# Drop tables with cascade to avoid dependent table errors
|
||||
# PG: http://www.postgresql.org/docs/current/static/ddl-depend.html
|
||||
# Add `IF EXISTS` because cascade could have already deleted a table.
|
||||
tables.each { |t| connection.execute("DROP TABLE IF EXISTS #{connection.quote_table_name(t)} CASCADE") }
|
||||
|
||||
# Drop all extra schema objects GitLab owns
|
||||
Gitlab::Database::EXTRA_SCHEMAS.each do |schema|
|
||||
connection.execute("DROP SCHEMA IF EXISTS #{connection.quote_table_name(schema)} CASCADE")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -208,8 +218,6 @@ namespace :gitlab do
|
|||
end
|
||||
|
||||
namespace :reindex do
|
||||
databases = ActiveRecord::Tasks::DatabaseTasks.setup_initial_database_yaml
|
||||
|
||||
ActiveRecord::Tasks::DatabaseTasks.for_each(databases) do |database_name|
|
||||
desc "Reindex #{database_name} database without downtime to eliminate bloat"
|
||||
task database_name => :environment do
|
||||
|
|
|
|||
|
|
@ -35570,6 +35570,12 @@ msgstr ""
|
|||
msgid "SuperSonics|You do not have an active subscription"
|
||||
msgstr ""
|
||||
|
||||
msgid "SuperSonics|You have a future dated license"
|
||||
msgstr ""
|
||||
|
||||
msgid "SuperSonics|You have added a license that activates on %{date}. Please see the subscription history table below for more details."
|
||||
msgstr ""
|
||||
|
||||
msgid "SuperSonics|You have successfully added a license that activates on %{date}. Please see the subscription history table below for more details."
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -96,6 +96,30 @@ RSpec.describe PreferencesHelper do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#user_application_dark_mode?' do
|
||||
context 'with a user' do
|
||||
it "returns true if user's selected dark theme" do
|
||||
stub_user(theme_id: 11)
|
||||
|
||||
expect(helper.user_application_dark_mode?).to eq true
|
||||
end
|
||||
|
||||
it "returns false if user's selected any light theme" do
|
||||
stub_user(theme_id: 1)
|
||||
|
||||
expect(helper.user_application_dark_mode?).to eq false
|
||||
end
|
||||
end
|
||||
|
||||
context 'without a user' do
|
||||
it 'returns false' do
|
||||
stub_user
|
||||
|
||||
expect(helper.user_application_dark_mode?).to eq false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#user_color_scheme' do
|
||||
context 'with a user' do
|
||||
it "returns user's scheme's css_class" do
|
||||
|
|
|
|||
|
|
@ -0,0 +1,209 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::Ci::Variables::Builder::Group do
|
||||
let_it_be(:group) { create(:group) }
|
||||
|
||||
let(:builder) { described_class.new(group) }
|
||||
|
||||
describe '#secret_variables' do
|
||||
let(:environment) { '*' }
|
||||
let(:protected_ref) { false }
|
||||
|
||||
let_it_be(:variable) do
|
||||
create(:ci_group_variable,
|
||||
value: 'secret',
|
||||
group: group)
|
||||
end
|
||||
|
||||
let_it_be(:protected_variable) do
|
||||
create(:ci_group_variable, :protected,
|
||||
value: 'protected',
|
||||
group: group)
|
||||
end
|
||||
|
||||
let(:variable_item) { item(variable) }
|
||||
let(:protected_variable_item) { item(protected_variable) }
|
||||
|
||||
subject do
|
||||
builder.secret_variables(
|
||||
environment: environment,
|
||||
protected_ref: protected_ref)
|
||||
end
|
||||
|
||||
context 'when the ref is not protected' do
|
||||
let(:protected_ref) { false }
|
||||
|
||||
it 'contains only the CI variables' do
|
||||
is_expected.to contain_exactly(variable_item)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the ref is protected' do
|
||||
let(:protected_ref) { true }
|
||||
|
||||
it 'contains all the variables' do
|
||||
is_expected.to contain_exactly(variable_item, protected_variable_item)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when environment name is specified' do
|
||||
let(:environment) { 'review/name' }
|
||||
|
||||
before do
|
||||
Ci::GroupVariable.update_all(environment_scope: environment_scope)
|
||||
end
|
||||
|
||||
context 'when environment scope is exactly matched' do
|
||||
let(:environment_scope) { 'review/name' }
|
||||
|
||||
it { is_expected.to contain_exactly(variable_item) }
|
||||
end
|
||||
|
||||
context 'when environment scope is matched by wildcard' do
|
||||
let(:environment_scope) { 'review/*' }
|
||||
|
||||
it { is_expected.to contain_exactly(variable_item) }
|
||||
end
|
||||
|
||||
context 'when environment scope does not match' do
|
||||
let(:environment_scope) { 'review/*/special' }
|
||||
|
||||
it { is_expected.not_to contain_exactly(variable_item) }
|
||||
end
|
||||
|
||||
context 'when environment scope has _' do
|
||||
let(:environment_scope) { '*_*' }
|
||||
|
||||
it 'does not treat it as wildcard' do
|
||||
is_expected.not_to contain_exactly(variable_item)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when environment name contains underscore' do
|
||||
let(:environment) { 'foo_bar/test' }
|
||||
let(:environment_scope) { 'foo_bar/*' }
|
||||
|
||||
it 'matches literally for _' do
|
||||
is_expected.to contain_exactly(variable_item)
|
||||
end
|
||||
end
|
||||
|
||||
# The environment name and scope cannot have % at the moment,
|
||||
# but we're considering relaxing it and we should also make sure
|
||||
# it doesn't break in case some data sneaked in somehow as we're
|
||||
# not checking this integrity in database level.
|
||||
context 'when environment scope has %' do
|
||||
let(:environment_scope) { '*%*' }
|
||||
|
||||
it 'does not treat it as wildcard' do
|
||||
is_expected.not_to contain_exactly(variable_item)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when environment name contains a percent' do
|
||||
let(:environment) { 'foo%bar/test' }
|
||||
let(:environment_scope) { 'foo%bar/*' }
|
||||
|
||||
it 'matches literally for _' do
|
||||
is_expected.to contain_exactly(variable_item)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when variables with the same name have different environment scopes' do
|
||||
let(:environment) { 'review/name' }
|
||||
|
||||
let_it_be(:partially_matched_variable) do
|
||||
create(:ci_group_variable,
|
||||
key: variable.key,
|
||||
value: 'partial',
|
||||
environment_scope: 'review/*',
|
||||
group: group)
|
||||
end
|
||||
|
||||
let_it_be(:perfectly_matched_variable) do
|
||||
create(:ci_group_variable,
|
||||
key: variable.key,
|
||||
value: 'prefect',
|
||||
environment_scope: 'review/name',
|
||||
group: group)
|
||||
end
|
||||
|
||||
it 'orders the variables from least to most matched' do
|
||||
variables_collection = Gitlab::Ci::Variables::Collection.new([
|
||||
variable,
|
||||
partially_matched_variable,
|
||||
perfectly_matched_variable
|
||||
]).to_runner_variables
|
||||
|
||||
expect(subject.to_runner_variables).to eq(variables_collection)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when group has children' do
|
||||
let(:protected_ref) { true }
|
||||
|
||||
let_it_be(:group_child_1) { create(:group, parent: group) }
|
||||
let_it_be(:group_child_2) { create(:group, parent: group_child_1) }
|
||||
|
||||
let_it_be_with_reload(:group_child_3) do
|
||||
create(:group, parent: group_child_2)
|
||||
end
|
||||
|
||||
let_it_be(:variable_child_1) do
|
||||
create(:ci_group_variable, group: group_child_1)
|
||||
end
|
||||
|
||||
let_it_be(:variable_child_2) do
|
||||
create(:ci_group_variable, group: group_child_2)
|
||||
end
|
||||
|
||||
let_it_be(:variable_child_3) do
|
||||
create(:ci_group_variable, group: group_child_3)
|
||||
end
|
||||
|
||||
context 'traversal queries' do
|
||||
shared_examples 'correct ancestor order' do
|
||||
let(:builder) { described_class.new(group_child_3) }
|
||||
|
||||
it 'returns all variables belonging to the group and parent groups' do
|
||||
expected_array1 = Gitlab::Ci::Variables::Collection.new(
|
||||
[protected_variable_item, variable_item])
|
||||
.to_runner_variables
|
||||
|
||||
expected_array2 = Gitlab::Ci::Variables::Collection.new(
|
||||
[variable_child_1, variable_child_2, variable_child_3]
|
||||
).to_runner_variables
|
||||
|
||||
got_array = subject.to_runner_variables
|
||||
|
||||
expect(got_array.shift(2)).to contain_exactly(*expected_array1)
|
||||
expect(got_array).to eq(expected_array2)
|
||||
end
|
||||
end
|
||||
|
||||
context 'recursive' do
|
||||
before do
|
||||
stub_feature_flags(use_traversal_ids: false)
|
||||
end
|
||||
|
||||
include_examples 'correct ancestor order'
|
||||
end
|
||||
|
||||
context 'linear' do
|
||||
before do
|
||||
stub_feature_flags(use_traversal_ids: true)
|
||||
end
|
||||
|
||||
include_examples 'correct ancestor order'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def item(variable)
|
||||
Gitlab::Ci::Variables::Collection::Item.fabricate(variable)
|
||||
end
|
||||
end
|
||||
|
|
@ -342,10 +342,88 @@ RSpec.describe Gitlab::Ci::Variables::Builder do
|
|||
let_it_be(:protected_variable) { create(:ci_group_variable, protected: true, group: group) }
|
||||
let_it_be(:unprotected_variable) { create(:ci_group_variable, protected: false, group: group) }
|
||||
|
||||
let(:protected_variable_item) { protected_variable }
|
||||
let(:unprotected_variable_item) { unprotected_variable }
|
||||
context 'with ci_variables_builder_memoize_secret_variables disabled' do
|
||||
before do
|
||||
stub_feature_flags(ci_variables_builder_memoize_secret_variables: false)
|
||||
end
|
||||
|
||||
include_examples "secret CI variables"
|
||||
let(:protected_variable_item) { protected_variable }
|
||||
let(:unprotected_variable_item) { unprotected_variable }
|
||||
|
||||
include_examples "secret CI variables"
|
||||
end
|
||||
|
||||
context 'with ci_variables_builder_memoize_secret_variables enabled' do
|
||||
before do
|
||||
stub_feature_flags(ci_variables_builder_memoize_secret_variables: true)
|
||||
end
|
||||
|
||||
let(:protected_variable_item) { Gitlab::Ci::Variables::Collection::Item.fabricate(protected_variable) }
|
||||
let(:unprotected_variable_item) { Gitlab::Ci::Variables::Collection::Item.fabricate(unprotected_variable) }
|
||||
|
||||
include_examples "secret CI variables"
|
||||
|
||||
context 'variables memoization' do
|
||||
let_it_be(:scoped_variable) { create(:ci_group_variable, group: group, environment_scope: 'scoped') }
|
||||
|
||||
let(:ref) { job.git_ref }
|
||||
let(:environment) { job.expanded_environment_name }
|
||||
let(:scoped_variable_item) { Gitlab::Ci::Variables::Collection::Item.fabricate(scoped_variable) }
|
||||
|
||||
context 'with protected environments' do
|
||||
it 'memoizes the result by environment' do
|
||||
expect(pipeline.project)
|
||||
.to receive(:protected_for?)
|
||||
.with(pipeline.jobs_git_ref)
|
||||
.once.and_return(true)
|
||||
|
||||
expect_next_instance_of(described_class::Group) do |group_variables_builder|
|
||||
expect(group_variables_builder)
|
||||
.to receive(:secret_variables)
|
||||
.with(environment: 'production', protected_ref: true)
|
||||
.once
|
||||
.and_call_original
|
||||
end
|
||||
|
||||
2.times do
|
||||
expect(builder.secret_group_variables(ref: ref, environment: 'production'))
|
||||
.to contain_exactly(unprotected_variable_item, protected_variable_item)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with unprotected environments' do
|
||||
it 'memoizes the result by environment' do
|
||||
expect(pipeline.project)
|
||||
.to receive(:protected_for?)
|
||||
.with(pipeline.jobs_git_ref)
|
||||
.once.and_return(false)
|
||||
|
||||
expect_next_instance_of(described_class::Group) do |group_variables_builder|
|
||||
expect(group_variables_builder)
|
||||
.to receive(:secret_variables)
|
||||
.with(environment: nil, protected_ref: false)
|
||||
.once
|
||||
.and_call_original
|
||||
|
||||
expect(group_variables_builder)
|
||||
.to receive(:secret_variables)
|
||||
.with(environment: 'scoped', protected_ref: false)
|
||||
.once
|
||||
.and_call_original
|
||||
end
|
||||
|
||||
2.times do
|
||||
expect(builder.secret_group_variables(ref: 'other', environment: nil))
|
||||
.to contain_exactly(unprotected_variable_item)
|
||||
|
||||
expect(builder.secret_group_variables(ref: 'other', environment: 'scoped'))
|
||||
.to contain_exactly(unprotected_variable_item, scoped_variable_item)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#secret_project_variables' do
|
||||
|
|
|
|||
|
|
@ -3,13 +3,13 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::Database::EachDatabase do
|
||||
describe '.each_database_connection' do
|
||||
describe '.each_database_connection', :add_ci_connection do
|
||||
before do
|
||||
allow(Gitlab::Database).to receive(:database_base_models)
|
||||
.and_return({ main: ActiveRecord::Base, ci: Ci::ApplicationRecord }.with_indifferent_access)
|
||||
end
|
||||
|
||||
it 'yields each connection after connecting SharedModel', :add_ci_connection do
|
||||
it 'yields each connection after connecting SharedModel' do
|
||||
expect(Gitlab::Database::SharedModel).to receive(:using_connection)
|
||||
.with(ActiveRecord::Base.connection).ordered.and_yield
|
||||
|
||||
|
|
@ -22,6 +22,42 @@ RSpec.describe Gitlab::Database::EachDatabase do
|
|||
[Ci::ApplicationRecord.connection, 'ci']
|
||||
)
|
||||
end
|
||||
|
||||
context 'when only certain databases are selected' do
|
||||
it 'yields the selected connections after connecting SharedModel' do
|
||||
expect(Gitlab::Database::SharedModel).to receive(:using_connection)
|
||||
.with(Ci::ApplicationRecord.connection).ordered.and_yield
|
||||
|
||||
expect { |b| described_class.each_database_connection(only: 'ci', &b) }
|
||||
.to yield_successive_args([Ci::ApplicationRecord.connection, 'ci'])
|
||||
end
|
||||
|
||||
context 'when the selected names are passed as symbols' do
|
||||
it 'yields the selected connections after connecting SharedModel' do
|
||||
expect(Gitlab::Database::SharedModel).to receive(:using_connection)
|
||||
.with(Ci::ApplicationRecord.connection).ordered.and_yield
|
||||
|
||||
expect { |b| described_class.each_database_connection(only: :ci, &b) }
|
||||
.to yield_successive_args([Ci::ApplicationRecord.connection, 'ci'])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the selected names are invalid' do
|
||||
it 'does not yield any connections' do
|
||||
expect do |b|
|
||||
described_class.each_database_connection(only: :notvalid, &b)
|
||||
rescue ArgumentError => e
|
||||
expect(e.message).to match(/notvalid is not a valid database name/)
|
||||
end.not_to yield_control
|
||||
end
|
||||
|
||||
it 'raises an error' do
|
||||
expect do
|
||||
described_class.each_database_connection(only: :notvalid) {}
|
||||
end.to raise_error(ArgumentError, /notvalid is not a valid database name/)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.each_model_connection' do
|
||||
|
|
|
|||
|
|
@ -43,6 +43,14 @@ RSpec.describe Ci::GroupVariable do
|
|||
end
|
||||
end
|
||||
|
||||
describe '.for_groups' do
|
||||
let_it_be(:group) { create(:group) }
|
||||
let_it_be(:group_variable) { create(:ci_group_variable, group: group) }
|
||||
let_it_be(:other_variable) { create(:ci_group_variable) }
|
||||
|
||||
it { expect(described_class.for_groups([group.id])).to eq([group_variable]) }
|
||||
end
|
||||
|
||||
it_behaves_like 'cleanup by a loose foreign key' do
|
||||
let!(:model) { create(:ci_group_variable) }
|
||||
|
||||
|
|
|
|||
|
|
@ -6,10 +6,10 @@ RSpec.describe BatchDestroyDependentAssociations do
|
|||
class TestProject < ActiveRecord::Base
|
||||
self.table_name = 'projects'
|
||||
|
||||
has_many :builds, dependent: :destroy
|
||||
has_many :builds
|
||||
has_many :notification_settings, as: :source, dependent: :delete_all
|
||||
has_many :pages_domains
|
||||
has_many :todos
|
||||
has_many :todos, dependent: :destroy
|
||||
|
||||
include BatchDestroyDependentAssociations
|
||||
end
|
||||
|
|
@ -18,7 +18,7 @@ RSpec.describe BatchDestroyDependentAssociations do
|
|||
let_it_be(:project) { TestProject.new }
|
||||
|
||||
it 'returns the right associations' do
|
||||
expect(project.dependent_associations_to_destroy.map(&:name)).to match_array([:builds])
|
||||
expect(project.dependent_associations_to_destroy.map(&:name)).to match_array([:todos])
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -26,36 +26,35 @@ RSpec.describe BatchDestroyDependentAssociations do
|
|||
let_it_be(:project) { create(:project) }
|
||||
let_it_be(:build) { create(:ci_build, project: project) }
|
||||
let_it_be(:notification_setting) { create(:notification_setting, project: project) }
|
||||
let_it_be(:note) { create(:note, project: project) }
|
||||
|
||||
let!(:todos) { create(:todo, project: project) }
|
||||
it 'destroys multiple notes' do
|
||||
create(:note, project: project)
|
||||
|
||||
it 'destroys multiple builds' do
|
||||
create(:ci_build, project: project)
|
||||
|
||||
expect(Ci::Build.count).to eq(2)
|
||||
expect(Note.count).to eq(2)
|
||||
|
||||
project.destroy_dependent_associations_in_batches
|
||||
|
||||
expect(Ci::Build.count).to eq(0)
|
||||
expect(Note.count).to eq(0)
|
||||
end
|
||||
|
||||
it 'destroys builds in batches' do
|
||||
expect(project).to receive_message_chain(:builds, :find_each).and_yield(build)
|
||||
expect(build).to receive(:destroy).and_call_original
|
||||
it 'destroys note in batches' do
|
||||
expect(project).to receive_message_chain(:notes, :find_each).and_yield(note)
|
||||
expect(note).to receive(:destroy).and_call_original
|
||||
|
||||
project.destroy_dependent_associations_in_batches
|
||||
|
||||
expect(Ci::Build.count).to eq(0)
|
||||
expect(Todo.count).to eq(1)
|
||||
expect(Ci::Build.count).to eq(1)
|
||||
expect(Note.count).to eq(0)
|
||||
expect(User.count).to be > 0
|
||||
expect(NotificationSetting.count).to eq(User.count)
|
||||
end
|
||||
|
||||
it 'excludes associations' do
|
||||
project.destroy_dependent_associations_in_batches(exclude: [:builds])
|
||||
project.destroy_dependent_associations_in_batches(exclude: [:notes])
|
||||
|
||||
expect(Note.count).to eq(1)
|
||||
expect(Ci::Build.count).to eq(1)
|
||||
expect(Todo.count).to eq(1)
|
||||
expect(User.count).to be > 0
|
||||
expect(NotificationSetting.count).to eq(User.count)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rake_helper'
|
||||
|
||||
RSpec.describe 'dev rake tasks' do
|
||||
before do
|
||||
Rake.application.rake_require 'tasks/gitlab/setup'
|
||||
Rake.application.rake_require 'tasks/gitlab/shell'
|
||||
Rake.application.rake_require 'tasks/dev'
|
||||
end
|
||||
|
||||
describe 'setup' do
|
||||
subject(:setup_task) { run_rake_task('dev:setup') }
|
||||
|
||||
let(:connections) { Gitlab::Database.database_base_models.values.map(&:connection) }
|
||||
|
||||
it 'sets up the development environment', :aggregate_failures do
|
||||
expect(Rake::Task['gitlab:setup']).to receive(:invoke)
|
||||
|
||||
expect(connections).to all(receive(:execute).with('ANALYZE'))
|
||||
|
||||
expect(Rake::Task['gitlab:shell:setup']).to receive(:invoke)
|
||||
|
||||
setup_task
|
||||
end
|
||||
end
|
||||
|
||||
describe 'load' do
|
||||
subject(:load_task) { run_rake_task('dev:load') }
|
||||
|
||||
it 'eager loads the application', :aggregate_failures do
|
||||
expect(Rails.configuration).to receive(:eager_load=).with(true)
|
||||
expect(Rails.application).to receive(:eager_load!)
|
||||
|
||||
load_task
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -261,45 +261,78 @@ RSpec.describe 'gitlab:db namespace rake task', :silence_stdout do
|
|||
end
|
||||
|
||||
describe 'drop_tables' do
|
||||
subject { run_rake_task('gitlab:db:drop_tables') }
|
||||
|
||||
let(:tables) { %w(one two) }
|
||||
let(:tables) { %w(one two schema_migrations) }
|
||||
let(:views) { %w(three four) }
|
||||
let(:connection) { ActiveRecord::Base.connection }
|
||||
let(:schemas) { Gitlab::Database::EXTRA_SCHEMAS }
|
||||
|
||||
before do
|
||||
allow(connection).to receive(:execute).and_return(nil)
|
||||
context 'with a single database' do
|
||||
let(:connection) { ActiveRecord::Base.connection }
|
||||
|
||||
allow(connection).to receive(:tables).and_return(tables)
|
||||
allow(connection).to receive(:views).and_return(views)
|
||||
before do
|
||||
skip_if_multiple_databases_are_setup
|
||||
|
||||
allow(connection).to receive(:execute).and_return(nil)
|
||||
|
||||
allow(connection).to receive(:tables).and_return(tables)
|
||||
allow(connection).to receive(:views).and_return(views)
|
||||
end
|
||||
|
||||
it 'drops all objects for the database', :aggregate_failures do
|
||||
expect_objects_to_be_dropped(connection)
|
||||
|
||||
run_rake_task('gitlab:db:drop_tables')
|
||||
end
|
||||
end
|
||||
|
||||
it 'drops all tables, except schema_migrations' do
|
||||
context 'with multiple databases', :aggregate_failures do
|
||||
let(:main_model) { double(:model, connection: double(:connection, tables: tables, views: views)) }
|
||||
let(:ci_model) { double(:model, connection: double(:connection, tables: tables, views: views)) }
|
||||
let(:base_models) { { 'main' => main_model, 'ci' => ci_model } }
|
||||
|
||||
before do
|
||||
skip_if_multiple_databases_not_setup
|
||||
|
||||
allow(Gitlab::Database).to receive(:database_base_models).and_return(base_models)
|
||||
|
||||
allow(main_model.connection).to receive(:table_exists?).with('schema_migrations').and_return(true)
|
||||
allow(ci_model.connection).to receive(:table_exists?).with('schema_migrations').and_return(true)
|
||||
|
||||
(tables + views + schemas).each do |name|
|
||||
allow(main_model.connection).to receive(:quote_table_name).with(name).and_return("\"#{name}\"")
|
||||
allow(ci_model.connection).to receive(:quote_table_name).with(name).and_return("\"#{name}\"")
|
||||
end
|
||||
end
|
||||
|
||||
it 'drops all objects for all databases', :aggregate_failures do
|
||||
expect_objects_to_be_dropped(main_model.connection)
|
||||
expect_objects_to_be_dropped(ci_model.connection)
|
||||
|
||||
run_rake_task('gitlab:db:drop_tables')
|
||||
end
|
||||
|
||||
context 'when the single database task is used' do
|
||||
it 'drops all objects for the given database', :aggregate_failures do
|
||||
expect_objects_to_be_dropped(main_model.connection)
|
||||
|
||||
expect(ci_model.connection).not_to receive(:execute)
|
||||
|
||||
run_rake_task('gitlab:db:drop_tables:main')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def expect_objects_to_be_dropped(connection)
|
||||
expect(connection).to receive(:execute).with('DROP TABLE IF EXISTS "one" CASCADE')
|
||||
expect(connection).to receive(:execute).with('DROP TABLE IF EXISTS "two" CASCADE')
|
||||
|
||||
subject
|
||||
end
|
||||
|
||||
it 'drops all views' do
|
||||
expect(connection).to receive(:execute).with('DROP VIEW IF EXISTS "three" CASCADE')
|
||||
expect(connection).to receive(:execute).with('DROP VIEW IF EXISTS "four" CASCADE')
|
||||
|
||||
subject
|
||||
end
|
||||
|
||||
it 'truncates schema_migrations table' do
|
||||
expect(connection).to receive(:execute).with('TRUNCATE schema_migrations')
|
||||
|
||||
subject
|
||||
end
|
||||
|
||||
it 'drops extra schemas' do
|
||||
Gitlab::Database::EXTRA_SCHEMAS.each do |schema|
|
||||
expect(connection).to receive(:execute).with("DROP SCHEMA IF EXISTS \"#{schema}\" CASCADE")
|
||||
end
|
||||
|
||||
subject
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue