Commit c764980f authored by John Jarvis's avatar John Jarvis

Merge branch '11-9-stable-patch-1' into '11-9-stable'

Prepare 11.9.1 release

See merge request gitlab-org/gitlab-ce!26538
parents a47124c7 7a83fbef
...@@ -806,7 +806,7 @@ ...@@ -806,7 +806,7 @@
.merge-request-tabs-holder { .merge-request-tabs-holder {
top: $header-height; top: $header-height;
z-index: 300; z-index: 250;
background-color: $white-light; background-color: $white-light;
border-bottom: 1px solid $border-color; border-bottom: 1px solid $border-color;
......
...@@ -4,7 +4,7 @@ class BroadcastMessage < ActiveRecord::Base ...@@ -4,7 +4,7 @@ class BroadcastMessage < ActiveRecord::Base
include CacheMarkdownField include CacheMarkdownField
include Sortable include Sortable
cache_markdown_field :message, pipeline: :broadcast_message cache_markdown_field :message, pipeline: :broadcast_message, whitelisted: true
validates :message, presence: true validates :message, presence: true
validates :starts_at, presence: true validates :starts_at, presence: true
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
# cache_markdown_field :foo # cache_markdown_field :foo
# cache_markdown_field :bar # cache_markdown_field :bar
# cache_markdown_field :baz, pipeline: :single_line # cache_markdown_field :baz, pipeline: :single_line
# cache_markdown_field :baz, whitelisted: true
# #
# Corresponding foo_html, bar_html and baz_html fields should exist. # Corresponding foo_html, bar_html and baz_html fields should exist.
module CacheMarkdownField module CacheMarkdownField
...@@ -37,7 +38,15 @@ module CacheMarkdownField ...@@ -37,7 +38,15 @@ module CacheMarkdownField
end end
def html_fields def html_fields
markdown_fields.map {|field| html_field(field) } markdown_fields.map { |field| html_field(field) }
end
def html_fields_whitelisted
markdown_fields.each_with_object([]) do |field, fields|
if @data[field].fetch(:whitelisted, false)
fields << html_field(field)
end
end
end end
end end
...@@ -149,13 +158,18 @@ module CacheMarkdownField ...@@ -149,13 +158,18 @@ module CacheMarkdownField
alias_method :attributes_before_markdown_cache, :attributes alias_method :attributes_before_markdown_cache, :attributes
def attributes def attributes
attrs = attributes_before_markdown_cache attrs = attributes_before_markdown_cache
html_fields = cached_markdown_fields.html_fields
whitelisted = cached_markdown_fields.html_fields_whitelisted
exclude_fields = html_fields - whitelisted
attrs.delete('cached_markdown_version') exclude_fields.each do |field|
cached_markdown_fields.html_fields.each do |field|
attrs.delete(field) attrs.delete(field)
end end
if whitelisted.empty?
attrs.delete('cached_markdown_version')
end
attrs attrs
end end
......
...@@ -1378,6 +1378,7 @@ class Project < ActiveRecord::Base ...@@ -1378,6 +1378,7 @@ class Project < ActiveRecord::Base
repository.raw_repository.write_ref('HEAD', "refs/heads/#{branch}") repository.raw_repository.write_ref('HEAD', "refs/heads/#{branch}")
repository.copy_gitattributes(branch) repository.copy_gitattributes(branch)
repository.after_change_head repository.after_change_head
ProjectCacheWorker.perform_async(self.id, [], [:commit_count])
reload_default_branch reload_default_branch
else else
errors.add(:base, "Could not change HEAD: branch '#{branch}' does not exist") errors.add(:base, "Could not change HEAD: branch '#{branch}' does not exist")
......
...@@ -57,7 +57,7 @@ class DiffFileEntity < DiffFileBaseEntity ...@@ -57,7 +57,7 @@ class DiffFileEntity < DiffFileBaseEntity
diff_file.diff_lines_for_serializer diff_file.diff_lines_for_serializer
end end
expose :is_fully_expanded, if: -> (diff_file, _) { Feature.enabled?(:expand_diff_full_file) && diff_file.text? } do |diff_file| expose :is_fully_expanded, if: -> (diff_file, _) { Feature.enabled?(:expand_diff_full_file, default_enabled: true) && diff_file.text? } do |diff_file|
diff_file.fully_expanded? diff_file.fully_expanded?
end end
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
%p %p
By default, protected branches are designed to: By default, protected branches are designed to:
%ul %ul
%li prevent their creation, if not already created, from everybody except users who are allowed to merge %li prevent their creation, if not already created, from everybody except Maintainers
%li prevent pushes from everybody except Maintainers %li prevent pushes from everybody except Maintainers
%li prevent <strong>anyone</strong> from force pushing to the branch %li prevent <strong>anyone</strong> from force pushing to the branch
%li prevent <strong>anyone</strong> from deleting the branch %li prevent <strong>anyone</strong> from deleting the branch
......
...@@ -27,6 +27,7 @@ class ProjectCacheWorker ...@@ -27,6 +27,7 @@ class ProjectCacheWorker
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
def update_statistics(project, statistics = []) def update_statistics(project, statistics = [])
return if Gitlab::Database.read_only?
return unless try_obtain_lease_for(project.id, :update_statistics) return unless try_obtain_lease_for(project.id, :update_statistics)
Rails.logger.info("Updating statistics for project #{project.id}") Rails.logger.info("Updating statistics for project #{project.id}")
......
---
title: Fix issue that caused the "Show all activity" button to appear on top of the
mini pipeline status dropdown on the merge request page
merge_request: 26274
author:
type: fixed
---
title: Fix duplicated bottom match line on merge request parallel diff view
merge_request: 26402
author:
type: fixed
---
title: Allow users who can push to protected branches to create protected branches
via CLI
merge_request: 26413
author:
type: fixed
---
title: Add missing .gitlab-ci.yml to Android template
merge_request: 26415
author:
type: fixed
---
title: Upgrade to Gitaly v1.27.1
merge_request: 26533
author:
type: changed
---
title: Refresh commit count after repository head changes
merge_request: 26473
author:
type: fixed
---
title: Set proper default-branch for repository on GitHub Import
merge_request: 26476
author:
type: fixed
---
title: 'GitHub importer: Use the project creator to create branches from forks'
merge_request: 26510
author:
type: fixed
...@@ -10,7 +10,7 @@ created protected branches. ...@@ -10,7 +10,7 @@ created protected branches.
By default, a protected branch does four simple things: By default, a protected branch does four simple things:
- it prevents its creation, if not already created, from everybody except users - it prevents its creation, if not already created, from everybody except users
who are allowed to merge with Maintainer permission
- it prevents pushes from everybody except users with Maintainer permission - it prevents pushes from everybody except users with Maintainer permission
- it prevents **anyone** from force pushing to the branch - it prevents **anyone** from force pushing to the branch
- it prevents **anyone** from deleting the branch - it prevents **anyone** from deleting the branch
......
...@@ -59,6 +59,8 @@ module Gitlab ...@@ -59,6 +59,8 @@ module Gitlab
def protected_branch_creation_checks def protected_branch_creation_checks
logger.log_timed(LOG_MESSAGES[:protected_branch_creation_checks]) do logger.log_timed(LOG_MESSAGES[:protected_branch_creation_checks]) do
break if user_access.can_push_to_branch?(branch_name)
unless user_access.can_merge_to_branch?(branch_name) unless user_access.can_merge_to_branch?(branch_name)
raise GitAccess::UnauthorizedError, ERROR_MESSAGES[:create_protected_branch] raise GitAccess::UnauthorizedError, ERROR_MESSAGES[:create_protected_branch]
end end
......
...@@ -158,7 +158,10 @@ module Gitlab ...@@ -158,7 +158,10 @@ module Gitlab
new_blob || old_blob new_blob || old_blob
end end
attr_writer :highlighted_diff_lines def highlighted_diff_lines=(value)
clear_memoization(:diff_lines_for_serializer)
@highlighted_diff_lines = value
end
# Array of Gitlab::Diff::Line objects # Array of Gitlab::Diff::Line objects
def diff_lines def diff_lines
...@@ -314,19 +317,21 @@ module Gitlab ...@@ -314,19 +317,21 @@ module Gitlab
# This adds the bottom match line to the array if needed. It contains # This adds the bottom match line to the array if needed. It contains
# the data to load more context lines. # the data to load more context lines.
def diff_lines_for_serializer def diff_lines_for_serializer
lines = highlighted_diff_lines strong_memoize(:diff_lines_for_serializer) do
lines = highlighted_diff_lines
return if lines.empty? next if lines.empty?
return if blob.nil? next if blob.nil?
last_line = lines.last last_line = lines.last
if last_line.new_pos < total_blob_lines(blob) && !deleted_file? if last_line.new_pos < total_blob_lines(blob) && !deleted_file?
match_line = Gitlab::Diff::Line.new("", 'match', nil, last_line.old_pos, last_line.new_pos) match_line = Gitlab::Diff::Line.new("", 'match', nil, last_line.old_pos, last_line.new_pos)
lines.push(match_line) lines.push(match_line)
end end
lines lines
end
end end
def fully_expanded? def fully_expanded?
......
...@@ -89,7 +89,7 @@ module Gitlab ...@@ -89,7 +89,7 @@ module Gitlab
return if project.repository.branch_exists?(source_branch) return if project.repository.branch_exists?(source_branch)
project.repository.add_branch(project.owner, source_branch, pull_request.source_branch_sha) project.repository.add_branch(project.creator, source_branch, pull_request.source_branch_sha)
rescue Gitlab::Git::CommandError => e rescue Gitlab::Git::CommandError => e
Gitlab::Sentry.track_acceptable_exception(e, Gitlab::Sentry.track_acceptable_exception(e,
extra: { extra: {
......
...@@ -5,6 +5,7 @@ module Gitlab ...@@ -5,6 +5,7 @@ module Gitlab
module Importer module Importer
class RepositoryImporter class RepositoryImporter
include Gitlab::ShellAdapter include Gitlab::ShellAdapter
include Gitlab::Utils::StrongMemoize
attr_reader :project, :client, :wiki_formatter attr_reader :project, :client, :wiki_formatter
...@@ -17,7 +18,7 @@ module Gitlab ...@@ -17,7 +18,7 @@ module Gitlab
# Returns true if we should import the wiki for the project. # Returns true if we should import the wiki for the project.
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def import_wiki? def import_wiki?
client.repository(project.import_source)&.has_wiki && client_repository&.has_wiki &&
!project.wiki_repository_exists? && !project.wiki_repository_exists? &&
Gitlab::GitalyClient::RemoteService.exists?(wiki_url) Gitlab::GitalyClient::RemoteService.exists?(wiki_url)
end end
...@@ -52,6 +53,7 @@ module Gitlab ...@@ -52,6 +53,7 @@ module Gitlab
refmap = Gitlab::GithubImport.refmap refmap = Gitlab::GithubImport.refmap
project.repository.fetch_as_mirror(project.import_url, refmap: refmap, forced: true, remote_name: 'github') project.repository.fetch_as_mirror(project.import_url, refmap: refmap, forced: true, remote_name: 'github')
project.change_head(default_branch) if default_branch
true true
rescue Gitlab::Git::Repository::NoRepository, Gitlab::Shell::Error => e rescue Gitlab::Git::Repository::NoRepository, Gitlab::Shell::Error => e
fail_import("Failed to import the repository: #{e.message}") fail_import("Failed to import the repository: #{e.message}")
...@@ -82,6 +84,18 @@ module Gitlab ...@@ -82,6 +84,18 @@ module Gitlab
project.import_state.mark_as_failed(message) project.import_state.mark_as_failed(message)
false false
end end
private
def default_branch
client_repository&.default_branch
end
def client_repository
strong_memoize(:client_repository) do
client.repository(project.import_source)
end
end
end end
end end
end end
......
...@@ -108,64 +108,86 @@ describe Gitlab::Checks::BranchCheck do ...@@ -108,64 +108,86 @@ describe Gitlab::Checks::BranchCheck do
end end
context 'protected branch creation feature is enabled' do context 'protected branch creation feature is enabled' do
context 'user is not allowed to create protected branches' do context 'user can push to branch' do
before do before do
allow(user_access) allow(user_access)
.to receive(:can_merge_to_branch?) .to receive(:can_push_to_branch?)
.with('feature') .with('feature')
.and_return(false) .and_return(true)
end end
it 'raises an error' do it 'does not raise an error' do
expect { subject.validate! }.to raise_error(Gitlab::GitAccess::UnauthorizedError, 'You are not allowed to create protected branches on this project.') expect { subject.validate! }.not_to raise_error
end end
end end
context 'user is allowed to create protected branches' do context 'user cannot push to branch' do
before do before do
allow(user_access) allow(user_access)
.to receive(:can_merge_to_branch?) .to receive(:can_push_to_branch?)
.with('feature') .with('feature')
.and_return(true) .and_return(false)
allow(project.repository)
.to receive(:branch_names_contains_sha)
.with(newrev)
.and_return(['branch'])
end end
context "newrev isn't in any protected branches" do context 'user cannot merge to branch' do
before do before do
allow(ProtectedBranch) allow(user_access)
.to receive(:any_protected?) .to receive(:can_merge_to_branch?)
.with(project, ['branch']) .with('feature')
.and_return(false) .and_return(false)
end end
it 'raises an error' do it 'raises an error' do
expect { subject.validate! }.to raise_error(Gitlab::GitAccess::UnauthorizedError, 'You can only use an existing protected branch ref as the basis of a new protected branch.') expect { subject.validate! }.to raise_error(Gitlab::GitAccess::UnauthorizedError, 'You are not allowed to create protected branches on this project.')
end end
end end
context 'newrev is included in a protected branch' do context 'user can merge to branch' do
before do before do
allow(ProtectedBranch) allow(user_access)
.to receive(:any_protected?) .to receive(:can_merge_to_branch?)
.with(project, ['branch']) .with('feature')
.and_return(true) .and_return(true)
allow(project.repository)
.to receive(:branch_names_contains_sha)
.with(newrev)
.and_return(['branch'])
end end
context 'via web interface' do context "newrev isn't in any protected branches" do
let(:protocol) { 'web' } before do
allow(ProtectedBranch)
.to receive(:any_protected?)
.with(project, ['branch'])
.and_return(false)
end
it 'allows branch creation' do it 'raises an error' do
expect { subject.validate! }.not_to raise_error expect { subject.validate! }.to raise_error(Gitlab::GitAccess::UnauthorizedError, 'You can only use an existing protected branch ref as the basis of a new protected branch.')
end end
end end
context 'via SSH' do context 'newrev is included in a protected branch' do
it 'raises an error' do before do
expect { subject.validate! }.to raise_error(Gitlab::GitAccess::UnauthorizedError, 'You can only create protected branches using the web interface and API.') allow(ProtectedBranch)
.to receive(:any_protected?)
.with(project, ['branch'])
.and_return(true)
end
context 'via web interface' do
let(:protocol) { 'web' }
it 'allows branch creation' do
expect { subject.validate! }.not_to raise_error
end
end
context 'via SSH' do
it 'raises an error' do
expect { subject.validate! }.to raise_error(Gitlab::GitAccess::UnauthorizedError, 'You can only create protected branches using the web interface and API.')
end
end end
end end
end end
......
...@@ -72,6 +72,13 @@ describe Gitlab::Diff::File do ...@@ -72,6 +72,13 @@ describe Gitlab::Diff::File do
expect(diff_file.diff_lines_for_serializer.last.type).to eq('match') expect(diff_file.diff_lines_for_serializer.last.type).to eq('match')
end end
context 'when called multiple times' do
it 'only adds bottom match line once' do
expect(diff_file.diff_lines_for_serializer.size).to eq(31)
expect(diff_file.diff_lines_for_serializer.size).to eq(31)
end
end
context 'when deleted' do context 'when deleted' do
let(:commit) { project.commit('d59c60028b053793cecfb4022de34602e1a9218e') } let(:commit) { project.commit('d59c60028b053793cecfb4022de34602e1a9218e') }
let(:diff_file) { commit.diffs.diff_file_with_old_path('files/js/commit.js.coffee') } let(:diff_file) { commit.diffs.diff_file_with_old_path('files/js/commit.js.coffee') }
......
...@@ -273,10 +273,10 @@ describe Gitlab::GithubImport::Importer::PullRequestImporter, :clean_gitlab_redi ...@@ -273,10 +273,10 @@ describe Gitlab::GithubImport::Importer::PullRequestImporter, :clean_gitlab_redi
mr.state = 'opened' mr.state = 'opened'
mr.save mr.save
# Ensure the project owner is creating the branches because the # Ensure the project creator is creating the branches because the
# merge request author may not have access to push to this # merge request author may not have access to push to this
# repository. # repository. The project owner may also be a group.
allow(project.repository).to receive(:add_branch).with(project.owner, anything, anything).and_call_original allow(project.repository).to receive(:add_branch).with(project.creator, anything, anything).and_call_original
importer.insert_git_data(mr, exists) importer.insert_git_data(mr, exists)
......
...@@ -179,6 +179,17 @@ describe Gitlab::GithubImport::Importer::RepositoryImporter do ...@@ -179,6 +179,17 @@ describe Gitlab::GithubImport::Importer::RepositoryImporter do
describe '#import_repository' do describe '#import_repository' do
it 'imports the repository' do it 'imports the repository' do
repo = double(:repo, default_branch: 'develop')
expect(client)
.to receive(:repository)
.with('foo/bar')
.and_return(repo)
expect(project)
.to receive(:change_head)
.with('develop')
expect(project) expect(project)
.to receive(:ensure_repository) .to receive(:ensure_repository)
......
...@@ -146,6 +146,18 @@ describe Gitlab::JsonCache do ...@@ -146,6 +146,18 @@ describe Gitlab::JsonCache do
expect(cache.read(key, BroadcastMessage)).to be_nil expect(cache.read(key, BroadcastMessage)).to be_nil
end end
it 'gracefully handles excluded fields from attributes during serialization' do
allow(backend).to receive(:read)
.with(expanded_key)
.and_return(broadcast_message.attributes.except("message_html").to_json)
result = cache.read(key, BroadcastMessage)
BroadcastMessage.cached_markdown_fields.html_fields.each do |field|
expect(result.public_send(field)).to be_nil
end
end
end end
context 'when the cached value is an array' do context 'when the cached value is an array' do
...@@ -327,7 +339,9 @@ describe Gitlab::JsonCache do ...@@ -327,7 +339,9 @@ describe Gitlab::JsonCache do
.with(expanded_key) .with(expanded_key)
.and_return('{') .and_return('{')
expect(cache.read(key, BroadcastMessage)).to be_nil result = cache.fetch(key, as: BroadcastMessage) { 'block result' }
expect(result).to eq 'block result'
end end
it 'gracefully handles an empty hash' do it 'gracefully handles an empty hash' do
...@@ -335,7 +349,7 @@ describe Gitlab::JsonCache do ...@@ -335,7 +349,7 @@ describe Gitlab::JsonCache do
.with(expanded_key) .with(expanded_key)
.and_return('{}') .and_return('{}')
expect(cache.read(key, BroadcastMessage)).to be_a(BroadcastMessage) expect(cache.fetch(key, as: BroadcastMessage)).to be_a(BroadcastMessage)
end end
it 'gracefully handles unknown attributes' do it 'gracefully handles unknown attributes' do
...@@ -343,17 +357,19 @@ describe Gitlab::JsonCache do ...@@ -343,17 +357,19 @@ describe Gitlab::JsonCache do
.with(expanded_key) .with(expanded_key)
.and_return(broadcast_message.attributes.merge(unknown_attribute: 1).to_json) .and_return(broadcast_message.attributes.merge(unknown_attribute: 1).to_json)
expect(cache.read(key, BroadcastMessage)).to be_nil result = cache.fetch(key, as: BroadcastMessage) { 'block result' }
expect(result).to eq 'block result'
end end
it 'gracefully handles excluded fields from attributes during serialization' do it 'gracefully handles excluded fields from attributes during serialization' do
backend.write(expanded_key, broadcast_message.to_json) allow(backend).to receive(:read)
.with(expanded_key)
.and_return(broadcast_message.attributes.except("message_html").to_json)
result = cache.fetch(key, as: BroadcastMessage) { 'block result' } result = cache.fetch(key, as: BroadcastMessage) { 'block result' }
excluded_fields = BroadcastMessage.cached_markdown_fields.html_fields BroadcastMessage.cached_markdown_fields.html_fields.each do |field|
(excluded_fields + ['cached_markdown_version']).each do |field|
expect(result.public_send(field)).to be_nil expect(result.public_send(field)).to be_nil
end end
end end
......
...@@ -95,6 +95,12 @@ describe BroadcastMessage do ...@@ -95,6 +95,12 @@ describe BroadcastMessage do
end end
end end
describe '#attributes' do
it 'includes message_html field' do
expect(subject.attributes.keys).to include("cached_markdown_version", "message_html")
end
end
describe '#active?' do describe '#active?' do
it 'is truthy when started and not ended' do it 'is truthy when started and not ended' do
message = build(:broadcast_message) message = build(:broadcast_message)
......
...@@ -23,6 +23,7 @@ describe CacheMarkdownField do ...@@ -23,6 +23,7 @@ describe CacheMarkdownField do
include CacheMarkdownField include CacheMarkdownField
cache_markdown_field :foo cache_markdown_field :foo
cache_markdown_field :baz, pipeline: :single_line cache_markdown_field :baz, pipeline: :single_line
cache_markdown_field :zoo, whitelisted: true
def self.add_attr(name) def self.add_attr(name)
self.attribute_names += [name] self.attribute_names += [name]
...@@ -35,7 +36,7 @@ describe CacheMarkdownField do ...@@ -35,7 +36,7 @@ describe CacheMarkdownField do
add_attr :cached_markdown_version add_attr :cached_markdown_version
[:foo, :foo_html, :bar, :baz, :baz_html].each do |name| [:foo, :foo_html, :bar, :baz, :baz_html, :zoo, :zoo_html].each do |name|
add_attr(name)