Commit 40275f41 authored by Martin Wortschack's avatar Martin Wortschack

Merge branch 'cat-fix-sprite-failed-builds' into 'master'

Fix retried builds icon sprite to use css_class

See merge request gitlab-org/gitlab!46955
parents c208348a 8712408b
# frozen_string_literal: true # frozen_string_literal: true
module PageLayoutHelper module PageLayoutHelper
include Gitlab::Utils::StrongMemoize
def page_title(*titles) def page_title(*titles)
@page_title ||= [] @page_title ||= []
...@@ -44,7 +46,7 @@ module PageLayoutHelper ...@@ -44,7 +46,7 @@ module PageLayoutHelper
if link if link
@page_canonical_link = link @page_canonical_link = link
else else
@page_canonical_link @page_canonical_link ||= generic_canonical_url
end end
end end
...@@ -147,4 +149,31 @@ module PageLayoutHelper ...@@ -147,4 +149,31 @@ module PageLayoutHelper
css_class.join(' ') css_class.join(' ')
end end
private
def generic_canonical_url
strong_memoize(:generic_canonical_url) do
next unless request.get? || request.head?
next unless generate_generic_canonical_url?
next unless Feature.enabled?(:generic_canonical, current_user)
# Request#url builds the url without the trailing slash
request.url
end
end
def generate_generic_canonical_url?
# For the main domain it doesn't matter whether there is
# a trailing slash or not, they're not considered different
# pages
return false if request.path == '/'
# We only need to generate the canonical url when the request has a trailing
# slash. In the request object, only the `original_fullpath` and
# `original_url` keep the slash if it's present. Both `path` and
# `fullpath` would return the path without the slash.
# Therefore, we need to process `original_fullpath`
request.original_fullpath.sub(request.path, '')[0] == '/'
end
end end
...@@ -90,7 +90,7 @@ module Storage ...@@ -90,7 +90,7 @@ module Storage
end end
def old_repository_storages def old_repository_storages
@old_repository_storage_paths ||= repository_storages @old_repository_storage_paths ||= repository_storages(legacy_only: true)
end end
def repository_storages(legacy_only: false) def repository_storages(legacy_only: false)
......
---
title: Generate canonical url and remove trailing slash
merge_request: 46435
author:
type: changed
---
title: Add Batch Support for Importing Pull Requests from Bitbucket
merge_request: 46696
author: Simon Schrottner
type: performance
---
title: Fix group destroy not working with Gitaly Cluster
merge_request: 46934
author:
type: fixed
---
title: Skip disabled features when importing a project from Gitea
merge_request: 46800
author: John Kristensen (@jerrykan)
type: fixed
---
name: generic_canonical
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/46435
rollout_issue_url:
type: development
group: group::editor
default_enabled: false
...@@ -8,9 +8,9 @@ module BitbucketServer ...@@ -8,9 +8,9 @@ module BitbucketServer
@connection = Connection.new(options) @connection = Connection.new(options)
end end
def pull_requests(project_key, repo) def pull_requests(project_key, repo, page_offset: 0, limit: nil)
path = "/projects/#{project_key}/repos/#{repo}/pull-requests?state=ALL" path = "/projects/#{project_key}/repos/#{repo}/pull-requests?state=ALL"
get_collection(path, :pull_request) get_collection(path, :pull_request, page_offset: page_offset, limit: limit)
end end
def activities(project_key, repo, pull_request_id) def activities(project_key, repo, pull_request_id)
......
...@@ -177,16 +177,24 @@ module Gitlab ...@@ -177,16 +177,24 @@ module Gitlab
# on the remote server. Then we have to issue a `git fetch` to download these # on the remote server. Then we have to issue a `git fetch` to download these
# branches. # branches.
def import_pull_requests def import_pull_requests
log_info(stage: 'import_pull_requests', message: 'starting') page = 0
pull_requests = client.pull_requests(project_key, repository_slug).to_a
log_info(stage: 'import_pull_requests', message: "starting")
loop do
log_debug(stage: 'import_pull_requests', message: "importing page #{page} and batch-size #{BATCH_SIZE} from #{page * BATCH_SIZE} to #{(page + 1) * BATCH_SIZE}")
pull_requests = client.pull_requests(project_key, repository_slug, page_offset: page, limit: BATCH_SIZE).to_a
break if pull_requests.empty?
# Creating branches on the server and fetching the newly-created branches # Creating branches on the server and fetching the newly-created branches
# may take a number of network round-trips. Do this in batches so that we can # may take a number of network round-trips. This used to be done in batches to
# avoid doing a git fetch for every new branch. # avoid doing a git fetch for every new branch, as the whole process is now
pull_requests.each_slice(BATCH_SIZE) do |batch| # batched, we do not need to separately do this in batches.
restore_branches(batch) if recover_missing_commits restore_branches(pull_requests) if recover_missing_commits
batch.each do |pull_request| pull_requests.each do |pull_request|
if already_imported?(pull_request) if already_imported?(pull_request)
log_info(stage: 'import_pull_requests', message: 'already imported', iid: pull_request.iid) log_info(stage: 'import_pull_requests', message: 'already imported', iid: pull_request.iid)
else else
...@@ -201,6 +209,9 @@ module Gitlab ...@@ -201,6 +209,9 @@ module Gitlab
backtrace = Gitlab::BacktraceCleaner.clean_backtrace(e.backtrace) backtrace = Gitlab::BacktraceCleaner.clean_backtrace(e.backtrace)
errors << { type: :pull_request, iid: pull_request.iid, errors: e.message, backtrace: backtrace.join("\n"), raw_response: pull_request.raw } errors << { type: :pull_request, iid: pull_request.iid, errors: e.message, backtrace: backtrace.join("\n"), raw_response: pull_request.raw }
end end
log_debug(stage: 'import_pull_requests', message: "finished page #{page} and batch-size #{BATCH_SIZE}")
page += 1
end end
end end
...@@ -416,6 +427,10 @@ module Gitlab ...@@ -416,6 +427,10 @@ module Gitlab
} }
end end
def log_debug(details)
logger.debug(log_base_data.merge(details))
end
def log_info(details) def log_info(details)
logger.info(log_base_data.merge(details)) logger.info(log_base_data.merge(details))
end end
......
...@@ -303,6 +303,8 @@ module Gitlab ...@@ -303,6 +303,8 @@ module Gitlab
end end
imported!(resource_type) imported!(resource_type)
rescue ::Octokit::NotFound => e
errors << { type: resource_type, errors: e.message }
end end
def imported?(resource_type) def imported?(resource_type)
......
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'Canonical link' do
include Spec::Support::Helpers::Features::CanonicalLinkHelpers
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :public, namespace: user.namespace) }
let_it_be(:issue) { create(:issue, project: project) }
let_it_be(:issue_request) { issue_url(issue) }
let_it_be(:project_request) { project_url(project) }
before do
sign_in(user)
end
shared_examples 'shows canonical link' do
specify do
visit request_url
expect(page).to have_canonical_link(expected_url)
end
end
shared_examples 'does not show canonical link' do
specify do
visit request_url
expect(page).not_to have_any_canonical_links
end
end
it_behaves_like 'does not show canonical link' do
let(:request_url) { issue_request }
end
it_behaves_like 'shows canonical link' do
let(:request_url) { issue_request + '/' }
let(:expected_url) { issue_request }
end
it_behaves_like 'shows canonical link' do
let(:request_url) { project_issues_url(project) + "/?state=opened" }
let(:expected_url) { project_issues_url(project, state: 'opened') }
end
it_behaves_like 'does not show canonical link' do
let(:request_url) { project_request }
end
it_behaves_like 'shows canonical link' do
let(:request_url) { project_request + '/' }
let(:expected_url) { project_request }
end
it_behaves_like 'shows canonical link' do
let(:query_params) { '?foo=bar' }
let(:request_url) { project_request + "/#{query_params}" }
let(:expected_url) { project_request + query_params }
end
# Hard-coded canonical links
it_behaves_like 'shows canonical link' do
let(:request_url) { explore_root_path }
let(:expected_url) { explore_projects_url }
end
context 'when feature flag generic_canonical is disabled' do
before do
stub_feature_flags(generic_canonical: false)
end
it_behaves_like 'does not show canonical link' do
let(:request_url) { issue_request + '/' }
end
it_behaves_like 'does not show canonical link' do
let(:request_url) { project_request + '/' }
end
end
end
...@@ -30,12 +30,4 @@ RSpec.describe 'Root explore' do ...@@ -30,12 +30,4 @@ RSpec.describe 'Root explore' do
include_examples 'shows public projects' include_examples 'shows public projects'
end end
it 'includes canonical link to explore projects url' do
visit explore_root_path
canonial_link = page.find("head link[rel='canonical']", visible: false)
expect(canonial_link[:href]).to eq explore_projects_url
end
end end
...@@ -137,4 +137,75 @@ RSpec.describe PageLayoutHelper do ...@@ -137,4 +137,75 @@ RSpec.describe PageLayoutHelper do
end end
end end
end end
describe '#page_canonical_link' do
let(:user) { build(:user) }
subject { helper.page_canonical_link(link) }
before do
allow(helper).to receive(:current_user).and_return(user)
end
context 'when link is passed' do
let(:link) { 'https://gitlab.com' }
it 'stores and returns the link value' do
expect(subject).to eq link
expect(helper.page_canonical_link(nil)).to eq link
end
end
context 'when no link is provided' do
let(:link) { nil }
let(:request) { ActionDispatch::Request.new(env) }
let(:env) do
{
'ORIGINAL_FULLPATH' => '/foo/',
'PATH_INFO' => '/foo',
'HTTP_HOST' => 'test.host',
'REQUEST_METHOD' => method,
'rack.url_scheme' => 'http'
}
end
before do
allow(helper).to receive(:request).and_return(request)
end
shared_examples 'generates the canonical url using the params in the context' do
specify { expect(subject).to eq 'http://test.host/foo' }
end
shared_examples 'does not return a canonical url' do
specify { expect(subject).to be_nil }
end
it_behaves_like 'generates the canonical url using the params in the context' do
let(:method) { 'GET' }
end
it_behaves_like 'generates the canonical url using the params in the context' do
let(:method) { 'HEAD' }
end
it_behaves_like 'does not return a canonical url' do
let(:method) { 'POST' }
end
it_behaves_like 'does not return a canonical url' do
let(:method) { 'PUT' }
end
context 'when feature flag generic_canonical is disabled' do
let(:method) { 'GET' }
before do
stub_feature_flags(generic_canonical: false)
end
it_behaves_like 'does not return a canonical url'
end
end
end
end end
...@@ -19,6 +19,15 @@ RSpec.describe BitbucketServer::Client do ...@@ -19,6 +19,15 @@ RSpec.describe BitbucketServer::Client do
subject.pull_requests(project, repo_slug) subject.pull_requests(project, repo_slug)
end end
it 'requests a collection with offset and limit' do
offset = 10
limit = 100
expect(BitbucketServer::Paginator).to receive(:new).with(anything, path, :pull_request, page_offset: offset, limit: limit)
subject.pull_requests(project, repo_slug, page_offset: offset, limit: limit)
end
end end
describe '#activities' do describe '#activities' do
......
...@@ -112,7 +112,7 @@ RSpec.describe Gitlab::BitbucketServerImport::Importer do ...@@ -112,7 +112,7 @@ RSpec.describe Gitlab::BitbucketServerImport::Importer do
allow(subject).to receive(:delete_temp_branches) allow(subject).to receive(:delete_temp_branches)
allow(subject).to receive(:restore_branches) allow(subject).to receive(:restore_branches)
allow(subject.client).to receive(:pull_requests).and_return([pull_request]) allow(subject.client).to receive(:pull_requests).and_return([pull_request], [])
end end
# As we are using Caching with redis, it is best to clean the cache after each test run, else we need to wait for # As we are using Caching with redis, it is best to clean the cache after each test run, else we need to wait for
...@@ -499,7 +499,7 @@ RSpec.describe Gitlab::BitbucketServerImport::Importer do ...@@ -499,7 +499,7 @@ RSpec.describe Gitlab::BitbucketServerImport::Importer do
before do before do
Gitlab::Cache::Import::Caching.set_add(subject.already_imported_cache_key, pull_request_already_imported.iid) Gitlab::Cache::Import::Caching.set_add(subject.already_imported_cache_key, pull_request_already_imported.iid)
allow(subject.client).to receive(:pull_requests).and_return([pull_request_to_be_imported, pull_request_already_imported]) allow(subject.client).to receive(:pull_requests).and_return([pull_request_to_be_imported, pull_request_already_imported], [])
end end
it 'only imports one Merge Request, as the other on is in the cache' do it 'only imports one Merge Request, as the other on is in the cache' do
...@@ -535,7 +535,7 @@ RSpec.describe Gitlab::BitbucketServerImport::Importer do ...@@ -535,7 +535,7 @@ RSpec.describe Gitlab::BitbucketServerImport::Importer do
updated_at: Time.now, updated_at: Time.now,
merged?: true) merged?: true)
expect(subject.client).to receive(:pull_requests).and_return([pull_request]) expect(subject.client).to receive(:pull_requests).and_return([pull_request], [])
expect(subject.client).to receive(:activities).and_return([]) expect(subject.client).to receive(:activities).and_return([])
expect(subject).to receive(:import_repository).twice expect(subject).to receive(:import_repository).twice
end end
......
...@@ -52,7 +52,7 @@ RSpec.describe Gitlab::LegacyGithubImport::Importer do ...@@ -52,7 +52,7 @@ RSpec.describe Gitlab::LegacyGithubImport::Importer do
allow_any_instance_of(Octokit::Client).to receive(:milestones).and_return([milestone, milestone]) allow_any_instance_of(Octokit::Client).to receive(:milestones).and_return([milestone, milestone])
allow_any_instance_of(Octokit::Client).to receive(:issues).and_return([issue1, issue2]) allow_any_instance_of(Octokit::Client).to receive(:issues).and_return([issue1, issue2])
allow_any_instance_of(Octokit::Client).to receive(:pull_requests).and_return([pull_request, pull_request]) allow_any_instance_of(Octokit::Client).to receive(:pull_requests).and_return([pull_request, pull_request])
allow_any_instance_of(Octokit::Client).to receive(:issues_comments).and_return([]) allow_any_instance_of(Octokit::Client).to receive(:issues_comments).and_raise(Octokit::NotFound)
allow_any_instance_of(Octokit::Client).to receive(:pull_requests_comments).and_return([]) allow_any_instance_of(Octokit::Client).to receive(:pull_requests_comments).and_return([])
allow_any_instance_of(Octokit::Client).to receive(:last_response).and_return(double(rels: { next: nil })) allow_any_instance_of(Octokit::Client).to receive(:last_response).and_return(double(rels: { next: nil }))
allow_any_instance_of(Octokit::Client).to receive(:releases).and_return([release1, release2]) allow_any_instance_of(Octokit::Client).to receive(:releases).and_return([release1, release2])
...@@ -169,6 +169,7 @@ RSpec.describe Gitlab::LegacyGithubImport::Importer do ...@@ -169,6 +169,7 @@ RSpec.describe Gitlab::LegacyGithubImport::Importer do
errors: [ errors: [
{ type: :label, url: "#{api_root}/repos/octocat/Hello-World/labels/bug", errors: "Validation failed: Title can't be blank, Title is invalid" }, { type: :label, url: "#{api_root}/repos/octocat/Hello-World/labels/bug", errors: "Validation failed: Title can't be blank, Title is invalid" },
{ type: :issue, url: "#{api_root}/repos/octocat/Hello-World/issues/1348", errors: "Validation failed: Title can't be blank" }, { type: :issue, url: "#{api_root}/repos/octocat/Hello-World/issues/1348", errors: "Validation failed: Title can't be blank" },
{ type: :issues_comments, errors: 'Octokit::NotFound' },
{ type: :wiki, errors: "Gitlab::Git::CommandError" } { type: :wiki, errors: "Gitlab::Git::CommandError" }
] ]
} }
......
...@@ -687,7 +687,7 @@ RSpec.describe Namespace do ...@@ -687,7 +687,7 @@ RSpec.describe Namespace do
let!(:project) { create(:project_empty_repo, namespace: namespace) } let!(:project) { create(:project_empty_repo, namespace: namespace) }
it 'has no repositories base directories to remove' do it 'has no repositories base directories to remove' do
allow(GitlabShellWorker).to receive(:perform_in) expect(GitlabShellWorker).not_to receive(:perform_in)
expect(File.exist?(path_in_dir)).to be(false) expect(File.exist?(path_in_dir)).to be(false)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment