Commit 6372fe21 authored by GitLab Bot's avatar GitLab Bot

Merge remote-tracking branch 'upstream/master' into ce-to-ee-2018-11-05

parents bdf1807e 7d4b717c
...@@ -109,6 +109,8 @@ module IconsHelper ...@@ -109,6 +109,8 @@ module IconsHelper
def file_type_icon_class(type, mode, name) def file_type_icon_class(type, mode, name)
if type == 'folder' if type == 'folder'
icon_class = 'folder' icon_class = 'folder'
elsif type == 'archive'
icon_class = 'archive'
elsif mode == '120000' elsif mode == '120000'
icon_class = 'share' icon_class = 'share'
else else
......
...@@ -31,11 +31,21 @@ module TreeHelper ...@@ -31,11 +31,21 @@ module TreeHelper
# mode - File unix mode # mode - File unix mode
# name - File name # name - File name
def tree_icon(type, mode, name) def tree_icon(type, mode, name)
icon("#{file_type_icon_class(type, mode, name)} fw") icon([file_type_icon_class(type, mode, name), 'fw'])
end end
def tree_hex_class(content) # Using Rails `*_path` methods can be slow, especially when generating
"file_#{hexdigest(content.name)}" # many paths, as with a repository tree that has thousands of items.
def fast_project_blob_path(project, blob_path)
Addressable::URI.escape(
File.join(relative_url_root, project.path_with_namespace, 'blob', blob_path)
)
end
def fast_project_tree_path(project, tree_path)
Addressable::URI.escape(
File.join(relative_url_root, project.path_with_namespace, 'tree', tree_path)
)
end end
# Simple shortcut to File.join # Simple shortcut to File.join
...@@ -142,4 +152,8 @@ module TreeHelper ...@@ -142,4 +152,8 @@ module TreeHelper
def selected_branch def selected_branch
@branch_name || tree_edit_branch @branch_name || tree_edit_branch
end end
def relative_url_root
Gitlab.config.gitlab.relative_url_root.presence || '/'
end
end end
- is_lfs_blob = @lfs_blob_ids.include?(blob_item.id)
%tr{ class: "tree-item #{tree_hex_class(blob_item)}" }
%td.tree-item-file-name
= tree_icon(type, blob_item.mode, blob_item.name)
- file_name = blob_item.name
= link_to project_blob_path(@project, tree_join(@id || @commit.id, blob_item.name)), class: 'str-truncated', title: file_name do
%span= file_name
- if is_lfs_blob
%span.badge.label-lfs.prepend-left-5 LFS
%td.d-none.d-sm-table-cell.tree-commit
%td.tree-time-ago.cgray.text-right
= render 'projects/tree/spinner'
%span.log_loading.hide
%i.fa.fa-spinner.fa-spin
Loading commit data...
%tr.tree-item
%td.tree-item-file-name
%i.fa.fa-archive.fa-fw
= submodule_link(submodule_item, @ref)
%td
%td.d-none.d-sm-table-cell
%tr{ class: "tree-item #{tree_hex_class(tree_item)}" }
%td.tree-item-file-name
= tree_icon(type, tree_item.mode, tree_item.name)
- path = flatten_tree(@path, tree_item)
= link_to project_tree_path(@project, tree_join(@id || @commit.id, path)), class: 'str-truncated', title: path do
%span= path
%td.d-none.d-sm-table-cell.tree-commit
%td.tree-time-ago.text-right
= render 'projects/tree/spinner'
- if tree_row.type == :tree - tree_row_name = tree_row.name
= render partial: 'projects/tree/tree_item', object: tree_row, as: 'tree_item', locals: { type: 'folder' } - tree_row_type = tree_row.type
- elsif tree_row.type == :blob
= render partial: 'projects/tree/blob_item', object: tree_row, as: 'blob_item', locals: { type: 'file' } %tr{ class: "tree-item file_#{hexdigest(tree_row_name)}" }
- elsif tree_row.type == :commit %td.tree-item-file-name
= render partial: 'projects/tree/submodule_item', object: tree_row, as: 'submodule_item' - if tree_row_type == :tree
= tree_icon('folder', tree_row.mode, tree_row.name)
- path = flatten_tree(@path, tree_row)
%a.str-truncated{ href: fast_project_tree_path(@project, tree_join(@id || @commit.id, path)), title: path }
%span= path
- elsif tree_row_type == :blob
= tree_icon('file', tree_row.mode, tree_row_name)
%a.str-truncated{ href: fast_project_blob_path(@project, tree_join(@id || @commit.id, tree_row_name)), title: tree_row_name }
%span= tree_row_name
- if @lfs_blob_ids.include?(tree_row.id)
%span.badge.label-lfs.prepend-left-5 LFS
- elsif tree_row_type == :commit
= tree_icon('archive', tree_row.mode, tree_row.name)
= submodule_link(tree_row, @ref)
%td.d-none.d-sm-table-cell.tree-commit
%td.tree-time-ago.text-right
%span.log_loading.hide
%i.fa.fa-spinner.fa-spin
Loading commit data...
---
title: Bump KUBERNETES_VERSION for Auto DevOps to latest 1.10 series
merge_request: 22757
author:
type: other
---
title: Improve performance of tree rendering in repositories with lots of items
merge_request:
author:
type: performance
...@@ -94,6 +94,7 @@ The following table depicts the various user permission levels in a project. ...@@ -94,6 +94,7 @@ The following table depicts the various user permission levels in a project.
| Manage GitLab Pages | | | | ✓ | ✓ | | Manage GitLab Pages | | | | ✓ | ✓ |
| Manage GitLab Pages domains and certificates | | | | ✓ | ✓ | | Manage GitLab Pages domains and certificates | | | | ✓ | ✓ |
| Remove GitLab Pages | | | | | ✓ | | Remove GitLab Pages | | | | | ✓ |
| View GitLab Pages protected by [access control](../administration/pages/index.md#access-control) | ✓ | ✓ | ✓ | ✓ | ✓ |
| Manage clusters | | | | ✓ | ✓ | | Manage clusters | | | | ✓ | ✓ |
| Manage license policy **[ULTIMATE]** | | | | ✓ | ✓ | | Manage license policy **[ULTIMATE]** | | | | ✓ | ✓ |
| Edit comments (posted by any user) | | | | ✓ | ✓ | | Edit comments (posted by any user) | | | | ✓ | ✓ |
...@@ -205,7 +206,7 @@ They will, like usual users, receive a role in the project or group with all ...@@ -205,7 +206,7 @@ They will, like usual users, receive a role in the project or group with all
the abilities that are mentioned in the table above. They cannot however create the abilities that are mentioned in the table above. They cannot however create
groups or projects, and they have the same access as logged out users in all groups or projects, and they have the same access as logged out users in all
other cases. other cases.
An administrator can flag a user as external [through the API](../api/users.md) An administrator can flag a user as external [through the API](../api/users.md)
or by checking the checkbox on the admin panel. As an administrator, navigate or by checking the checkbox on the admin panel. As an administrator, navigate
to **Admin > Users** to create a new user or edit an existing one. There, you to **Admin > Users** to create a new user or edit an existing one. There, you
...@@ -216,7 +217,7 @@ by an administrator under **Admin > Application Settings**. ...@@ -216,7 +217,7 @@ by an administrator under **Admin > Application Settings**.
### Default internal users ### Default internal users
The "Internal users" field allows specifying an e-mail address regex pattern to identify default internal users. The "Internal users" field allows specifying an e-mail address regex pattern to identify default internal users.
New users whose email address matches the regex pattern will be set to internal by default rather than an external collaborator. New users whose email address matches the regex pattern will be set to internal by default rather than an external collaborator.
......
...@@ -49,7 +49,7 @@ variables: ...@@ -49,7 +49,7 @@ variables:
POSTGRES_ENABLED: "true" POSTGRES_ENABLED: "true"
POSTGRES_DB: $CI_ENVIRONMENT_SLUG POSTGRES_DB: $CI_ENVIRONMENT_SLUG
KUBERNETES_VERSION: 1.8.6 KUBERNETES_VERSION: 1.10.9
HELM_VERSION: 2.11.0 HELM_VERSION: 2.11.0
DOCKER_DRIVER: overlay2 DOCKER_DRIVER: overlay2
......
...@@ -52,14 +52,18 @@ module QA ...@@ -52,14 +52,18 @@ module QA
end end
def api_get def api_get
url = Runtime::API::Request.new(api_client, api_get_path).url process_api_response(parse_body(api_get_from(api_get_path)))
end
def api_get_from(get_path)
url = Runtime::API::Request.new(api_client, get_path).url
response = get(url) response = get(url)
unless response.code == HTTP_STATUS_OK unless response.code == HTTP_STATUS_OK
raise ResourceNotFoundError, "Resource at #{url} could not be found (#{response.code}): `#{response}`." raise ResourceNotFoundError, "Resource at #{url} could not be found (#{response.code}): `#{response}`."
end end
process_api_response(parse_body(response)) response
end end
def api_post def api_post
......
...@@ -15,44 +15,17 @@ module QA ...@@ -15,44 +15,17 @@ module QA
end end
end end
def visit_project_with_retry
# The user intermittently fails to stay signed in after visiting the
# project page. The new user is registered and then signs in and a
# screenshot shows that signing in was successful. Then the project
# page is visited but a screenshot shows the user is no longer signed
# in. It's difficult to reproduce locally but GDK logs don't seem to
# show anything unexpected. This method attempts to work around the
# problem and capture data to help troubleshoot.
Capybara::Screenshot.screenshot_and_save_page
start = Time.now
while Time.now - start < 20
push.project.visit!
puts "Visited project page"
Capybara::Screenshot.screenshot_and_save_page
return if Page::Main::Menu.act { has_personal_area?(wait: 0) }
puts "Not signed in. Attempting to sign in again."
Capybara::Screenshot.screenshot_and_save_page
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.perform do |login|
login.sign_in_using_credentials(user)
end
end
raise "Failed to load project page and stay logged in"
end
def fabricate! def fabricate!
populate(:push, :user) populate(:push, :user)
visit_project_with_retry # Sign out as admin and sign is as the fork user
Page::Main::Menu.perform(&:sign_out)
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.perform do |login|
login.sign_in_using_credentials(user)
end
push.project.visit!
Page::Project::Show.perform(&:fork_project) Page::Project::Show.perform(&:fork_project)
......
...@@ -50,6 +50,42 @@ module QA ...@@ -50,6 +50,42 @@ module QA
end end
end end
end end
def fabricate_via_api!
resource_web_url(api_get)
rescue ResourceNotFoundError
super
end
def api_get_path
"/users/#{fetch_id(username)}"
end
def api_post_path
'/users'
end
def api_post_body
{
email: email,
password: password,
username: username,
name: name,
skip_confirmation: true
}
end
private
def fetch_id(username)
users = parse_body(api_get_from("/users?username=#{username}"))
unless users.size == 1 && users.first[:username] == username
raise ResourceNotFoundError, "Expected one user with username #{username} but found: `#{users}`."
end
users.first[:id]
end
end end
end end
end end
......
...@@ -5,7 +5,7 @@ module QA ...@@ -5,7 +5,7 @@ module QA
it 'user registers and logs in' do it 'user registers and logs in' do
Runtime::Browser.visit(:gitlab, Page::Main::Login) Runtime::Browser.visit(:gitlab, Page::Main::Login)
Factory::Resource::User.fabricate! Factory::Resource::User.fabricate_via_browser_ui!
# TODO, since `Signed in successfully` message was removed # TODO, since `Signed in successfully` message was removed
# this is the only way to tell if user is signed in correctly. # this is the only way to tell if user is signed in correctly.
......
...@@ -5,18 +5,16 @@ module QA ...@@ -5,18 +5,16 @@ module QA
describe 'Add project member' do describe 'Add project member' do
it 'user adds project member' do it 'user adds project member' do
Runtime::Browser.visit(:gitlab, Page::Main::Login) Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.perform(&:sign_in_using_credentials)
user = Factory::Resource::User.fabricate! user = Factory::Resource::User.fabricate!
Page::Main::Menu.perform { |main| main.sign_out }
Page::Main::Login.act { sign_in_using_credentials }
project = Factory::Resource::Project.fabricate! do |resource| project = Factory::Resource::Project.fabricate! do |resource|
resource.name = 'add-member-project' resource.name = 'add-member-project'
end end
project.visit! project.visit!
Page::Project::Menu.act { click_members_settings } Page::Project::Menu.perform(&:click_members_settings)
Page::Project::Settings::Members.perform do |page| Page::Project::Settings::Members.perform do |page|
page.add_member(user.username) page.add_member(user.username)
end end
......
# frozen_string_literal: true
describe QA::Factory::Resource::User do
describe "#fabricate_via_api!" do
Response = Struct.new(:code, :body)
it 'fetches an existing user' do
existing_users = [
{
id: '0',
name: 'name',
username: 'name',
web_url: ''
}
]
users_response = Response.new('200', JSON.dump(existing_users))
single_user_response = Response.new('200', JSON.dump(existing_users.first))
expect(subject).to receive(:api_get_from).with("/users?username=name").and_return(users_response)
expect(subject).to receive(:api_get_from).with("/users/0").and_return(single_user_response)
subject.username = 'name'
subject.fabricate_via_api!
expect(subject.api_response).to eq(existing_users.first)
end
it 'tries to create a user if it does not exist' do
expect(subject).to receive(:api_get_from).with("/users?username=foo").and_return(Response.new('200', '[]'))
expect(subject).to receive(:api_post).and_return({ web_url: '' })
subject.username = 'foo'
subject.fabricate_via_api!
end
end
end
...@@ -3,7 +3,7 @@ require 'spec_helper' ...@@ -3,7 +3,7 @@ require 'spec_helper'
describe TreeHelper do describe TreeHelper do
let(:project) { create(:project, :repository) } let(:project) { create(:project, :repository) }
let(:repository) { project.repository } let(:repository) { project.repository }
let(:sha) { 'ce369011c189f62c815f5971d096b26759bab0d1' } let(:sha) { 'c1c67abbaf91f624347bb3ae96eabe3a1b742478' }
describe '.render_tree' do describe '.render_tree' do
before do before do
...@@ -32,6 +32,49 @@ describe TreeHelper do ...@@ -32,6 +32,49 @@ describe TreeHelper do
end end
end end
describe '.fast_project_blob_path' do
it 'generates the same path as project_blob_path' do
blob_path = repository.tree(sha, 'with space').entries.first.path
fast_path = fast_project_blob_path(project, blob_path)
std_path = project_blob_path(project, blob_path)
expect(fast_path).to eq(std_path)
end
it 'generates the same path with encoded file names' do
tree = repository.tree(sha, 'encoding')
blob_path = tree.entries.find { |entry| entry.path == 'encoding/テスト.txt' }.path
fast_path = fast_project_blob_path(project, blob_path)
std_path = project_blob_path(project, blob_path)
expect(fast_path).to eq(std_path)
end
it 'respects a configured relative URL' do
allow(Gitlab.config.gitlab).to receive(:relative_url_root).and_return('/gitlab/root')
blob_path = repository.tree(sha, '').entries.first.path
fast_path = fast_project_blob_path(project, blob_path)
expect(fast_path).to start_with('/gitlab/root')
end
end
describe '.fast_project_tree_path' do
let(:tree_path) { repository.tree(sha, 'with space').path }
let(:fast_path) { fast_project_tree_path(project, tree_path) }
let(:std_path) { project_tree_path(project, tree_path) }
it 'generates the same path as project_tree_path' do
expect(fast_path).to eq(std_path)
end
it 'respects a configured relative URL' do
allow(Gitlab.config.gitlab).to receive(:relative_url_root).and_return('/gitlab/root')
expect(fast_path).to start_with('/gitlab/root')
end
end
describe 'flatten_tree' do describe 'flatten_tree' do
let(:tree) { repository.tree(sha, 'files') } let(:tree) { repository.tree(sha, 'files') }
let(:root_path) { 'files' } let(:root_path) { 'files' }
......
require 'spec_helper' require 'spec_helper'
describe 'projects/tree/_blob_item' do describe 'projects/tree/_tree_row' do
let(:project) { create(:project, :repository) } let(:project) { create(:project, :repository) }
let(:repository) { project.repository } let(:repository) { project.repository }
let(:blob_item) { Gitlab::Git::Tree.where(repository, SeedRepo::Commit::ID, 'files/ruby').first } let(:blob_item) { Gitlab::Git::Tree.where(repository, SeedRepo::Commit::ID, 'files/ruby').first }
...@@ -31,10 +31,7 @@ describe 'projects/tree/_blob_item' do ...@@ -31,10 +31,7 @@ describe 'projects/tree/_blob_item' do
end end
end end
def render_partial(blob_item) def render_partial(items)
render partial: 'projects/tree/blob_item', locals: { render partial: 'projects/tree/tree_row', collection: [items].flatten
blob_item: blob_item,
type: 'blob'
}
end end
end end
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