Skip to content
Snippets Groups Projects
Commit 2ed36892 authored by GitLab Bot's avatar GitLab Bot
Browse files

Add latest changes from gitlab-org/gitlab@master

parent f1a57558
No related branches found
No related tags found
No related merge requests found
Showing
with 280 additions and 58 deletions
Loading
Loading
@@ -11,7 +11,7 @@
= hidden_field_tag "#{issuable_type}[assignee_ids][]", 0, id: nil
- else
- assignees.each do |assignee|
= hidden_field_tag "#{issuable_type}[assignee_ids][]", assignee.id, id: nil, data: { avatar_url: assignee.avatar_url, name: assignee.name, username: assignee.username }
= hidden_field_tag "#{issuable_type}[assignee_ids][]", assignee.id, id: nil, data: assignee_sidebar_data(assignee, merge_request: @merge_request)
 
- options = { toggle_class: 'js-user-search js-author-search',
title: _('Assign to'),
Loading
Loading
---
title: Remove map-get($grid-breakpoints, xs) for max-width
merge_request: 17420
author: Takuya Noguchi
type: other
---
title: Fix cannot merge icon showing in dropdown for users who can merge
merge_request: 17306
author:
type: fixed
---
title: Fix signup link in admin area not being disabled
merge_request: 16726
author: Illya Klymov
type: fixed
---
title: Add allowFilter and allowAnySHA1InWant for partial clones
merge_request: 16850
author:
type: added
---
title: Add timeout mechanism for CI config validation
merge_request: 16807
author:
type: fixed
Loading
Loading
@@ -2289,6 +2289,10 @@ Nested includes allow you to compose a set of includes.
A total of 50 includes is allowed.
Duplicate includes are considered a configuration error.
 
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/28212) in GitLab 12.4.
A hard limit of 30 seconds was set for resolving all files.
#### `include` examples
 
