Commit 8146271d authored by Jacob Vosmaer's avatar Jacob Vosmaer

Merge branch 'master' of dev.gitlab.org:gitlab/gitlabhq into backup_restore_drop_sequences

Conflicts:
	CHANGELOG
parents 57471894 8388bbe8
...@@ -4,6 +4,8 @@ v 7.5.0 ...@@ -4,6 +4,8 @@ v 7.5.0
- Fix LDAP authentication for Git HTTP access - Fix LDAP authentication for Git HTTP access
- Fix LDAP config lookup for provider 'ldap' - Fix LDAP config lookup for provider 'ldap'
- Drop all sequences during Postgres database restore - Drop all sequences during Postgres database restore
- Add Atlassian Bamboo CI service (Drew Blessing)
- Mentioned @user will receive email even if he is not participating in issue or commit
v 7.4.2 v 7.4.2
- Fix internal snippet exposing for unauthenticated users - Fix internal snippet exposing for unauthenticated users
......
...@@ -37,7 +37,7 @@ gem "gitlab_git", '7.0.0.rc10' ...@@ -37,7 +37,7 @@ gem "gitlab_git", '7.0.0.rc10'
gem 'gitlab-grack', '~> 2.0.0.pre', require: 'grack' gem 'gitlab-grack', '~> 2.0.0.pre', require: 'grack'
# LDAP Auth # LDAP Auth
gem 'gitlab_omniauth-ldap', '1.1.0', require: "omniauth-ldap" gem 'gitlab_omniauth-ldap', '1.2.0', require: "omniauth-ldap"
# Git Wiki # Git Wiki
gem 'gollum-lib', '~> 3.0.0' gem 'gollum-lib', '~> 3.0.0'
...@@ -186,6 +186,7 @@ gem "gon", '~> 5.0.0' ...@@ -186,6 +186,7 @@ gem "gon", '~> 5.0.0'
gem 'nprogress-rails' gem 'nprogress-rails'
gem 'request_store' gem 'request_store'
gem "virtus" gem "virtus"
gem 'addressable'
group :development do group :development do
gem "annotate", "~> 2.6.0.beta2" gem "annotate", "~> 2.6.0.beta2"
......
...@@ -185,11 +185,11 @@ GEM ...@@ -185,11 +185,11 @@ GEM
gitlab-linguist (~> 3.0) gitlab-linguist (~> 3.0)
rugged (~> 0.21.0) rugged (~> 0.21.0)
gitlab_meta (7.0) gitlab_meta (7.0)
gitlab_omniauth-ldap (1.1.0) gitlab_omniauth-ldap (1.2.0)
net-ldap (~> 0.7.0) net-ldap (~> 0.9)
omniauth (~> 1.0) omniauth (~> 1.0)
pyu-ruby-sasl (~> 0.0.3.1) pyu-ruby-sasl (~> 0.0.3.1)
rubyntlm (~> 0.1.1) rubyntlm (~> 0.3)
gollum-lib (3.0.0) gollum-lib (3.0.0)
github-markup (~> 1.1.0) github-markup (~> 1.1.0)
gitlab-grit (~> 2.6.5) gitlab-grit (~> 2.6.5)
...@@ -299,7 +299,7 @@ GEM ...@@ -299,7 +299,7 @@ GEM
multi_xml (0.5.5) multi_xml (0.5.5)
multipart-post (1.2.0) multipart-post (1.2.0)
mysql2 (0.3.16) mysql2 (0.3.16)
net-ldap (0.7.0) net-ldap (0.9.0)
net-scp (1.1.2) net-scp (1.1.2)
net-ssh (>= 2.6.5) net-ssh (>= 2.6.5)
net-ssh (2.8.0) net-ssh (2.8.0)
...@@ -445,7 +445,7 @@ GEM ...@@ -445,7 +445,7 @@ GEM
rspec-expectations (~> 2.14.0) rspec-expectations (~> 2.14.0)
rspec-mocks (~> 2.14.0) rspec-mocks (~> 2.14.0)
ruby-progressbar (1.2.0) ruby-progressbar (1.2.0)
rubyntlm (0.1.1) rubyntlm (0.4.0)
rubypants (0.2.0) rubypants (0.2.0)
rugged (0.21.0) rugged (0.21.0)
safe_yaml (0.9.7) safe_yaml (0.9.7)
...@@ -592,6 +592,7 @@ DEPENDENCIES ...@@ -592,6 +592,7 @@ DEPENDENCIES
RedCloth RedCloth
ace-rails-ap ace-rails-ap
acts-as-taggable-on acts-as-taggable-on
addressable
annotate (~> 2.6.0.beta2) annotate (~> 2.6.0.beta2)
asciidoctor (= 0.1.4) asciidoctor (= 0.1.4)
awesome_print awesome_print
...@@ -626,7 +627,7 @@ DEPENDENCIES ...@@ -626,7 +627,7 @@ DEPENDENCIES
gitlab_emoji (~> 0.0.1.1) gitlab_emoji (~> 0.0.1.1)
gitlab_git (= 7.0.0.rc10) gitlab_git (= 7.0.0.rc10)
gitlab_meta (= 7.0) gitlab_meta (= 7.0)
gitlab_omniauth-ldap (= 1.1.0) gitlab_omniauth-ldap (= 1.2.0)
gollum-lib (~> 3.0.0) gollum-lib (~> 3.0.0)
gon (~> 5.0.0) gon (~> 5.0.0)
grape (~> 0.6.1) grape (~> 0.6.1)
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#= require jquery.turbolinks #= require jquery.turbolinks
#= require turbolinks #= require turbolinks
#= require bootstrap #= require bootstrap
#= require password_strength
#= require select2 #= require select2
#= require raphael #= require raphael
#= require g.raphael-min #= require g.raphael-min
...@@ -63,7 +64,7 @@ window.extractLast = (term) -> ...@@ -63,7 +64,7 @@ window.extractLast = (term) ->
return split( term ).pop() return split( term ).pop()
window.rstrip = (val) -> window.rstrip = (val) ->
return val.replace(/\s+$/, '') return if val then val.replace(/\s+$/, '') else val
# Disable button if text field is empty # Disable button if text field is empty
window.disableButtonIfEmptyField = (field_selector, button_selector) -> window.disableButtonIfEmptyField = (field_selector, button_selector) ->
......
...@@ -62,6 +62,7 @@ class Dispatcher ...@@ -62,6 +62,7 @@ class Dispatcher
new TeamMembers() new TeamMembers()
when 'groups:members' when 'groups:members'
new GroupMembers() new GroupMembers()
new UsersSelect()
when 'groups:new', 'groups:edit', 'admin:groups:edit' when 'groups:new', 'groups:edit', 'admin:groups:edit'
new GroupAvatar() new GroupAvatar()
when 'projects:tree:show' when 'projects:tree:show'
...@@ -83,6 +84,8 @@ class Dispatcher ...@@ -83,6 +84,8 @@ class Dispatcher
when 'admin' when 'admin'
new Admin() new Admin()
switch path[1] switch path[1]
when 'groups'
new UsersSelect()
when 'projects' when 'projects'
new NamespaceSelect() new NamespaceSelect()
when 'dashboard' when 'dashboard'
...@@ -99,6 +102,8 @@ class Dispatcher ...@@ -99,6 +102,8 @@ class Dispatcher
new ProjectNew() new ProjectNew()
when 'show' when 'show'
new ProjectShow() new ProjectShow()
when 'issues', 'merge_requests'
new ProjectUsersSelect()
when 'wikis' when 'wikis'
new Wikis() new Wikis()
shortcut_handler = new ShortcutsNavigation() shortcut_handler = new ShortcutsNavigation()
...@@ -107,6 +112,7 @@ class Dispatcher ...@@ -107,6 +112,7 @@ class Dispatcher
shortcut_handler = new ShortcutsNavigation() shortcut_handler = new ShortcutsNavigation()
when 'team_members', 'deploy_keys', 'hooks', 'services', 'protected_branches' when 'team_members', 'deploy_keys', 'hooks', 'services', 'protected_branches'
shortcut_handler = new ShortcutsNavigation() shortcut_handler = new ShortcutsNavigation()
new UsersSelect()
# If we haven't installed a custom shortcut handler, install the default one # If we haven't installed a custom shortcut handler, install the default one
......
#= require pwstrength-bootstrap-1.2.2
overwritten_messages =
wordSimilarToUsername: "Your password should not contain your username"
overwritten_rules =
wordSequences: false
options =
showProgressBar: false
showVerdicts: false
showPopover: true
showErrors: true
showStatus: true
errorMessages: overwritten_messages
$(document).ready ->
profileOptions = {}
profileOptions.ui = options
profileOptions.rules =
activated: overwritten_rules
deviseOptions = {}
deviseOptions.common =
usernameField: "#user_username"
deviseOptions.ui = options
deviseOptions.rules =
activated: overwritten_rules
$("#user_password_profile").pwstrength profileOptions
$("#user_password_sign_up").pwstrength deviseOptions
$("#user_password_recover").pwstrength deviseOptions
@projectUsersSelect = class @ProjectUsersSelect
init: -> constructor: ->
$('.ajax-project-users-select').each (i, select) -> $('.ajax-project-users-select').each (i, select) =>
project_id = $(select).data('project-id') || $('body').data('project-id') project_id = $(select).data('project-id') || $('body').data('project-id')
$(select).select2 $(select).select2
...@@ -28,14 +28,16 @@ ...@@ -28,14 +28,16 @@
Api.user(id, callback) Api.user(id, callback)
formatResult: projectUsersSelect.projectUserFormatResult formatResult: (args...) =>
formatSelection: projectUsersSelect.projectUserFormatSelection @formatResult(args...)
formatSelection: (args...) =>
@formatSelection(args...)
dropdownCssClass: "ajax-project-users-dropdown" dropdownCssClass: "ajax-project-users-dropdown"
dropdownAutoWidth: true dropdownAutoWidth: true
escapeMarkup: (m) -> # we do not want to escape markup since we are displaying html in results escapeMarkup: (m) -> # we do not want to escape markup since we are displaying html in results
m m
projectUserFormatResult: (user) -> formatResult: (user) ->
if user.avatar_url if user.avatar_url
avatar = user.avatar_url avatar = user.avatar_url
else else
...@@ -52,8 +54,5 @@ ...@@ -52,8 +54,5 @@
<div class='user-username'>#{user.username}</div> <div class='user-username'>#{user.username}</div>
</div>" </div>"
projectUserFormatSelection: (user) -> formatSelection: (user) ->
user.name user.name
$ ->
projectUsersSelect.init()
$ -> class @UsersSelect
userFormatResult = (user) -> constructor: ->
if user.avatar_url $('.ajax-users-select').each (i, select) =>
avatar = user.avatar_url
else
avatar = gon.default_avatar_url
"<div class='user-result'>
<div class='user-image'><img class='avatar s24' src='#{avatar}'></div>
<div class='user-name'>#{user.name}</div>
<div class='user-username'>#{user.username}</div>
</div>"
userFormatSelection = (user) ->
user.name
$('.ajax-users-select').each (i, select) ->
$(select).select2 $(select).select2
placeholder: "Search for a user" placeholder: "Search for a user"
multiple: $(select).hasClass('multiselect') multiple: $(select).hasClass('multiselect')
...@@ -30,8 +16,25 @@ $ -> ...@@ -30,8 +16,25 @@ $ ->
Api.user(id, callback) Api.user(id, callback)
formatResult: userFormatResult formatResult: (args...) =>
formatSelection: userFormatSelection @formatResult(args...)
formatSelection: (args...) =>
@formatSelection(args...)
dropdownCssClass: "ajax-users-dropdown" dropdownCssClass: "ajax-users-dropdown"
escapeMarkup: (m) -> # we do not want to escape markup since we are displaying html in results escapeMarkup: (m) -> # we do not want to escape markup since we are displaying html in results
m m
formatResult: (user) ->
if user.avatar_url
avatar = user.avatar_url
else
avatar = gon.default_avatar_url
"<div class='user-result'>
<div class='user-image'><img class='avatar s24' src='#{avatar}'></div>
<div class='user-name'>#{user.name}</div>
<div class='user-username'>#{user.username}</div>
</div>"
formatSelection: (user) ->
user.name
...@@ -111,3 +111,20 @@ ...@@ -111,3 +111,20 @@
height: 50px; height: 50px;
} }
} }
//CSS for password-strength indicator
#password-strength {
margin-bottom: 0;
}
.has-success input {
background-color: #D6F1D7 !important;
}
.has-error input {
background-color: #F3CECE !important;
}
.has-warning input {
background-color: #FFE9A4 !important;
}
...@@ -5,7 +5,6 @@ class ApplicationController < ActionController::Base ...@@ -5,7 +5,6 @@ class ApplicationController < ActionController::Base
before_filter :authenticate_user! before_filter :authenticate_user!
before_filter :reject_blocked! before_filter :reject_blocked!
before_filter :check_password_expiration before_filter :check_password_expiration
before_filter :add_abilities
before_filter :ldap_security_check before_filter :ldap_security_check
before_filter :default_headers before_filter :default_headers
before_filter :add_gon_variables before_filter :add_gon_variables
...@@ -72,7 +71,7 @@ class ApplicationController < ActionController::Base ...@@ -72,7 +71,7 @@ class ApplicationController < ActionController::Base
end end
def abilities def abilities
@abilities ||= Six.new Ability.abilities
end end
def can?(object, action, subject) def can?(object, action, subject)
...@@ -113,10 +112,6 @@ class ApplicationController < ActionController::Base ...@@ -113,10 +112,6 @@ class ApplicationController < ActionController::Base
nil nil
end end
def add_abilities
abilities << Ability
end
def authorize_project!(action) def authorize_project!(action)
return access_denied! unless can?(current_user, action, project) return access_denied! unless can?(current_user, action, project)
end end
......
class Explore::GroupsController < ApplicationController class Explore::GroupsController < ApplicationController
skip_before_filter :authenticate_user!, skip_before_filter :authenticate_user!,
:reject_blocked, :set_current_user_for_observers, :reject_blocked, :set_current_user_for_observers
:add_abilities
layout "explore" layout "explore"
......
class Explore::ProjectsController < ApplicationController class Explore::ProjectsController < ApplicationController
skip_before_filter :authenticate_user!, skip_before_filter :authenticate_user!,
:reject_blocked, :reject_blocked
:add_abilities
layout 'explore' layout 'explore'
......
...@@ -41,7 +41,8 @@ class Projects::ServicesController < Projects::ApplicationController ...@@ -41,7 +41,8 @@ class Projects::ServicesController < Projects::ApplicationController
params.require(:service).permit( params.require(:service).permit(
:title, :token, :type, :active, :api_key, :subdomain, :title, :token, :type, :active, :api_key, :subdomain,
:room, :recipients, :project_url, :webhook, :room, :recipients, :project_url, :webhook,
:user_key, :device, :priority, :sound :user_key, :device, :priority, :sound, :bamboo_url, :username, :password,
:build_key
) )
end end
end end
...@@ -48,7 +48,7 @@ class IssuableFinder ...@@ -48,7 +48,7 @@ class IssuableFinder
else else
[] []
end end
elsif current_user && params[:authorized_only].presence elsif current_user && params[:authorized_only].presence && !current_user_related?
klass.of_projects(current_user.authorized_projects).references(:project) klass.of_projects(current_user.authorized_projects).references(:project)
else else
klass.of_projects(ProjectsFinder.new.execute(current_user)).references(:project) klass.of_projects(ProjectsFinder.new.execute(current_user)).references(:project)
...@@ -142,4 +142,8 @@ class IssuableFinder ...@@ -142,4 +142,8 @@ class IssuableFinder
def project def project
Project.where(id: params[:project_id]).first if params[:project_id].present? Project.where(id: params[:project_id]).first if params[:project_id].present?
end end
def current_user_related?
params[:scope] == 'created-by-me' || params[:scope] == 'authored' || params[:scope] == 'assigned-to-me'
end
end end
...@@ -262,5 +262,13 @@ class Ability ...@@ -262,5 +262,13 @@ class Ability
end end
rules rules
end end
def abilities
@abilities ||= begin
abilities = Six.new
abilities << self
abilities
end
end
end end
end end
...@@ -51,12 +51,8 @@ module Mentionable ...@@ -51,12 +51,8 @@ module Mentionable
identifier = match.delete "@" identifier = match.delete "@"
if identifier == "all" if identifier == "all"
users += project.team.members.flatten users += project.team.members.flatten
else
if has_project
id = project.team.members.find_by(username: identifier).try(:id)
else else
id = User.find_by(username: identifier).try(:id) id = User.find_by(username: identifier).try(:id)
end
users << User.find(id) unless id.blank? users << User.find(id) unless id.blank?
end end
end end
......
...@@ -65,6 +65,7 @@ class Project < ActiveRecord::Base ...@@ -65,6 +65,7 @@ class Project < ActiveRecord::Base
has_one :gemnasium_service, dependent: :destroy has_one :gemnasium_service, dependent: :destroy
has_one :slack_service, dependent: :destroy has_one :slack_service, dependent: :destroy
has_one :buildbox_service, dependent: :destroy has_one :buildbox_service, dependent: :destroy
has_one :bamboo_service, dependent: :destroy
has_one :pushover_service, dependent: :destroy has_one :pushover_service, dependent: :destroy
has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id" has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id"
has_one :forked_from_project, through: :forked_project_link has_one :forked_from_project, through: :forked_project_link
...@@ -313,7 +314,7 @@ class Project < ActiveRecord::Base ...@@ -313,7 +314,7 @@ class Project < ActiveRecord::Base
end end
def available_services_names def available_services_names
%w(gitlab_ci campfire hipchat pivotaltracker flowdock assembla emails_on_push gemnasium slack pushover buildbox) %w(gitlab_ci campfire hipchat pivotaltracker flowdock assembla emails_on_push gemnasium slack pushover buildbox bamboo)
end end
def gitlab_ci? def gitlab_ci?
......
class BambooService < CiService
include HTTParty
prop_accessor :bamboo_url, :build_key, :username, :password
validates :bamboo_url, presence: true,
format: { with: URI::regexp }, if: :activated?
validates :build_key, presence: true, if: :activated?
validates :username, presence: true,
if: ->(service) { service.password? }, if: :activated?
validates :password, presence: true,
if: ->(service) { service.username? }, if: :activated?
attr_accessor :response
after_save :compose_service_hook, if: :activated?
def compose_service_hook
hook = service_hook || build_service_hook
hook.save
end
def title
'Atlassian Bamboo CI'
end
def description
'A continuous integration and build server'
end
def help
'You must set up automatic revision labeling and a repository trigger in Bamboo.'
end
def to_param
'bamboo'
end
def fields
[
{ type: 'text', name: 'bamboo_url',
placeholder: 'Bamboo root URL like https://bamboo.example.com' },
{ type: 'text', name: 'build_key',
placeholder: 'Bamboo build plan key like KEY' },
{ type: 'text', name: 'username',
placeholder: 'A user with API access, if applicable' },
{ type: 'password', name: 'password' },
]
end
def build_info(sha)
url = URI.parse("#{bamboo_url}/rest/api/latest/result?label=#{sha}")
if username.blank? && password.blank?
@response = HTTParty.get(parsed_url.to_s, verify: false)
else
get_url = "#{url}&os_authType=basic"
auth = {
username: username,
password: password,
}
@response = HTTParty.get(get_url, verify: false, basic_auth: auth)
end
end
def build_page(sha)
build_info(sha) if @response.nil? || !@response.code
if @response.code != 200 || @response['results']['results']['size'] == '0'
# If actual build link can't be determined, send user to build summary page.
"#{bamboo_url}/browse/#{build_key}"
else
# If actual build link is available, go to build result page.
result_key = @response['results']['results']['result']['planResultKey']['key']
"#{bamboo_url}/browse/#{result_key}"
end
end
def commit_status(sha)
build_info(sha) if @response.nil? || !@response.code
return :error unless @response.code == 200 || @response.code == 404
status = if @response.code == 404 || @response['results']['results']['size'] == '0'
'Pending'
else
@response['results']['results']['result']['buildState']
end
if status.include?('Success')
'success'
elsif status.include?('Failed')
'failed'
elsif status.include?('Pending')
'pending'
else
:error
end
end
def execute(_data)
# Bamboo requires a GET and does not take any data.
self.class.get("#{bamboo_url}/updateAndBuild.action?buildKey=#{build_key}",
verify: false)
end
end
...@@ -12,6 +12,8 @@ ...@@ -12,6 +12,8 @@
# properties :text # properties :text
# #
require "addressable/uri"
class BuildboxService < CiService class BuildboxService < CiService
prop_accessor :project_url, :token prop_accessor :project_url, :token
......
...@@ -330,11 +330,7 @@ class User < ActiveRecord::Base ...@@ -330,11 +330,7 @@ class User < ActiveRecord::Base
end end
def abilities def abilities
@abilities ||= begin Ability.abilities
abilities = Six.new
abilities << Ability
abilities
end
end end
def can_select_namespace? def can_select_namespace?
......
...@@ -6,11 +6,7 @@ class BaseService ...@@ -6,11 +6,7 @@ class BaseService
end end
def abilities def abilities
@abilities ||= begin Ability.abilities
abilities = Six.new
abilities << Ability
abilities
end
end end
def can?(object, action, subject) def can?(object, action, subject)
......
...@@ -124,6 +124,7 @@ class NotificationService ...@@ -124,6 +124,7 @@ class NotificationService
opts = { noteable_type: note.noteable_type, project_id: note.project_id } opts = { noteable_type: note.noteable_type, project_id: note.project_id }
target = note.noteable target = note.noteable
if target.respond_to?(:participants) if target.respond_to?(:participants)
recipients = target.participants recipients = target.participants
else else
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
= form_tag admin_groups_path, method: :get, class: 'form-inline' do = form_tag admin_groups_path, method: :get, class: 'form-inline' do
.form-group .form-group
= text_field_tag :name, params[:name], class: "form-control input-mn-300" = text_field_tag :name, params[:name], class: "form-control input-mn-300"
= submit_tag "Search", class: "btn submit btn-primary" = button_tag "Search", class: "btn submit btn-primary"
%hr %hr
......
...@@ -64,7 +64,7 @@ ...@@ -64,7 +64,7 @@
%div.prepend-top-10 %div.prepend-top-10
= select_tag :access_level, options_for_select(GroupMember.access_level_roles), class: "project-access-select select2" = select_tag :access_level, options_for_select(GroupMember.access_level_roles), class: "project-access-select select2"
%hr %hr
= submit_tag 'Add users into group', class: "btn btn-create" = button_tag 'Add users into group', class: "btn btn-create"
.panel.panel-default .panel.panel-default
.panel-heading .panel-heading
%h3.panel-title %h3.panel-title
......
...@@ -35,7 +35,7 @@ ...@@ -35,7 +35,7 @@
= label = label
%hr %hr
= hidden_field_tag :sort, params[:sort] = hidden_field_tag :sort, params[:sort]
= submit_tag "Search", class: "btn submit btn-primary" = button_tag "Search", class: "btn submit btn-primary"
= link_to "Reset", admin_projects_path, class: "btn btn-cancel" = link_to "Reset", admin_projects_path, class: "btn btn-cancel"
.col-md-9 .col-md-9
......
...@@ -6,8 +6,8 @@ ...@@ -6,8 +6,8 @@
.devise-errors .devise-errors
= devise_error_messages! = devise_error_messages!
= f.hidden_field :reset_password_token = f.hidden_field :reset_password_token
%div .form-group#password-strength
= f.password_field :password, class: "form-control top", placeholder: "New password", required: true = f.password_field :password, class: "form-control top", id: "user_password_recover", placeholder: "New password", required: true
%div %div
= f.password_field :password_confirmation, class: "form-control bottom", placeholder: "Confirm new password", required: true = f.password_field :password_confirmation, class: "form-control bottom", placeholder: "Confirm new password", required: true
.clearfix.append-bottom-10 .clearfix.append-bottom-10
......
...@@ -11,8 +11,8 @@ ...@@ -11,8 +11,8 @@
= f.text_field :username, class: "form-control middle", placeholder: "Username", required: true = f.text_field :username, class: "form-control middle", placeholder: "Username", required: true
%div %div
= f.email_field :email, class: "form-control middle", placeholder: "Email", required: true = f.email_field :email, class: "form-control middle", placeholder: "Email", required: true
%div .form-group#password-strength
= f.password_field :password, class: "form-control middle", placeholder: "Password", required: true = f.password_field :password, class: "form-control middle", id: "user_password_sign_up", placeholder: "Password", required: true
%div %div
= f.password_field :password_confirmation, class: "form-control bottom", placeholder: "Confirm password", required: true = f.password_field :password_confirmation, class: "form-control bottom", placeholder: "Confirm password", required: true
%div %div
......
...@@ -2,4 +2,4 @@ ...@@ -2,4 +2,4 @@
= text_field_tag :username, nil, {class: "form-control top", placeholder: "LDAP Login", autofocus: "autofocus"} = text_field_tag :username, nil, {class: "form-control top", placeholder: "LDAP Login", autofocus: "autofocus"}
= password_field_tag :password, nil, {class: "form-control bottom", placeholder: "Password"} = password_field_tag :password, nil, {class: "form-control bottom", placeholder: "Password"}
%br/ %br/
= submit_tag "LDAP Sign in", class: "btn-save btn" = button_tag "LDAP Sign in", class: "btn-save btn"
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
.form-group .form-group
= search_field_tag :search, params[:search], placeholder: "Filter by name", class: "form-control search-text-input input-mn-300", id: "groups_search" = search_field_tag :search, params[:search], placeholder: "Filter by name", class: "form-control search-text-input input-mn-300", id: "groups_search"
.form-group .form-group
= submit_tag 'Search', class: "btn btn-primary wide" = button_tag 'Search', class: "btn btn-primary wide"
.pull-right .pull-right
.dropdown.inline .dropdown.inline
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
.form-group .form-group
= search_field_tag :search, params[:search], placeholder: "Filter by name", class: "form-control search-text-input input-mn-300", id: "projects_search" = search_field_tag :search, params[:search], placeholder: "Filter by name", class: "form-control search-text-input input-mn-300", id: "projects_search"
.form-group .form-group
= submit_tag 'Search', class: "btn btn-primary wide" = button_tag 'Search', class: "btn btn-primary wide"
.pull-right .pull-right
.dropdown.inline .dropdown.inline
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
= form_tag members_group_path(@group), method: :get, class: 'form-inline member-search-form' do = form_tag members_group_path(@group), method: :get, class: 'form-inline member-search-form' do
.form-group .form-group
= search_field_tag :search, params[:search], { placeholder: 'Find existing member by name', class: 'form-control search-text-input input-mn-300' } = search_field_tag :search, params[:search], { placeholder: 'Find existing member by name', class: 'form-control search-text-input input-mn-300' }
= submit_tag 'Search', class: 'btn' = button_tag 'Search', class: 'btn'
- if current_user && current_user.can?(:manage_group, @group) - if current_user && current_user.can?(:manage_group, @group)
.pull-right .pull-right
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
- if @snippet || @snippets - if @snippet || @snippets
= hidden_field_tag :snippets, true = hidden_field_tag :snippets, true
= hidden_field_tag :repository_ref, @ref = hidden_field_tag :repository_ref, @ref
= submit_tag 'Go' if ENV['RAILS_ENV'] == 'test' = button_tag 'Go' if ENV['RAILS_ENV'] == 'test'
.search-autocomplete-opts.hide{:'data-autocomplete-path' => search_autocomplete_path, :'data-autocomplete-project-id' => @project.try(:id), :'data-autocomplete-project-ref' => @ref } .search-autocomplete-opts.hide{:'data-autocomplete-path' => search_autocomplete_path, :'data-autocomplete-project-id' => @project.try(:id), :'data-autocomplete-project-ref' => @ref }
:javascript :javascript
......
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
.form-group .form-group
= f.label :password, 'New password', class: 'control-label' = f.label :password, 'New password', class: 'control-label'
.col-sm-10 .col-sm-10
= f.password_field :password, required: true, class: 'form-control' = f.password_field :password, required: true, class: 'form-control', id: 'user_password_profile'
.form-group .form-group
= f.label :password_confirmation, class: 'control-label' = f.label :password_confirmation, class: 'control-label'
.col-sm-10 .col-sm-10
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
.col-sm-10= f.password_field :current_password, required: true, class: 'form-control' .col-sm-10= f.password_field :current_password, required: true, class: 'form-control'
.form-group .form-group
= f.label :password, class: 'control-label' = f.label :password, class: 'control-label'
.col-sm-10= f.password_field :password, required: true, class: 'form-control' .col-sm-10= f.password_field :password, required: true, class: 'form-control', id: 'user_password_profile'
.form-group .form-group
= f.label :password_confirmation, class: 'control-label' = f.label :password_confirmation, class: 'control-label'
.col-sm-10 .col-sm-10
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
.form-group .form-group
.col-sm-2 .col-sm-2
.col-sm-10 .col-sm-10
= submit_tag 'Remove file', class: 'btn btn-remove btn-remove-file' = button_tag 'Remove file', class: 'btn btn-remove btn-remove-file'
= link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal" = link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal"
:javascript :javascript
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
.col-sm-10 .col-sm-10
= text_field_tag :ref, params[:ref], placeholder: 'existing branch name, tag or commit SHA', required: true, tabindex: 2, class: 'form-control' = text_field_tag :ref, params[:ref], placeholder: 'existing branch name, tag or commit SHA', required: true, tabindex: 2, class: 'form-control'
.form-actions .form-actions
= submit_tag 'Create branch', class: 'btn btn-create', tabindex: 3 = button_tag 'Create branch', class: 'btn btn-create', tabindex: 3
= link_to 'Cancel', project_branches_path(@project), class: 'btn btn-cancel' = link_to 'Cancel', project_branches_path(@project), class: 'btn btn-cancel'
:javascript :javascript
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
%span.input-group-addon to %span.input-group-addon to
= text_field_tag :to, params[:to], class: "form-control" = text_field_tag :to, params[:to], class: "form-control"
&nbsp; &nbsp;
= submit_tag "Compare", class: "btn btn-create commits-compare-btn" = button_tag "Compare", class: "btn btn-create commits-compare-btn"
- if compare_to_mr_button? - if compare_to_mr_button?
= link_to compare_mr_path, class: 'prepend-left-10 btn' do = link_to compare_mr_path, class: 'prepend-left-10 btn' do
%strong Make a merge request %strong Make a merge request
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
= f.label :name, class: 'control-label' do = f.label :name, class: 'control-label' do
Project name Project name
.col-sm-10 .col-sm-10
= f.text_field :name, placeholder: "Example Project", class: "form-control" = f.text_field :name, placeholder: "Example Project", class: "form-control", id: "project_name_edit"
.form-group .form-group
...@@ -124,6 +124,12 @@ ...@@ -124,6 +124,12 @@
.errors-holder .errors-holder
.panel-body .panel-body
= form_for(@project, html: { class: 'form-horizontal' }) do |f| = form_for(@project, html: { class: 'form-horizontal' }) do |f|
.form-group.project_name_holder
= f.label :name, class: 'control-label' do
Project name
.col-sm-9
.form-group
= f.text_field :name, placeholder: "Example Project", class: "form-control"
.form-group .form-group
= f.label :path, class: 'control-label' do = f.label :path, class: 'control-label' do
%span Path %span Path
......
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
- @service.fields.each do |field| - @service.fields.each do |field|
- name = field[:name] - name = field[:name]
- value = @service.send(name) - value = @service.send(name) unless field[:type] == 'password'
- type = field[:type] - type = field[:type]
- placeholder = field[:placeholder] - placeholder = field[:placeholder]
- choices = field[:choices] - choices = field[:choices]
...@@ -45,6 +45,8 @@ ...@@ -45,6 +45,8 @@
= f.check_box name = f.check_box name
- elsif type == 'select' - elsif type == 'select'
= f.select name, options_for_select(choices, value ? value : default_choice), {}, { class: "form-control" } = f.select name, options_for_select(choices, value ? value : default_choice), {}, { class: "form-control" }
- elsif type == 'password'
= f.password_field name, class: 'form-control'
.form-actions .form-actions
= f.submit 'Save', class: 'btn btn-save' = f.submit 'Save', class: 'btn btn-save'
......
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
= text_field_tag :message, nil, placeholder: 'Enter message.', required: false, tabindex: 3, class: 'form-control' = text_field_tag :message, nil, placeholder: 'Enter message.', required: false, tabindex: 3, class: 'form-control'
.light (Optional) Entering a message will create an annotated tag. .light (Optional) Entering a message will create an annotated tag.
.form-actions .form-actions
= submit_tag 'Create tag', class: 'btn btn-create', tabindex: 3 = button_tag 'Create tag', class: 'btn btn-create', tabindex: 3
= link_to 'Cancel', project_tags_path(@project), class: 'btn btn-cancel' = link_to 'Cancel', project_tags_path(@project), class: 'btn btn-cancel'
:javascript :javascript
......
...@@ -9,6 +9,6 @@ ...@@ -9,6 +9,6 @@
.col-sm-10= select_tag(:source_project_id, options_from_collection_for_select(current_user.authorized_projects, :id, :name_with_namespace), prompt: "Select project", class: "select2 lg", required: true) .col-sm-10= select_tag(:source_project_id, options_from_collection_for_select(current_user.authorized_projects, :id, :name_with_namespace), prompt: "Select project", class: "select2 lg", required: true)
.form-actions .form-actions
= submit_tag 'Import project members', class: "btn btn-create" = button_tag 'Import project members', class: "btn btn-create"
= link_to "Cancel", project_team_index_path(@project), class: "btn btn-cancel" = link_to "Cancel", project_team_index_path(@project), class: "btn btn-cancel"
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
.col-sm-6 .col-sm-6
= search_field_tag :search, params[:search], placeholder: "issue 143", class: "form-control search-text-input", id: "dashboard_search" = search_field_tag :search, params[:search], placeholder: "issue 143", class: "form-control search-text-input", id: "dashboard_search"
.col-sm-4 .col-sm-4
= submit_tag 'Search', class: "btn btn-create" = button_tag 'Search', class: "btn btn-create"
.form-group .form-group
.col-sm-2 .col-sm-2
- unless params[:snippets].eql? 'true' - unless params[:snippets].eql? 'true'
......
...@@ -39,6 +39,8 @@ production: &base ...@@ -39,6 +39,8 @@ production: &base
# time_zone: 'UTC' # time_zone: 'UTC'
## Email settings ## Email settings
# Uncomment and set to false if you need to disable email sending from GitLab (default: true)
# email_enabled: true
# Email address used in the "From" field in mails sent by GitLab # Email address used in the "From" field in mails sent by GitLab
email_from: example@example.com email_from: example@example.com
......
...@@ -95,6 +95,7 @@ Settings.gitlab['https'] = false if Settings.gitlab['https'].nil? ...@@ -95,6 +95,7 @@ Settings.gitlab['https'] = false if Settings.gitlab['https'].nil?
Settings.gitlab['port'] ||= Settings.gitlab.https ? 443 : 80 Settings.gitlab['port'] ||= Settings.gitlab.https ? 443 : 80
Settings.gitlab['relative_url_root'] ||= ENV['RAILS_RELATIVE_URL_ROOT'] || '' Settings.gitlab['relative_url_root'] ||= ENV['RAILS_RELATIVE_URL_ROOT'] || ''
Settings.gitlab['protocol'] ||= Settings.gitlab.https ? "https" : "http" Settings.gitlab['protocol'] ||= Settings.gitlab.https ? "https" : "http"
Settings.gitlab['email_enabled'] ||= true if Settings.gitlab['email_enabled'].nil?
Settings.gitlab['email_from'] ||= "gitlab@#{Settings.gitlab.host}" Settings.gitlab['email_from'] ||= "gitlab@#{Settings.gitlab.host}"
Settings.gitlab['url'] ||= Settings.send(:build_gitlab_url) Settings.gitlab['url'] ||= Settings.send(:build_gitlab_url)
Settings.gitlab['user'] ||= 'git' Settings.gitlab['user'] ||= 'git'
......
# Interceptor in lib/disable_email_interceptor.rb
ActionMailer::Base.register_interceptor(DisableEmailInterceptor) unless Gitlab.config.gitlab.email_enabled
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
- [API](api/README.md) Explore how you can access GitLab via a simple and powerful API. - [API](api/README.md) Explore how you can access GitLab via a simple and powerful API.
- [Markdown](markdown/markdown.md) Learn what you can do with GitLab's advanced formatting system. - [Markdown](markdown/markdown.md) Learn what you can do with GitLab's advanced formatting system.
- [Permissions](permissions/permissions.md) Learn what each role in a project (guest/reporter/developer/master/owner) can do. - [Permissions](permissions/permissions.md) Learn what each role in a project (guest/reporter/developer/master/owner) can do.
- [Project Services](project_services/project_services.md) Explore how project services can integrate a project with external services, such as for CI.
- [Public access](public_access/public_access.md) Learn how you can allow public and internal access to a project. - [Public access](public_access/public_access.md) Learn how you can allow public and internal access to a project.
- [SSH](ssh/README.md) Setup your ssh keys and deploy keys for secure access to your projects. - [SSH](ssh/README.md) Setup your ssh keys and deploy keys for secure access to your projects.
- [Web hooks](web_hooks/web_hooks.md) Let GitLab notify you when new code has been pushed to your project. - [Web hooks](web_hooks/web_hooks.md) Let GitLab notify you when new code has been pushed to your project.
......
...@@ -150,6 +150,17 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da ...@@ -150,6 +150,17 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da
# Enable Redis socket for default Debian / Ubuntu path # Enable Redis socket for default Debian / Ubuntu path
echo 'unixsocket /var/run/redis/redis.sock' | sudo tee -a /etc/redis/redis.conf echo 'unixsocket /var/run/redis/redis.sock' | sudo tee -a /etc/redis/redis.conf
# Grant permission to the socket to all members of the redis group
echo 'unixsocketperm 770' | sudo tee -a /etc/redis/redis.conf
# Create the directory which contains the socket
mkdir /var/run/redis
chown redis:redis /var/run/redis
chmod 755 /var/run/redis
# Persist the directory which contains the socket, if applicable
if [ -d /etc/tmpfiles.d ]; then
echo 'd /var/run/redis 0755 redis redis 10d -' | sudo tee -a /etc/tmpfiles.d/redis.conf
fi
# Activate the changes to redis.conf # Activate the changes to redis.conf
sudo service redis-server restart sudo service redis-server restart
......
...@@ -24,7 +24,7 @@ For the installations options please see [the installation page on the GitLab we ...@@ -24,7 +24,7 @@ For the installations options please see [the installation page on the GitLab we
On the above unsupported distributions is still possible to install GitLab yourself. On the above unsupported distributions is still possible to install GitLab yourself.
Please see the [manual installation guide](https://github.com/gitlabhq/gitlabhq/blob/master/doc/install/installation.md) and the [unofficial installation guides](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Unofficial-Installation-Guides) on the public wiki for more information. Please see the [manual installation guide](https://github.com/gitlabhq/gitlabhq/blob/master/doc/install/installation.md) and the [unofficial installation guides](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Unofficial-Installation-Guides) on the public wiki for more information.
### Non Unix operating systems such as Windows ### Non-Unix operating systems such as Windows
GitLab is developed for Unix operating systems. GitLab is developed for Unix operating systems.
GitLab does **not** run on Windows and we have no plans of supporting it in the near future. GitLab does **not** run on Windows and we have no plans of supporting it in the near future.
...@@ -53,8 +53,8 @@ We love [JRuby](http://jruby.org/) and [Rubinius](http://rubini.us/) but GitLab ...@@ -53,8 +53,8 @@ We love [JRuby](http://jruby.org/) and [Rubinius](http://rubini.us/) but GitLab
- 512MB is the absolute minimum but we do not recommend this amount of memory. - 512MB is the absolute minimum but we do not recommend this amount of memory.
You will either need to configure 512MB or 1.5GB of swap space. You will either need to configure 512MB or 1.5GB of swap space.
With 512MB of swap space you must configure only one unicorn worker. With 512MB of swap space you must configure only one unicorn worker.
With one unicorn worker only git over ssh access will work because the git over http access requires two running workers (one worker to receive the user request and one worker for the authorization check). With one unicorn worker only git over ssh access will work because the git over HTTP access requires two running workers (one worker to receive the user request and one worker for the authorization check).
If you use SSD storage and configure 1.5GB of swap space you can use two Unicorn workers, this will allow http access but it will still be slow. If you use SSD storage and configure 1.5GB of swap space you can use two Unicorn workers, this will allow HTTP access but it will still be slow.
- 1GB RAM + 1GB swap supports up to 100 users - 1GB RAM + 1GB swap supports up to 100 users
- **2GB RAM** is the **recommended** memory size and supports up to 500 users - **2GB RAM** is the **recommended** memory size and supports up to 500 users
- 4GB RAM supports up to 2,000 users - 4GB RAM supports up to 2,000 users
...@@ -67,7 +67,7 @@ Notice: The 25 workers of Sidekiq will show up as separate processes in your pro ...@@ -67,7 +67,7 @@ Notice: The 25 workers of Sidekiq will show up as separate processes in your pro
### Storage ### Storage
The necessary hard drive space largely depends on the size of the repos you want to store in GitLab. But as a *rule of thumb* you should have at least twice as much free space as your all repos combined take up. You need twice the storage because [GitLab satellites](structure.md) contain an extra copy of each repo. The necessary hard drive space largely depends on the size of the repos you want to store in GitLab but as a *rule of thumb* you should have at least twice as much free space as all your repos combined take up. You need twice the storage because [GitLab satellites](structure.md) contain an extra copy of each repo.
If you want to be flexible about growing your hard drive space in the future consider mounting it using LVM so you can add more hard drives when you need them. If you want to be flexible about growing your hard drive space in the future consider mounting it using LVM so you can add more hard drives when you need them.
...@@ -90,7 +90,7 @@ On a very active server (10,000 active users) the Sidekiq process can use 1GB+ o ...@@ -90,7 +90,7 @@ On a very active server (10,000 active users) the Sidekiq process can use 1GB+ o
## Supported web browsers ## Supported web browsers
- Chrome (Latest stable version) - Chrome (Latest stable version)
- Firefox (Latest released version) - Firefox (Latest released version and [latest ESR version](https://www.mozilla.org/en-US/firefox/organizations/))
- Safari 7+ (known problem: required fields in html5 do not work) - Safari 7+ (known problem: required fields in html5 do not work)
- Opera (Latest released version) - Opera (Latest released version)
- IE 10+ - IE 10+
# Atlassian Bamboo CI Service
GitLab provides integration with Atlassian Bamboo for continuous integration.
When configured, pushes to a project will trigger a build in Bamboo automatically.
Merge requests will also display CI status showing whether the build is pending,
failed, or completed successfully. It also provides a link to the Bamboo build
page for more information.
Bamboo doesn't quite provide the same features as a traditional build system when
it comes to accepting webhooks and commit data. There are a few things that
need to be configured in a Bamboo build plan before GitLab can integrate.
## Setup
### Complete these steps in Bamboo:
1. Navigate to a Bamboo build plan and choose 'Configure plan' from the 'Actions'
dropdown.
1. Select the 'Triggers' tab.
1. Click 'Add trigger'.
1. Enter a description such as 'GitLab trigger'
1. Choose 'Repository triggers the build when changes are committed'
1. Check one or more repositories checkboxes
1. Enter the GitLab IP address in the 'Trigger IP addresses' box. This is a
whitelist of IP addresses that are allowed to trigger Bamboo builds.
1. Save the trigger.
1. In the left pane, select a build stage. If you have multiple build stages
you want to select the last stage that contains the git checkout task.
1. Select the 'Miscellaneous' tab.
1. Under 'Pattern Match Labelling' put '${bamboo.repository.revision.number}'
in the 'Labels' box.
1. Save
Bamboo is now ready to accept triggers from GitLab. Next, set up the Bamboo
service in GitLab
### Complete these steps in GitLab:
1. Navigate to the project you want to configure to trigger builds.
1. Select 'Settings' in the top navigation.
1. Select 'Services' in the left navigation.
1. Click 'Atlassian Bamboo CI'
1. Select the 'Active' checkbox.
1. Enter the base URL of your Bamboo server. 'https://bamboo.example.com'
1. Enter the build key from your Bamboo build plan. Build keys are a short,
all capital letter, identifier that is unique. It will be something like PR-BLD
1. If necessary, enter username and password for a Bamboo user that has
access to trigger the build plan. Leave these fields blank if you do not require
authentication.
1. Save or optionally click 'Test Settings'. Please note that 'Test Settings'
will actually trigger a build in Bamboo.
## Troubleshooting
If builds are not triggered, these are a couple of things to keep in mind.
1. Ensure you entered the right GitLab IP address in Bamboo under 'Trigger
IP addresses'.
1. Remember that GitLab only triggers builds on push events. A commit via the
web interface will not trigger CI currently.
# Project Services
__Project integrations with external services for continuous integration and more.__
## Services
- Assemblia
- [Atlassian Bamboo CI](bamboo.md) An Atlassian product for continous integration.
- Build box
- Campfire
- Emails on push
- Flowdock
- Gemnasium
- GitLab CI
- Hipchat
- PivotalTracker
- Pushover
- Slack
...@@ -26,6 +26,6 @@ Otherwise include it in the monthly release and note there was a regression fix ...@@ -26,6 +26,6 @@ Otherwise include it in the monthly release and note there was a regression fix
1. Apply the patch to GitLab Cloud and the private GitLab development server 1. Apply the patch to GitLab Cloud and the private GitLab development server
1. [Build new packages with the latest version](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/release.md) 1. [Build new packages with the latest version](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/release.md)
1. Cherry-pick the changelog update back into master 1. Cherry-pick the changelog update back into master
1. Create blog post 1. Create and publish a blog post
1. Send tweets about the release from `@gitlabhq`, tweet should include the most important feature that the release is addressing as well as the link to the changelog 1. Send tweets about the release from `@gitlabhq`, tweet should include the most important feature that the release is addressing and link to the blog post
1. Note in the 'GitLab X.X regressions' issue that the patch was published (CE only) 1. Note in the 'GitLab X.X regressions' issue that the patch was published (CE only)
# From 6.x or 7.x to 7.4 # From 6.x or 7.x to 7.3
This allows you to upgrade any version of GitLab from 6.0 and up (including 7.0 and up) to 7.4. This allows you to upgrade any version of GitLab from 6.0 and up (including 7.0 and up) to 7.3.
## Global issue numbers ## Global issue numbers
...@@ -70,7 +70,7 @@ sudo -u git -H git checkout -- db/schema.rb # local changes will be restored aut ...@@ -70,7 +70,7 @@ sudo -u git -H git checkout -- db/schema.rb # local changes will be restored aut
For GitLab Community Edition: For GitLab Community Edition:
```bash ```bash
sudo -u git -H git checkout 7-4-stable sudo -u git -H git checkout 7-3-stable
``` ```
OR OR
...@@ -78,7 +78,7 @@ OR ...@@ -78,7 +78,7 @@ OR
For GitLab Enterprise Edition: For GitLab Enterprise Edition:
```bash ```bash
sudo -u git -H git checkout 7-4-stable-ee sudo -u git -H git checkout 7-3-stable-ee
``` ```
## 4. Install additional packages ## 4. Install additional packages
...@@ -154,14 +154,14 @@ sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab ...@@ -154,14 +154,14 @@ sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
TIP: to see what changed in `gitlab.yml.example` in this release use next command: TIP: to see what changed in `gitlab.yml.example` in this release use next command:
``` ```
git diff 6-0-stable:config/gitlab.yml.example 7-4-stable:config/gitlab.yml.example git diff 6-0-stable:config/gitlab.yml.example 7-3-stable:config/gitlab.yml.example
``` ```
* Make `/home/git/gitlab/config/gitlab.yml` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-4-stable/config/gitlab.yml.example but with your settings. * Make `/home/git/gitlab/config/gitlab.yml` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-3-stable/config/gitlab.yml.example but with your settings.
* Make `/home/git/gitlab/config/unicorn.rb` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-4-stable/config/unicorn.rb.example but with your settings. * Make `/home/git/gitlab/config/unicorn.rb` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-3-stable/config/unicorn.rb.example but with your settings.
* Make `/home/git/gitlab-shell/config.yml` the same as https://gitlab.com/gitlab-org/gitlab-shell/blob/v2.0.1/config.yml.example but with your settings. * Make `/home/git/gitlab-shell/config.yml` the same as https://gitlab.com/gitlab-org/gitlab-shell/blob/v2.0.1/config.yml.example but with your settings.
* HTTP setups: Make `/etc/nginx/sites-available/gitlab` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-4-stable/lib/support/nginx/gitlab but with your settings. * HTTP setups: Make `/etc/nginx/sites-available/gitlab` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-3-stable/lib/support/nginx/gitlab but with your settings.
* HTTPS setups: Make `/etc/nginx/sites-available/gitlab-ssl` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-4-stable/lib/support/nginx/gitlab-ssl but with your settings. * HTTPS setups: Make `/etc/nginx/sites-available/gitlab-ssl` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-3-stable/lib/support/nginx/gitlab-ssl but with your settings.
* Copy rack attack middleware config * Copy rack attack middleware config
```bash ```bash
......
...@@ -83,3 +83,22 @@ Feature: Profile ...@@ -83,3 +83,22 @@ Feature: Profile
Given I visit profile design page Given I visit profile design page
When I change my code preview theme When I change my code preview theme
Then I should receive feedback that the changes were saved Then I should receive feedback that the changes were saved
@javascript
Scenario: I see the password strength indicator
Given I visit profile password page
When I try to set a weak password
Then I should see the input field yellow
@javascript
Scenario: I see the password strength indicator error
Given I visit profile password page
When I try to set a short password
Then I should see the input field red
And I should see the password error message
@javascript
Scenario: I see the password strength indicator with success
Given I visit profile password page
When I try to set a strong password
Then I should see the input field green
\ No newline at end of file
...@@ -54,3 +54,9 @@ Feature: Project Services ...@@ -54,3 +54,9 @@ Feature: Project Services
And I click email on push service link And I click email on push service link
And I fill email on push settings And I fill email on push settings
Then I should see email on push service settings saved Then I should see email on push service settings saved
Scenario: Activate Atlassian Bamboo CI service
When I visit project "Shop" services page
And I click Atlassian Bamboo CI service link
And I fill Atlassian Bamboo CI settings
Then I should see Atlassian Bamboo CI service settings saved
...@@ -10,6 +10,7 @@ class Spinach::Features::DashboardIssues < Spinach::FeatureSteps ...@@ -10,6 +10,7 @@ class Spinach::Features::DashboardIssues < Spinach::FeatureSteps
step 'I should see issues authored by me' do step 'I should see issues authored by me' do
should_see(authored_issue) should_see(authored_issue)
should_see(authored_issue_on_public_project)
should_not_see(assigned_issue) should_not_see(assigned_issue)
should_not_see(other_issue) should_not_see(other_issue)
end end
...@@ -22,6 +23,7 @@ class Spinach::Features::DashboardIssues < Spinach::FeatureSteps ...@@ -22,6 +23,7 @@ class Spinach::Features::DashboardIssues < Spinach::FeatureSteps
step 'I have authored issues' do step 'I have authored issues' do
authored_issue authored_issue
authored_issue_on_public_project
end end
step 'I have assigned issues' do step 'I have assigned issues' do
...@@ -64,6 +66,10 @@ class Spinach::Features::DashboardIssues < Spinach::FeatureSteps ...@@ -64,6 +66,10 @@ class Spinach::Features::DashboardIssues < Spinach::FeatureSteps
@other_issue ||= create :issue, project: project @other_issue ||= create :issue, project: project
end end
def authored_issue_on_public_project
@authored_issue_on_public_project ||= create :issue, author: current_user, project: public_project
end
def project def project
@project ||= begin @project ||= begin
project =create :project project =create :project
...@@ -71,4 +77,8 @@ class Spinach::Features::DashboardIssues < Spinach::FeatureSteps ...@@ -71,4 +77,8 @@ class Spinach::Features::DashboardIssues < Spinach::FeatureSteps
project project
end end
end end
def public_project
@public_project ||= create :project, :public
end
end end
...@@ -4,13 +4,17 @@ class Spinach::Features::DashboardMergeRequests < Spinach::FeatureSteps ...@@ -4,13 +4,17 @@ class Spinach::Features::DashboardMergeRequests < Spinach::FeatureSteps
step 'I should see merge requests assigned to me' do step 'I should see merge requests assigned to me' do
should_see(assigned_merge_request) should_see(assigned_merge_request)
should_see(assigned_merge_request_from_fork)
should_not_see(authored_merge_request) should_not_see(authored_merge_request)
should_not_see(authored_merge_request_from_fork)
should_not_see(other_merge_request) should_not_see(other_merge_request)
end end
step 'I should see merge requests authored by me' do step 'I should see merge requests authored by me' do
should_see(authored_merge_request) should_see(authored_merge_request)
should_see(authored_merge_request_from_fork)
should_not_see(assigned_merge_request) should_not_see(assigned_merge_request)
should_not_see(assigned_merge_request_from_fork)
should_not_see(other_merge_request) should_not_see(other_merge_request)
end end
...@@ -22,10 +26,12 @@ class Spinach::Features::DashboardMergeRequests < Spinach::FeatureSteps ...@@ -22,10 +26,12 @@ class Spinach::Features::DashboardMergeRequests < Spinach::FeatureSteps
step 'I have authored merge requests' do step 'I have authored merge requests' do
authored_merge_request authored_merge_request
authored_merge_request_from_fork
end end
step 'I have assigned merge requests' do step 'I have assigned merge requests' do
assigned_merge_request assigned_merge_request
assigned_merge_request_from_fork
end end
step 'I have other merge requests' do step 'I have other merge requests' do
...@@ -53,15 +59,41 @@ class Spinach::Features::DashboardMergeRequests < Spinach::FeatureSteps ...@@ -53,15 +59,41 @@ class Spinach::Features::DashboardMergeRequests < Spinach::FeatureSteps
end end
def assigned_merge_request def assigned_merge_request
@assigned_merge_request ||= create :merge_request, assignee: current_user, target_project: project, source_project: project @assigned_merge_request ||= create :merge_request,
assignee: current_user,
target_project: project,
source_project: project
end end
def authored_merge_request def authored_merge_request
@authored_merge_request ||= create :merge_request, source_branch: 'simple_merge_request', author: current_user, target_project: project, source_project: project @authored_merge_request ||= create :merge_request,
source_branch: 'simple_merge_request',
author: current_user,
target_project: project,
source_project: project
end end
def other_merge_request def other_merge_request
@other_merge_request ||= create :merge_request, source_branch: '2_3_notes_fix', target_project: project, source_project: project @other_merge_request ||= create :merge_request,
source_branch: '2_3_notes_fix',
target_project: project,
source_project: project
end
def authored_merge_request_from_fork
@authored_merge_request_from_fork ||= create :merge_request,
source_branch: 'basic_page',
author: current_user,
target_project: public_project,
source_project: forked_project
end
def assigned_merge_request_from_fork
@assigned_merge_request_from_fork ||= create :merge_request,
source_branch: 'basic_page_fix',
assignee: current_user,
target_project: public_project,
source_project: forked_project
end end
def project def project
...@@ -71,4 +103,12 @@ class Spinach::Features::DashboardMergeRequests < Spinach::FeatureSteps ...@@ -71,4 +103,12 @@ class Spinach::Features::DashboardMergeRequests < Spinach::FeatureSteps
project project
end end
end end
def public_project
@public_project ||= create :project, :public
end
def forked_project
@forked_project ||= Projects::ForkService.new(public_project, current_user).execute
end
end end
...@@ -58,16 +58,34 @@ class Spinach::Features::Profile < Spinach::FeatureSteps ...@@ -58,16 +58,34 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
step 'I try change my password w/o old one' do step 'I try change my password w/o old one' do
within '.update-password' do within '.update-password' do
fill_in "user_password", with: "22233344" fill_in "user_password_profile", with: "22233344"
fill_in "user_password_confirmation", with: "22233344" fill_in "user_password_confirmation", with: "22233344"
click_button "Save" click_button "Save"
end end
end end
step 'I try to set a weak password' do
within '.update-password' do
fill_in "user_password_profile", with: "22233344"
end
end
step 'I try to set a short password' do
within '.update-password' do
fill_in "user_password_profile", with: "short"
end
end
step 'I try to set a strong password' do
within '.update-password' do
fill_in "user_password_profile", with: "Itulvo9z8uud%$"
end
end
step 'I change my password' do step 'I change my password' do
within '.update-password' do within '.update-password' do
fill_in "user_current_password", with: "12345678" fill_in "user_current_password", with: "12345678"
fill_in "user_password", with: "22233344" fill_in "user_password_profile", with: "22233344"
fill_in "user_password_confirmation", with: "22233344" fill_in "user_password_confirmation", with: "22233344"
click_button "Save" click_button "Save"
end end
...@@ -76,7 +94,7 @@ class Spinach::Features::Profile < Spinach::FeatureSteps ...@@ -76,7 +94,7 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
step 'I unsuccessfully change my password' do step 'I unsuccessfully change my password' do
within '.update-password' do within '.update-password' do
fill_in "user_current_password", with: "12345678" fill_in "user_current_password", with: "12345678"
fill_in "user_password", with: "password" fill_in "user_password_profile", with: "password"
fill_in "user_password_confirmation", with: "confirmation" fill_in "user_password_confirmation", with: "confirmation"
click_button "Save" click_button "Save"
end end
...@@ -86,6 +104,22 @@ class Spinach::Features::Profile < Spinach::FeatureSteps ...@@ -86,6 +104,22 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
page.should have_content "You must provide a valid current password" page.should have_content "You must provide a valid current password"
end end
step 'I should see the input field yellow' do
page.should have_css 'div.has-warning'
end
step 'I should see the input field green' do
page.should have_css 'div.has-success'
end
step 'I should see the input field red' do
page.should have_css 'div.has-error'
end
step 'I should see the password error message' do
page.should have_content 'Your password is too short'
end
step "I should see a password error message" do step "I should see a password error message" do
page.should have_content "Password confirmation doesn't match" page.should have_content "Password confirmation doesn't match"
end end
...@@ -146,7 +180,7 @@ class Spinach::Features::Profile < Spinach::FeatureSteps ...@@ -146,7 +180,7 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
step 'I submit new password' do step 'I submit new password' do
fill_in :user_current_password, with: '12345678' fill_in :user_current_password, with: '12345678'
fill_in :user_password, with: '12345678' fill_in :user_password_profile, with: '12345678'
fill_in :user_password_confirmation, with: '12345678' fill_in :user_password_confirmation, with: '12345678'
click_button "Set new password" click_button "Set new password"
end end
......
...@@ -4,7 +4,7 @@ class Spinach::Features::Project < Spinach::FeatureSteps ...@@ -4,7 +4,7 @@ class Spinach::Features::Project < Spinach::FeatureSteps
include SharedPaths include SharedPaths
step 'change project settings' do step 'change project settings' do
fill_in 'project_name', with: 'NewName' fill_in 'project_name_edit', with: 'NewName'
uncheck 'project_issues_enabled' uncheck 'project_issues_enabled'
end end
......
...@@ -14,6 +14,7 @@ class Spinach::Features::ProjectServices < Spinach::FeatureSteps ...@@ -14,6 +14,7 @@ class Spinach::Features::ProjectServices < Spinach::FeatureSteps
page.should have_content 'GitLab CI' page.should have_content 'GitLab CI'
page.should have_content 'Assembla' page.should have_content 'Assembla'
page.should have_content 'Pushover' page.should have_content 'Pushover'
page.should have_content 'Atlassian Bamboo'
end end
step 'I click gitlab-ci service link' do step 'I click gitlab-ci service link' do
...@@ -137,4 +138,23 @@ class Spinach::Features::ProjectServices < Spinach::FeatureSteps ...@@ -137,4 +138,23 @@ class Spinach::Features::ProjectServices < Spinach::FeatureSteps
find_field('Priority').find('option[selected]').value.should == '1' find_field('Priority').find('option[selected]').value.should == '1'
find_field('Sound').find('option[selected]').value.should == 'bike' find_field('Sound').find('option[selected]').value.should == 'bike'
end end
step 'I click Atlassian Bamboo CI service link' do
click_link 'Atlassian Bamboo CI'
end
step 'I fill Atlassian Bamboo CI settings' do
check 'Active'
fill_in 'Bamboo url', with: 'http://bamboo.example.com'
fill_in 'Build key', with: 'KEY'
fill_in 'Username', with: 'user'
fill_in 'Password', with: 'verySecret'
click_button 'Save'
end
step 'I should see Atlassian Bamboo CI service settings saved' do
find_field('Bamboo url').value.should == 'http://bamboo.example.com'
find_field('Build key').value.should == 'KEY'
find_field('Username').value.should == 'user'
end
end end
...@@ -27,6 +27,7 @@ module API ...@@ -27,6 +27,7 @@ module API
helpers APIHelpers helpers APIHelpers
mount Groups mount Groups
mount GroupMembers
mount Users mount Users
mount Projects mount Projects
mount Repositories mount Repositories
......
module API
class GroupMembers < Grape::API
before { authenticate! }
resource :groups do
helpers do
def find_group(id)
group = Group.find(id)
if can?(current_user, :read_group, group)
group
else
render_api_error!("403 Forbidden - #{current_user.username} lacks sufficient access to #{group.name}", 403)
end
end
def validate_access_level?(level)
Gitlab::Access.options_with_owner.values.include? level.to_i
end
end
# Get a list of group members viewable by the authenticated user.
#
# Example Request:
# GET /groups/:id/members
get ":id/members" do
group = find_group(params[:id])
members = group.group_members
users = (paginate members).collect(&:user)
present users, with: Entities::GroupMember, group: group
end
# Add a user to the list of group members
#
# Parameters:
# id (required) - group id
# user_id (required) - the users id
# access_level (required) - Project access level
# Example Request:
# POST /groups/:id/members
post ":id/members" do
group = find_group(params[:id])
authorize! :manage_group, group
required_attributes! [:user_id, :access_level]
unless validate_access_level?(params[:access_level])
render_api_error!("Wrong access level", 422)
end
if group.group_members.find_by(user_id: params[:user_id])
render_api_error!("Already exists", 409)
end
group.add_users([params[:user_id]], params[:access_level])
member = group.group_members.find_by(user_id: params[:user_id])
present member.user, with: Entities::GroupMember, group: group
end
# Remove member.
#
# Parameters:
# id (required) - group id
# user_id (required) - the users id
#
# Example Request:
# DELETE /groups/:id/members/:user_id
delete ":id/members/:user_id" do
group = find_group(params[:id])
authorize! :manage_group, group
member = group.group_members.find_by(user_id: params[:user_id])
if member.nil?
render_api_error!("404 Not Found - user_id:#{params[:user_id]} not a member of group #{group.name}",404)
else
member.destroy
end
end
end
end
end
...@@ -97,57 +97,6 @@ module API ...@@ -97,57 +97,6 @@ module API
not_found! not_found!
end end
end end
# Get a list of group members viewable by the authenticated user.
#
# Example Request:
# GET /groups/:id/members
get ":id/members" do
group = find_group(params[:id])
members = group.group_members
users = (paginate members).collect(&:user)
present users, with: Entities::GroupMember, group: group
end
# Add a user to the list of group members
#
# Parameters:
# id (required) - group id
# user_id (required) - the users id
# access_level (required) - Project access level
# Example Request:
# POST /groups/:id/members
post ":id/members" do
required_attributes! [:user_id, :access_level]
unless validate_access_level?(params[:access_level])
render_api_error!("Wrong access level", 422)
end
group = find_group(params[:id])
if group.group_members.find_by(user_id: params[:user_id])
render_api_error!("Already exists", 409)
end
group.add_users([params[:user_id]], params[:access_level])
member = group.group_members.find_by(user_id: params[:user_id])
present member.user, with: Entities::GroupMember, group: group
end
# Remove member.
#
# Parameters:
# id (required) - group id
# user_id (required) - the users id
#
# Example Request:
# DELETE /groups/:id/members/:user_id
delete ":id/members/:user_id" do
group = find_group(params[:id])
member = group.group_members.find_by(user_id: params[:user_id])
if member.nil?
render_api_error!("404 Not Found - user_id:#{params[:user_id]} not a member of group #{group.name}",404)
else
member.destroy
end
end
end end
end end
end end
...@@ -178,7 +178,7 @@ module API ...@@ -178,7 +178,7 @@ module API
# DELETE /projects/:id # DELETE /projects/:id
delete ":id" do delete ":id" do
authorize! :remove_project, user_project authorize! :remove_project, user_project
user_project.destroy ::Projects::DestroyService.new(user_project, current_user, {}).execute
end end
# Mark this project as forked from another # Mark this project as forked from another
......
# Read about interceptors in http://guides.rubyonrails.org/action_mailer_basics.html#intercepting-emails
class DisableEmailInterceptor
def self.delivering_email(message)
message.perform_deliveries = false
Rails.logger.info "Emails disabled! Interceptor prevented sending mail #{message.subject}"
end
end
require 'spec_helper' require 'spec_helper'
describe "Projects", feature: true do describe "Projects", feature: true, js: true do
before { login_as :user } before { login_as :user }
describe "DELETE /projects/:id" do describe "DELETE /projects/:id" do
...@@ -10,12 +10,23 @@ describe "Projects", feature: true do ...@@ -10,12 +10,23 @@ describe "Projects", feature: true do
visit edit_project_path(@project) visit edit_project_path(@project)
end end
it "should be correct path", js: true do it "should remove project" do
expect { expect { remove_project }.to change {Project.count}.by(-1)
end
it 'should delete the project from disk' do
expect(GitlabShellWorker).to(
receive(:perform_async).with(:remove_repository,
/#{@project.path_with_namespace}/)
).twice
remove_project
end
end
def remove_project
click_link "Remove project" click_link "Remove project"
fill_in 'confirm_name_input', with: @project.path fill_in 'confirm_name_input', with: @project.path
click_button 'Confirm' click_button 'Confirm'
}.to change {Project.count}.by(-1)
end
end end
end end
...@@ -11,7 +11,7 @@ describe 'Users', feature: true do ...@@ -11,7 +11,7 @@ describe 'Users', feature: true do
fill_in "user_name", with: "Name Surname" fill_in "user_name", with: "Name Surname"
fill_in "user_username", with: "Great" fill_in "user_username", with: "Great"
fill_in "user_email", with: "name@mail.com" fill_in "user_email", with: "name@mail.com"
fill_in "user_password", with: "password1234" fill_in "user_password_sign_up", with: "password1234"
fill_in "user_password_confirmation", with: "password1234" fill_in "user_password_confirmation", with: "password1234"
expect { click_button "Sign up" }.to change {User.count}.by(1) expect { click_button "Sign up" }.to change {User.count}.by(1)
end end
......
require 'spec_helper'
describe DisableEmailInterceptor do
before do
ActionMailer::Base.register_interceptor(DisableEmailInterceptor)
end
it 'should not send emails' do
Gitlab.config.gitlab.stub(:email_enabled).and_return(false)
expect {
deliver_mail
}.not_to change(ActionMailer::Base.deliveries, :count)
end
after do
# Removing interceptor from the list because unregister_interceptor is
# implemented in later version of mail gem
# See: https://github.com/mikel/mail/pull/705
Mail.class_variable_set(:@@delivery_interceptors, [])
end
def deliver_mail
key = create :personal_key
Notify.new_ssh_key_email(key.id)
end
end
require 'spec_helper'
describe Issue, "Mentionable" do
describe :mentioned_users do
let!(:user) { create(:user, username: 'stranger') }
let!(:user2) { create(:user, username: 'john') }
let!(:issue) { create(:issue, description: '@stranger mentioned') }
subject { issue.mentioned_users }
it { should include(user) }
it { should_not include(user2) }
end
end
require 'spec_helper'
describe API::API, api: true do
include ApiHelpers
let(:owner) { create(:user) }
let(:reporter) { create(:user) }
let(:developer) { create(:user) }
let(:master) { create(:user) }
let(:guest) { create(:user) }
let(:stranger) { create(:user) }
let!(:group_with_members) do
group = create(:group)
group.add_users([reporter.id], GroupMember::REPORTER)
group.add_users([developer.id], GroupMember::DEVELOPER)
group.add_users([master.id], GroupMember::MASTER)
group.add_users([guest.id], GroupMember::GUEST)
group
end
let!(:group_no_members) { create(:group) }
before do
group_with_members.add_owner owner
group_no_members.add_owner owner
end
describe "GET /groups/:id/members" do
context "when authenticated as user that is part or the group" do
it "each user: should return an array of members groups of group3" do
[owner, master, developer, reporter, guest].each do |user|
get api("/groups/#{group_with_members.id}/members", user)
response.status.should == 200
json_response.should be_an Array
json_response.size.should == 5
json_response.find { |e| e['id']==owner.id }['access_level'].should == GroupMember::OWNER
json_response.find { |e| e['id']==reporter.id }['access_level'].should == GroupMember::REPORTER
json_response.find { |e| e['id']==developer.id }['access_level'].should == GroupMember::DEVELOPER
json_response.find { |e| e['id']==master.id }['access_level'].should == GroupMember::MASTER
json_response.find { |e| e['id']==guest.id }['access_level'].should == GroupMember::GUEST
end
end
it "users not part of the group should get access error" do
get api("/groups/#{group_with_members.id}/members", stranger)
response.status.should == 403
end
end
end
describe "POST /groups/:id/members" do
context "when not a member of the group" do
it "should not add guest as member of group_no_members when adding being done by person outside the group" do
post api("/groups/#{group_no_members.id}/members", reporter), user_id: guest.id, access_level: GroupMember::MASTER
response.status.should == 403
end
end
context "when a member of the group" do
it "should return ok and add new member" do
new_user = create(:user)
expect {
post api("/groups/#{group_no_members.id}/members", owner),
user_id: new_user.id, access_level: GroupMember::MASTER
}.to change { group_no_members.members.count }.by(1)
response.status.should == 201
json_response['name'].should == new_user.name
json_response['access_level'].should == GroupMember::MASTER
end
it "should not allow guest to modify group members" do
new_user = create(:user)
expect {
post api("/groups/#{group_with_members.id}/members", guest),
user_id: new_user.id, access_level: GroupMember::MASTER
}.not_to change { group_with_members.members.count }
response.status.should == 403
end
it "should return error if member already exists" do
post api("/groups/#{group_with_members.id}/members", owner), user_id: master.id, access_level: GroupMember::MASTER
response.status.should == 409
end
it "should return a 400 error when user id is not given" do
post api("/groups/#{group_no_members.id}/members", owner), access_level: GroupMember::MASTER
response.status.should == 400
end
it "should return a 400 error when access level is not given" do
post api("/groups/#{group_no_members.id}/members", owner), user_id: master.id
response.status.should == 400
end
it "should return a 422 error when access level is not known" do
post api("/groups/#{group_no_members.id}/members", owner), user_id: master.id, access_level: 1234
response.status.should == 422
end
end
end
describe "DELETE /groups/:id/members/:user_id" do
context "when not a member of the group" do
it "should not delete guest's membership of group_with_members" do
random_user = create(:user)
delete api("/groups/#{group_with_members.id}/members/#{owner.id}", random_user)
response.status.should == 403
end
end
context "when a member of the group" do
it "should delete guest's membership of group" do
expect {
delete api("/groups/#{group_with_members.id}/members/#{guest.id}", owner)
}.to change { group_with_members.members.count }.by(-1)
response.status.should == 200
end
it "should return a 404 error when user id is not known" do
delete api("/groups/#{group_with_members.id}/members/1328", owner)
response.status.should == 404
end
it "should not allow guest to modify group members" do
delete api("/groups/#{group_with_members.id}/members/#{master.id}", guest)
response.status.should == 403
end
end
end
end
...@@ -165,114 +165,4 @@ describe API::API, api: true do ...@@ -165,114 +165,4 @@ describe API::API, api: true do
end end
end end
end end
describe "members" do
let(:owner) { create(:user) }
let(:reporter) { create(:user) }
let(:developer) { create(:user) }
let(:master) { create(:user) }
let(:guest) { create(:user) }
let!(:group_with_members) do
group = create(:group)
group.add_users([reporter.id], GroupMember::REPORTER)
group.add_users([developer.id], GroupMember::DEVELOPER)
group.add_users([master.id], GroupMember::MASTER)
group.add_users([guest.id], GroupMember::GUEST)
group
end
let!(:group_no_members) { create(:group) }
before do
group_with_members.add_owner owner
group_no_members.add_owner owner
end
describe "GET /groups/:id/members" do
context "when authenticated as user that is part or the group" do
it "each user: should return an array of members groups of group3" do
[owner, master, developer, reporter, guest].each do |user|
get api("/groups/#{group_with_members.id}/members", user)
response.status.should == 200
json_response.should be_an Array
json_response.size.should == 5
json_response.find { |e| e['id']==owner.id }['access_level'].should == GroupMember::OWNER
json_response.find { |e| e['id']==reporter.id }['access_level'].should == GroupMember::REPORTER
json_response.find { |e| e['id']==developer.id }['access_level'].should == GroupMember::DEVELOPER
json_response.find { |e| e['id']==master.id }['access_level'].should == GroupMember::MASTER
json_response.find { |e| e['id']==guest.id }['access_level'].should == GroupMember::GUEST
end
end
it "users not part of the group should get access error" do
get api("/groups/#{group_with_members.id}/members", user1)
response.status.should == 403
end
end
end
describe "POST /groups/:id/members" do
context "when not a member of the group" do
it "should not add guest as member of group_no_members when adding being done by person outside the group" do
post api("/groups/#{group_no_members.id}/members", reporter), user_id: guest.id, access_level: GroupMember::MASTER
response.status.should == 403
end
end
context "when a member of the group" do
it "should return ok and add new member" do
count_before=group_no_members.group_members.count
new_user = create(:user)
post api("/groups/#{group_no_members.id}/members", owner), user_id: new_user.id, access_level: GroupMember::MASTER
response.status.should == 201
json_response['name'].should == new_user.name
json_response['access_level'].should == GroupMember::MASTER
group_no_members.group_members.count.should == count_before + 1
end
it "should return error if member already exists" do
post api("/groups/#{group_with_members.id}/members", owner), user_id: master.id, access_level: GroupMember::MASTER
response.status.should == 409
end
it "should return a 400 error when user id is not given" do
post api("/groups/#{group_no_members.id}/members", owner), access_level: GroupMember::MASTER
response.status.should == 400
end
it "should return a 400 error when access level is not given" do
post api("/groups/#{group_no_members.id}/members", owner), user_id: master.id
response.status.should == 400
end
it "should return a 422 error when access level is not known" do
post api("/groups/#{group_no_members.id}/members", owner), user_id: master.id, access_level: 1234
response.status.should == 422
end
end
end
describe "DELETE /groups/:id/members/:user_id" do
context "when not a member of the group" do
it "should not delete guest's membership of group_with_members" do
random_user = create(:user)
delete api("/groups/#{group_with_members.id}/members/#{owner.id}", random_user)
response.status.should == 403
end
end
context "when a member of the group" do
it "should delete guest's membership of group" do
count_before=group_with_members.group_members.count
delete api("/groups/#{group_with_members.id}/members/#{guest.id}", owner)
response.status.should == 200
group_with_members.group_members.count.should == count_before - 1
end
it "should return a 404 error when user id is not known" do
delete api("/groups/#{group_with_members.id}/members/1328", owner)
response.status.should == 404
end
end
end
end
end end
...@@ -632,6 +632,11 @@ describe API::API, api: true do ...@@ -632,6 +632,11 @@ describe API::API, api: true do
describe "DELETE /projects/:id" do describe "DELETE /projects/:id" do
context "when authenticated as user" do context "when authenticated as user" do
it "should remove project" do it "should remove project" do
expect(GitlabShellWorker).to(
receive(:perform_async).with(:remove_repository,
/#{project.path_with_namespace}/)
).twice
delete api("/projects/#{project.id}", user) delete api("/projects/#{project.id}", user)
response.status.should == 200 response.status.should == 200
end end
......
...@@ -3,15 +3,12 @@ require 'spec_helper' ...@@ -3,15 +3,12 @@ require 'spec_helper'
describe Projects::TransferService do describe Projects::TransferService do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:group) { create(:group) } let(:group) { create(:group) }
let(:group2) { create(:group) }
let(:project) { create(:project, namespace: user.namespace) } let(:project) { create(:project, namespace: user.namespace) }
context 'namespace -> namespace' do context 'namespace -> namespace' do
before do before do
group.add_owner(user) group.add_owner(user)
@service = Projects::TransferService.new(project, user, namespace_id: group.id) @result = transfer_project(project, user, namespace_id: group.id)
@service.gitlab_shell.stub(mv_repository: true)
@result = @service.execute
end end
it { @result.should be_true } it { @result.should be_true }
...@@ -20,24 +17,25 @@ describe Projects::TransferService do ...@@ -20,24 +17,25 @@ describe Projects::TransferService do
context 'namespace -> no namespace' do context 'namespace -> no namespace' do
before do before do
group.add_owner(user) @result = transfer_project(project, user, namespace_id: nil)
@service = Projects::TransferService.new(project, user, namespace_id: nil)
@service.gitlab_shell.stub(mv_repository: true)
@result = @service.execute
end end
it { @result.should_not be_nil } # { result.should be_false } passes on nil
it { @result.should be_false } it { @result.should be_false }
it { project.namespace.should == user.namespace } it { project.namespace.should == user.namespace }
end end
context 'namespace -> not allowed namespace' do context 'namespace -> not allowed namespace' do
before do before do
@service = Projects::TransferService.new(project, user, namespace_id: group2.id) @result = transfer_project(project, user, namespace_id: group.id)
@service.gitlab_shell.stub(mv_repository: true)
@result = @service.execute
end end
it { @result.should_not be_nil } # { result.should be_false } passes on nil
it { @result.should be_false } it { @result.should be_false }
it { project.namespace.should == user.namespace } it { project.namespace.should == user.namespace }
end end
def transfer_project(project, user, params)
Projects::TransferService.new(project, user, params).execute
end
end end
This diff is collapsed.
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