Commit 6c07afab authored by John Skarbek's avatar John Skarbek

Merge remote-tracking branch 'origin/11-10-stable' into 11-10-stable

parents 3d81a2d2 e9a60068
Pipeline #59221295 passed with stages
in 63 minutes and 53 seconds
......@@ -64,7 +64,7 @@
"number-leading-zero":"always",
"number-no-trailing-zeros":true,
"property-no-unknown":true,
"property-no-vendor-prefix":true,
"property-no-vendor-prefix": [true, { "ignoreProperties": ["user-select"] }],
"rule-empty-line-before":[
"always-multi-line",
{
......
......@@ -221,6 +221,7 @@ export default {
<gl-dropdown-item
v-for="environment in store.environmentsData"
:key="environment.id"
:href="environment.metrics_path"
:active="environment.name === currentEnvironmentName"
active-class="is-active"
>{{ environment.name }}</gl-dropdown-item
......
import Vue from 'vue';
import { parseBoolean } from '~/lib/utils/common_utils';
import Dashboard from './components/dashboard.vue';
import Dashboard from 'ee_else_ce/monitoring/components/dashboard.vue';
export default (props = {}) => {
const el = document.getElementById('prometheus-graphs');
......
......@@ -22,13 +22,10 @@ const handleUserPopoverMouseOut = ({ target }) => {
* Adds a MergeRequestPopover component to the body, hands over as much data as the target element has in data attributes.
* loads based on data-project-path and data-iid more data about an MR from the API and sets it on the popover
*/
const handleMRPopoverMount = apolloProvider => ({ target }) => {
const handleMRPopoverMount = ({ apolloProvider, projectPath, mrTitle, iid }) => ({ target }) => {
// Add listener to actually remove it again
target.addEventListener('mouseleave', handleUserPopoverMouseOut);
const { projectPath, mrTitle, iid } = target.dataset;
const mergeRequest = {};
renderFn = setTimeout(() => {
const MRPopoverComponent = Vue.extend(MRPopover);
renderedPopover = new MRPopoverComponent({
......@@ -36,7 +33,6 @@ const handleMRPopoverMount = apolloProvider => ({ target }) => {
target,
projectPath,
mergeRequestIID: iid,
mergeRequest,
mergeRequestTitle: mrTitle,
},
apolloProvider,
......@@ -57,8 +53,13 @@ export default elements => {
const listenerAddedAttr = 'data-mr-listener-added';
mrLinks.forEach(el => {
if (!el.getAttribute(listenerAddedAttr)) {
el.addEventListener('mouseenter', handleMRPopoverMount(apolloProvider));
const { projectPath, mrTitle, iid } = el.dataset;
if (!el.getAttribute(listenerAddedAttr) && projectPath && mrTitle && iid) {
el.addEventListener(
'mouseenter',
handleMRPopoverMount({ apolloProvider, projectPath, mrTitle, iid }),
);
el.setAttribute(listenerAddedAttr, true);
}
});
......
......@@ -34,6 +34,7 @@ export default () => {
props: {
isLoading: this.mediator.state.isLoading,
pipeline: this.mediator.store.state.pipeline,
mediator: this.mediator,
},
on: {
refreshPipelineGraph: this.requestRefreshPipelineGraph,
......
......@@ -22,6 +22,7 @@ import noteHeader from '~/notes/components/note_header.vue';
import Icon from '~/vue_shared/components/icon.vue';
import TimelineEntryItem from './timeline_entry_item.vue';
import { spriteIcon } from '../../../lib/utils/common_utils';
import initMRPopovers from '~/mr_popover/';
const MAX_VISIBLE_COMMIT_LIST_COUNT = 3;
......@@ -71,6 +72,9 @@ export default {
);
},
},
mounted() {
initMRPopovers(this.$el.querySelectorAll('.gfm-merge_request'));
},
};
</script>
......
......@@ -11,6 +11,9 @@
opacity: 1 !important;
* {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
// !important to make sure no style can override this when dragging
cursor: grabbing !important;
......
......@@ -84,6 +84,9 @@ input[type='checkbox']:hover {
.search-icon {
transition: color $default-transition-duration;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
......
......@@ -19,7 +19,7 @@ module Projects
redirect_to project_settings_ci_cd_path(@project)
else
render 'show'
redirect_to project_settings_ci_cd_path(@project), alert: result[:message]
end
end
end
......
......@@ -4,7 +4,7 @@ require 'nokogiri'
module MarkupHelper
include ActionView::Helpers::TagHelper
include ActionView::Context
include ::Gitlab::ActionViewOutput::Context
def plain?(filename)
Gitlab::MarkupHelper.plain?(filename)
......@@ -83,7 +83,8 @@ module MarkupHelper
text = sanitize(
text,
tags: tags,
attributes: Rails::Html::WhiteListSanitizer.allowed_attributes + ['style', 'data-src', 'data-name', 'data-unicode-version']
attributes: Rails::Html::WhiteListSanitizer.allowed_attributes +
%w(style data-src data-name data-unicode-version data-iid data-project-path data-mr-title)
)
# since <img> tags are stripped, this can leave empty <a> tags hanging around
......
......@@ -4,6 +4,7 @@ module Ci
class Bridge < CommitStatus
include Ci::Processable
include Ci::Contextable
include Ci::PipelineDelegator
include Importable
include AfterCommitQueue
include HasRef
......@@ -13,8 +14,6 @@ module Ci
belongs_to :trigger_request
validates :ref, presence: true
delegate :merge_request_event?, to: :pipeline
def self.retry(bridge, current_user)
raise NotImplementedError
end
......
......@@ -6,6 +6,7 @@ module Ci
include Ci::Processable
include Ci::Metadatable
include Ci::Contextable
include Ci::PipelineDelegator
include TokenAuthenticatable
include AfterCommitQueue
include ObjectStorage::BackgroundMove
......@@ -48,8 +49,6 @@ module Ci
delegate :terminal_specification, to: :runner_session, allow_nil: true
delegate :gitlab_deploy_token, to: :project
delegate :trigger_short_token, to: :trigger_request, allow_nil: true
delegate :merge_request_event?, :merge_request_ref?,
:legacy_detached_merge_request_pipeline?, to: :pipeline
##
# Since Gitlab 11.5, deployments records started being created right after
......
......@@ -754,6 +754,22 @@ module Ci
self.sha == sha || self.source_sha == sha
end
def triggered_by?(current_user)
user == current_user
end
def source_ref
if triggered_by_merge_request?
merge_request.source_branch
else
ref
end
end
def source_ref_slug
Gitlab::Utils.slugify(source_ref.to_s)
end
private
def ci_yaml_from_repo
......
......@@ -70,8 +70,8 @@ module Ci
variables.append(key: 'CI_COMMIT_SHA', value: sha)
variables.append(key: 'CI_COMMIT_SHORT_SHA', value: short_sha)
variables.append(key: 'CI_COMMIT_BEFORE_SHA', value: before_sha)
variables.append(key: 'CI_COMMIT_REF_NAME', value: ref)
variables.append(key: 'CI_COMMIT_REF_SLUG', value: ref_slug)
variables.append(key: 'CI_COMMIT_REF_NAME', value: source_ref)
variables.append(key: 'CI_COMMIT_REF_SLUG', value: source_ref_slug)
variables.append(key: "CI_COMMIT_TAG", value: ref) if tag?
variables.append(key: "CI_PIPELINE_TRIGGERED", value: 'true') if trigger_request
variables.append(key: "CI_JOB_MANUAL", value: 'true') if action?
......@@ -85,8 +85,8 @@ module Ci
Gitlab::Ci::Variables::Collection.new.tap do |variables|
variables.append(key: 'CI_BUILD_REF', value: sha)
variables.append(key: 'CI_BUILD_BEFORE_SHA', value: before_sha)
variables.append(key: 'CI_BUILD_REF_NAME', value: ref)
variables.append(key: 'CI_BUILD_REF_SLUG', value: ref_slug)
variables.append(key: 'CI_BUILD_REF_NAME', value: source_ref)
variables.append(key: 'CI_BUILD_REF_SLUG', value: source_ref_slug)
variables.append(key: 'CI_BUILD_NAME', value: name)
variables.append(key: 'CI_BUILD_STAGE', value: stage)
variables.append(key: "CI_BUILD_TAG", value: ref) if tag?
......
# frozen_string_literal: true
##
# This module is mainly used by child associations of `Ci::Pipeline` that needs to look up
# single source of truth. For example, `Ci::Build` has `git_ref` method, which behaves
# slightly different from `Ci::Pipeline`'s `git_ref`. This is very confusing as
# the system could behave differently time to time.
# We should have a single interface in `Ci::Pipeline` and access the method always.
module Ci
module PipelineDelegator
extend ActiveSupport::Concern
included do
delegate :merge_request_event?,
:merge_request_ref?,
:source_ref,
:source_ref_slug,
:legacy_detached_merge_request_pipeline?, to: :pipeline
end
end
end
# frozen_string_literal: true
##
# We will disable `ref` and `sha` attributes in `Ci::Build` in the future
# and remove this module in favor of Ci::PipelineDelegator.
module HasRef
extend ActiveSupport::Concern
......
......@@ -79,7 +79,11 @@ class Deployment < ApplicationRecord
end
def cluster
project.deployment_platform(environment: environment.name)&.cluster
platform = project.deployment_platform(environment: environment.name)
if platform.present? && platform.respond_to?(:cluster)
platform.cluster
end
end
def last?
......
......@@ -3,7 +3,7 @@
module UserStatusTooltip
extend ActiveSupport::Concern
include ActionView::Helpers::TagHelper
include ActionView::Context
include ::Gitlab::ActionViewOutput::Context
include EmojiHelper
include UsersHelper
......
......@@ -21,10 +21,9 @@
- if @scope == 'projects'
.term
= render 'shared/projects/list', projects: @search_objects, pipeline_status: false
- elsif %w[blobs wiki_blobs].include?(@scope)
= render partial: 'search/results/blob', collection: @search_objects, locals: { projects: blob_projects(@search_objects) }
- else
= render partial: "search/results/#{@scope.singularize}", collection: @search_objects
- locals = { projects: blob_projects(@search_objects) } if %w[blobs wiki_blobs].include?(@scope)
= render partial: "search/results/#{@scope.singularize}", collection: @search_objects, locals: locals
- if @scope != 'projects'
= paginate_collection(@search_objects)
......@@ -3,20 +3,26 @@
class PipelineScheduleWorker
include ApplicationWorker
include CronjobQueue
include ::Gitlab::ExclusiveLeaseHelpers
EXCLUSIVE_LOCK_KEY = 'pipeline_schedules:run:lock'
LOCK_TIMEOUT = 50.minutes
# rubocop: disable CodeReuse/ActiveRecord
def perform
Ci::PipelineSchedule.active.where("next_run_at < ?", Time.now)
.preload(:owner, :project).find_each do |schedule|
Ci::CreatePipelineService.new(schedule.project,
schedule.owner,
ref: schedule.ref)
.execute!(:schedule, ignore_skip_ci: true, save_on_errors: true, schedule: schedule)
rescue => e
error(schedule, e)
ensure
schedule.schedule_next_run!
in_lock(EXCLUSIVE_LOCK_KEY, ttl: LOCK_TIMEOUT, retries: 1) do
Ci::PipelineSchedule.active.where("next_run_at < ?", Time.now)
.preload(:owner, :project).find_each do |schedule|
schedule.schedule_next_run!
Ci::CreatePipelineService.new(schedule.project,
schedule.owner,
ref: schedule.ref)
.execute!(:schedule, ignore_skip_ci: true, save_on_errors: true, schedule: schedule)
rescue => e
error(schedule, e)
end
end
end
# rubocop: enable CodeReuse/ActiveRecord
......
---
title: Fix 500 in general pipeline settings when passing an invalid build timeout.
merge_request: 27416
author:
type: fixed
---
title: Fix MR popover on ToDos page
merge_request: 27382
author:
type: fixed
---
title: Fix Metrics Environments dropdown
merge_request:
author:
type: fixed
---
title: Fix Kubernetes service template deployment jobs broken as of 11.10.0
merge_request: 27687
author:
type: fixed
---
title: Fix bug where system note MR has no popover
merge_request: 27589
author:
type: fixed
---
title: Show proper wiki links in search results
merge_request: 27634
author:
type: fixed
---
title: Make `CI_COMMIT_REF_NAME` and `SLUG` variable idempotent
merge_request: 27663
author:
type: fixed
---
title: Fix bug when project export to remote url fails
merge_request: 27614
author:
type: fixed
---
title: Prevent concurrent execution of PipelineScheduleWorker
merge_request: 27781
author:
type: performance
---
title: Fix slow performance with compiling HAML templates
merge_request: 27782
author:
type: performance
---
title: Update Workhorse to v8.5.2
merge_request: 27631
author:
type: fixed
---
title: Prevent text selection when dragging in issue boards
merge_request: 27724
author:
type: fixed
......@@ -39,7 +39,14 @@ options:
### Improving NFS performance with GitLab
NOTE: **Note:** This is only available with GitLab 11.9 and up.
NOTE: **Note:**
This is only available starting in certain versions of GitLab:
* 11.5.11
* 11.6.11
* 11.7.12
* 11.8.8
* 11.9.0 and up (e.g. 11.10, 11.11, etc.)
If you are using NFS to share Git data, we recommend that you enable a
number of feature flags that will allow GitLab application processes to
......
# frozen_string_literal: true
# This file was simplified from https://raw.githubusercontent.com/rails/rails/195f39804a7a4a0034f25e8704220e03d95a752a/actionview/lib/action_view/context.rb.
#
# It is only needed by modules that need to call ActionView helper
# methods (e.g. those in
# https://github.com/rails/rails/tree/c4d3e202e10ae627b3b9c34498afb45450652421/actionview/lib/action_view/helpers)
# to generate tags outside of a Rails controller (e.g. API, Sidekiq,
# etc.).
#
# In Rails 5, ActionView::Context automatically includes CompiledTemplates.
# This means that any module that includes ActionView::Context is now a descendant
# of CompiledTemplates.
#
# When a partial is rendered for the first time, it runs
# Module#module_eval, which will evaluate a string source that defines a
# new method. For example:
#
# def _app_views_profiles_show_html_haml___1285955918103175884_70307801785400(local_assigns, output_buffer)
# "hello world"
# end
#
# When a new method is defined, the Ruby interpreter clears the method
# cache for all descendants, and all methods for those modules will have
# to be redefined. This can lead to a significant performance penalty.
#
# Rails 6 fixes this behavior by moving out the `include
# CompiledTemplates` into ActionView::Base so that including `ActionView::Context`
# doesn't quietly affect other modules in this way.
if Rails::VERSION::STRING.start_with?('6')
raise 'This module is no longer needed in Rails 6. Use ActionView::Context instead.'
end
module Gitlab
module ActionViewOutput
module Context
attr_accessor :output_buffer, :view_flow
end
end
end
......@@ -7,6 +7,7 @@ module Gitlab
ERROR_MESSAGE = 'LFS objects are missing. Ensure LFS is properly set up or try a manual "git lfs push --all".'.freeze
def validate!
return unless Feature.enabled?(:lfs_check, default_enabled: true)
return unless project.lfs_enabled?
return if skip_lfs_integrity_check
......
......@@ -27,13 +27,9 @@ module Gitlab
# don't expose `file` attribute at all (stems from what the runner
# expects).
#
# If the `variable_masking` feature is enabled we expose the `masked`
# attribute, otherwise it's not exposed.
#
def to_runner_variable
@variable.reject do |hash_key, hash_value|
(hash_key == :file && hash_value == false) ||
(hash_key == :masked && !Feature.enabled?(:variable_masking))
hash_key == :file && hash_value == false
end
end
......
......@@ -30,10 +30,7 @@ module Gitlab
def handle_response_error(response)
unless response.success?
error_code = response.dig('Error', 'Code') || response.code
error_message = response.dig('Error', 'Message') || response.message
raise StrategyError.new("Error uploading the project. Code #{error_code}: #{error_message}")
raise StrategyError.new("Error uploading the project. Code #{response.code}: #{response.message}")
end
end
......
# frozen_string_literal: true
require_relative '../spec_helpers'
module RuboCop
module Cop
# Cop that makes sure workers include `::Gitlab::ActionViewOutput::Context`, not `ActionView::Context`.
class IncludeActionViewContext < RuboCop::Cop::Cop
include SpecHelpers
MSG = 'Include `::Gitlab::ActionViewOutput::Context`, not `ActionView::Context`, for Rails 5.'.freeze
def_node_matcher :includes_action_view_context?, <<~PATTERN
(send nil? :include (const (const nil? :ActionView) :Context))
PATTERN
def on_send(node)
return if in_spec?(node)
return unless includes_action_view_context?(node)
add_offense(node.arguments.first, location: :expression)
end
def autocorrect(node)
lambda do |corrector|
corrector.replace(node.source_range, '::Gitlab::ActionViewOutput::Context')
end
end
end
end
end
......@@ -4,6 +4,7 @@ require_relative 'cop/gitlab/predicate_memoization'
require_relative 'cop/gitlab/httparty'
require_relative 'cop/gitlab/finder_with_find_by'
require_relative 'cop/gitlab/union'
require_relative 'cop/include_action_view_context'
require_relative 'cop/include_sidekiq_worker'
require_relative 'cop/safe_params'
require_relative 'cop/avoid_return_from_blocks'
......
......@@ -189,6 +189,15 @@ describe Projects::Settings::CiCdController do
expect(project.build_timeout).to eq(5400)
end
end
context 'when build_timeout_human_readable is invalid' do
let(:params) { { build_timeout_human_readable: '5m' } }
it 'set specified timeout' do
expect(subject).to set_flash[:alert]
expect(response).to redirect_to(namespace_project_settings_ci_cd_path)
end
end
end
end
end
......@@ -9,6 +9,30 @@ describe SearchController do
sign_in(user)
end
context 'uses the right partials depending on scope' do
using RSpec::Parameterized::TableSyntax
render_views
set(:project) { create(:project, :public, :repository, :wiki_repo) }
subject { get(:show, params: { project_id: project.id, scope: scope, search: 'merge' }) }
where(:partial, :scope) do
'_blob' | :blobs
'_wiki_blob' | :wiki_blobs
'_commit' | :commits
end
with_them do
it do
project_wiki = create(:project_wiki, project: project, user: user)
create(:wiki_page, wiki: project_wiki, attrs: { title: 'merge', content: 'merge' })
expect(subject).to render_template("search/results/#{partial}")
end
end
end
it 'finds issue comments' do
project = create(:project, :public)
note = create(:note_on_issue, project: project)
......
......@@ -17,6 +17,26 @@ describe 'Dashboard Todos' do
end
end
context 'when the todo references a merge request' do
let(:referenced_mr) { create(:merge_request, source_project: project) }
let(:note) { create(:note, project: project, note: "Check out #{referenced_mr.to_reference}") }
let!(:todo) { create(:todo, :mentioned, user: user, project: project, author: author, note: note) }
before do
sign_in(user)
visit dashboard_todos_path
end
it 'renders the mr link with the extra attributes' do
link = page.find_link(referenced_mr.to_reference)
expect(link).not_to be_nil
expect(link['data-iid']).to eq(referenced_mr.iid.to_s)
expect(link['data-project-path']).to eq(referenced_mr.project.full_path)
expect(link['data-mr-title']).to eq(referenced_mr.title)
end
end
context 'User has a todo', :js do
before do
create(:todo, :mentioned, user: user, project: project, target: issue, author: author)
......
......@@ -7,18 +7,28 @@ createDefaultClient.default = jest.fn();
describe('initMRPopovers', () => {
let mr1;
let mr2;
let mr3;
beforeEach(() => {
setHTMLFixture(`
<div id="one" class="gfm-merge_request">MR1</div>
<div id="two" class="gfm-merge_request">MR2</div>
<div id="one" class="gfm-merge_request" data-mr-title="title" data-iid="1" data-project-path="group/project">
MR1
</div>
<div id="two" class="gfm-merge_request" data-mr-title="title" data-iid="1" data-project-path="group/project">
MR2
</div>
<div id="three" class="gfm-merge_request">
MR3
</div>
`);
mr1 = document.querySelector('#one');
mr2 = document.querySelector('#two');
mr3 = document.querySelector('#three');
mr1.addEventListener = jest.fn();
mr2.addEventListener = jest.fn();
mr3.addEventListener = jest.fn();
});
it('does not add the same event listener twice', () => {
......@@ -27,4 +37,10 @@ describe('initMRPopovers', () => {
expect(mr1.addEventListener).toHaveBeenCalledTimes(1);
expect(mr2.addEventListener).toHaveBeenCalledTimes(1);
});
it('does not add listener if it does not have the necessary data attributes', () => {
initMRPopovers([mr1, mr2, mr3]);
expect(mr3.addEventListener).not.toHaveBeenCalled();
});
});
......@@ -175,7 +175,7 @@ describe('Dashboard', () => {
setTimeout(() => {
const dropdownItems = component.$el.querySelectorAll(
'.js-environments-dropdown .dropdown-item[active="true"]',
'.js-environments-dropdown .dropdown-item.is-active',
);
expect(dropdownItems.length).toEqual(1);
......
<
......@@ -11,6 +11,7 @@ describe('MrWidgetAlertMessage', () => {
wrapper = shallowMount(localVue.extend(MrWidgetAlertMessage), {