Here are a few more `include` examples.
Loading
Loading
Loading
Loading
@@ -104,6 +104,51 @@ document.addEventListener('DOMContentLoaded', () => new Vue({
}));
```
 
#### Accessing feature flags
Use Vue's [provide/inject](https://vuejs.org/v2/api/#provide-inject) mechanism
to make feature flags available to any descendant components in a Vue
application. The `glFeatures` object is already provided in `commons/vue.js`, so
only the mixin is required to utilize the flags:
```javascript
// An arbitrary descendant component
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
export default {
// ...
mixins: [glFeatureFlagsMixin()],
// ...
created() {
if (this.glFeatures.myFlag) {
// ...
}
},
}
```
This approach has a few benefits:
- Arbitrarily deeply nested components can opt-in and access the flag without
intermediate components being aware of it (c.f. passing the flag down via
props).
- Good testability, since the flag can be provided to `mount`/`shallowMount`
from `vue-test-utils` as easily as a prop.
```javascript
import { shallowMount } from '@vue/test-utils';
shallowMount(component, {
provide: {
glFeatures: { myFlag: true },
},
});
```
- No need to access a global variable, except in the application's
[entry point](#accessing-the-gl-object).
### A folder for Components
 
This folder holds all components that are specific of this new feature.
Loading
Loading
Loading
Loading
@@ -109,6 +109,9 @@ if ( gon.features.vimBindings ) {
The name of the feature flag in JavaScript will always be camelCased, meaning
that checking for `gon.features.vim_bindings` would not work.
 
See the [Vue guide](../fe_guide/vue.md#accessing-feature-flags) for details about
how to access feature flags in a Vue component.
### Specs
 
In the test environment `Feature.enabled?` is stubbed to always respond to `true`,
Loading
Loading
Loading
Loading
@@ -78,6 +78,10 @@ module API
receive_max_input_size = Gitlab::CurrentSettings.receive_max_input_size.to_i
if receive_max_input_size > 0
payload[:git_config_options] << "receive.maxInputSize=#{receive_max_input_size.megabytes}"
if Feature.enabled?(:gitaly_upload_pack_filter, project)
payload[:git_config_options] << "uploadpack.allowFilter=true" << "uploadpack.allowAnySHA1InWant=true"
end
end
 
response_with_status(**payload)
Loading
Loading
Loading
Loading
@@ -7,6 +7,8 @@ module Gitlab
#
class Config
ConfigError = Class.new(StandardError)
TIMEOUT_SECONDS = 30.seconds
TIMEOUT_MESSAGE = 'Resolving config took longer than expected'
 
RESCUE_ERRORS = [
Gitlab::Config::Loader::FormatError,
Loading
Loading
@@ -17,17 +19,17 @@ module Gitlab
attr_reader :root
 
def initialize(config, project: nil, sha: nil, user: nil)
@config = Config::Extendable
.new(build_config(config, project: project, sha: sha, user: user))
.to_hash
@context = build_context(project: project, sha: sha, user: user)
if Feature.enabled?(:ci_limit_yaml_expansion, project, default_enabled: true)
@context.set_deadline(TIMEOUT_SECONDS)
end
@config = expand_config(config)
 
@root = Entry::Root.new(@config)
@root.compose!
 
rescue Gitlab::Config::Loader::Yaml::DataTooLargeError => e
Gitlab::Sentry.track_exception(e, extra: { user: user.inspect, project: project.inspect })
raise Config::ConfigError, e.message
rescue *rescue_errors => e
raise Config::ConfigError, e.message
end
Loading
Loading
@@ -61,18 +63,34 @@ module Gitlab
 
private
 
def build_config(config, project:, sha:, user:)
def expand_config(config)
build_config(config)
rescue Gitlab::Config::Loader::Yaml::DataTooLargeError => e
track_exception(e)
raise Config::ConfigError, e.message
rescue Gitlab::Ci::Config::External::Context::TimeoutError => e
track_exception(e)
raise Config::ConfigError, TIMEOUT_MESSAGE
end
def build_config(config)
initial_config = Gitlab::Config::Loader::Yaml.new(config).load!
initial_config = Config::External::Processor.new(initial_config, @context).perform
 
process_external_files(initial_config, project: project, sha: sha, user: user)
Config::Extendable.new(initial_config).to_hash
end
 
def process_external_files(config, project:, sha:, user:)
Config::External::Processor.new(config,
def build_context(project:, sha:, user:)
Config::External::Context.new(
project: project,
sha: sha || project&.repository&.root_ref_sha,
user: user,
expandset: Set.new).perform
user: user)
end
def track_exception(error)
Gitlab::Sentry.track_exception(error, extra: @context.sentry_payload)
end
 
# Overriden in EE
Loading
Loading
# frozen_string_literal: true
module Gitlab
module Ci
class Config
module External
class Context
TimeoutError = Class.new(StandardError)
attr_reader :project, :sha, :user
attr_reader :expandset, :execution_deadline
def initialize(project: nil, sha: nil, user: nil)
@project = project
@sha = sha
@user = user
@expandset = Set.new
@execution_deadline = 0
yield self if block_given?
end
def mutate(attrs = {})
self.class.new(**attrs) do |ctx|
ctx.expandset = expandset
ctx.execution_deadline = execution_deadline
end
end
def set_deadline(timeout_seconds)
@execution_deadline = current_monotonic_time + timeout_seconds.to_f
end
def check_execution_time!
raise TimeoutError if execution_expired?
end
def sentry_payload
{
user: user.inspect,
project: project.inspect
}
end
protected
attr_writer :expandset, :execution_deadline
private
def current_monotonic_time
Gitlab::Metrics::System.monotonic_time
end
def execution_expired?
return false if execution_deadline.zero?
current_monotonic_time > execution_deadline
end
end
end
end
end
end
Loading
Loading
@@ -12,8 +12,6 @@ module Gitlab
 
YAML_WHITELIST_EXTENSION = /.+\.(yml|yaml)$/i.freeze
 
Context = Struct.new(:project, :sha, :user, :expandset)
def initialize(params, context)
@params = params
@context = context
Loading
Loading
@@ -69,11 +67,16 @@ module Gitlab
end
 
def validate!
validate_execution_time!
validate_location!
validate_content! if errors.none?
validate_hash! if errors.none?
end
 
def validate_execution_time!
context.check_execution_time!
end
def validate_location!
if invalid_location_type?
errors.push("Included file `#{location}` needs to be a string")
Loading
Loading
@@ -95,11 +98,11 @@ module Gitlab
end
 
def expand_includes(hash)
External::Processor.new(hash, **expand_context).perform
External::Processor.new(hash, context.mutate(expand_context_attrs)).perform
end
 
def expand_context
{ project: nil, sha: nil, user: nil, expandset: context.expandset }
def expand_context_attrs
{}
end
end
end
Loading
Loading
Loading
Loading
@@ -6,6 +6,7 @@ module Gitlab
module External
module File
class Local < Base
extend ::Gitlab::Utils::Override
include Gitlab::Utils::StrongMemoize
 
def initialize(params, context)
Loading
Loading
@@ -34,11 +35,13 @@ module Gitlab
context.project.repository.blob_data_at(context.sha, location)
end
 
def expand_context
super.merge(
override :expand_context_attrs
def expand_context_attrs
{
project: context.project,
sha: context.sha,
user: context.user)
user: context.user
}
end
end
end
Loading
Loading
Loading
Loading
@@ -6,11 +6,12 @@ module Gitlab
module External
module File
class Project < Base
extend ::Gitlab::Utils::Override
include Gitlab::Utils::StrongMemoize
 
attr_reader :project_name, :ref_name
 
def initialize(params, context = {})
def initialize(params, context)
@location = params[:file]
@project_name = params[:project]
@ref_name = params[:ref] || 'HEAD'
Loading
Loading
@@ -65,11 +66,13 @@ module Gitlab
end
end
 
def expand_context
super.merge(
override :expand_context_attrs
def expand_context_attrs
{
project: project,
sha: sha,
user: context.user)
user: context.user
}
end
end
end
Loading
Loading
Loading
Loading
@@ -21,14 +21,9 @@ module Gitlab
DuplicateIncludesError = Class.new(Error)
TooManyIncludesError = Class.new(Error)
 
def initialize(values, project:, sha:, user:, expandset:)
raise Error, 'Expanded needs to be `Set`' unless expandset.is_a?(Set)
def initialize(values, context)
@locations = Array.wrap(values.fetch(:include, []))
@project = project
@sha = sha
@user = user
@expandset = expandset
@context = context
end
 
def process
Loading
Loading
@@ -43,7 +38,9 @@ module Gitlab
 
private
 
attr_reader :locations, :project, :sha, :user, :expandset
attr_reader :locations, :context
delegate :expandset, to: :context
 
# convert location if String to canonical form
def normalize_location(location)
Loading
Loading
@@ -68,11 +65,11 @@ module Gitlab
end
 
# We scope location to context, as this allows us to properly support
# relative incldues, and similarly looking relative in another project
# relative includes, and similarly looking relative in another project
# does not trigger duplicate error
scoped_location = location.merge(
context_project: project,
context_sha: sha)
context_project: context.project,
context_sha: context.sha)
 
unless expandset.add?(scoped_location)
raise DuplicateIncludesError, "Include `#{location.to_json}` was already included!"
Loading
Loading
@@ -88,12 +85,6 @@ module Gitlab
 
matching.first
end
def context
strong_memoize(:context) do
External::File::Base::Context.new(project, sha, user, expandset)
end
end
end
end
end
Loading
Loading
Loading
Loading
@@ -7,9 +7,9 @@ module Gitlab
class Processor
IncludeError = Class.new(StandardError)
 
def initialize(values, project:, sha:, user:, expandset:)
def initialize(values, context)
@values = values
@external_files = External::Mapper.new(values, project: project, sha: sha, user: user, expandset: expandset).process
@external_files = External::Mapper.new(values, context).process
@content = {}
rescue External::Mapper::Error,
OpenSSL::SSL::SSLError => e
Loading
Loading
Loading
Loading
@@ -15527,6 +15527,9 @@ msgstr ""
msgid "The merge conflicts for this merge request have already been resolved. Please return to the merge request."
msgstr ""
 
msgid "The merge request can now be merged."
msgstr ""
msgid "The name %{entryName} is already taken in this directory."
msgstr ""
 
Loading
Loading
Loading
Loading
@@ -483,10 +483,8 @@ describe Projects::EnvironmentsController do
end
 
shared_examples_for 'the default dashboard' do
all_dashboards = Feature.enabled?(:environment_metrics_show_multiple_dashboards)
it_behaves_like '200 response'
it_behaves_like 'includes all dashboards' if all_dashboards
it_behaves_like 'includes all dashboards'
 
it 'is the default dashboard' do
get :metrics_dashboard, params: environment_params(dashboard_params)
Loading
Loading
@@ -618,16 +616,6 @@ describe Projects::EnvironmentsController do
it_behaves_like 'the default dashboard'
it_behaves_like 'dashboard can be specified'
it_behaves_like 'dashboard can be embedded'
context 'when multiple dashboards is disabled' do
before do
stub_feature_flags(environment_metrics_show_multiple_dashboards: false)
end
it_behaves_like 'the default dashboard'
it_behaves_like 'dashboard cannot be specified'
it_behaves_like 'dashboard can be embedded'
end
end
 
describe 'GET #search' do
Loading
Loading
# frozen_string_literal: true
require 'spec_helper'
describe 'Merge request > User edits assignees sidebar', :js do
let(:project) { create(:project, :public, :repository) }
let(:protected_branch) { create(:protected_branch, :maintainers_can_push, name: 'master', project: project) }
let(:merge_request) { create(:merge_request, :simple, source_project: project, target_branch: protected_branch.name) }
let(:users_find_limit) { 5 }
# Insert more than limit so that response doesn't include assigned user
let(:project_developers) { Array.new(users_find_limit + 1) { create(:user).tap { |u| project.add_developer(u) } } }
let(:project_maintainers) { Array.new(users_find_limit + 1) { create(:user).tap { |u| project.add_maintainer(u) } } }
# DOM finders to simplify and improve readability
let(:sidebar_assignee_block) { page.find('.js-issuable-sidebar .assignee') }
let(:sidebar_assignee_avatar_link) { sidebar_assignee_block.find_all('a').find { |a| a['href'].include? assignee.username } }
let(:sidebar_assignee_tooltip) { sidebar_assignee_avatar_link['data-original-title'] || '' }
let(:sidebar_assignee_dropdown_item) { sidebar_assignee_block.find(".dropdown-menu li[data-user-id=\"#{assignee.id}\"]") }
let(:sidebar_assignee_dropdown_tooltip) { sidebar_assignee_dropdown_item.find('a')['data-title'] || '' }
before do
stub_const('Autocomplete::UsersFinder::LIMIT', users_find_limit)
sign_in(project.owner)
merge_request.assignees << assignee
visit project_merge_request_path(project, merge_request)
wait_for_requests
end
shared_examples 'when assigned' do |expected_tooltip: ''|
it 'shows assignee name' do
expect(sidebar_assignee_block).to have_text(assignee.name)
end
it "shows assignee tooltip '#{expected_tooltip}'" do
expect(sidebar_assignee_tooltip).to eql(expected_tooltip)
end
context 'when edit is clicked' do
before do
sidebar_assignee_block.click_link('Edit')
wait_for_requests
end
it "shows assignee tooltip '#{expected_tooltip}" do
expect(sidebar_assignee_dropdown_tooltip).to eql(expected_tooltip)
end
end
end
context 'when assigned to maintainer' do
let(:assignee) { project_maintainers.last }
it_behaves_like 'when assigned', expected_tooltip: ''
end
context 'when assigned to developer' do
let(:assignee) { project_developers.last }
it_behaves_like 'when assigned', expected_tooltip: 'Cannot merge'
end
end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment