Merge remote-tracking branch 'upstream/master' into new-issue-by-email

* upstream/master: (38 commits)
  Remove useless new route
  Update gitlab-shell version to 3.2.1 in the 8.9->8.10 update guide
  Fix typo in Elixir CI template
  Add a spec for access_for_user_ids
  Fix typo in comment
  Rubocop offenses
  Optimize the invited group link access level check
  Incorporate review comments
  Optimize maximum user access level lookup in loading of notes
  Fix missing schema update for 20160722221922
  Whitelist 'Simplified BSD' license
  Fix a bug where forking a project from a repository storage to another would fail
  Remove inline scripts from import pages.
  Make branches sortable without push permission (!5462)
  Profile requests when a header is passed
  Upgrade database_cleaner from 1.4.1 to 1.5.3.
  Show release notes in tag list
  Fix expand all diffs button in compare view
  Add route for Import::GithubController#new
  Update CHANGELOG
  ...
This commit is contained in:
Lin Jen-Shin 2016-07-27 18:11:50 +08:00
commit a1f08a76b5
74 changed files with 725 additions and 221 deletions

View File

@ -1,23 +1,40 @@
Please view this file on the master branch, on stable branches it's out of date.
v 8.11.0 (unreleased)
- Remove magic comments (`# encoding: UTF-8`) from Ruby files !5456 (winniehell)
- Remove magic comments (`# encoding: UTF-8`) from Ruby files. !5456 (winniehell)
- Fix CI status icon link underline (ClemMakesApps)
- Fix of 'Commits being passed to custom hooks are already reachable when using the UI'
- Add support for using RequestStore within Sidekiq tasks via SIDEKIQ_REQUEST_STORE env variable
- Optimize maximum user access level lookup in loading of notes
- Limit git rev-list output count to one in forced push check
- Add green outline to New Branch button !5447 (winniehell)
- Clean up unused routes (Josef Strzibny)
- Add green outline to New Branch button. !5447 (winniehell)
- Retrieve rendered HTML from cache in one request
- Nokogiri's various parsing methods are now instrumented
- Make fork counter always clickable !5463 (winniehell)
- Load project invited groups and members eagerly in ProjectTeam#fetch_members
- Add a way to send an email and create an issue based on private personal token. Find the email address from issues page. !3363
- Make fork counter always clickable. !5463 (winniehell)
- Remove `search_id` of labels dropdown filter to fix 'Missleading URI for labels in Merge Requests and Issues view'. !5368 (Scott Le)
- Load project invited groups and members eagerly in `ProjectTeam#fetch_members`
- Make branches sortable without push permission !5462 (winniehell)
- Add GitLab Workhorse version to admin dashboard (Katarzyna Kobierska Ula Budziszewska)
- Add ES6 gem
- Add the `sprockets-es6` gem
- Multiple trigger variables show in separate lines (Katarzyna Kobierska Ula Budziszewska)
- Profile requests when a header is passed
v 8.10.2 (unreleased)
- User can now search branches by name. !5144
- Add ENV variable to skip repository storages validations
- Fix backup restore. !5459
- Rescue Rugged::OSError (lock exists) when creating references. !5497
- Disable MySQL foreign key checks before dropping all tables. !5472
- Fix a bug where forking a project from a repository storage to another would fail
- Show release notes in tags list
- Use project ID in repository cache to prevent stale data from persisting across projects. !5460
- Ensure relative paths for video are rewritten as we do for images. !5474
- Ensure current user can retry a build before showing the 'Retry' button. !5476
- Fix expand all diffs button in compare view
v 8.10.1
- Refactor repository storages documentation. !5428
@ -27,10 +44,6 @@ v 8.10.1
- Fix bug where replies to commit notes displayed in the MR discussion tab wouldn't show up on the commit page. !5446
- Ignore invalid trusted proxies in X-Forwarded-For header. !5454
- Add links to the real markdown.md file for all GFM examples. !5458
- Remove `search_id` of labels dropdown filter to fix 'Missleading URI for labels in Merge Requests and Issues view' !5368 (Scott Le)
v 8.10.1 (unreleased)
- Fix bug where replies to commit notes displayed in the MR discussion tab wouldn't show up on the commit page
v 8.10.0
- Fix profile activity heatmap to show correct day name (eanplatter)

View File

@ -1 +1 @@
3.2.0
3.2.1

View File

@ -275,7 +275,7 @@ group :development, :test do
gem 'awesome_print', '~> 1.2.0', require: false
gem 'fuubar', '~> 2.0.0'
gem 'database_cleaner', '~> 1.4.0'
gem 'database_cleaner', '~> 1.5.0'
gem 'factory_girl_rails', '~> 4.6.0'
gem 'rspec-rails', '~> 3.5.0'
gem 'rspec-retry', '~> 0.4.5'
@ -334,6 +334,8 @@ gem 'mail_room', '~> 0.8'
gem 'email_reply_parser', '~> 0.5.8'
gem 'ruby-prof', '~> 0.15.9'
## CI
gem 'activerecord-session_store', '~> 1.0.0'
gem 'nested_form', '~> 0.3.2'

View File

@ -153,7 +153,7 @@ GEM
d3_rails (3.5.11)
railties (>= 3.1.0)
daemons (1.2.3)
database_cleaner (1.4.1)
database_cleaner (1.5.3)
debug_inspector (0.0.2)
debugger-ruby_core_source (1.3.8)
default_value_for (3.0.1)
@ -620,6 +620,7 @@ GEM
rubocop (>= 0.40.0)
ruby-fogbugz (0.2.1)
crack (~> 0.4)
ruby-prof (0.15.9)
ruby-progressbar (1.8.1)
ruby-saml (1.3.0)
nokogiri (>= 1.5.10)
@ -841,7 +842,7 @@ DEPENDENCIES
connection_pool (~> 2.0)
creole (~> 0.5.0)
d3_rails (~> 3.5.0)
database_cleaner (~> 1.4.0)
database_cleaner (~> 1.5.0)
default_value_for (~> 3.0.0)
devise (~> 4.0)
devise-two-factor (~> 3.0.0)
@ -948,6 +949,7 @@ DEPENDENCIES
rubocop (~> 0.41.2)
rubocop-rspec (~> 1.5.0)
ruby-fogbugz (~> 0.2.1)
ruby-prof (~> 0.15.9)
sanitize (~> 2.0)
sass-rails (~> 5.0.0)
scss_lint (~> 0.47.0)

View File

@ -66,4 +66,12 @@
})();
$(function() {
if ($('.js-importer-status').length) {
var jobsImportPath = $('.js-importer-status').data('jobs-import-path');
var importPath = $('.js-importer-status').data('import-path');
new ImporterStatus(jobsImportPath, importPath);
}
});
}).call(this);

View File

@ -0,0 +1,17 @@
class Admin::RequestsProfilesController < Admin::ApplicationController
def index
@profile_token = Gitlab::RequestProfiler.profile_token
@profiles = Gitlab::RequestProfiler::Profile.all.group_by(&:request_path)
end
def show
clean_name = Rack::Utils.clean_path_info(params[:name])
profile = Gitlab::RequestProfiler::Profile.find(clean_name)
if profile
render text: profile.content
else
redirect_to admin_requests_profiles_path, alert: 'Profile not found'
end
end
end

View File

@ -1,4 +1,5 @@
class Projects::IssuesController < Projects::ApplicationController
include NotesHelper
include ToggleSubscriptionAction
include IssuableActions
include ToggleAwardEmoji
@ -70,6 +71,8 @@ class Projects::IssuesController < Projects::ApplicationController
@note = @project.notes.new(noteable: @issue)
@noteable = @issue
preload_max_access_for_authors(@notes, @project)
respond_to do |format|
format.html
format.json do

View File

@ -3,6 +3,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
include DiffForPath
include DiffHelper
include IssuableActions
include NotesHelper
include ToggleAwardEmoji
before_action :module_enabled
@ -385,6 +386,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@project_wiki,
@ref
)
preload_max_access_for_authors(@notes, @project)
end
def define_widget_vars

View File

@ -10,11 +10,12 @@ class Projects::TagsController < Projects::ApplicationController
@tags = @repository.tags_sorted_by(@sort)
@tags = Kaminari.paginate_array(@tags).page(params[:page])
@releases = project.releases.where(tag: @tags)
@releases = project.releases.where(tag: @tags.map(&:name))
end
def show
@tag = @repository.find_tag(params[:id])
@release = @project.releases.find_or_initialize_by(tag: @tag.name)
@commit = @repository.commit(@tag.target)
end

View File

@ -7,7 +7,7 @@ module NotesHelper
end
def note_editable?(note)
note.editable? && can?(current_user, :admin_note, note)
Ability.can_edit_note?(current_user, note)
end
def noteable_json(noteable)
@ -87,14 +87,13 @@ module NotesHelper
end
end
def note_max_access_for_user(note)
@max_access_by_user_id ||= Hash.new do |hash, key|
project = key[:project]
hash[key] = project.team.human_max_access(key[:user_id])
end
def preload_max_access_for_authors(notes, project)
user_ids = notes.map(&:author_id)
project.team.max_member_access_for_user_ids(user_ids)
end
full_key = { project: note.project, user_id: note.author_id }
@max_access_by_user_id[full_key]
def note_max_access_for_user(note)
note.project.team.human_max_access(note.author_id)
end
def discussion_diff_path(discussion)

View File

@ -388,6 +388,18 @@ class Ability
GroupProjectsFinder.new(group).execute(user).any?
end
def can_edit_note?(user, note)
return false if !note.editable? || !user.present?
return true if note.author == user || user.admin?
if note.project
max_access_level = note.project.team.max_member_access(user.id)
max_access_level >= Gitlab::Access::MASTER
else
false
end
end
def namespace_abilities(user, namespace)
rules = []

View File

@ -31,6 +31,10 @@ class Blob < SimpleDelegator
text? && language && language.name == 'SVG'
end
def video?
UploaderHelper::VIDEO_EXT.include?(extname.downcase.delete('.'))
end
def to_partial_path
if lfs_pointer?
'download'

View File

@ -295,8 +295,8 @@ class Commit
def uri_type(path)
entry = @raw.tree.path(path)
if entry[:type] == :blob
blob = Gitlab::Git::Blob.new(name: entry[:name])
blob.image? ? :raw : :blob
blob = ::Blob.decorate(Gitlab::Git::Blob.new(name: entry[:name]))
blob.image? || blob.video? ? :raw : :blob
else
entry[:type]
end

View File

@ -53,6 +53,10 @@ class Member < ActiveRecord::Base
default_value_for :notification_level, NotificationSetting.levels[:global]
class << self
def access_for_user_ids(user_ids)
where(user_id: user_ids).has_access.pluck(:user_id, :access_level).to_h
end
def find_by_invite_token(invite_token)
invite_token = Devise.token_generator.digest(self, :invite_token, invite_token)
find_by(invite_token: invite_token)

View File

@ -451,7 +451,9 @@ class Project < ActiveRecord::Base
def add_import_job
if forked?
job_id = RepositoryForkWorker.perform_async(self.id, forked_from_project.path_with_namespace, self.namespace.path)
job_id = RepositoryForkWorker.perform_async(id, forked_from_project.repository_storage_path,
forked_from_project.path_with_namespace,
self.namespace.path)
else
job_id = RepositoryImportWorker.perform_async(self.id)
end

View File

@ -132,39 +132,63 @@ class ProjectTeam
Gitlab::Access.options_with_owner.key(max_member_access(user_id))
end
# This method assumes project and group members are eager loaded for optimal
# performance.
# Determine the maximum access level for a group of users in bulk.
#
# Returns a Hash mapping user ID -> maximum access level.
def max_member_access_for_user_ids(user_ids)
user_ids = user_ids.uniq
key = "max_member_access:#{project.id}"
RequestStore.store[key] ||= {}
access = RequestStore.store[key]
# Lookup only the IDs we need
user_ids = user_ids - access.keys
if user_ids.present?
user_ids.each { |id| access[id] = Gitlab::Access::NO_ACCESS }
member_access = project.members.access_for_user_ids(user_ids)
merge_max!(access, member_access)
if group
group_access = group.members.access_for_user_ids(user_ids)
merge_max!(access, group_access)
end
# Each group produces a list of maximum access level per user. We take the
# max of the values produced by each group.
if project.invited_groups.any? && project.allowed_to_share_with_group?
project.project_group_links.each do |group_link|
invited_access = max_invited_level_for_users(group_link, user_ids)
merge_max!(access, invited_access)
end
end
end
access
end
def max_member_access(user_id)
access = []
access += project.members.where(user_id: user_id).has_access.pluck(:access_level)
if group
access += group.members.where(user_id: user_id).has_access.pluck(:access_level)
end
if project.invited_groups.any? && project.allowed_to_share_with_group?
access << max_invited_level(user_id)
end
access.compact.max
max_member_access_for_user_ids([user_id])[user_id]
end
private
def max_invited_level(user_id)
project.project_group_links.map do |group_link|
invited_group = group_link.group
access = invited_group.group_members.find_by(user_id: user_id).try(:access_field)
# For a given group, return the maximum access level for the user. This is the min of
# the invited access level of the group and the access level of the user within the group.
# For example, if the group has been given DEVELOPER access but the member has MASTER access,
# the user should receive only DEVELOPER access.
def max_invited_level_for_users(group_link, user_ids)
invited_group = group_link.group
capped_access_level = group_link.group_access
access = invited_group.group_members.access_for_user_ids(user_ids)
# If group member has higher access level we should restrict it
# to max allowed access level
if access && access > group_link.group_access
access = group_link.group_access
end
# If the user is not in the list, assume he/she does not have access
missing_users = user_ids - access.keys
missing_users.each { |id| access[id] = Gitlab::Access::NO_ACCESS }
access
end.compact.max
# Cap the maximum access by the invited level access
access.each { |key, value| access[key] = [value, capped_access_level].min }
end
def fetch_members(level = nil)
@ -215,4 +239,8 @@ class ProjectTeam
def group
project.group
end
def merge_max!(first_hash, second_hash)
first_hash.merge!(second_hash) { |_key, old, new| old > new ? old : new }
end
end

View File

@ -211,6 +211,9 @@ class Repository
rugged.references.create(keep_around_ref_name(sha), sha, force: true)
rescue Rugged::ReferenceError => ex
Rails.logger.error "Unable to create keep-around reference for repository #{path}: #{ex}"
rescue Rugged::OSError => ex
raise unless ex.message =~ /Failed to create locked file/ && ex.message =~ /File exists/
Rails.logger.error "Unable to create keep-around reference for repository #{path}: #{ex}"
end
end

View File

@ -16,3 +16,7 @@
= link_to admin_health_check_path, title: 'Health Check' do
%span
Health Check
= nav_link(controller: :requests_profiles) do
= link_to admin_requests_profiles_path, title: 'Requests Profiles' do
%span
Requests Profiles

View File

@ -0,0 +1,26 @@
- @no_container = true
- page_title 'Requests Profiles'
= render 'admin/background_jobs/head'
%div{ class: container_class }
%h3.page-title
= page_title
.bs-callout.clearfix
Pass the header
%code X-Profile-Token: #{@profile_token}
to profile the request
- if @profiles.present?
.prepend-top-default
- @profiles.each do |path, profiles|
.panel.panel-default.panel-small
.panel-heading
%code= path
%ul.content-list
- profiles.each do |profile|
%li
= link_to profile.time.to_s(:long), admin_requests_profile_path(profile), data: {no_turbolink: true}
- else
%p
No profiles found

View File

@ -74,6 +74,4 @@
= link_to "import flow", status_import_bitbucket_path, "data-no-turbolink" => "true"
again.
:javascript
new ImporterStatus("#{jobs_import_bitbucket_path}", "#{import_bitbucket_path}");
.js-importer-status{ data: { jobs_import_path: "#{jobs_import_bitbucket_path}", import_path: "#{import_bitbucket_path}" } }

View File

@ -56,5 +56,4 @@
Import
= icon("spinner spin", class: "loading-icon")
:javascript
new ImporterStatus("#{jobs_import_fogbugz_path}", "#{import_fogbugz_path}");
.js-importer-status{ data: { jobs_import_path: "#{jobs_import_fogbugz_path}", import_path: "#{import_fogbugz_path}" } }

View File

@ -55,5 +55,4 @@
Import
= icon("spinner spin", class: "loading-icon")
:javascript
new ImporterStatus("#{jobs_import_github_path}", "#{import_github_path}");
.js-importer-status{ data: { jobs_import_path: "#{jobs_import_github_path}", import_path: "#{import_github_path}" } }

View File

@ -51,5 +51,4 @@
Import
= icon("spinner spin", class: "loading-icon")
:javascript
new ImporterStatus("#{jobs_import_gitlab_path}", "#{import_gitlab_path}");
.js-importer-status{ data: { jobs_import_path: "#{jobs_import_gitlab_path}", import_path: "#{import_gitlab_path}" } }

View File

@ -51,5 +51,4 @@
Import
= icon("spinner spin", class: "loading-icon")
:javascript
new ImporterStatus("#{jobs_import_gitorious_path}", "#{import_gitorious_path}");
.js-importer-status{ data: { jobs_import_path: "#{jobs_import_gitorious_path}", import_path: "#{import_gitorious_path}" } }

View File

@ -77,5 +77,4 @@
= link_to "import flow", new_import_google_code_path
again.
:javascript
new ImporterStatus("#{jobs_import_google_code_path}", "#{import_google_code_path}");
.js-importer-status{ data: { jobs_import_path: "#{jobs_import_google_code_path}", import_path: "#{import_google_code_path}" } }

View File

@ -9,7 +9,7 @@
= link_to admin_root_path, title: 'Overview', class: 'shortcuts-tree' do
%span
Overview
= nav_link(controller: %w(system_info background_jobs logs health_check)) do
= nav_link(controller: %w(system_info background_jobs logs health_check requests_profiles)) do
= link_to admin_system_info_path, title: 'Monitoring' do
%span
Monitoring

View File

@ -7,28 +7,31 @@
.nav-text
Protected branches can be managed in project settings
- if can? current_user, :push_code, @project
.nav-controls
= form_tag(filter_branches_path, method: :get) do
= search_field_tag :search, params[:search], { placeholder: 'Filter by branch name', id: 'branch-search', class: 'form-control search-text-input input-short', spellcheck: false }
.dropdown.inline
%button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
%span.light
- if params[:sort].present?
= params[:sort].humanize
- else
Name
%b.caret
%ul.dropdown-menu.dropdown-menu-align-right
%li
= link_to filter_branches_path(sort: nil) do
= sort_title_name
= link_to filter_branches_path(sort: 'recently_updated') do
= sort_title_recently_updated
= link_to filter_branches_path(sort: 'last_updated') do
= sort_title_oldest_updated
.nav-controls
= form_tag(filter_branches_path, method: :get) do
= search_field_tag :search, params[:search], { placeholder: 'Filter by branch name', id: 'branch-search', class: 'form-control search-text-input input-short', spellcheck: false }
.dropdown.inline
%button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
%span.light
- if params[:sort].present?
= params[:sort].humanize
- else
Name
%b.caret
%ul.dropdown-menu.dropdown-menu-align-right
%li
= link_to filter_branches_path(sort: nil) do
= sort_title_name
= link_to filter_branches_path(sort: 'recently_updated') do
= sort_title_recently_updated
= link_to filter_branches_path(sort: 'last_updated') do
= sort_title_oldest_updated
- if can? current_user, :push_code, @project
= link_to new_namespace_project_branch_path(@project.namespace, @project), class: 'btn btn-create' do
New branch
- if @branches.any?
%ul.content-list.all-branches
- @branches.each do |branch|

View File

@ -40,7 +40,7 @@
.block{ class: ("block-first" if !@build.coverage && !(can?(current_user, :read_build, @project) && (@build.artifacts? || @build.artifacts_expired?))) }
.title
Build details
- if @build.retryable?
- if can?(current_user, :update_build, @build) && @build.retryable?
= link_to "Retry", retry_namespace_project_build_path(@project.namespace, @project, @build), class: 'pull-right', method: :post
- if @build.merge_request
%p.build-detail-row
@ -88,8 +88,9 @@
%p
%span.build-light-text Variables:
%code
- @build.trigger_request.variables.each do |key, value|
- @build.trigger_request.variables.each do |key, value|
%code
#{key}=#{value}
.block

View File

@ -22,6 +22,8 @@
- if defined?(ref) && ref
- if build.ref
.icon-container
= build.tag? ? icon('tag') : icon('code-fork')
= link_to build.ref, namespace_project_commits_path(build.project.namespace, build.project, build.ref), class: "monospace branch-name"
- else
.light none

View File

@ -7,7 +7,7 @@
.content-block.oneline-block.files-changed
.inline-parallel-buttons
- if !expand_all_diffs? && diff_files.any? { |diff_file| diff_file.collapsed? }
= link_to 'Expand all', url_for(params.merge(expand_all_diffs: 1, format: 'html')), class: 'btn btn-default'
= link_to 'Expand all', url_for(params.merge(expand_all_diffs: 1, format: nil)), class: 'btn btn-default'
- if show_whitespace_toggle
- if current_controller?(:commit)
= commit_diff_whitespace_link(@project, @commit, class: 'hidden-xs')

View File

@ -19,4 +19,9 @@
]
}
var ctx = $("#build_timesChart").get(0).getContext("2d");
new Chart(ctx).Bar(data,{"scaleOverlay": true, responsive: true, maintainAspectRatio: false});
var options = { scaleOverlay: true, responsive: true, maintainAspectRatio: false };
if (window.innerWidth < 768) {
// Scale fonts if window width lower than 768px (iPad portrait)
options.scaleFontSize = 8
}
new Chart(ctx).Bar(data, options);

View File

@ -48,4 +48,9 @@
]
}
var ctx = $("##{scope}Chart").get(0).getContext("2d");
new Chart(ctx).Line(data,{"scaleOverlay": true, responsive: true, maintainAspectRatio: false});
var options = { scaleOverlay: true, responsive: true, maintainAspectRatio: false };
if (window.innerWidth < 768) {
// Scale fonts if window width lower than 768px (iPad portrait)
options.scaleFontSize = 8
}
new Chart(ctx).Line(data, options);

View File

@ -59,6 +59,10 @@
var container = $(selector).parent();
var generateChart = function() {
selector.attr('width', $(container).width());
if (window.innerWidth < 768) {
// Scale fonts if window width lower than 768px (iPad portrait)
options.scaleFontSize = 8
}
return new Chart(ctx).Bar(data, options);
};
// enabling auto-resizing

View File

@ -4,7 +4,7 @@ class RepositoryForkWorker
sidekiq_options queue: :gitlab_shell
def perform(project_id, source_path, target_path)
def perform(project_id, forked_from_repository_storage_path, source_path, target_path)
project = Project.find_by_id(project_id)
unless project.present?
@ -12,7 +12,8 @@ class RepositoryForkWorker
return
end
result = gitlab_shell.fork_repository(project.repository_storage_path, source_path, target_path)
result = gitlab_shell.fork_repository(forked_from_repository_storage_path, source_path,
project.repository_storage_path, target_path)
unless result
logger.error("Unable to fork project #{project_id} for repository #{source_path} -> #{target_path}")
project.mark_import_as_failed('The project could not be forked.')

View File

@ -0,0 +1,9 @@
class RequestsProfilesWorker
include Sidekiq::Worker
sidekiq_options queue: :default
def perform
Gitlab::RequestProfiler.remove_all_profiles
end
end

View File

@ -68,6 +68,25 @@
:why: https://opensource.org/licenses/BSD-2-Clause
:versions: []
:when: 2016-05-02 05:55:09.796363000 Z
- - :whitelist
- LGPLv2+
- :who: Stan Hu
:why: Equivalent to LGPLv2
:versions: []
:when: 2016-06-07 17:14:10.907682000 Z
- - :whitelist
- Artistic 2.0
- :who: Josh Frye
:why: Disk/mount information display on Admin pages
:versions: []
:when: 2016-06-29 16:32:45.432113000 Z
- - :whitelist
- Simplified BSD
- :who: Douwe Maan
:why: https://opensource.org/licenses/BSD-2-Clause
:versions: []
:when: 2016-07-26 21:24:07.248480000 Z
# LICENSE BLACKLIST
- - :blacklist
@ -175,15 +194,3 @@
:why: https://github.com/jmcnevin/rubypants/blob/master/LICENSE.rdoc
:versions: []
:when: 2016-05-02 05:56:50.696858000 Z
- - :whitelist
- LGPLv2+
- :who: Stan Hu
:why: Equivalent to LGPLv2
:versions: []
:when: 2016-06-07 17:14:10.907682000 Z
- - :whitelist
- Artistic 2.0
- :who: Josh Frye
:why: Disk/mount information display on Admin pages
:versions: []
:when: 2016-06-29 16:32:45.432113000 Z

View File

@ -290,6 +290,9 @@ Settings.cron_jobs['repository_archive_cache_worker']['job_class'] = 'Repository
Settings.cron_jobs['gitlab_remove_project_export_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['gitlab_remove_project_export_worker']['cron'] ||= '0 * * * *'
Settings.cron_jobs['gitlab_remove_project_export_worker']['job_class'] = 'GitlabRemoveProjectExportWorker'
Settings.cron_jobs['requests_profiles_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['requests_profiles_worker']['cron'] ||= '0 0 * * *'
Settings.cron_jobs['requests_profiles_worker']['job_class'] = 'RequestsProfilesWorker'
#
# GitLab Shell

View File

@ -26,4 +26,4 @@ def validate_storages
end
end
validate_storages unless Rails.env.test?
validate_storages unless Rails.env.test? || ENV['SKIP_STORAGE_VALIDATION'] == 'true'

View File

@ -0,0 +1,3 @@
Rails.application.configure do |config|
config.middleware.use(Gitlab::RequestProfiler::Middleware)
end

View File

@ -7,6 +7,7 @@ Sidekiq.configure_server do |config|
config.server_middleware do |chain|
chain.add Gitlab::SidekiqMiddleware::ArgumentsLogger if ENV['SIDEKIQ_LOG_ARGUMENTS']
chain.add Gitlab::SidekiqMiddleware::MemoryKiller if ENV['SIDEKIQ_MEMORY_KILLER_MAX_RSS']
chain.add Gitlab::SidekiqMiddleware::RequestStoreMiddleware unless ENV['SIDEKIQ_REQUEST_STORE'] == '0'
end
# Sidekiq-cron: load recurring jobs from gitlab.yml

View File

@ -42,10 +42,9 @@ Rails.application.routes.draw do
resource :lint, only: [:show, :create]
resources :projects do
resources :projects, only: [:index, :show] do
member do
get :status, to: 'projects#badge'
get :integration
end
end
@ -144,13 +143,13 @@ Rails.application.routes.draw do
get :jobs
end
resource :gitlab, only: [:create, :new], controller: :gitlab do
resource :gitlab, only: [:create], controller: :gitlab do
get :status
get :callback
get :jobs
end
resource :bitbucket, only: [:create, :new], controller: :bitbucket do
resource :bitbucket, only: [:create], controller: :bitbucket do
get :status
get :callback
get :jobs
@ -243,7 +242,6 @@ Rails.application.routes.draw do
get :projects
get :keys
get :groups
put :team_update
put :block
put :unblock
put :unlock
@ -281,6 +279,7 @@ Rails.application.routes.draw do
resource :health_check, controller: 'health_check', only: [:show]
resource :background_jobs, controller: 'background_jobs', only: [:show]
resource :system_info, controller: 'system_info', only: [:show]
resources :requests_profiles, only: [:index, :show], param: :name
resources :namespaces, path: '/projects', constraints: { id: /[a-zA-Z.0-9_\-]+/ }, only: [] do
root to: 'projects#index', as: :projects
@ -300,7 +299,7 @@ Rails.application.routes.draw do
end
end
resource :appearances, path: 'appearance' do
resource :appearances, only: [:show, :create, :update], path: 'appearance' do
member do
get :preview
delete :logo
@ -309,7 +308,7 @@ Rails.application.routes.draw do
end
resource :application_settings, only: [:show, :update] do
resources :services
resources :services, only: [:index, :edit, :update]
put :reset_runners_token
put :reset_health_check_token
put :clear_repository_check_states
@ -346,7 +345,7 @@ Rails.application.routes.draw do
end
scope module: :profiles do
resource :account, only: [:show, :update] do
resource :account, only: [:show] do
member do
delete :unlink
end
@ -358,7 +357,7 @@ Rails.application.routes.draw do
end
end
resource :preferences, only: [:show, :update]
resources :keys
resources :keys, only: [:index, :show, :new, :create, :destroy]
resources :emails, only: [:index, :create, :destroy]
resource :avatar, only: [:destroy]
@ -660,7 +659,7 @@ Rails.application.routes.draw do
post '/wikis/*id/markdown_preview', to: 'wikis#markdown_preview', constraints: WIKI_SLUG_ID, as: 'wiki_markdown_preview'
end
resource :repository, only: [:show, :create] do
resource :repository, only: [:create] do
member do
get 'archive', constraints: { format: Gitlab::Regex.archive_formats_regex }
end
@ -782,7 +781,7 @@ Rails.application.routes.draw do
end
end
resources :labels, constraints: { id: /\d+/ } do
resources :labels, except: [:show], constraints: { id: /\d+/ } do
collection do
post :generate
post :set_priorities
@ -807,7 +806,7 @@ Rails.application.routes.draw do
end
end
resources :project_members, except: [:new, :edit], constraints: { id: /[a-zA-Z.\/0-9_\-#%+]+/ }, concerns: :access_requestable do
resources :project_members, except: [:show, :new, :edit], constraints: { id: /[a-zA-Z.\/0-9_\-#%+]+/ }, concerns: :access_requestable do
collection do
delete :leave

View File

@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20160721081015) do
ActiveRecord::Schema.define(version: 20160722221922) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"

View File

@ -38,7 +38,7 @@ GitLab Runner then executes build scripts as the `gitlab-runner` user.
$ sudo gitlab-ci-multi-runner register -n \
--url https://gitlab.com/ci \
--registration-token REGISTRATION_TOKEN \
--executor shell
--executor shell \
--description "My Runner"
```

View File

@ -46,7 +46,7 @@ sudo -u git -H git checkout 8-10-stable-ee
```bash
cd /home/git/gitlab-shell
sudo -u git -H git fetch --all --tags
sudo -u git -H git checkout v3.2.0
sudo -u git -H git checkout v3.2.1
```
### 5. Update gitlab-workhorse

View File

@ -20,7 +20,7 @@ module Banzai
process_link_attr el.attribute('href')
end
doc.search('img').each do |el|
doc.css('img, video').each do |el|
process_link_attr el.attribute('src')
end

View File

@ -7,6 +7,7 @@ module Gitlab
module Access
class AccessDeniedError < StandardError; end
NO_ACCESS = 0
GUEST = 10
REPORTER = 20
DEVELOPER = 30

View File

@ -60,16 +60,18 @@ module Gitlab
end
# Fork repository to new namespace
# storage - project's storage path
# forked_from_storage - forked-from project's storage path
# path - project path with namespace
# forked_to_storage - forked-to project's storage path
# fork_namespace - namespace for forked project
#
# Ex.
# fork_repository("/path/to/storage", "gitlab/gitlab-ci", "randx")
# fork_repository("/path/to/forked_from/storage", "gitlab/gitlab-ci", "/path/to/forked_to/storage", "randx")
#
def fork_repository(storage, path, fork_namespace)
def fork_repository(forked_from_storage, path, forked_to_storage, fork_namespace)
Gitlab::Utils.system_silent([gitlab_shell_projects_path, 'fork-project',
storage, "#{path}.git", fork_namespace])
forked_from_storage, "#{path}.git", forked_to_storage,
fork_namespace])
end
# Remove repository from file system

View File

@ -0,0 +1,19 @@
require 'fileutils'
module Gitlab
module RequestProfiler
PROFILES_DIR = "#{Gitlab.config.shared.path}/tmp/requests_profiles"
def profile_token
Rails.cache.fetch('profile-token') do
Devise.friendly_token
end
end
module_function :profile_token
def remove_all_profiles
FileUtils.rm_rf(PROFILES_DIR)
end
module_function :remove_all_profiles
end
end

View File

@ -0,0 +1,47 @@
require 'ruby-prof'
module Gitlab
module RequestProfiler
class Middleware
def initialize(app)
@app = app
end
def call(env)
if profile?(env)
call_with_profiling(env)
else
@app.call(env)
end
end
def profile?(env)
header_token = env['HTTP_X_PROFILE_TOKEN']
return unless header_token.present?
profile_token = RequestProfiler.profile_token
return unless profile_token.present?
header_token == profile_token
end
def call_with_profiling(env)
ret = nil
result = RubyProf::Profile.profile do
ret = @app.call(env)
end
printer = RubyProf::CallStackPrinter.new(result)
file_name = "#{env['PATH_INFO'].tr('/', '|')}_#{Time.current.to_i}.html"
file_path = "#{PROFILES_DIR}/#{file_name}"
FileUtils.mkdir_p(PROFILES_DIR)
File.open(file_path, 'wb') do |file|
printer.print(file)
end
ret
end
end
end
end

View File

@ -0,0 +1,43 @@
module Gitlab
module RequestProfiler
class Profile
attr_reader :name, :time, :request_path
alias_method :to_param, :name
def self.all
Dir["#{PROFILES_DIR}/*.html"].map do |path|
new(File.basename(path))
end
end
def self.find(name)
name_dup = name.dup
name_dup << '.html' unless name.end_with?('.html')
file_path = "#{PROFILES_DIR}/#{name_dup}"
return unless File.exist?(file_path)
new(name_dup)
end
def initialize(name)
@name = name
set_attributes
end
def content
File.read("#{PROFILES_DIR}/#{name}")
end
private
def set_attributes
_, path, timestamp = name.split(/(.*)_(\d+)\.html$/)
@request_path = path.tr('|', '/')
@time = Time.at(timestamp.to_i).utc
end
end
end
end

View File

@ -0,0 +1,13 @@
module Gitlab
module SidekiqMiddleware
class RequestStoreMiddleware
def call(worker, job, queue)
RequestStore.begin!
yield
ensure
RequestStore.end!
RequestStore.clear!
end
end
end
end

View File

@ -25,6 +25,10 @@ namespace :gitlab do
desc 'Drop all tables'
task :drop_tables => :environment do
connection = ActiveRecord::Base.connection
# If MySQL, turn off foreign key checks
connection.execute('SET FOREIGN_KEY_CHECKS=0') if Gitlab::Database.mysql?
tables = connection.tables
tables.delete 'schema_migrations'
# Truncate schema_migrations to ensure migrations re-run
@ -35,6 +39,9 @@ namespace :gitlab do
# MySQL: http://dev.mysql.com/doc/refman/5.7/en/drop-table.html
# Add `IF EXISTS` because cascade could have already deleted a table.
tables.each { |t| connection.execute("DROP TABLE IF EXISTS #{connection.quote_table_name(t)} CASCADE") }
# If MySQL, re-enable foreign key checks
connection.execute('SET FOREIGN_KEY_CHECKS=1') if Gitlab::Database.mysql?
end
desc 'Configures the database by running migrate, or by loading the schema and seeding if needed'

View File

@ -0,0 +1,20 @@
require 'spec_helper'
describe Projects::TagsController do
let(:project) { create(:project, :public) }
let!(:release) { create(:release, project: project) }
let!(:invalid_release) { create(:release, project: project, tag: 'does-not-exist') }
describe 'GET index' do
before { get :index, namespace_id: project.namespace.to_param, project_id: project.to_param }
it 'returns the tags for the page' do
expect(assigns(:tags).map(&:name)).to eq(['v1.1.0', 'v1.0.0'])
end
it 'returns releases matching those tags' do
expect(assigns(:releases)).to include(release)
expect(assigns(:releases)).not_to include(invalid_release)
end
end
end

View File

@ -5,7 +5,8 @@ FactoryGirl.define do
variables do
{
TRIGGER_KEY: 'TRIGGER_VALUE'
TRIGGER_KEY_1: 'TRIGGER_VALUE_1',
TRIGGER_KEY_2: 'TRIGGER_VALUE_2'
}
end
end

View File

@ -199,9 +199,13 @@ describe "Builds" do
click_link 'Retry'
end
it { expect(page.status_code).to eq(200) }
it { expect(page).to have_content 'pending' }
it { expect(page).to have_content 'Cancel' }
it 'shows the right status and buttons' do
expect(page).to have_http_status(200)
expect(page).to have_content 'pending'
page.within('aside.right-sidebar') do
expect(page).to have_content 'Cancel'
end
end
end
context "Build from other project" do
@ -212,7 +216,25 @@ describe "Builds" do
page.driver.post(retry_namespace_project_build_path(@project.namespace, @project, @build2))
end
it { expect(page.status_code).to eq(404) }
it { expect(page).to have_http_status(404) }
end
context "Build that current user is not allowed to retry" do
before do
@build.run!
@build.cancel!
@project.update(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
logout_direct
login_with(create(:user))
visit namespace_project_build_path(@project.namespace, @project, @build)
end
it 'does not show the Retry button' do
page.within('aside.right-sidebar') do
expect(page).not_to have_content 'Retry'
end
end
end
end

View File

@ -1,32 +0,0 @@
require 'spec_helper'
describe 'Branches', feature: true do
let(:project) { create(:project) }
let(:repository) { project.repository }
before do
login_as :user
project.team << [@user, :developer]
end
describe 'Initial branches page' do
it 'shows all the branches' do
visit namespace_project_branches_path(project.namespace, project)
repository.branches { |branch| expect(page).to have_content("#{branch.name}") }
expect(page).to have_content("Protected branches can be managed in project settings")
end
end
describe 'Find branches' do
it 'shows filtered branches', js: true do
visit namespace_project_branches_path(project.namespace, project, project.id)
fill_in 'branch-search', with: 'fix'
find('#branch-search').native.send_keys(:enter)
expect(page).to have_content('fix')
expect(find('.all-branches')).to have_selector('li', count: 1)
end
end
end

View File

@ -20,7 +20,7 @@ describe BranchesFinder do
result = branches_finder.execute
expect(result.first.name).to eq('expand-collapse-lines')
expect(result.first.name).to eq('video')
end
it 'sorts by last_updated' do

View File

@ -1,37 +1,30 @@
require "spec_helper"
describe NotesHelper do
let(:owner) { create(:owner) }
let(:group) { create(:group) }
let(:project) { create(:empty_project, namespace: group) }
let(:master) { create(:user) }
let(:reporter) { create(:user) }
let(:guest) { create(:user) }
let(:owner_note) { create(:note, author: owner, project: project) }
let(:master_note) { create(:note, author: master, project: project) }
let(:reporter_note) { create(:note, author: reporter, project: project) }
let!(:notes) { [owner_note, master_note, reporter_note] }
before do
group.add_owner(owner)
project.team << [master, :master]
project.team << [reporter, :reporter]
project.team << [guest, :guest]
end
describe "#notes_max_access_for_users" do
let(:owner) { create(:owner) }
let(:group) { create(:group) }
let(:project) { create(:empty_project, namespace: group) }
let(:master) { create(:user) }
let(:reporter) { create(:user) }
let(:guest) { create(:user) }
let(:owner_note) { create(:note, author: owner, project: project) }
let(:master_note) { create(:note, author: master, project: project) }
let(:reporter_note) { create(:note, author: reporter, project: project) }
let!(:notes) { [owner_note, master_note, reporter_note] }
before do
group.add_owner(owner)
project.team << [master, :master]
project.team << [reporter, :reporter]
project.team << [guest, :guest]
end
it 'return human access levels' do
original_method = project.team.method(:human_max_access)
expect_any_instance_of(ProjectTeam).to receive(:human_max_access).exactly(3).times do |*args|
original_method.call(args[1])
end
expect(helper.note_max_access_for_user(owner_note)).to eq('Owner')
expect(helper.note_max_access_for_user(master_note)).to eq('Master')
expect(helper.note_max_access_for_user(reporter_note)).to eq('Reporter')
# Call it again to ensure value is cached
expect(helper.note_max_access_for_user(owner_note)).to eq('Owner')
end
it 'handles access in different projects' do
@ -43,4 +36,16 @@ describe NotesHelper do
expect(helper.note_max_access_for_user(other_note)).to eq('Reporter')
end
end
describe '#preload_max_access_for_authors' do
it 'loads multiple users' do
expected_access = {
owner.id => Gitlab::Access::OWNER,
master.id => Gitlab::Access::MASTER,
reporter.id => Gitlab::Access::REPORTER
}
expect(helper.preload_max_access_for_authors(notes, project)).to eq(expected_access)
end
end
end

View File

@ -17,6 +17,10 @@ describe Banzai::Filter::RelativeLinkFilter, lib: true do
%(<img src="#{path}" />)
end
def video(path)
%(<video src="#{path}"></video>)
end
def link(path)
%(<a href="#{path}">#{path}</a>)
end
@ -37,6 +41,12 @@ describe Banzai::Filter::RelativeLinkFilter, lib: true do
doc = filter(image('files/images/logo-black.png'))
expect(doc.at_css('img')['src']).to eq 'files/images/logo-black.png'
end
it 'does not modify any relative URL in video' do
doc = filter(video('files/videos/intro.mp4'), commit: project.commit('video'), ref: 'video')
expect(doc.at_css('video')['src']).to eq 'files/videos/intro.mp4'
end
end
shared_examples :relative_to_requested do
@ -111,11 +121,26 @@ describe Banzai::Filter::RelativeLinkFilter, lib: true do
end
it 'rebuilds relative URL for an image in the repo' do
doc = filter(image('files/images/logo-black.png'))
expect(doc.at_css('img')['src']).
to eq "/#{project_path}/raw/#{ref}/files/images/logo-black.png"
end
it 'rebuilds relative URL for link to an image in the repo' do
doc = filter(link('files/images/logo-black.png'))
expect(doc.at_css('a')['href']).
to eq "/#{project_path}/raw/#{ref}/files/images/logo-black.png"
end
it 'rebuilds relative URL for a video in the repo' do
doc = filter(video('files/videos/intro.mp4'), commit: project.commit('video'), ref: 'video')
expect(doc.at_css('video')['src']).
to eq "/#{project_path}/raw/video/files/videos/intro.mp4"
end
it 'does not modify relative URL with an anchor only' do
doc = filter(link('#section-1'))
expect(doc.at_css('a')['href']).to eq '#section-1'

View File

@ -1,6 +1,62 @@
require 'spec_helper'
describe Ability, lib: true do
describe '.can_edit_note?' do
let(:project) { create(:empty_project) }
let!(:note) { create(:note_on_issue, project: project) }
context 'using an anonymous user' do
it 'returns false' do
expect(described_class.can_edit_note?(nil, note)).to be_falsy
end
end
context 'using a system note' do
it 'returns false' do
system_note = create(:note, system: true)
user = create(:user)
expect(described_class.can_edit_note?(user, system_note)).to be_falsy
end
end
context 'using users with different access levels' do
let(:user) { create(:user) }
it 'returns true for the author' do
expect(described_class.can_edit_note?(note.author, note)).to be_truthy
end
it 'returns false for a guest user' do
project.team << [user, :guest]
expect(described_class.can_edit_note?(user, note)).to be_falsy
end
it 'returns false for a developer' do
project.team << [user, :developer]
expect(described_class.can_edit_note?(user, note)).to be_falsy
end
it 'returns true for a master' do
project.team << [user, :master]
expect(described_class.can_edit_note?(user, note)).to be_truthy
end
it 'returns true for a group owner' do
group = create(:group)
project.project_group_links.create(
group: group,
group_access: Gitlab::Access::MASTER)
group.add_owner(user)
expect(described_class.can_edit_note?(user, note)).to be_truthy
end
end
end
describe '.users_that_can_read_project' do
context 'using a public project' do
it 'returns all the users' do

View File

@ -33,6 +33,22 @@ describe Blob do
end
end
describe '#video?' do
it 'is falsey with image extension' do
git_blob = Gitlab::Git::Blob.new(name: 'image.png')
expect(described_class.decorate(git_blob)).not_to be_video
end
UploaderHelper::VIDEO_EXT.each do |ext|
it "is truthy when extension is .#{ext}" do
git_blob = Gitlab::Git::Blob.new(name: "video.#{ext}")
expect(described_class.decorate(git_blob)).to be_video
end
end
end
describe '#to_partial_path' do
def stubbed_blob(overrides = {})
overrides.reverse_merge!(

View File

@ -259,7 +259,7 @@ describe Ci::Build, models: true do
let(:trigger) { create(:ci_trigger, project: project) }
let(:trigger_request) { create(:ci_trigger_request_with_variables, pipeline: pipeline, trigger: trigger) }
let(:user_trigger_variable) do
{ key: :TRIGGER_KEY, value: 'TRIGGER_VALUE', public: false }
{ key: :TRIGGER_KEY_1, value: 'TRIGGER_VALUE_1', public: false }
end
let(:predefined_trigger_variable) do
{ key: 'CI_BUILD_TRIGGERED', value: 'true', public: true }

View File

@ -212,6 +212,7 @@ eos
it 'returns the URI type at the given path' do
expect(commit.uri_type('files/html')).to be(:tree)
expect(commit.uri_type('files/images/logo-black.png')).to be(:raw)
expect(project.commit('video').uri_type('files/videos/intro.mp4')).to be(:raw)
expect(commit.uri_type('files/js/application.js')).to be(:blob)
end

View File

@ -79,6 +79,18 @@ describe Member, models: true do
@accepted_request_member = project.requesters.find_by(user_id: accepted_request_user.id).tap { |m| m.accept_request }
end
describe '.access_for_user_ids' do
it 'returns the right access levels' do
users = [@owner_user.id, @master_user.id]
expected = {
@owner_user.id => Gitlab::Access::OWNER,
@master_user.id => Gitlab::Access::MASTER
}
expect(described_class.access_for_user_ids(users)).to eq(expected)
end
end
describe '.invite' do
it { expect(described_class.invite).not_to include @master }
it { expect(described_class.invite).to include @invited_member }

View File

@ -1272,6 +1272,32 @@ describe Project, models: true do
end
end
describe '#add_import_job' do
context 'forked' do
let(:forked_project_link) { create(:forked_project_link) }
let(:forked_from_project) { forked_project_link.forked_from_project }
let(:project) { forked_project_link.forked_to_project }
it 'schedules a RepositoryForkWorker job' do
expect(RepositoryForkWorker).to receive(:perform_async).
with(project.id, forked_from_project.repository_storage_path,
forked_from_project.path_with_namespace, project.namespace.path)
project.add_import_job
end
end
context 'not forked' do
let(:project) { create(:project) }
it 'schedules a RepositoryImportWorker job' do
expect(RepositoryImportWorker).to receive(:perform_async).with(project.id)
project.add_import_job
end
end
end
describe '.where_paths_in' do
context 'without any paths' do
it 'returns an empty relation' do

View File

@ -151,8 +151,8 @@ describe ProjectTeam, models: true do
it { expect(project.team.max_member_access(master.id)).to eq(Gitlab::Access::MASTER) }
it { expect(project.team.max_member_access(reporter.id)).to eq(Gitlab::Access::REPORTER) }
it { expect(project.team.max_member_access(guest.id)).to eq(Gitlab::Access::GUEST) }
it { expect(project.team.max_member_access(nonmember.id)).to be_nil }
it { expect(project.team.max_member_access(requester.id)).to be_nil }
it { expect(project.team.max_member_access(nonmember.id)).to eq(Gitlab::Access::NO_ACCESS) }
it { expect(project.team.max_member_access(requester.id)).to eq(Gitlab::Access::NO_ACCESS) }
end
context 'when project is shared with group' do
@ -168,14 +168,14 @@ describe ProjectTeam, models: true do
it { expect(project.team.max_member_access(master.id)).to eq(Gitlab::Access::DEVELOPER) }
it { expect(project.team.max_member_access(reporter.id)).to eq(Gitlab::Access::REPORTER) }
it { expect(project.team.max_member_access(nonmember.id)).to be_nil }
it { expect(project.team.max_member_access(requester.id)).to be_nil }
it { expect(project.team.max_member_access(nonmember.id)).to eq(Gitlab::Access::NO_ACCESS) }
it { expect(project.team.max_member_access(requester.id)).to eq(Gitlab::Access::NO_ACCESS) }
context 'but share_with_group_lock is true' do
before { project.namespace.update(share_with_group_lock: true) }
it { expect(project.team.max_member_access(master.id)).to be_nil }
it { expect(project.team.max_member_access(reporter.id)).to be_nil }
it { expect(project.team.max_member_access(master.id)).to eq(Gitlab::Access::NO_ACCESS) }
it { expect(project.team.max_member_access(reporter.id)).to eq(Gitlab::Access::NO_ACCESS) }
end
end
end
@ -194,8 +194,53 @@ describe ProjectTeam, models: true do
it { expect(project.team.max_member_access(master.id)).to eq(Gitlab::Access::MASTER) }
it { expect(project.team.max_member_access(reporter.id)).to eq(Gitlab::Access::REPORTER) }
it { expect(project.team.max_member_access(guest.id)).to eq(Gitlab::Access::GUEST) }
it { expect(project.team.max_member_access(nonmember.id)).to be_nil }
it { expect(project.team.max_member_access(requester.id)).to be_nil }
it { expect(project.team.max_member_access(nonmember.id)).to eq(Gitlab::Access::NO_ACCESS) }
it { expect(project.team.max_member_access(requester.id)).to eq(Gitlab::Access::NO_ACCESS) }
end
end
describe "#max_member_access_for_users" do
it 'returns correct roles for different users' do
master = create(:user)
reporter = create(:user)
promoted_guest = create(:user)
guest = create(:user)
project = create(:project)
project.team << [master, :master]
project.team << [reporter, :reporter]
project.team << [promoted_guest, :guest]
project.team << [guest, :guest]
group = create(:group)
group_developer = create(:user)
second_developer = create(:user)
project.project_group_links.create(
group: group,
group_access: Gitlab::Access::DEVELOPER)
group.add_master(promoted_guest)
group.add_developer(group_developer)
group.add_developer(second_developer)
second_group = create(:group)
project.project_group_links.create(
group: second_group,
group_access: Gitlab::Access::MASTER)
second_group.add_master(second_developer)
users = [master, reporter, promoted_guest, guest, group_developer, second_developer].map(&:id)
expected = {
master.id => Gitlab::Access::MASTER,
reporter.id => Gitlab::Access::REPORTER,
promoted_guest.id => Gitlab::Access::DEVELOPER,
guest.id => Gitlab::Access::GUEST,
group_developer.id => Gitlab::Access::DEVELOPER,
second_developer.id => Gitlab::Access::MASTER
}
expect(project.team.max_member_access_for_user_ids(users)).to eq(expected)
end
end
end

View File

@ -98,7 +98,7 @@ describe Ci::API::API do
{ "key" => "CI_BUILD_TRIGGERED", "value" => "true", "public" => true },
{ "key" => "DB_NAME", "value" => "postgres", "public" => true },
{ "key" => "SECRET_KEY", "value" => "secret_value", "public" => false },
{ "key" => "TRIGGER_KEY", "value" => "TRIGGER_VALUE", "public" => false }
{ "key" => "TRIGGER_KEY_1", "value" => "TRIGGER_VALUE_1", "public" => false }
)
end

View File

@ -1,6 +1,5 @@
require 'spec_helper'
# team_update_admin_user PUT /admin/users/:id/team_update(.:format) admin/users#team_update
# block_admin_user PUT /admin/users/:id/block(.:format) admin/users#block
# unblock_admin_user PUT /admin/users/:id/unblock(.:format) admin/users#unblock
# admin_users GET /admin/users(.:format) admin/users#index
@ -11,10 +10,6 @@ require 'spec_helper'
# PUT /admin/users/:id(.:format) admin/users#update
# DELETE /admin/users/:id(.:format) admin/users#destroy
describe Admin::UsersController, "routing" do
it "to #team_update" do
expect(put("/admin/users/1/team_update")).to route_to('admin/users#team_update', id: '1')
end
it "to #block" do
expect(put("/admin/users/1/block")).to route_to('admin/users#block', id: '1')
end

View File

@ -135,10 +135,6 @@ describe Projects::RepositoriesController, 'routing' do
it 'to #archive format:tar.bz2' do
expect(get('/gitlab/gitlabhq/repository/archive.tar.bz2')).to route_to('projects/repositories#archive', namespace_id: 'gitlab', project_id: 'gitlabhq', format: 'tar.bz2')
end
it 'to #show' do
expect(get('/gitlab/gitlabhq/repository')).to route_to('projects/repositories#show', namespace_id: 'gitlab', project_id: 'gitlabhq')
end
end
describe Projects::BranchesController, 'routing' do

View File

@ -176,18 +176,10 @@ describe Profiles::KeysController, "routing" do
expect(post("/profile/keys")).to route_to('profiles/keys#create')
end
it "to #edit" do
expect(get("/profile/keys/1/edit")).to route_to('profiles/keys#edit', id: '1')
end
it "to #show" do
expect(get("/profile/keys/1")).to route_to('profiles/keys#show', id: '1')
end
it "to #update" do
expect(put("/profile/keys/1")).to route_to('profiles/keys#update', id: '1')
end
it "to #destroy" do
expect(delete("/profile/keys/1")).to route_to('profiles/keys#destroy', id: '1')
end

View File

@ -20,7 +20,8 @@ module TestEnv
'gitattributes' => '5a62481',
'expand-collapse-diffs' => '4842455',
'expand-collapse-files' => '025db92',
'expand-collapse-lines' => '238e82d'
'expand-collapse-lines' => '238e82d',
'video' => '8879059'
}
# gitlab-test-fork is a fork of gitlab-fork, but we don't necessarily

View File

@ -44,9 +44,29 @@ describe 'projects/builds/show' do
it 'shows commit title and not show commit message' do
render
expect(rendered).to have_css('p.build-light-text.append-bottom-0',
text: /\A\n#{Regexp.escape(commit_title)}\n\Z/)
end
end
describe 'shows trigger variables in sidebar' do
let(:trigger_request) { create(:ci_trigger_request_with_variables, pipeline: pipeline) }
before do
build.trigger_request = trigger_request
render
end
it 'shows trigger variables in separate lines' do
expect(rendered).to have_css('code', text: variable_regexp('TRIGGER_KEY_1', 'TRIGGER_VALUE_1'))
expect(rendered).to have_css('code', text: variable_regexp('TRIGGER_KEY_2', 'TRIGGER_VALUE_2'))
end
end
private
def variable_regexp(key, value)
/\A#{Regexp.escape("#{key}=#{value}")}\Z/
end
end

View File

@ -14,21 +14,24 @@ describe RepositoryForkWorker do
describe "#perform" do
it "creates a new repository from a fork" do
expect(shell).to receive(:fork_repository).with(
project.repository_storage_path,
'/test/path',
project.path_with_namespace,
project.repository_storage_path,
fork_project.namespace.path
).and_return(true)
subject.perform(
project.id,
'/test/path',
project.path_with_namespace,
fork_project.namespace.path)
end
it 'flushes various caches' do
expect(shell).to receive(:fork_repository).with(
project.repository_storage_path,
'/test/path',
project.path_with_namespace,
project.repository_storage_path,
fork_project.namespace.path
).and_return(true)
@ -38,7 +41,7 @@ describe RepositoryForkWorker do
expect_any_instance_of(Repository).to receive(:expire_exists_cache).
and_call_original
subject.perform(project.id, project.path_with_namespace,
subject.perform(project.id, '/test/path', project.path_with_namespace,
fork_project.namespace.path)
end
@ -49,6 +52,7 @@ describe RepositoryForkWorker do
subject.perform(
project.id,
'/test/path',
project.path_with_namespace,
fork_project.namespace.path)
end

View File

@ -2,7 +2,7 @@
# The image already has Hex installed. You might want to consider to use `elixir:latest`
image: trenpixster/elixir:latest
# Pic zero or more services to be used on all builds.
# Pick zero or more services to be used on all builds.
# Only needed when using a docker container to run your tests in.
# Check out: http://docs.gitlab.com/ce/ci/docker/using_docker_images.html#what-is-service
services: