Commit c5c52cc2 authored by Marin Jankovski's avatar Marin Jankovski

Merge branch 'master' of dev.gitlab.org:gitlab/gitlab-ee

parents 8c927937 7202371c
...@@ -877,7 +877,7 @@ Lint/ParenthesesAsGroupedExpression: ...@@ -877,7 +877,7 @@ Lint/ParenthesesAsGroupedExpression:
Checks for method calls with a space before the opening Checks for method calls with a space before the opening
parenthesis. parenthesis.
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#parens-no-spaces' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#parens-no-spaces'
Enabled: false Enabled: true
Lint/RequireParentheses: Lint/RequireParentheses:
Description: >- Description: >-
......
Please view this file on the master branch, on stable branches it's out of date. Please view this file on the master branch, on stable branches it's out of date.
v 7.9.0 (unreleased) v 7.9.0 (unreleased)
- Fix merge request URL passed to Webhooks. (Stan Hu)
- Fix bug that caused a server error when editing a comment to "+1" or "-1" (Stan Hu)
- Move labels/milestones tabs to sidebar - Move labels/milestones tabs to sidebar
- Upgrade Rails gem to version 4.1.9. - Upgrade Rails gem to version 4.1.9.
- Improve error messages for file edit failures - Improve error messages for file edit failures
...@@ -16,6 +18,8 @@ v 7.9.0 (unreleased) ...@@ -16,6 +18,8 @@ v 7.9.0 (unreleased)
- Allow user confirmation to be skipped for new users via API - Allow user confirmation to be skipped for new users via API
- Add a service to send updates to an Irker gateway (Romain Coltel) - Add a service to send updates to an Irker gateway (Romain Coltel)
- Ignore case of LDAP user DN when checking group membership. - Ignore case of LDAP user DN when checking group membership.
- Add brakeman (security scanner for Ruby on Rails)
- Slack username and channel options
v 7.8.1 v 7.8.1
- Fix run of custom post receive hooks - Fix run of custom post receive hooks
...@@ -92,7 +96,6 @@ v 7.8.0 ...@@ -92,7 +96,6 @@ v 7.8.0
- Improve database performance for GitLab - Improve database performance for GitLab
- Add Asana service (Jeremy Benoist) - Add Asana service (Jeremy Benoist)
- Improve project web hooks with extra data - Improve project web hooks with extra data
- Slack username and channel options
v 7.7.2 v 7.7.2
- Update GitLab Shell to version 2.4.2 that fixes a bug when developers can push to protected branch - Update GitLab Shell to version 2.4.2 that fixes a bug when developers can push to protected branch
......
...@@ -2,6 +2,7 @@ v 7.9.0 (unreleased) ...@@ -2,6 +2,7 @@ v 7.9.0 (unreleased)
- Strip prefixes and suffixes from synced SSH keys: - Strip prefixes and suffixes from synced SSH keys:
`SSHKey:ssh-rsa keykeykey` and `ssh-rsa keykeykey (SSH key)` will now work `SSHKey:ssh-rsa keykeykey` and `ssh-rsa keykeykey (SSH key)` will now work
- Check if LDAP admin group exists before querying for user membership - Check if LDAP admin group exists before querying for user membership
- Use one custom header logo for all GitLab themes in appearance settings
v 7.8.0 v 7.8.0
- Improved Jira issue closing integration - Improved Jira issue closing integration
......
...@@ -201,6 +201,7 @@ gem "virtus" ...@@ -201,6 +201,7 @@ gem "virtus"
gem 'addressable' gem 'addressable'
group :development do group :development do
gem 'brakeman', require: false
gem "annotate", "~> 2.6.0.beta2" gem "annotate", "~> 2.6.0.beta2"
gem "letter_opener" gem "letter_opener"
gem 'quiet_assets', '~> 1.0.1' gem 'quiet_assets', '~> 1.0.1'
......
...@@ -63,6 +63,16 @@ GEM ...@@ -63,6 +63,16 @@ GEM
bootstrap-sass (3.3.3) bootstrap-sass (3.3.3)
autoprefixer-rails (>= 5.0.0.1) autoprefixer-rails (>= 5.0.0.1)
sass (>= 3.2.19) sass (>= 3.2.19)
brakeman (3.0.1)
erubis (~> 2.6)
fastercsv (~> 1.5)
haml (>= 3.0, < 5.0)
highline (~> 1.6.20)
multi_json (~> 1.2)
ruby2ruby (~> 2.1.1)
ruby_parser (~> 3.5.0)
sass (~> 3.0)
terminal-table (~> 1.4)
browser (0.7.2) browser (0.7.2)
builder (3.2.2) builder (3.2.2)
byebug (3.2.0) byebug (3.2.0)
...@@ -154,6 +164,7 @@ GEM ...@@ -154,6 +164,7 @@ GEM
multipart-post (~> 1.2.0) multipart-post (~> 1.2.0)
faraday_middleware (0.9.0) faraday_middleware (0.9.0)
faraday (>= 0.7.4, < 0.9) faraday (>= 0.7.4, < 0.9)
fastercsv (1.5.5)
ffaker (1.22.1) ffaker (1.22.1)
ffi (1.9.3) ffi (1.9.3)
fog (1.21.0) fog (1.21.0)
...@@ -258,6 +269,7 @@ GEM ...@@ -258,6 +269,7 @@ GEM
haml (>= 3.1, < 5.0) haml (>= 3.1, < 5.0)
railties (>= 4.0.1) railties (>= 4.0.1)
hashie (2.1.2) hashie (2.1.2)
highline (1.6.21)
hike (1.2.3) hike (1.2.3)
hipchat (1.4.0) hipchat (1.4.0)
httparty httparty
...@@ -497,6 +509,11 @@ GEM ...@@ -497,6 +509,11 @@ GEM
rainbow (>= 1.99.1, < 3.0) rainbow (>= 1.99.1, < 3.0)
ruby-progressbar (~> 1.4) ruby-progressbar (~> 1.4)
ruby-progressbar (1.7.1) ruby-progressbar (1.7.1)
ruby2ruby (2.1.3)
ruby_parser (~> 3.1)
sexp_processor (~> 4.0)
ruby_parser (3.5.0)
sexp_processor (~> 4.1)
rubyntlm (0.4.0) rubyntlm (0.4.0)
rubypants (0.2.0) rubypants (0.2.0)
rugged (0.21.4) rugged (0.21.4)
...@@ -522,6 +539,7 @@ GEM ...@@ -522,6 +539,7 @@ GEM
select2-rails (3.5.2) select2-rails (3.5.2)
thor (~> 0.14) thor (~> 0.14)
settingslogic (2.0.9) settingslogic (2.0.9)
sexp_processor (4.4.5)
shoulda-matchers (2.7.0) shoulda-matchers (2.7.0)
activesupport (>= 3.0.0) activesupport (>= 3.0.0)
sidekiq (3.3.0) sidekiq (3.3.0)
...@@ -577,6 +595,7 @@ GEM ...@@ -577,6 +595,7 @@ GEM
temple (0.6.7) temple (0.6.7)
term-ansicolor (1.2.2) term-ansicolor (1.2.2)
tins (~> 0.8) tins (~> 0.8)
terminal-table (1.4.5)
test_after_commit (0.2.2) test_after_commit (0.2.2)
therubyracer (0.12.0) therubyracer (0.12.0)
libv8 (~> 3.16.14.0) libv8 (~> 3.16.14.0)
...@@ -656,6 +675,7 @@ DEPENDENCIES ...@@ -656,6 +675,7 @@ DEPENDENCIES
better_errors better_errors
binding_of_caller binding_of_caller
bootstrap-sass (~> 3.0) bootstrap-sass (~> 3.0)
brakeman
browser browser
byebug byebug
cal-heatmap-rails (~> 0.0.1) cal-heatmap-rails (~> 0.0.1)
......
...@@ -11,6 +11,7 @@ class @ProjectUsersSelect ...@@ -11,6 +11,7 @@ class @ProjectUsersSelect
Api.projectUsers project_id, query.term, (users) -> Api.projectUsers project_id, query.term, (users) ->
data = { results: users } data = { results: users }
if query.term.length == 0
nullUser = { nullUser = {
name: 'Unassigned', name: 'Unassigned',
avatar: null, avatar: null,
......
...@@ -3,10 +3,9 @@ ...@@ -3,10 +3,9 @@
margin-bottom: 20px; margin-bottom: 20px;
} }
.appearance-dark-logo-preview {
background-color: #F1F1F1;
}
.appearance-light-logo-preview { .appearance-light-logo-preview {
background-color: #373737; background-color: $style_color;
max-width: 72px;
padding: 10px;
margin-bottom: 10px;
} }
...@@ -37,11 +37,9 @@ class Admin::AppearancesController < Admin::ApplicationController ...@@ -37,11 +37,9 @@ class Admin::AppearancesController < Admin::ApplicationController
def header_logos def header_logos
appearance = Appearance.last appearance = Appearance.last
appearance.remove_light_logo! appearance.remove_light_logo!
appearance.remove_dark_logo!
appearance.save appearance.save
redirect_to admin_appearances_path, notice: 'Header logos were succesfully removed.' redirect_to admin_appearances_path, notice: 'Header logo were succesfully removed.'
end end
private private
......
...@@ -3,7 +3,7 @@ class Import::BaseController < ApplicationController ...@@ -3,7 +3,7 @@ class Import::BaseController < ApplicationController
private private
def get_or_create_namespace def get_or_create_namespace
existing_namespace = Namespace.find_by("path = ? OR name = ?", @target_namespace, @target_namespace) existing_namespace = Namespace.find_by_path_or_name(@target_namespace)
if existing_namespace if existing_namespace
if existing_namespace.owner == current_user if existing_namespace.owner == current_user
......
...@@ -26,7 +26,7 @@ class Projects::ImportsController < Projects::ApplicationController ...@@ -26,7 +26,7 @@ class Projects::ImportsController < Projects::ApplicationController
def show def show
unless @project.import_in_progress? unless @project.import_in_progress?
if @project.import_finished? if @project.import_finished?
redirect_to(@project) and return redirect_to(project_path(@project)) and return
else else
redirect_to new_namespace_project_import_path(@project.namespace, redirect_to new_namespace_project_import_path(@project.namespace,
@project) && return @project) && return
......
...@@ -3,10 +3,10 @@ class Projects::NotesController < Projects::ApplicationController ...@@ -3,10 +3,10 @@ class Projects::NotesController < Projects::ApplicationController
before_filter :authorize_read_note! before_filter :authorize_read_note!
before_filter :authorize_write_note!, only: [:create] before_filter :authorize_write_note!, only: [:create]
before_filter :authorize_admin_note!, only: [:update, :destroy] before_filter :authorize_admin_note!, only: [:update, :destroy]
before_filter :find_current_user_notes, except: [:destroy, :delete_attachment]
def index def index
current_fetched_at = Time.now.to_i current_fetched_at = Time.now.to_i
@notes = NotesFinder.new.execute(project, current_user, params)
notes_json = { notes: [], last_fetched_at: current_fetched_at } notes_json = { notes: [], last_fetched_at: current_fetched_at }
...@@ -116,4 +116,10 @@ class Projects::NotesController < Projects::ApplicationController ...@@ -116,4 +116,10 @@ class Projects::NotesController < Projects::ApplicationController
:attachment, :line_code, :commit_id :attachment, :line_code, :commit_id
) )
end end
private
def find_current_user_notes
@notes = NotesFinder.new.execute(project, current_user, params)
end
end end
...@@ -30,12 +30,7 @@ class Projects::TeamMembersController < Projects::ApplicationController ...@@ -30,12 +30,7 @@ class Projects::TeamMembersController < Projects::ApplicationController
AuditEventService.new(current_user, @project, details).security_event AuditEventService.new(current_user, @project, details).security_event
end end
if params[:redirect_to] redirect_to namespace_project_team_index_path(@project.namespace, @project)
redirect_to params[:redirect_to]
else
redirect_to namespace_project_team_index_path(@project.namespace,
@project)
end
end end
def update def update
......
...@@ -97,7 +97,7 @@ class Projects::WikisController < Projects::ApplicationController ...@@ -97,7 +97,7 @@ class Projects::WikisController < Projects::ApplicationController
@project_wiki.wiki @project_wiki.wiki
rescue ProjectWiki::CouldNotCreateWikiError => ex rescue ProjectWiki::CouldNotCreateWikiError => ex
flash[:notice] = "Could not create Wiki Repository at this time. Please try again later." flash[:notice] = "Could not create Wiki Repository at this time. Please try again later."
redirect_to @project redirect_to project_path(@project)
return false return false
end end
......
class ProjectsController < ApplicationController class ProjectsController < ApplicationController
prepend_before_filter :render_go_import, only: [:show]
skip_before_filter :authenticate_user!, only: [:show] skip_before_filter :authenticate_user!, only: [:show]
before_filter :project, except: [:new, :create] before_filter :project, except: [:new, :create]
before_filter :repository, except: [:new, :create] before_filter :repository, except: [:new, :create]
...@@ -185,4 +186,14 @@ class ProjectsController < ApplicationController ...@@ -185,4 +186,14 @@ class ProjectsController < ApplicationController
end end
end end
end end
def render_go_import
return unless params["go-get"] == "1"
@namespace = params[:namespace_id]
@id = params[:project_id] || params[:id]
@id = @id.gsub(/\.git\Z/, "")
render "go_import", layout: false
end
end end
...@@ -3,22 +3,54 @@ class UploadsController < ApplicationController ...@@ -3,22 +3,54 @@ class UploadsController < ApplicationController
before_filter :authorize_access before_filter :authorize_access
def show def show
model = params[:model].camelize.constantize.find(params[:id]) unless upload_model && upload_mount
uploader = model.send(params[:mounted_as]) return not_found!
end
return not_found! if model.respond_to?(:project) && !can?(current_user, :read_project, model.project) model = upload_model.find(params[:id])
uploader = model.send(upload_mount)
return redirect_to uploader.url unless uploader.file_storage? if model.respond_to?(:project) && !can?(current_user, :read_project, model.project)
return not_found!
end
return not_found! unless uploader.file.exists? unless uploader.file_storage?
return redirect_to uploader.url
end
unless uploader.file.exists?
return not_found!
end
disposition = uploader.image? ? 'inline' : 'attachment' disposition = uploader.image? ? 'inline' : 'attachment'
send_file uploader.file.path, disposition: disposition send_file uploader.file.path, disposition: disposition
end end
private
def authorize_access def authorize_access
unless params[:mounted_as] == 'avatar' unless %w(avatar logo light_logo).include?(params[:mounted_as])
authenticate_user! && reject_blocked! authenticate_user! && reject_blocked!
end end
end end
def upload_model
upload_models = {
user: User,
project: Project,
note: Note,
group: Group,
appearance: Appearance
}
upload_models[params[:model].to_sym]
end
def upload_mount
upload_mounts = %w(avatar attachment file logo light_logo)
if upload_mounts.include?(params[:mounted_as])
params[:mounted_as]
end
end
end end
...@@ -15,21 +15,6 @@ module AppearancesHelper ...@@ -15,21 +15,6 @@ module AppearancesHelper
end end
end end
def brand_header_logo
if brand_item.header_logos?
haml_tag(:style) do
# Dark theme/light logo
haml_concat ".dark_theme .app_logo a h1 {" \
"background: url('#{brand_item.light_logo}') " \
"no-repeat center center !important; }"
# Light theme/dark logo
haml_concat ".light_theme .app_logo a h1 {" \
"background: url('#{brand_item.dark_logo}') " \
"no-repeat center center !important; }"
end
end
end
def brand_text def brand_text
markdown(brand_item.description) markdown(brand_item.description)
end end
...@@ -39,6 +24,10 @@ module AppearancesHelper ...@@ -39,6 +24,10 @@ module AppearancesHelper
end end
def brand_header_logo def brand_header_logo
if brand_item && brand_item.light_logo?
image_tag brand_item.light_logo
else
image_tag 'logo-white.png' image_tag 'logo-white.png'
end end
end
end end
...@@ -4,9 +4,9 @@ module NotesHelper ...@@ -4,9 +4,9 @@ module NotesHelper
(@noteable.class.name == note.noteable_type && !note.for_diff_line?) (@noteable.class.name == note.noteable_type && !note.for_diff_line?)
end end
def note_target_fields def note_target_fields(note)
hidden_field_tag(:target_type, @target_type) + hidden_field_tag(:target_type, note.noteable.class.name.underscore) +
hidden_field_tag(:target_id, @target_id) hidden_field_tag(:target_id, note.noteable.id)
end end
def link_to_commit_diff_line_note(note) def link_to_commit_diff_line_note(note)
......
...@@ -2,19 +2,8 @@ class Appearance < ActiveRecord::Base ...@@ -2,19 +2,8 @@ class Appearance < ActiveRecord::Base
validates :title, presence: true validates :title, presence: true
validates :description, presence: true validates :description, presence: true
validates :logo, file_size: { maximum: 1000.kilobytes.to_i } validates :logo, file_size: { maximum: 1000.kilobytes.to_i }
validates :dark_logo, validates :light_logo, file_size: { maximum: 1000.kilobytes.to_i }
file_size: { maximum: 1000.kilobytes.to_i },
presence: true, if: :light_logo?
validates :light_logo,
file_size: { maximum: 1000.kilobytes.to_i },
presence: true, if: :dark_logo?
mount_uploader :logo, AttachmentUploader mount_uploader :logo, AttachmentUploader
mount_uploader :dark_logo, AttachmentUploader
mount_uploader :light_logo, AttachmentUploader mount_uploader :light_logo, AttachmentUploader
def header_logos?
dark_logo? && light_logo?
end
end end
...@@ -48,6 +48,11 @@ class Namespace < ActiveRecord::Base ...@@ -48,6 +48,11 @@ class Namespace < ActiveRecord::Base
where('lower(path) = :value', value: path.downcase).first where('lower(path) = :value', value: path.downcase).first
end end
# Case insensetive search for namespace by path or name
def self.find_by_path_or_name(path)
find_by("lower(path) = :path OR lower(name) = :path", path: path.downcase)
end
def self.search(query) def self.search(query)
where("name LIKE :query OR path LIKE :query", query: "%#{query}%") where("name LIKE :query OR path LIKE :query", query: "%#{query}%")
end end
......
...@@ -146,7 +146,7 @@ class Repository ...@@ -146,7 +146,7 @@ class Repository
end end
def timestamps_by_user_log(user) def timestamps_by_user_log(user)
args = %W(git log --author=#{user.email} --since=#{(Date.today - 1.year).to_s} --pretty=format:%cd --date=short) args = %W(git log --author=#{user.email} --since=#{(Date.today - 1.year).to_s} --branches --pretty=format:%cd --date=short)
dates = Gitlab::Popen.popen(args, path_to_repo).first.split("\n") dates = Gitlab::Popen.popen(args, path_to_repo).first.split("\n")
if dates.present? if dates.present?
......
...@@ -33,23 +33,16 @@ ...@@ -33,23 +33,16 @@
%legend %legend
Navigation bar: Navigation bar:
.form-group .form-group
= f.label :dark_logo, class: 'control-label' = f.label :light_logo, 'Header logo', class: 'control-label'
.col-sm-10
- if @appearance.dark_logo?
= image_tag @appearance.dark_logo, class: 'appearance-dark-logo-preview'
= f.file_field :dark_logo, class: ""
.hint
Maximum size is 1MB, page optimized for logo size 40x40px
= f.label :light_logo, class: 'control-label'
.col-sm-10 .col-sm-10
- if @appearance.light_logo? - if @appearance.light_logo?
= image_tag @appearance.light_logo, class: 'appearance-light-logo-preview' = image_tag @appearance.light_logo, class: 'appearance-light-logo-preview'
= f.file_field :light_logo, class: "" = f.file_field :light_logo, class: ""
.hint .hint
Maximum size is 1MB, page optimized for logo size 41x41px Maximum size is 1MB, page optimized for logo size 72x72px
-if @appearance.light_logo? || @appearance.dark_logo? -if @appearance.light_logo?
%br %br
= link_to 'Remove header logos', header_logos_admin_appearances_path, data: { confirm: "Header logos will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-small remove-logo" = link_to 'Remove header logo', header_logos_admin_appearances_path, data: { confirm: "Header logos will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-small remove-logo"
.form-actions .form-actions
......
...@@ -44,7 +44,7 @@ ...@@ -44,7 +44,7 @@
Projects (#{@projects.total_count}) Projects (#{@projects.total_count})
.panel-head-actions .panel-head-actions
.dropdown.inline .dropdown.inline
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"} %button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
%span.light sort: %span.light sort:
- if @sort.present? - if @sort.present?
= sort_options_hash[@sort] = sort_options_hash[@sort]
......
...@@ -30,19 +30,3 @@ ...@@ -30,19 +30,3 @@
- elsif signin_enabled? - elsif signin_enabled?
= render 'devise/sessions/new_base' = render 'devise/sessions/new_base'
- else
%div
No authentication methods configured.
- if Gitlab.config.omniauth.enabled && devise_mapping.omniauthable?
.clearfix.prepend-top-20
%p
%span.light
Sign in with &nbsp;
- providers = additional_providers
- providers.each do |provider|
%span.light
- if default_providers.include?(provider)
= link_to authbutton(provider, 32), omniauth_authorize_path(resource_name, provider)
- else
= link_to provider.to_s.titleize, omniauth_authorize_path(resource_name, provider), class: "btn"
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
.pull-right .pull-right
.dropdown.inline .dropdown.inline
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"} %button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
%span.light sort: %span.light sort:
- if @sort.present? - if @sort.present?
= sort_options_hash[@sort] = sort_options_hash[@sort]
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
.pull-right .pull-right
.dropdown.inline .dropdown.inline
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"} %button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
%span.light sort: %span.light sort:
- if @sort.present? - if @sort.present?
= sort_options_hash[@sort] = sort_options_hash[@sort]
......
...@@ -14,7 +14,8 @@ ...@@ -14,7 +14,8 @@
%strong= member.human_access %strong= member.human_access
- if show_controls - if show_controls
- if can?(current_user, :modify, member) - if can?(current_user, :modify, member)
= link_to '#', class: "btn-tiny btn js-toggle-button", title: 'Edit access level' do = button_tag class: "btn-tiny btn js-toggle-button",
title: 'Edit access level', type: 'button' do
%i.fa.fa-pencil-square-o %i.fa.fa-pencil-square-o
- if can?(current_user, :destroy, member) - if can?(current_user, :destroy, member)
- if current_user == member.user - if current_user == member.user
......
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
= link_to reset_access_group_ldap_path(@group), class: 'btn btn-grouped', data: { confirm: "Force GitLab to do LDAP permission checks for all group members? All members besides yourself will be reduced to 'Guest' access until their next interaction with GitLab." }, method: :put do = link_to reset_access_group_ldap_path(@group), class: 'btn btn-grouped', data: { confirm: "Force GitLab to do LDAP permission checks for all group members? All members besides yourself will be reduced to 'Guest' access until their next interaction with GitLab." }, method: :put do
Clear LDAP permission cache Clear LDAP permission cache
= link_to '#', class: 'btn btn-new js-toggle-button' do = button_tag class: 'btn btn-new js-toggle-button', type: 'button' do
Add members Add members
%i.fa.fa-chevron-down %i.fa.fa-chevron-down
......
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
.navbar-inner .navbar-inner
.container .container
%div.app_logo %div.app_logo
- brand_header_logo if brand_item
= link_to root_path, class: "home has_bottom_tooltip", title: "Dashboard" do = link_to root_path, class: "home has_bottom_tooltip", title: "Dashboard" do
= brand_header_logo = brand_header_logo
%h1.title= title %h1.title= title
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
New branch New branch
&nbsp; &nbsp;
.dropdown.inline .dropdown.inline
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"} %button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
%span.light sort: %span.light sort:
- if @sort.present? - if @sort.present?
= @sort.humanize = @sort.humanize
......
!!! 5
%html
%head
- web_url = [Gitlab.config.gitlab.url, @namespace, @id].join('/')
%meta{name: "go-import", content: "#{web_url.split('://')[1]} git #{web_url}.git"}
.note-edit-form .note-edit-form
= form_for note, url: namespace_project_note_path(@project.namespace, @project, note), method: :put, remote: true, authenticity_token: true do |f| = form_for note, url: namespace_project_note_path(@project.namespace, @project, note), method: :put, remote: true, authenticity_token: true do |f|
= note_target_fields(note)
= render layout: 'projects/md_preview', locals: { preview_class: "note-text" } do = render layout: 'projects/md_preview', locals: { preview_class: "note-text" } do
= render 'projects/zen', f: f, attr: :note, = render 'projects/zen', f: f, attr: :note,
classes: 'note_text js-note-text' classes: 'note_text js-note-text'
......
= form_for [@project.namespace.becomes(Namespace), @project, @note], remote: true, html: { :'data-type' => 'json', multipart: true, id: nil, class: "new_note js-new-note-form common-note-form gfm-form" }, authenticity_token: true do |f| = form_for [@project.namespace.becomes(Namespace), @project, @note], remote: true, html: { :'data-type' => 'json', multipart: true, id: nil, class: "new_note js-new-note-form common-note-form gfm-form" }, authenticity_token: true do |f|
= note_target_fields = note_target_fields(@note)
= f.hidden_field :commit_id = f.hidden_field :commit_id
= f.hidden_field :line_code = f.hidden_field :line_code
= f.hidden_field :noteable_id = f.hidden_field :noteable_id
......
.dropdown.inline .dropdown.inline
%a.dropdown-toggle.btn.btn-small{href: '#', "data-toggle" => "dropdown"} %button.dropdown-toggle.btn.btn-small{type: 'button', 'data-toggle' => 'dropdown'}
%i.fa.fa-tags %i.fa.fa-tags
%span.light Group: %span.light Group:
- if @group.present? - if @group.present?
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
= group.name = group.name
.dropdown.inline.prepend-left-10.project-filter .dropdown.inline.prepend-left-10.project-filter
%a.dropdown-toggle.btn.btn-small{href: '#', "data-toggle" => "dropdown"} %button.dropdown-toggle.btn.btn-small{type: 'button', 'data-toggle' => 'dropdown'}
%i.fa.fa-tags %i.fa.fa-tags
%span.light Project: %span.light Project:
- if @project.present? - if @project.present?
......
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
disabled: !can?(current_user, :modify_issue, @project) disabled: !can?(current_user, :modify_issue, @project)
.issues-other-filters .issues-other-filters
.dropdown.inline.assignee-filter .dropdown.inline.assignee-filter
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"} %button.dropdown-toggle.btn{type: 'button', "data-toggle" => "dropdown"}
%i.fa.fa-user %i.fa.fa-user
%span.light assignee: %span.light assignee:
- if @assignee.present? - if @assignee.present?
...@@ -45,7 +45,7 @@ ...@@ -45,7 +45,7 @@
= user.name = user.name
.dropdown.inline.prepend-left-10.author-filter .dropdown.inline.prepend-left-10.author-filter
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"} %button.dropdown-toggle.btn{type: 'button', "data-toggle" => "dropdown"}
%i.fa.fa-user %i.fa.fa-user
%span.light author: %span.light author:
- if @author.present? - if @author.present?
...@@ -68,7 +68,7 @@ ...@@ -68,7 +68,7 @@
= user.name = user.name
.dropdown.inline.prepend-left-10.milestone-filter .dropdown.inline.prepend-left-10.milestone-filter
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"} %button.dropdown-toggle.btn{type: 'button', "data-toggle" => "dropdown"}
%i.fa.fa-clock-o %i.fa.fa-clock-o
%span.light milestone: %span.light milestone:
- if @milestone.present? - if @milestone.present?
...@@ -92,7 +92,7 @@ ...@@ -92,7 +92,7 @@
- if @project - if @project
.dropdown.inline.prepend-left-10.labels-filter .dropdown.inline.prepend-left-10.labels-filter
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"} %button.dropdown-toggle.btn{type: 'button', "data-toggle" => "dropdown"}
%i.fa.fa-tags %i.fa.fa-tags
%span.light label: %span.light label:
- if params[:label_name].present? - if params[:label_name].present?
......
.dropdown.inline.prepend-left-10 .dropdown.inline.prepend-left-10
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"} %button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
%span.light sort: %span.light sort:
- if @sort.present? - if @sort.present?
= sort_options_hash[@sort] = sort_options_hash[@sort]
......
...@@ -37,7 +37,10 @@ bundle install --deployment --path vendor/bundle (Setup) ...@@ -37,7 +37,10 @@ bundle install --deployment --path vendor/bundle (Setup)
cp config/gitlab.yml.example config/gitlab.yml (Setup) cp config/gitlab.yml.example config/gitlab.yml (Setup)
bundle exec rake db:create (Setup) bundle exec rake db:create (Setup)
bundle exec rake spinach (Thread #1) bundle exec rake spinach (Thread #1)
bundle exec rake spec (Thread #2) bundle exec rake spec (thread #2)
bundle exec rake rubocop (thread #3)
bundle exec rake brakeman (thread #4)
bundle exec rake jasmine:ci (thread #5)
``` ```
Use rubygems mirror. Use rubygems mirror.
# Installation # Installation from source
## Consider the Omnibus package installation ## Consider the Omnibus package installation
......
...@@ -41,3 +41,9 @@ Feature: Project Commits Comments ...@@ -41,3 +41,9 @@ Feature: Project Commits Comments
Given I leave a comment like "XML attached" Given I leave a comment like "XML attached"
And I delete a comment And I delete a comment
Then I should not see a comment saying "XML attached" Then I should not see a comment saying "XML attached"
@javascript
Scenario: I can edit a comment with +1
Given I leave a comment like "XML attached"
And I edit the last comment with a +1
Then I should see +1 in the description
...@@ -139,6 +139,15 @@ Feature: Project Issues ...@@ -139,6 +139,15 @@ Feature: Project Issues
And I leave a comment with task markdown And I leave a comment with task markdown
Then I should not see task checkboxes in the comment Then I should not see task checkboxes in the comment
@javascript
Scenario: Issue notes should be editable with +1
Given project "Shop" has "Tasks-open" open issue with task markdown
When I visit issue page "Tasks-open"
And I leave a comment with a header containing "Comment with a header"
Then The comment with the header should not have an ID
And I edit the last comment with a +1
Then I should see +1 in the description
# Task status in issues list # Task status in issues list
Scenario: Issues list should display task status Scenario: Issues list should display task status
......
...@@ -39,7 +39,6 @@ class Spinach::Features::AdminAppearance < Spinach::FeatureSteps ...@@ -39,7 +39,6 @@ class Spinach::Features::AdminAppearance < Spinach::FeatureSteps
step 'I attach header logos' do step 'I attach header logos' do
attach_file(:appearance_light_logo, File.join(Rails.root, 'public', 'header_logo_light.png')) attach_file(:appearance_light_logo, File.join(Rails.root, 'public', 'header_logo_light.png'))
attach_file(:appearance_dark_logo, File.join(Rails.root, 'public', 'header_logo_dark.png'))
click_button 'Save' click_button 'Save'
end end
...@@ -49,7 +48,6 @@ class Spinach::Features::AdminAppearance < Spinach::FeatureSteps ...@@ -49,7 +48,6 @@ class Spinach::Features::AdminAppearance < Spinach::FeatureSteps
step 'I should see header logos' do step 'I should see header logos' do
page.should have_xpath('//img[@src="/uploads/appearance/light_logo/1/header_logo_light.png"]') page.should have_xpath('//img[@src="/uploads/appearance/light_logo/1/header_logo_light.png"]')
page.should have_xpath('//img[@src="/uploads/appearance/dark_logo/1/header_logo_dark.png"]')
end end
step 'I remove the logo' do step 'I remove the logo' do
...@@ -57,7 +55,7 @@ class Spinach::Features::AdminAppearance < Spinach::FeatureSteps ...@@ -57,7 +55,7 @@ class Spinach::Features::AdminAppearance < Spinach::FeatureSteps
end end
step 'I remove the header logos' do step 'I remove the header logos' do
click_link 'Remove header logos' click_link 'Remove header logo'
end end
step 'I should see logo removed' do step 'I should see logo removed' do
...@@ -66,7 +64,6 @@ class Spinach::Features::AdminAppearance < Spinach::FeatureSteps ...@@ -66,7 +64,6 @@ class Spinach::Features::AdminAppearance < Spinach::FeatureSteps
step 'I should see header logos removed' do step 'I should see header logos removed' do
page.should_not have_xpath('//img[@src="/uploads/appearance/light_logo/1/header_logo_light.png"]') page.should_not have_xpath('//img[@src="/uploads/appearance/light_logo/1/header_logo_light.png"]')
page.should_not have_xpath('//img[@src="/uploads/appearance/dark_logo/1/header_logo_dark.png"]')
end end
def appearance def appearance
......
...@@ -29,7 +29,7 @@ class Spinach::Features::Groups < Spinach::FeatureSteps ...@@ -29,7 +29,7 @@ class Spinach::Features::Groups < Spinach::FeatureSteps
step 'I select user "Mary Jane" from list with role "Reporter"' do step 'I select user "Mary Jane" from list with role "Reporter"' do
user = User.find_by(name: "Mary Jane") || create(:user, name: "Mary Jane") user = User.find_by(name: "Mary Jane") || create(:user, name: "Mary Jane")
click_link 'Add members' click_button 'Add members'
within ".users-group-form" do within ".users-group-form" do
select2(user.id, from: "#user_ids", multiple: true) select2(user.id, from: "#user_ids", multiple: true)
select "Reporter", from: "access_level" select "Reporter", from: "access_level"
......
...@@ -135,4 +135,21 @@ module SharedNote ...@@ -135,4 +135,21 @@ module SharedNote
'li.note div.timeline-content input[type="checkbox"]' 'li.note div.timeline-content input[type="checkbox"]'
) )
end end
step 'I edit the last comment with a +1' do
find(".note").hover
find('.js-note-edit').click
within(".current-note-edit-form") do
fill_in 'note[note]', with: '+1 Awesome!'
click_button 'Save Comment'
sleep 0.05
end
end
step 'I should see +1 in the description' do
within(".note") do
page.should have_content("+1 Awesome!")
end
end
end end
...@@ -153,7 +153,7 @@ module API ...@@ -153,7 +153,7 @@ module API
class ProjectEntity < Grape::Entity class ProjectEntity < Grape::Entity
expose :id, :iid expose :id, :iid
expose (:project_id) { |entity| entity.project.id } expose(:project_id) { |entity| entity.project.id }
expose :title, :description expose :title, :description
expose :state, :created_at, :updated_at expose :state, :created_at, :updated_at
end end
......
...@@ -16,6 +16,17 @@ module API ...@@ -16,6 +16,17 @@ module API
# #
post "/allowed" do post "/allowed" do
status 200 status 200
actor = if params[:key_id]
Key.find_by(id: params[:key_id])
elsif params[:user_id]
User.find_by(id: params[:user_id])
end
unless actor
return Gitlab::GitAccessStatus.new(false, 'No such user or key')
end
project_path = params[:project] project_path = params[:project]
# Check for *.wiki repositories. # Check for *.wiki repositories.
...@@ -32,21 +43,8 @@ module API ...@@ -32,21 +43,8 @@ module API
project = Project.find_with_namespace(project_path) project = Project.find_with_namespace(project_path)
unless project if project
return Gitlab::GitAccessStatus.new(false, 'No such project') status = access.check(
end
actor = if params[:key_id]
Key.find_by(id: params[:key_id])
elsif params[:user_id]
User.find_by(id: params[:user_id])
end
unless actor
return Gitlab::GitAccessStatus.new(false, 'No such user or key')
end
access.check(
actor, actor,
params[:action], params[:action],
project, project,
...@@ -54,6 +52,13 @@ module API ...@@ -54,6 +52,13 @@ module API
) )
end end
if project && status && status.allowed?
status
else
Gitlab::GitAccessStatus.new(false, 'No such project')
end
end
# #
# Discover user by ssh key # Discover user by ssh key
# #
......
...@@ -10,8 +10,9 @@ module Grack ...@@ -10,8 +10,9 @@ module Grack
@request = Rack::Request.new(env) @request = Rack::Request.new(env)
@auth = Request.new(env) @auth = Request.new(env)
# Need this patch due to the rails mount @gitlab_ci = false
# Need this patch due to the rails mount
# Need this if under RELATIVE_URL_ROOT # Need this if under RELATIVE_URL_ROOT
unless Gitlab.config.gitlab.relative_url_root.empty? unless Gitlab.config.gitlab.relative_url_root.empty?
# If website is mounted using relative_url_root need to remove it first # If website is mounted using relative_url_root need to remove it first
...@@ -22,8 +23,12 @@ module Grack ...@@ -22,8 +23,12 @@ module Grack
@env['SCRIPT_NAME'] = "" @env['SCRIPT_NAME'] = ""
if project
auth! auth!
if project && authorized_request?
@app.call(env)
elsif @user.nil? && !@gitlab_ci
unauthorized
else else
render_not_found render_not_found
end end
...@@ -32,7 +37,8 @@ module Grack ...@@ -32,7 +37,8 @@ module Grack
private private
def auth! def auth!
if @auth.provided? return unless @auth.provided?
return bad_request unless @auth.basic? return bad_request unless @auth.basic?
# Authentication with username and password # Authentication with username and password
...@@ -41,7 +47,8 @@ module Grack ...@@ -41,7 +47,8 @@ module Grack
# Allow authentication for GitLab CI service # Allow authentication for GitLab CI service
# if valid token passed # if valid token passed
if gitlab_ci_request?(login, password) if gitlab_ci_request?(login, password)
return @app.call(env) @gitlab_ci = true
return
end end
@user = authenticate_user(login, password) @user = authenticate_user(login, password)
...@@ -52,15 +59,8 @@ module Grack ...@@ -52,15 +59,8 @@ module Grack
end end
end end
if authorized_request?
@app.call(env)
else
unauthorized
end
end
def gitlab_ci_request?(login, password) def gitlab_ci_request?(login, password)
if login == "gitlab-ci-token" && project.gitlab_ci? if login == "gitlab-ci-token" && project && project.gitlab_ci?
token = project.gitlab_ci_service.token token = project.gitlab_ci_service.token
if token.present? && token == password && git_cmd == 'git-upload-pack' if token.present? && token == password && git_cmd == 'git-upload-pack'
...@@ -107,6 +107,8 @@ module Grack ...@@ -107,6 +107,8 @@ module Grack
end end
def authorized_request? def authorized_request?
return true if @gitlab_ci
case git_cmd case git_cmd
when *Gitlab::GitAccess::DOWNLOAD_COMMANDS when *Gitlab::GitAccess::DOWNLOAD_COMMANDS
if user if user
...@@ -141,7 +143,9 @@ module Grack ...@@ -141,7 +143,9 @@ module Grack
end end
def project def project
@project ||= project_by_path(@request.path_info) return @project if defined?(@project)
@project = project_by_path(@request.path_info)
end end
def project_by_path(path) def project_by_path(path)
......
module Gitlab module Gitlab
class UrlBuilder class UrlBuilder
include Rails.application.routes.url_helpers include Rails.application.routes.url_helpers
include GitlabRoutingHelper
def initialize(type) def initialize(type)
@type = type @type = type
...@@ -9,18 +10,22 @@ module Gitlab ...@@ -9,18 +10,22 @@ module Gitlab
def build(id) def build(id)
case @type case @type
when :issue when :issue
issue_url(id) build_issue_url(id)
when :merge_request
build_merge_request_url(id)
end end
end end
private private
def issue_url(id) def build_issue_url(id)
issue = Issue.find(id) issue = Issue.find(id)
namespace_project_issue_url(namespace_id: issue.project.namespace, issue_url(issue, host: Gitlab.config.gitlab['url'])
id: issue.iid, end
project_id: issue.project,
host: Gitlab.config.gitlab['url']) def build_merge_request_url(id)
merge_request = MergeRequest.find(id)
merge_request_url(merge_request, host: Gitlab.config.gitlab['url'])
end end
end end
end end
...@@ -77,16 +77,6 @@ server { ...@@ -77,16 +77,6 @@ server {
proxy_pass http://gitlab; proxy_pass http://gitlab;
} }
## If ``go get`` detected, return go-import meta tag.
## This works for public and for private repositories.
## See also http://golang.org/cmd/go/#hdr-Remote_import_paths
if ($http_user_agent ~* "Go") {
return 200 "
<!DOCTYPE html>
<head><meta content='$host$uri git $scheme://$host$uri.git' name='go-import'></head>
</html>";
}
## If a file, which is not found in the root folder is requested, ## If a file, which is not found in the root folder is requested,
## then the proxy passes the request to the upsteam (gitlab unicorn). ## then the proxy passes the request to the upsteam (gitlab unicorn).
location @gitlab { location @gitlab {
......
...@@ -123,16 +123,6 @@ server { ...@@ -123,16 +123,6 @@ server {
proxy_pass http://gitlab; proxy_pass http://gitlab;
} }
## If ``go get`` detected, return go-import meta tag.
## This works for public and for private repositories.
## See also http://golang.org/cmd/go/#hdr-Remote_import_paths
if ($http_user_agent ~* "Go") {
return 200 "
<!DOCTYPE html>
<head><meta content='$host$uri git $scheme://$host$uri.git' name='go-import'></head>
</html>";
}
## If a file, which is not found in the root folder is requested, ## If a file, which is not found in the root folder is requested,
## then the proxy passes the request to the upsteam (gitlab unicorn). ## then the proxy passes the request to the upsteam (gitlab unicorn).
location @gitlab { location @gitlab {
......
desc 'Security check via brakeman'
task :brakeman do
if system("brakeman --skip-files lib/backup/repository.rb -w3 -z")
exit 0
else
puts 'Security check failed'
exit 1
end
end
...@@ -9,5 +9,5 @@ unless Rails.env.production? ...@@ -9,5 +9,5 @@ unless Rails.env.production?
require 'coveralls/rake/task' require 'coveralls/rake/task'
Coveralls::RakeTask.new Coveralls::RakeTask.new
desc "GITLAB | Run all tests on CI with simplecov" desc "GITLAB | Run all tests on CI with simplecov"
task :test_ci => [:rubocop, :spinach, :spec, 'coveralls:push'] task :test_ci => [:rubocop, :brakeman, 'jasmine:ci', :spinach, :spec, 'coveralls:push']
end end
...@@ -7,6 +7,22 @@ describe ProjectsController do ...@@ -7,6 +7,22 @@ describe ProjectsController do
let(:jpg) { fixture_file_upload(Rails.root + 'spec/fixtures/rails_sample.jpg', 'image/jpg') } let(:jpg) { fixture_file_upload(Rails.root + 'spec/fixtures/rails_sample.jpg', 'image/jpg') }
let(:txt) { fixture_file_upload(Rails.root + 'spec/fixtures/doc_sample.txt', 'text/plain') } let(:txt) { fixture_file_upload(Rails.root + 'spec/fixtures/doc_sample.txt', 'text/plain') }
describe "GET show" do
context "when requested by `go get`" do
render_views
it "renders the go-import meta tag" do
get :show, "go-get" => "1", namespace_id: "bogus_namespace", id: "bogus_project"
expect(response.body).to include("name='go-import'")
content = "localhost/bogus_namespace/bogus_project git http://localhost/bogus_namespace/bogus_project.git"
expect(response.body).to include("content='#{content}'")
end
end
end
describe "POST #toggle_star" do describe "POST #toggle_star" do
it "toggles star if user is signed in" do it "toggles star if user is signed in" do
sign_in(user) sign_in(user)
......
require "spec_helper"
describe Grack::Auth do
let(:user) { create(:user) }
let(:project) { create(:project) }
let(:app) { lambda { |env| [200, {}, "Success!"] } }
let!(:auth) { Grack::Auth.new(app) }
let(:env) {
{
"rack.input" => "",
"REQUEST_METHOD" => "GET",
"QUERY_STRING" => "service=git-upload-pack"
}
}
let(:status) { auth.call(env).first }
describe "#call" do
context "when the project doesn't exist" do
before do
env["PATH_INFO"] = "doesnt/exist.git"
end
context "when no authentication is provided" do
it "responds with status 401" do
expect(status).to eq(401)
end
end
context "when username and password are provided" do
context "when authentication fails" do
before do
env["HTTP_AUTHORIZATION"] = ActionController::HttpAuthentication::Basic.encode_credentials(user.username, "nope")
end
it "responds with status 401" do
expect(status).to eq(401)
end
end
context "when authentication succeeds" do
before do
env["HTTP_AUTHORIZATION"] = ActionController::HttpAuthentication::Basic.encode_credentials(user.username, user.password)
end
it "responds with status 404" do
expect(status).to eq(404)
end
end
end
end
context "when the project exists" do
before do
env["PATH_INFO"] = project.path_with_namespace + ".git"
end
context "when the project is public" do
before do
project.update_attribute(:visibility_level, Project::PUBLIC)
end
it "responds with status 200" do
expect(status).to eq(200)
end
end
context "when the project is private" do
before do
project.update_attribute(:visibility_level, Project::PRIVATE)
end
context "when no authentication is provided" do
it "responds with status 401" do
expect(status).to eq(401)
end
end
context "when username and password are provided" do
context "when authentication fails" do
before do
env["HTTP_AUTHORIZATION"] = ActionController::HttpAuthentication::Basic.encode_credentials(user.username, "nope")
end
it "responds with status 401" do
expect(status).to eq(401)
end
end
context "when authentication succeeds" do
before do
env["HTTP_AUTHORIZATION"] = ActionController::HttpAuthentication::Basic.encode_credentials(user.username, user.password)
end
context "when the user has access to the project" do
before do
project.team << [user, :master]
end
context "when the user is blocked" do
before do
user.block
project.team << [user, :master]
end
it "responds with status 404" do
expect(status).to eq(404)
end
end
context "when the user isn't blocked" do
it "responds with status 200" do
expect(status).to eq(200)
end
end
end
context "when the user doesn't have access to the project" do
it "responds with status 404" do
expect(status).to eq(404)
end
end
end
end
context "when a gitlab ci token is provided" do
let(:token) { "123" }
before do
gitlab_ci_service = project.build_gitlab_ci_service
gitlab_ci_service.active = true
gitlab_ci_service.token = token
gitlab_ci_service.project_url = "http://google.com"
gitlab_ci_service.save
env["HTTP_AUTHORIZATION"] = ActionController::HttpAuthentication::Basic.encode_credentials("gitlab-ci-token", token)
end
it "responds with status 200" do
expect(status).to eq(200)
end
end
end
end
end
end
...@@ -8,4 +8,12 @@ describe Gitlab::UrlBuilder do ...@@ -8,4 +8,12 @@ describe Gitlab::UrlBuilder do
expect(url).to eq "#{Settings.gitlab['url']}/#{issue.project.path_with_namespace}/issues/#{issue.iid}" expect(url).to eq "#{Settings.gitlab['url']}/#{issue.project.path_with_namespace}/issues/#{issue.iid}"
end end
end end
describe 'When asking for an merge request' do
it 'returns the merge request url' do
merge_request = create(:merge_request)
url = Gitlab::UrlBuilder.new(:merge_request).build(merge_request.id)
expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.path_with_namespace}/merge_requests/#{merge_request.iid}"
end
end
end end
...@@ -75,4 +75,14 @@ describe Namespace do ...@@ -75,4 +75,14 @@ describe Namespace do
expect(namespace.rm_dir).to be_truthy expect(namespace.rm_dir).to be_truthy
end end
end end
describe :find_by_path_or_name do
before do
@namespace = create(:namespace, name: 'WoW', path: 'woW')
end
it { expect(Namespace.find_by_path_or_name('wow')).to eq(@namespace) }
it { expect(Namespace.find_by_path_or_name('WOW')).to eq(@namespace) }
it { expect(Namespace.find_by_path_or_name('unknown')).to eq(nil) }
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