Commit f619d34f authored by Valery Sizov's avatar Valery Sizov

Merge branch 'ce-upstream1' into 'master'

CE upstream



See merge request !348
parents 0d035441 c67b67bd
...@@ -706,7 +706,7 @@ Metrics/ClassLength: ...@@ -706,7 +706,7 @@ Metrics/ClassLength:
# of test cases needed to validate a method. # of test cases needed to validate a method.
Metrics/CyclomaticComplexity: Metrics/CyclomaticComplexity:
Enabled: true Enabled: true
Max: 17 Max: 18
# Limit lines to 80 characters. # Limit lines to 80 characters.
Metrics/LineLength: Metrics/LineLength:
......
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 8.7.0 (unreleased) v 8.7.0 (unreleased)
- Transactions for /internal/allowed now have an "action" tag set
- Method instrumentation now uses Module#prepend instead of aliasing methods - Method instrumentation now uses Module#prepend instead of aliasing methods
- Repository.clean_old_archives is now instrumented - Repository.clean_old_archives is now instrumented
- Add support for environment variables on a job level in CI configuration file - Add support for environment variables on a job level in CI configuration file
...@@ -13,18 +14,21 @@ v 8.7.0 (unreleased) ...@@ -13,18 +14,21 @@ v 8.7.0 (unreleased)
- Project switcher uses new dropdown styling - Project switcher uses new dropdown styling
- Load award emoji images separately unless opening the full picker. Saves several hundred KBs of data for most pages. (Connor Shea) - Load award emoji images separately unless opening the full picker. Saves several hundred KBs of data for most pages. (Connor Shea)
- Do not include award_emojis in issue and merge_request comment_count !3610 (Lucas Charles) - Do not include award_emojis in issue and merge_request comment_count !3610 (Lucas Charles)
- Restrict user profiles when public visibility level is restricted.
- All images in discussions and wikis now link to their source files !3464 (Connor Shea). - All images in discussions and wikis now link to their source files !3464 (Connor Shea).
- Return status code 303 after a branch DELETE operation to avoid project deletion (Stan Hu) - Return status code 303 after a branch DELETE operation to avoid project deletion (Stan Hu)
- Add setting for customizing the list of trusted proxies !3524 - Add setting for customizing the list of trusted proxies !3524
- Allow projects to be transfered to a lower visibility level group - Allow projects to be transfered to a lower visibility level group
- Fix `signed_in_ip` being set to 127.0.0.1 when using a reverse proxy !3524 - Fix `signed_in_ip` being set to 127.0.0.1 when using a reverse proxy !3524
- Improved Markdown rendering performance !3389 - Improved Markdown rendering performance !3389
- Make shared runners text in box configurable
- Don't attempt to look up an avatar in repo if repo directory does not exist (Stan Hu) - Don't attempt to look up an avatar in repo if repo directory does not exist (Stan Hu)
- API: Ability to subscribe and unsubscribe from issues and merge requests (Robert Schilling) - API: Ability to subscribe and unsubscribe from issues and merge requests (Robert Schilling)
- Expose project badges in project settings - Expose project badges in project settings
- Make /profile/keys/new redirect to /profile/keys for back-compat. !3717 - Make /profile/keys/new redirect to /profile/keys for back-compat. !3717
- Preserve time notes/comments have been updated at when moving issue - Preserve time notes/comments have been updated at when moving issue
- Make HTTP(s) label consistent on clone bar (Stan Hu) - Make HTTP(s) label consistent on clone bar (Stan Hu)
- Add support for `after_script`, requires Runner 1.2 (Kamil Trzciński)
- Expose label description in API (Mariusz Jachimowicz) - Expose label description in API (Mariusz Jachimowicz)
- API: Ability to update a group (Robert Schilling) - API: Ability to update a group (Robert Schilling)
- API: Ability to move issues (Robert Schilling) - API: Ability to move issues (Robert Schilling)
...@@ -49,6 +53,7 @@ v 8.7.0 (unreleased) ...@@ -49,6 +53,7 @@ v 8.7.0 (unreleased)
- Use rugged to change HEAD in Project#change_head (P.S.V.R) - Use rugged to change HEAD in Project#change_head (P.S.V.R)
- API: Ability to filter milestones by state `active` and `closed` (Robert Schilling) - API: Ability to filter milestones by state `active` and `closed` (Robert Schilling)
- API: Fix milestone filtering by `iid` (Robert Schilling) - API: Fix milestone filtering by `iid` (Robert Schilling)
- Make before_script and after_script overridable on per-job (Kamil Trzciński)
- API: Delete notes of issues, snippets, and merge requests (Robert Schilling) - API: Delete notes of issues, snippets, and merge requests (Robert Schilling)
- Implement 'Groups View' as an option for dashboard preferences !3379 (Elias W.) - Implement 'Groups View' as an option for dashboard preferences !3379 (Elias W.)
- Better errors handling when creating milestones inside groups - Better errors handling when creating milestones inside groups
...@@ -84,6 +89,15 @@ v 8.7.0 (unreleased) ...@@ -84,6 +89,15 @@ v 8.7.0 (unreleased)
- Author and participants are displayed first on users autocompletion - Author and participants are displayed first on users autocompletion
- Show number sign on external issue reference text (Florent Baldino) - Show number sign on external issue reference text (Florent Baldino)
- Updated print style for issues - Updated print style for issues
- Use GitHub Issue/PR number as iid to keep references
- Import GitHub labels
- Import GitHub milestones
- Fix emoji catgories in the emoji picker
- Execute system web hooks on push to the project
- Allow enable/disable push events for system hooks
v 8.6.7 (unreleased)
- Fix vulnerability that made it possible to enumerate private projects belonging to group
v 8.6.6 v 8.6.6
- Expire the exists cache before deletion to ensure project dir actually exists (Stan Hu). !3413 - Expire the exists cache before deletion to ensure project dir actually exists (Stan Hu). !3413
......
...@@ -325,7 +325,7 @@ end ...@@ -325,7 +325,7 @@ end
gem "newrelic_rpm", '~> 3.14' gem "newrelic_rpm", '~> 3.14'
gem 'octokit', '~> 3.8.0' gem 'octokit', '~> 4.3.0'
gem "mail_room", "~> 0.6.1" gem "mail_room", "~> 0.6.1"
......
...@@ -509,8 +509,8 @@ GEM ...@@ -509,8 +509,8 @@ GEM
multi_json (~> 1.3) multi_json (~> 1.3)
multi_xml (~> 0.5) multi_xml (~> 0.5)
rack (~> 1.2) rack (~> 1.2)
octokit (3.8.0) octokit (4.3.0)
sawyer (~> 0.6.0, >= 0.5.3) sawyer (~> 0.7.0, >= 0.5.3)
omniauth (1.3.1) omniauth (1.3.1)
hashie (>= 1.2, < 4) hashie (>= 1.2, < 4)
rack (>= 1.0, < 3) rack (>= 1.0, < 3)
...@@ -736,8 +736,8 @@ GEM ...@@ -736,8 +736,8 @@ GEM
sprockets (>= 2.8, < 4.0) sprockets (>= 2.8, < 4.0)
sprockets-rails (>= 2.0, < 4.0) sprockets-rails (>= 2.0, < 4.0)
tilt (>= 1.1, < 3) tilt (>= 1.1, < 3)
sawyer (0.6.0) sawyer (0.7.0)
addressable (~> 2.3.5) addressable (>= 2.3.5, < 2.5)
faraday (~> 0.8, < 0.10) faraday (~> 0.8, < 0.10)
scss_lint (0.47.1) scss_lint (0.47.1)
rake (>= 0.9, < 11) rake (>= 0.9, < 11)
...@@ -1001,7 +1001,7 @@ DEPENDENCIES ...@@ -1001,7 +1001,7 @@ DEPENDENCIES
newrelic_rpm (~> 3.14) newrelic_rpm (~> 3.14)
nokogiri (~> 1.6.7, >= 1.6.7.2) nokogiri (~> 1.6.7, >= 1.6.7.2)
oauth2 (~> 1.0.0) oauth2 (~> 1.0.0)
octokit (~> 3.8.0) octokit (~> 4.3.0)
omniauth (~> 1.3.1) omniauth (~> 1.3.1)
omniauth-auth0 (~> 1.4.1) omniauth-auth0 (~> 1.4.1)
omniauth-azure-oauth2 (~> 0.0.6) omniauth-azure-oauth2 (~> 0.0.6)
......
...@@ -177,7 +177,7 @@ $ -> ...@@ -177,7 +177,7 @@ $ ->
$('.trigger-submit').on 'change', -> $('.trigger-submit').on 'change', ->
$(@).parents('form').submit() $(@).parents('form').submit()
gl.utils.localTimeAgo($('abbr.timeago, .js-timeago'), false) gl.utils.localTimeAgo($('abbr.timeago, .js-timeago'), true)
# Flash # Flash
if (flash = $(".flash-container")).length > 0 if (flash = $(".flash-container")).length > 0
......
...@@ -61,6 +61,7 @@ class @DropzoneInput ...@@ -61,6 +61,7 @@ class @DropzoneInput
return return
drop: -> drop: ->
$mdArea.removeClass 'is-dropzone-hover'
form.find(".div-dropzone-hover").css "opacity", 0 form.find(".div-dropzone-hover").css "opacity", 0
form_textarea.focus() form_textarea.focus()
return return
......
...@@ -32,10 +32,8 @@ class GitLabDropdownFilter ...@@ -32,10 +32,8 @@ class GitLabDropdownFilter
else if @input.val() is "" and $inputContainer.hasClass HAS_VALUE_CLASS else if @input.val() is "" and $inputContainer.hasClass HAS_VALUE_CLASS
$inputContainer.removeClass HAS_VALUE_CLASS $inputContainer.removeClass HAS_VALUE_CLASS
if keyCode is 13 and @input.val() isnt "" if keyCode is 13
if @options.enterCallback return false
@options.enterCallback()
return
clearTimeout timeout clearTimeout timeout
timeout = setTimeout => timeout = setTimeout =>
...@@ -132,7 +130,6 @@ class GitLabDropdown ...@@ -132,7 +130,6 @@ class GitLabDropdown
@filterInput = @getElement(FILTER_INPUT) @filterInput = @getElement(FILTER_INPUT)
@highlight = false @highlight = false
@filterInputBlur = true @filterInputBlur = true
@enterCallback = true
} = @options } = @options
self = @ self = @
...@@ -178,9 +175,6 @@ class GitLabDropdown ...@@ -178,9 +175,6 @@ class GitLabDropdown
callback: (data) => callback: (data) =>
currentIndex = -1 currentIndex = -1
@parseData data @parseData data
enterCallback: =>
if @enterCallback
@selectRowAtIndex 0
# Event listeners # Event listeners
......
...@@ -183,9 +183,10 @@ class @MergeRequestTabs ...@@ -183,9 +183,10 @@ class @MergeRequestTabs
else else
$diffLine = $('td', $diffLine) $diffLine = $('td', $diffLine)
$diffLine.addClass 'hll' if $diffLine.length
diffLineTop = $diffLine.offset().top $diffLine.addClass 'hll'
navBarHeight = $('.navbar-gitlab').outerHeight() diffLineTop = $diffLine.offset().top
navBarHeight = $('.navbar-gitlab').outerHeight()
loadBuilds: (source) -> loadBuilds: (source) ->
return if @buildsLoaded return if @buildsLoaded
......
...@@ -45,9 +45,10 @@ class @Profile ...@@ -45,9 +45,10 @@ class @Profile
saveForm: -> saveForm: ->
self = @ self = @
formData = new FormData(@form[0]) formData = new FormData(@form[0])
formData.append('user[avatar]', @avatarGlCrop.getBlob(), 'avatar.png')
avatarBlob = @avatarGlCrop.getBlob()
formData.append('user[avatar]', avatarBlob, 'avatar.png') if avatarBlob?
$.ajax $.ajax
url: @form.attr('action') url: @form.attr('action')
......
...@@ -71,7 +71,7 @@ header { ...@@ -71,7 +71,7 @@ header {
.header-content { .header-content {
position: relative; position: relative;
height: $header-height; height: $header-height;
padding-right: 20px; padding-right: 40px;
@media (min-width: $screen-sm-min) { @media (min-width: $screen-sm-min) {
padding-right: 0; padding-right: 0;
...@@ -122,6 +122,10 @@ header { ...@@ -122,6 +122,10 @@ header {
} }
} }
.project-item-select-holder {
display: inline;
}
.impersonation i { .impersonation i {
color: $red-normal; color: $red-normal;
} }
......
...@@ -61,11 +61,11 @@ ...@@ -61,11 +61,11 @@
padding: $gl-padding-top $gl-padding; padding: $gl-padding-top $gl-padding;
border: 1px solid $note-form-border-color; border: 1px solid $note-form-border-color;
border-radius: $border-radius-base; border-radius: $border-radius-base;
transition: border-color ease-in-out 0.15s,
box-shadow ease-in-out 0.15s;
&.is-focused { &.is-focused {
border-color: $focus-border-color; @extend .form-control:focus;
box-shadow: 0 0 2px $black-transparent,
0 0 4px rgba($focus-border-color, .4);
.comment-toolbar, .comment-toolbar,
.nav-links { .nav-links {
......
...@@ -171,6 +171,11 @@ ul.notes { ...@@ -171,6 +171,11 @@ ul.notes {
&.parallel { &.parallel {
border-width: 1px; border-width: 1px;
.code,
code {
white-space: pre-wrap;
}
} }
.notes { .notes {
......
...@@ -76,6 +76,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController ...@@ -76,6 +76,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:admin_notification_email, :admin_notification_email,
:user_oauth_applications, :user_oauth_applications,
:shared_runners_enabled, :shared_runners_enabled,
:shared_runners_text,
:max_artifacts_size, :max_artifacts_size,
:max_pages_size, :max_pages_size,
:metrics_enabled, :metrics_enabled,
......
...@@ -39,6 +39,6 @@ class Admin::HooksController < Admin::ApplicationController ...@@ -39,6 +39,6 @@ class Admin::HooksController < Admin::ApplicationController
end end
def hook_params def hook_params
params.require(:hook).permit(:url, :enable_ssl_verification) params.require(:hook).permit(:url, :enable_ssl_verification, :push_events, :tag_push_events)
end end
end end
...@@ -7,10 +7,12 @@ class Projects::GroupLinksController < Projects::ApplicationController ...@@ -7,10 +7,12 @@ class Projects::GroupLinksController < Projects::ApplicationController
end end
def create def create
link = project.project_group_links.new group = Group.find(params[:link_group_id])
link.group_id = params[:link_group_id] return render_404 unless can?(current_user, :read_group, group)
link.group_access = params[:link_group_access]
link.save project.project_group_links.create(
group: group, group_access: params[:link_group_access]
)
redirect_to namespace_project_group_links_path(project.namespace, project) redirect_to namespace_project_group_links_path(project.namespace, project)
end end
......
class UsersController < ApplicationController class UsersController < ApplicationController
skip_before_action :authenticate_user! skip_before_action :authenticate_user!
before_action :set_user before_action :user
before_action :authorize_read_user!, only: [:show]
def show def show
respond_to do |format| respond_to do |format|
...@@ -75,22 +76,26 @@ class UsersController < ApplicationController ...@@ -75,22 +76,26 @@ class UsersController < ApplicationController
private private
def set_user def authorize_read_user!
@user = User.find_by_username!(params[:username]) render_404 unless can?(current_user, :read_user, user)
end
def user
@user ||= User.find_by_username!(params[:username])
end end
def contributed_projects def contributed_projects
ContributedProjectsFinder.new(@user).execute(current_user) ContributedProjectsFinder.new(user).execute(current_user)
end end
def contributions_calendar def contributions_calendar
@contributions_calendar ||= Gitlab::ContributionsCalendar. @contributions_calendar ||= Gitlab::ContributionsCalendar.
new(contributed_projects, @user) new(contributed_projects, user)
end end
def load_events def load_events
# Get user activity feed for projects common for both users # Get user activity feed for projects common for both users
@events = @user.recent_events. @events = user.recent_events.
merge(projects_for_current_user). merge(projects_for_current_user).
references(:project). references(:project).
with_associations. with_associations.
...@@ -99,16 +104,16 @@ class UsersController < ApplicationController ...@@ -99,16 +104,16 @@ class UsersController < ApplicationController
def load_projects def load_projects
@projects = @projects =
PersonalProjectsFinder.new(@user).execute(current_user) PersonalProjectsFinder.new(user).execute(current_user)
.page(params[:page]) .page(params[:page])
end end
def load_contributed_projects def load_contributed_projects
@contributed_projects = contributed_projects.joined(@user) @contributed_projects = contributed_projects.joined(user)
end end
def load_groups def load_groups
@groups = JoinedGroupsFinder.new(@user).execute(current_user) @groups = JoinedGroupsFinder.new(user).execute(current_user)
end end
def projects_for_current_user def projects_for_current_user
......
...@@ -19,6 +19,10 @@ module ApplicationSettingsHelper ...@@ -19,6 +19,10 @@ module ApplicationSettingsHelper
current_application_settings.help_text current_application_settings.help_text
end end
def shared_runners_text
current_application_settings.shared_runners_text
end
def user_oauth_applications? def user_oauth_applications?
current_application_settings.user_oauth_applications current_application_settings.user_oauth_applications
end end
......
...@@ -8,7 +8,7 @@ class RepositoryCheckMailer < BaseMailer ...@@ -8,7 +8,7 @@ class RepositoryCheckMailer < BaseMailer
mail( mail(
to: User.admins.pluck(:email), to: User.admins.pluck(:email),
subject: @message subject: "GitLab Admin | #{@message}"
) )
end end
end end
...@@ -19,6 +19,7 @@ class Ability ...@@ -19,6 +19,7 @@ class Ability
when Namespace then namespace_abilities(user, subject) when Namespace then namespace_abilities(user, subject)
when GroupMember then group_member_abilities(user, subject) when GroupMember then group_member_abilities(user, subject)
when ProjectMember then project_member_abilities(user, subject) when ProjectMember then project_member_abilities(user, subject)
when User then user_abilities
else [] else []
end.concat(global_abilities(user)) end.concat(global_abilities(user))
...@@ -49,6 +50,8 @@ class Ability ...@@ -49,6 +50,8 @@ class Ability
anonymous_project_abilities(subject) anonymous_project_abilities(subject)
when subject.is_a?(Group) || subject.respond_to?(:group) when subject.is_a?(Group) || subject.respond_to?(:group)
anonymous_group_abilities(subject) anonymous_group_abilities(subject)
when subject.is_a?(User)
anonymous_user_abilities
else else
[] []
end end
...@@ -95,17 +98,17 @@ class Ability ...@@ -95,17 +98,17 @@ class Ability
end end
def anonymous_group_abilities(subject) def anonymous_group_abilities(subject)
rules = []
group = if subject.is_a?(Group) group = if subject.is_a?(Group)
subject subject
else else
subject.group subject.group
end end
if group && group.public? rules << :read_group if group.public?
[:read_group]
else rules
[]
end
end end
def anonymous_personal_snippet_abilities(snippet) def anonymous_personal_snippet_abilities(snippet)
...@@ -124,9 +127,14 @@ class Ability ...@@ -124,9 +127,14 @@ class Ability
end end
end end
def anonymous_user_abilities
[:read_user] unless restricted_public_level?
end
def global_abilities(user) def global_abilities(user)
rules = [] rules = []
rules << :create_group if user.can_create_group rules << :create_group if user.can_create_group
rules << :read_users_list
rules rules
end end
...@@ -177,7 +185,7 @@ class Ability ...@@ -177,7 +185,7 @@ class Ability
@public_project_rules ||= project_guest_rules + [ @public_project_rules ||= project_guest_rules + [
:download_code, :download_code,
:fork_project, :fork_project,
:read_commit_status, :read_commit_status
] ]
end end
...@@ -302,7 +310,6 @@ class Ability ...@@ -302,7 +310,6 @@ class Ability
def group_abilities(user, group) def group_abilities(user, group)
rules = [] rules = []
rules << :read_group if can_read_group?(user, group) rules << :read_group if can_read_group?(user, group)
# Only group masters and group owners can create new projects # Only group masters and group owners can create new projects
...@@ -478,6 +485,10 @@ class Ability ...@@ -478,6 +485,10 @@ class Ability
rules rules
end end
def user_abilities
[:read_user]
end
def abilities def abilities
@abilities ||= begin @abilities ||= begin
abilities = Six.new abilities = Six.new
...@@ -492,6 +503,10 @@ class Ability ...@@ -492,6 +503,10 @@ class Ability
private private
def restricted_public_level?
current_application_settings.restricted_visibility_levels.include?(Gitlab::VisibilityLevel::PUBLIC)
end
def named_abilities(name) def named_abilities(name)
[ [
:"read_#{name}", :"read_#{name}",
......
...@@ -7,11 +7,13 @@ module InternalId ...@@ -7,11 +7,13 @@ module InternalId
end end
def set_iid def set_iid
records = project.send(self.class.name.tableize) if iid.blank?
records = records.with_deleted if self.paranoid? records = project.send(self.class.name.tableize)
max_iid = records.maximum(:iid) records = records.with_deleted if self.paranoid?
max_iid = records.maximum(:iid)
self.iid = max_iid.to_i + 1 self.iid = max_iid.to_i + 1
end
end end
def to_param def to_param
......
...@@ -27,8 +27,6 @@ class ProjectHook < WebHook ...@@ -27,8 +27,6 @@ class ProjectHook < WebHook
belongs_to :project belongs_to :project
scope :push_hooks, -> { where(push_events: true) }
scope :tag_push_hooks, -> { where(tag_push_events: true) }
scope :issue_hooks, -> { where(issues_events: true) } scope :issue_hooks, -> { where(issues_events: true) }
scope :note_hooks, -> { where(note_events: true) } scope :note_hooks, -> { where(note_events: true) }
scope :merge_request_hooks, -> { where(merge_requests_events: true) } scope :merge_request_hooks, -> { where(merge_requests_events: true) }
......
...@@ -21,4 +21,7 @@ ...@@ -21,4 +21,7 @@
# #
class SystemHook < WebHook class SystemHook < WebHook
def async_execute(data, hook_name)
Sidekiq::Client.enqueue(SystemHookWorker, id, data, hook_name)
end
end end
...@@ -32,6 +32,9 @@ class WebHook < ActiveRecord::Base ...@@ -32,6 +32,9 @@ class WebHook < ActiveRecord::Base
default_value_for :build_events, false default_value_for :build_events, false
default_value_for :enable_ssl_verification, true default_value_for :enable_ssl_verification, true
scope :push_hooks, -> { where(push_events: true) }
scope :tag_push_hooks, -> { where(tag_push_events: true) }
# HTTParty timeout # HTTParty timeout
default_timeout Gitlab.config.gitlab.webhook_timeout default_timeout Gitlab.config.gitlab.webhook_timeout
......
...@@ -964,8 +964,8 @@ class Project < ActiveRecord::Base ...@@ -964,8 +964,8 @@ class Project < ActiveRecord::Base
end end
end end
def hook_attrs def hook_attrs(backward: true)
{ attrs = {
name: name, name: name,
description: description, description: description,
web_url: web_url, web_url: web_url,
...@@ -976,12 +976,19 @@ class Project < ActiveRecord::Base ...@@ -976,12 +976,19 @@ class Project < ActiveRecord::Base
visibility_level: visibility_level, visibility_level: visibility_level,
path_with_namespace: path_with_namespace, path_with_namespace: path_with_namespace,
default_branch: default_branch, default_branch: default_branch,
# Backward compatibility
homepage: web_url,
url: url_to_repo,
ssh_url: ssh_url_to_repo,
http_url: http_url_to_repo
} }
# Backward compatibility
if backward
attrs.merge!({
homepage: web_url,
url: url_to_repo,
ssh_url: ssh_url_to_repo,
http_url: http_url_to_repo
})
end
attrs
end end
# Reset events cache related to this project # Reset events cache related to this project
......
...@@ -74,6 +74,7 @@ class GitPushService < BaseService ...@@ -74,6 +74,7 @@ class GitPushService < BaseService
mirror_update = @project.mirror? && @project.repository.up_to_date_with_upstream?(branch_name) mirror_update = @project.mirror? && @project.repository.up_to_date_with_upstream?(branch_name)
EventCreateService.new.push(@project, current_user, build_push_data) EventCreateService.new.push(@project, current_user, build_push_data)
SystemHooksService.new.execute_hooks(build_push_data_system_hook.dup, :push_hooks)
@project.execute_hooks(build_push_data.dup, :push_hooks) @project.execute_hooks(build_push_data.dup, :push_hooks)
@project.execute_services(build_push_data.dup, :push_hooks) @project.execute_services(build_push_data.dup, :push_hooks)
...@@ -152,6 +153,11 @@ class GitPushService < BaseService ...@@ -152,6 +153,11 @@ class GitPushService < BaseService
build(@project, current_user, params[:oldrev], params[:newrev], params[:ref], push_commits) build(@project, current_user, params[:oldrev], params[:newrev], params[:ref], push_commits)
end end
def build_push_data_system_hook
@push_data_system ||= Gitlab::PushDataBuilder.
build(@project, current_user, params[:oldrev], params[:newrev], params[:ref], [])
end
def push_to_existing_branch? def push_to_existing_branch?
# Return if this is not a push to a branch (e.g. new commits) # Return if this is not a push to a branch (e.g. new commits)
Gitlab::Git.branch_ref?(params[:ref]) && !Gitlab::Git.blank_ref?(params[:oldrev]) Gitlab::Git.branch_ref?(params[:ref]) && !Gitlab::Git.blank_ref?(params[:oldrev])
......
class GitTagPushService class GitTagPushService < BaseService
attr_accessor :project, :user, :push_data attr_accessor :push_data
def execute(project, user, oldrev, newrev, ref, mirror_update: false) def execute
project.repository.before_push_tag project.repository.before_push_tag
@project, @user = project, user @push_data = build_push_data
@push_data = build_push_data(oldrev, newrev, ref)
EventCreateService.new.push(project, user, @push_data) EventCreateService.new.push(project, current_user, @push_data)
SystemHooksService.new.execute_hooks(build_system_push_data.dup, :tag_push_hooks)
project.execute_hooks(@push_data.dup, :tag_push_hooks) project.execute_hooks(@push_data.dup, :tag_push_hooks)
project.execute_services(@push_data.dup, :tag_push_hooks) project.execute_services(@push_data.dup, :tag_push_hooks)
CreateCommitBuildsService.new.execute(project, @user, @push_data, mirror_update: mirror_update) CreateCommitBuildsService.new.execute(
project,
current_user,
@push_data,
mirror_update: params[:mirror_update]
)
ProjectCacheWorker.perform_async(project.id) ProjectCacheWorker.perform_async(project.id)
true true
...@@ -18,14 +23,14 @@ class GitTagPushService ...@@ -18,14 +23,14 @@ class GitTagPushService
private private
def build_push_data(oldrev, newrev, ref) def build_push_data
commits = [] commits = []
message = nil message = nil
if !Gitlab::Git.blank_ref?(newrev) if !Gitlab::Git.blank_ref?(params[:newrev])
tag_name = Gitlab::Git.ref_name(ref) tag_name = Gitlab::Git.ref_name(params[:ref])
tag = project.repository.find_tag(tag_name) tag = project.repository.find_tag(tag_name)
if tag && tag.target == newrev if tag && tag.target == params[:newrev]
commit = project.commit(tag.target) commit = project.commit(tag.target)
commits = [commit].compact commits = [commit].compact
message = tag.message message = tag.message
...@@ -33,6 +38,11 @@ class GitTagPushService ...@@ -33,6 +38,11 @@ class GitTagPushService
end end
Gitlab::PushDataBuilder. Gitlab::PushDataBuilder.
build(project, user, oldrev, newrev, ref, commits, message) build(project, current_user, params[:oldrev], params[:newrev], params[:ref], commits, message)
end
def build_system_push_data
Gitlab::PushDataBuilder.
build(project, current_user, params[:oldrev], params[:newrev], params[:ref], [], '')
end end
end end
...@@ -75,7 +75,16 @@ module Projects ...@@ -75,7 +75,16 @@ module Projects
next if old_tag_target == tag.target next if old_tag_target == tag.target
GitTagPushService.new.execute(project, current_user, old_tag_target, tag.target, "#{Gitlab::Git::TAG_REF_PREFIX}#{tag.name}", mirror_update: true) GitTagPushService.new(
project,
current_user,
{
oldrev: old_tag_target,
newrev: tag.target,
ref: "#{Gitlab::Git::TAG_REF_PREFIX}#{tag.name}",
mirror_update: true
}
).execute
end end
fetch_result fetch_result
......
...@@ -3,17 +3,13 @@ class SystemHooksService ...@@ -3,17 +3,13 @@ class SystemHooksService
execute_hooks(build_event_data(model, event)) execute_hooks(build_event_data(model, event))
end end
private def execute_hooks(data, hooks_scope = :all)
SystemHook.send(hooks_scope).each do |hook|
def execute_hooks(data) hook.async_execute(data, 'system_hooks')
SystemHook.all.each do |sh|
async_execute_hook(sh, data, 'system_hooks')
end end
end end
def async_execute_hook(hook, data, hook_name) private
Sidekiq::Client.enqueue(SystemHookWorker, hook.id, data, hook_name)
end
def build_event_data(model, event) def build_event_data(model, event)
data = { data = {
......
...@@ -26,7 +26,9 @@ ...@@ -26,7 +26,9 @@
.btn-group{ data: data_attrs } .btn-group{ data: data_attrs }
- restricted_level_checkboxes('restricted-visibility-help').each do |level| - restricted_level_checkboxes('restricted-visibility-help').each do |level|
= level = level
%span.help-block#restricted-visibility-help Selected levels cannot be used by non-admin users for projects or snippets %span.help-block#restricted-visibility-help
Selected levels cannot be used by non-admin users for projects or snippets.
If the public level is restricted, user profiles are only visible to logged in users.
.form-group .form-group
= f.label :import_sources, class: 'control-label col-sm-2' = f.label :import_sources, class: 'control-label col-sm-2'
.col-sm-10 .col-sm-10
...@@ -166,7 +168,11 @@ ...@@ -166,7 +168,11 @@
= f.label :shared_runners_enabled do = f.label :shared_runners_enabled do
= f.check_box :shared_runners_enabled = f.check_box :shared_runners_enabled
Enable shared runners for new projects Enable shared runners for new projects
.form-group
= f.label :shared_runners_text, class: 'control-label col-sm-2'
.col-sm-10
= f.text_area :shared_runners_text, class: 'form-control', rows: 4
.help-block Markdown enabled
.form-group .form-group
= f.label :max_artifacts_size, 'Maximum artifacts size (MB)', class: 'control-label col-sm-2' = f.label :max_artifacts_size, 'Maximum artifacts size (MB)', class: 'control-label col-sm-2'
.col-sm-10 .col-sm-10
......
...@@ -16,6 +16,27 @@ ...@@ -16,6 +16,27 @@
= f.label :url, "URL:", class: 'control-label' = f.label :url, "URL:", class: 'control-label'
.col-sm-10 .col-sm-10
= f.text_field :url, class: "form-control" = f.text_field :url, class: "form-control"
.form-group
= f.label :url, "Trigger", class: 'control-label'
.col-sm-10.prepend-top-10
%div
System hook will be triggered on set of events like creating project
or adding ssh key. But you can also enable extra triggers like Push events.
%div.prepend-top-default
= f.check_box :push_events, class: 'pull-left'
.prepend-left-20
= f.label :push_events, class: 'list-label' do
%strong Push events
%p.light
This url will be triggered by a push to the repository
%div
= f.check_box :tag_push_events, class: 'pull-left'
.prepend-left-20
= f.label :tag_push_events, class: 'list-label' do
%strong Tag push events
%p.light
This url will be triggered when a new tag is pushed to the repository
.form-group .form-group
= f.label :enable_ssl_verification, "SSL verification", class: 'control-label checkbox' = f.label :enable_ssl_verification, "SSL verification", class: 'control-label checkbox'
.col-sm-10 .col-sm-10
...@@ -31,13 +52,16 @@ ...@@ -31,13 +52,16 @@
.panel.panel-default .panel.panel-default
.panel-heading .panel-heading
System hooks (#{@hooks.count}) System hooks (#{@hooks.count})
%ul.well-list %ul.content-list
- @hooks.each do |hook| - @hooks.each do |hook|
%li %li
.list-item-name .controls
%strong= hook.url
%p SSL Verification: #{hook.enable_ssl_verification ? "enabled" : "disabled"}
.pull-right
= link_to 'Test Hook', admin_hook_test_path(hook), class: "btn btn-sm" = link_to 'Test Hook', admin_hook_test_path(hook), class: "btn btn-sm"
= link_to 'Remove', admin_hook_path(hook), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-remove btn-sm" = link_to 'Remove', admin_hook_path(hook), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-remove btn-sm"
.monospace= hook.url
%div
- %w(push_events tag_push_events issues_events note_events merge_requests_events build_events).each do |trigger|
- if hook.send(trigger)
%span.label.label-gray= trigger.titleize
%span.label.label-gray SSL Verification: #{hook.enable_ssl_verification ? "enabled" : "disabled"}
...@@ -31,7 +31,8 @@ ...@@ -31,7 +31,8 @@
%span Request to merge %span Request to merge
%span.label-branch= source_branch_with_namespace(@merge_request) %span.label-branch= source_branch_with_namespace(@merge_request)
%span into %span into
= link_to @merge_request.target_branch, namespace_project_commits_path(@project.namespace, @project, @merge_request.target_branch), class: "label-branch" %span.label-branch
= link_to @merge_request.target_branch, namespace_project_commits_path(@project.namespace, @project, @merge_request.target_branch)
- if @merge_request.open? && @merge_request.diverged_from_target_branch? - if @merge_request.open? && @merge_request.diverged_from_target_branch?
%span (#{pluralize(@merge_request.diverged_commits_count, 'commit')} behind) %span (#{pluralize(@merge_request.diverged_commits_count, 'commit')} behind)
......
%h3 Shared runners %h3 Shared runners
.bs-callout.bs-callout-warning .bs-callout.bs-callout-warning.shared-runners-description
GitLab Runners do not offer secure isolation between projects that they do builds for. You are TRUSTING all GitLab users who can push code to project A, B or C to run shell scripts on the machine hosting runner X. - if shared_runners_text.present?
= markdown(shared_runners_text, pipeline: 'plain_markdown')
- else
GitLab Runners do not offer secure isolation between projects that they do builds for. You are TRUSTING all GitLab users who can push code to project A, B or C to run shell scripts on the machine hosting runner X.
%hr %hr
- if @project.shared_runners_enabled? - if @project.shared_runners_enabled?
= link_to toggle_shared_runners_namespace_project_runners_path(@project.namespace, @project), class: 'btn btn-warning', method: :post do = link_to toggle_shared_runners_namespace_project_runners_path(@project.namespace, @project), class: 'btn btn-warning', method: :post do
......
...@@ -3,3 +3,6 @@ ...@@ -3,3 +3,6 @@
%p %p
= link_to "See the affected projects in the GitLab admin panel", admin_namespaces_projects_url(last_repository_check_failed: 1) = link_to "See the affected projects in the GitLab admin panel", admin_namespaces_projects_url(last_repository_check_failed: 1)
%p
You are receiving this message because you are a GitLab administrator for #{Gitlab.config.gitlab.url}.
#{@message}. #{@message}.
\ \
View details: #{admin_namespaces_projects_url(last_repository_check_failed: 1)} View details: #{admin_namespaces_projects_url(last_repository_check_failed: 1)}
You are receiving this message because you are a GitLab administrator
for #{Gitlab.config.gitlab.url}.
...@@ -77,18 +77,16 @@ ...@@ -77,18 +77,16 @@
.panel.panel-default .panel.panel-default
.panel-heading .panel-heading
Webhooks (#{hooks.count}) Webhooks (#{hooks.count})
%ul.well-list %ul.content-list
- hook_types = %w(push_events tag_push_events note_events issues_events merge_requests_events build_events)
- hooks.each do |hook| - hooks.each do |hook|
%li %li
.pull-right .controls
= link_to 'Test Hook', polymorphic_path(url_components + [hook], action: :test), class: "btn btn-sm btn-grouped" = link_to 'Test Hook', polymorphic_path(url_components + [hook], action: :test), class: "btn btn-sm btn-grouped"
= link_to 'Remove', polymorphic_path(url_components + [hook]), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-remove btn-sm btn-grouped" = link_to 'Remove', polymorphic_path(url_components + [hook]), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-remove btn-sm btn-grouped"
.clearfix
%span.monospace= hook.url
%p %p
- hook_types.each do |hook_type| .monospace= hook.url
%div
- %w(push_events tag_push_events issues_events note_events merge_requests_events build_events).each do |hook_type|
- if hook.send(hook_type) - if hook.send(hook_type)
%span.label.label-gray= hook_type.titleize %span.label.label-gray= hook_type.titleize
SSL Verification: #{hook.enable_ssl_verification ? "enabled" : "disabled"} %span.label.label-gray SSL Verification: #{hook.enable_ssl_verification ? "enabled" : "disabled"}
...@@ -45,7 +45,7 @@ class PostReceive ...@@ -45,7 +45,7 @@ class PostReceive
end end
if Gitlab::Git.tag_ref?(ref) if Gitlab::Git.tag_ref?(ref)
GitTagPushService.new.execute(post_received.project, @user, oldrev, newrev, ref) GitTagPushService.new(post_received.project, @user, oldrev: oldrev, newrev: newrev, ref: ref).execute
elsif Gitlab::Git.branch_ref?(ref) elsif Gitlab::Git.branch_ref?(ref)
GitPushService.new(post_received.project, @user, oldrev: oldrev, newrev: newrev, ref: ref).execute GitPushService.new(post_received.project, @user, oldrev: oldrev, newrev: newrev, ref: ref).execute
end end
......
...@@ -15,10 +15,10 @@ module RepositoryCheck ...@@ -15,10 +15,10 @@ module RepositoryCheck
private private
def check(project) def check(project)
repositories = [project.repository]
repositories << project.wiki.repository if project.wiki_enabled?
# Use 'map do', not 'all? do', to prevent short-circuiting # Use 'map do', not 'all? do', to prevent short-circuiting
[project.repository, project.wiki.repository].map do |repository| repositories.map { |repository| git_fsck(repository.path_to_repo) }.all?
git_fsck(repository.path_to_repo)
end.all?
end end
def git_fsck(path) def git_fsck(path)
......
class MigrateRepoSize < ActiveRecord::Migration class MigrateRepoSize < ActiveRecord::Migration
def up def up
Project.reset_column_information project_data = execute('SELECT projects.id, namespaces.path AS namespace_path, projects.path AS project_path FROM projects LEFT JOIN namespaces ON projects.namespace_id = namespaces.id')
Project.find_each(batch_size: 500) do |project|
project_data.each do |project|
id = project['id']
namespace_path = project['namespace_path'] || ''
path = File.join(Gitlab.config.gitlab_shell.repos_path, namespace_path, project['project_path'] + '.git')
begin begin
if project.empty_repo? repo = Gitlab::Git::Repository.new(path)
if repo.empty?
print '-' print '-'
else else
project.update_repository_size size = repo.size
print '.' print '.'
execute("UPDATE projects SET repository_size = #{size} WHERE id = #{id}")
end end
rescue rescue => e
print 'F' puts "\nFailed to update project #{id}: #{e}"
end end
end end
puts 'Done' puts "\nDone"
end end
def down def down
......
class AddSharedRunnersTextToApplicationSettings < ActiveRecord::Migration
def change
add_column :application_settings, :shared_runners_text, :text
end
end
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20160414064845) do ActiveRecord::Schema.define(version: 20160415133440) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
...@@ -81,6 +81,7 @@ ActiveRecord::Schema.define(version: 20160414064845) do ...@@ -81,6 +81,7 @@ ActiveRecord::Schema.define(version: 20160414064845) do
t.boolean "email_author_in_body", default: false t.boolean "email_author_in_body", default: false
t.integer "default_group_visibility" t.integer "default_group_visibility"
t.boolean "repository_checks_enabled", default: true t.boolean "repository_checks_enabled", default: true
t.text "shared_runners_text"
end end
create_table "approvals", force: :cascade do |t| create_table "approvals", force: :cascade do |t|
......
...@@ -7,6 +7,10 @@ through the coordinator API of GitLab CI. ...@@ -7,6 +7,10 @@ through the coordinator API of GitLab CI.
A runner can be specific to a certain project or serve any project A runner can be specific to a certain project or serve any project
in GitLab CI. A runner that serves all projects is called a shared runner. in GitLab CI. A runner that serves all projects is called a shared runner.
Ideally, GitLab Runner should not be installed on the same machine as GitLab.
Read the [requirements documentation](../../install/requirements.md#gitlab-runner)
for more information.
## Shared vs. Specific Runners ## Shared vs. Specific Runners
A runner that is specific only runs for the specified project. A shared runner A runner that is specific only runs for the specified project. A shared runner
...@@ -140,7 +144,7 @@ to it. This means that if you have shared runners setup for a project and ...@@ -140,7 +144,7 @@ to it. This means that if you have shared runners setup for a project and
someone forks that project, the shared runners will also serve jobs of this someone forks that project, the shared runners will also serve jobs of this
project. project.
# Attack vectors in runners ## Attack vectors in Runners
Mentioned briefly earlier, but the following things of runners can be exploited. Mentioned briefly earlier, but the following things of runners can be exploited.
We're always looking for contributions that can mitigate these [Security Considerations](https://gitlab.com/gitlab-org/gitlab-ci-multi-runner/blob/master/docs/security/index.md). We're always looking for contributions that can mitigate these [Security Considerations](https://gitlab.com/gitlab-org/gitlab-ci-multi-runner/blob/master/docs/security/index.md).
...@@ -15,6 +15,7 @@ If you want a quick introduction to GitLab CI, follow our ...@@ -15,6 +15,7 @@ If you want a quick introduction to GitLab CI, follow our
- [.gitlab-ci.yml](#gitlab-ci-yml) - [.gitlab-ci.yml](#gitlab-ci-yml)
- [image and services](#image-and-services) - [image and services](#image-and-services)
- [before_script](#before_script) - [before_script](#before_script)
- [after_script](#after_script)
- [stages](#stages) - [stages](#stages)
- [types](#types) - [types](#types)
- [variables](#variables) - [variables](#variables)
...@@ -30,6 +31,7 @@ If you want a quick introduction to GitLab CI, follow our ...@@ -30,6 +31,7 @@ If you want a quick introduction to GitLab CI, follow our
- [artifacts](#artifacts) - [artifacts](#artifacts)
- [artifacts:name](#artifacts-name) - [artifacts:name](#artifacts-name)
- [dependencies](#dependencies) - [dependencies](#dependencies)
- [before_script and after_script](#before_script-and-after_script)
- [Hidden jobs](#hidden-jobs) - [Hidden jobs](#hidden-jobs)
- [Special YAML features](#special-yaml-features) - [Special YAML features](#special-yaml-features)
- [Anchors](#anchors) - [Anchors](#anchors)
...@@ -81,6 +83,9 @@ services: ...@@ -81,6 +83,9 @@ services:
before_script: before_script:
- bundle install - bundle install
after_script:
- rm secrets
stages: stages:
- build - build
- test - test
...@@ -105,6 +110,7 @@ There are a few reserved `keywords` that **cannot** be used as job names: ...@@ -105,6 +110,7 @@ There are a few reserved `keywords` that **cannot** be used as job names:
| stages | no | Define build stages | | stages | no | Define build stages |
| types | no | Alias for `stages` | | types | no | Alias for `stages` |
| before_script | no | Define commands that run before each job's script | | before_script | no | Define commands that run before each job's script |
| after_script | no | Define commands that run after each job's script |
| variables | no | Define build variables | | variables | no | Define build variables |
| cache | no | Define list of files that should be cached between subsequent runs | | cache | no | Define list of files that should be cached between subsequent runs |
...@@ -119,6 +125,14 @@ used for time of the build. The configuration of this feature is covered in ...@@ -119,6 +125,14 @@ used for time of the build. The configuration of this feature is covered in
`before_script` is used to define the command that should be run before all `before_script` is used to define the command that should be run before all
builds, including deploy builds. This can be an array or a multi-line string. builds, including deploy builds. This can be an array or a multi-line string.
### after_script
>**Note:**
Introduced in GitLab 8.7 and GitLab Runner v1.2.
`after_script` is used to define the command that will be run after for all
builds. This has to be an array or a multi-line string.
### stages ### stages
`stages` is used to define build stages that can be used by jobs. `stages` is used to define build stages that can be used by jobs.
...@@ -336,6 +350,8 @@ job_name: ...@@ -336,6 +350,8 @@ job_name:
| dependencies | no | Define other builds that a build depends on so that you can pass artifacts between them| | dependencies | no | Define other builds that a build depends on so that you can pass artifacts between them|
| artifacts | no | Define list build artifacts | | artifacts | no | Define list build artifacts |
| cache | no | Define list of files that should be cached between subsequent runs | | cache | no | Define list of files that should be cached between subsequent runs |
| before_script | no | Override a set of commands that are executed before build |
| after_script | no | Override a set of commands that are executed after build |
### script ### script
...@@ -692,6 +708,23 @@ deploy: ...@@ -692,6 +708,23 @@ deploy:
script: make deploy script: make deploy
``` ```
### before_script and after_script
It's possible to overwrite globally defined `before_script` and `after_script`:
```yaml
before_script
- global before script
job:
before_script:
- execute this instead of global before script
script:
- my command
after_script:
- execute this after my script
```
## Hidden jobs ## Hidden jobs
>**Note:** >**Note:**
......
...@@ -79,6 +79,26 @@ With less memory GitLab will give strange errors during the reconfigure run and ...@@ -79,6 +79,26 @@ With less memory GitLab will give strange errors during the reconfigure run and
Notice: The 25 workers of Sidekiq will show up as separate processes in your process overview (such as top or htop) but they share the same RAM allocation since Sidekiq is a multithreaded application. Please see the section below about Unicorn workers for information about many you need of those. Notice: The 25 workers of Sidekiq will show up as separate processes in your process overview (such as top or htop) but they share the same RAM allocation since Sidekiq is a multithreaded application. Please see the section below about Unicorn workers for information about many you need of those.
## Gitlab Runner
We strongly advise against installing GitLab Runner on the same machine you plan
to install GitLab on. Depending on how you decide to configure GitLab Runner and
what tools you use to exercise your application in the CI environment, GitLab
Runner can consume significant amount of available memory.
Memory consumption calculations, that are available above, will not be valid if
you decide to run GitLab Runner and the GitLab Rails application on the same
machine.
It is also not safe to install everything on a single machine, because of the
[security reasons] - especially when you plan to use shell executor with GitLab
Runner.
We recommend using a separate machine for each GitLab Runner, if you plan to
use the CI features.
[security reasons]: https://gitlab.com/gitlab-org/gitlab-ci-multi-runner/blob/master/docs/security/index.md
## Unicorn Workers ## Unicorn Workers
It's possible to increase the amount of unicorn workers and this will usually help for to reduce the response time of the applications and increase the ability to handle parallel requests. It's possible to increase the amount of unicorn workers and this will usually help for to reduce the response time of the applications and increase the ability to handle parallel requests.
......
...@@ -58,6 +58,9 @@ you are logged in or not. ...@@ -58,6 +58,9 @@ you are logged in or not.
When visiting the public page of a user, you can only see the projects which When visiting the public page of a user, you can only see the projects which
you are privileged to. you are privileged to.
If the public level is restricted, user profiles are only visible to logged in users.
## Restricting the use of public or internal projects ## Restricting the use of public or internal projects
In the Admin area under **Settings** (`/admin/application_settings`), you can In the Admin area under **Settings** (`/admin/application_settings`), you can
......
...@@ -4,6 +4,12 @@ Your GitLab instance can perform HTTP POST requests on the following events: `pr ...@@ -4,6 +4,12 @@ Your GitLab instance can perform HTTP POST requests on the following events: `pr
System hooks can be used, e.g. for logging or changing information in a LDAP server. System hooks can be used, e.g. for logging or changing information in a LDAP server.
> **Note:**
>
> We follow the same structure from Webhooks for Push and Tag events, but we never display commits.
>
> Same deprecations from Webhooks are valid here.
## Hooks request example ## Hooks request example
**Request header**: **Request header**:
...@@ -240,3 +246,110 @@ X-Gitlab-Event: System Hook ...@@ -240,3 +246,110 @@ X-Gitlab-Event: System Hook
"user_id": 41 "user_id": 41
} }
``` ```
## Push events
Triggered when you push to the repository except when pushing tags.
**Request header**:
```
X-Gitlab-Event: System Hook
```
**Request body:**
```json
{
"event_name": "push",
"before": "95790bf891e76fee5e1747ab589903a6a1f80f22",
"after": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
"ref": "refs/heads/master",
"checkout_sha": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
"user_id": 4,
"user_name": "John Smith",
"user_email": "john@example.com",
"user_avatar": "https://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=8://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=80",
"project_id": 15,
"project":{
"name":"Diaspora",
"description":"",
"web_url":"http://example.com/mike/diaspora",
"avatar_url":null,
"git_ssh_url":"git@example.com:mike/diaspora.git",
"git_http_url":"http://example.com/mike/diaspora.git",
"namespace":"Mike",
"visibility_level":0,
"path_with_namespace":"mike/diaspora",
"default_branch":"master",
"homepage":"http://example.com/mike/diaspora",
"url":"git@example.com:mike/diaspora.git",
"ssh_url":"git@example.com:mike/diaspora.git",
"http_url":"http://example.com/mike/diaspora.git"
},
"repository":{
"name": "Diaspora",
"url": "git@example.com:mike/diaspora.git",
"description": "",
"homepage": "http://example.com/mike/diaspora",
"git_http_url":"http://example.com/mike/diaspora.git",
"git_ssh_url":"git@example.com:mike/diaspora.git",
"visibility_level":0
},
"commits": [],
"total_commits_count": 0
}
```
## Tag events
Triggered when you create (or delete) tags to the repository.
**Request header**:
```
X-Gitlab-Event: System Hook
```
**Request body:**
```json
{
"event_name": "tag_push",
"before": "0000000000000000000000000000000000000000",
"after": "82b3d5ae55f7080f1e6022629cdb57bfae7cccc7",
"ref": "refs/tags/v1.0.0",
"checkout_sha": "5937ac0a7beb003549fc5fd26fc247adbce4a52e",
"user_id": 1,
"user_name": "John Smith",
"user_avatar": "https://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=8://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=80",
"project_id": 1,
"project":{
"name":"Example",
"description":"",
"web_url":"http://example.com/jsmith/example",
"avatar_url":null,
"git_ssh_url":"git@example.com:jsmith/example.git",
"git_http_url":"http://example.com/jsmith/example.git",
"namespace":"Jsmith",
"visibility_level":0,
"path_with_namespace":"jsmith/example",
"default_branch":"master",
"homepage":"http://example.com/jsmith/example",
"url":"git@example.com:jsmith/example.git",
"ssh_url":"git@example.com:jsmith/example.git",
"http_url":"http://example.com/jsmith/example.git"
},
"repository":{
"name": "Example",
"url": "ssh://git@example.com/jsmith/example.git",
"description": "",
"homepage": "http://example.com/jsmith/example",
"git_http_url":"http://example.com/jsmith/example.git",
"git_ssh_url":"git@example.com:jsmith/example.git",
"visibility_level":0
},
"commits": [],
"total_commits_count": 0
}
```
...@@ -44,6 +44,7 @@ X-Gitlab-Event: Push Hook ...@@ -44,6 +44,7 @@ X-Gitlab-Event: Push Hook
"before": "95790bf891e76fee5e1747ab589903a6a1f80f22", "before": "95790bf891e76fee5e1747ab589903a6a1f80f22",
"after": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7", "after": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
"ref": "refs/heads/master", "ref": "refs/heads/master",
"checkout_sha": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
"user_id": 4, "user_id": 4,
"user_name": "John Smith", "user_name": "John Smith",
"user_email": "john@example.com", "user_email": "john@example.com",
...@@ -121,9 +122,10 @@ X-Gitlab-Event: Tag Push Hook ...@@ -121,9 +122,10 @@ X-Gitlab-Event: Tag Push Hook
```json ```json
{ {
"object_kind": "tag_push", "object_kind": "tag_push",
"ref": "refs/tags/v1.0.0",
"before": "0000000000000000000000000000000000000000", "before": "0000000000000000000000000000000000000000",
"after": "82b3d5ae55f7080f1e6022629cdb57bfae7cccc7", "after": "82b3d5ae55f7080f1e6022629cdb57bfae7cccc7",
"ref": "refs/tags/v1.0.0",
"checkout_sha": "82b3d5ae55f7080f1e6022629cdb57bfae7cccc7",
"user_id": 1, "user_id": 1,
"user_name": "John Smith", "user_name": "John Smith",
"user_avatar": "https://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=8://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=80", "user_avatar": "https://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=8://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=80",
......
...@@ -23,6 +23,8 @@ module API ...@@ -23,6 +23,8 @@ module API
end end
post "/allowed" do post "/allowed" do
Gitlab::Metrics.tag_transaction('action', 'Grape#/internal/allowed')
status 200 status 200
actor = actor =
......
...@@ -11,6 +11,10 @@ module API ...@@ -11,6 +11,10 @@ module API
# GET /users?search=Admin # GET /users?search=Admin
# GET /users?username=root # GET /users?username=root
get do get do
unless can?(current_user, :read_users_list, nil)
render_api_error!("Not authorized.", 403)
end
if params[:username].present? if params[:username].present?
@users = User.where(username: params[:username]) @users = User.where(username: params[:username])
else else
...@@ -38,10 +42,12 @@ module API ...@@ -38,10 +42,12 @@ module API
get ":id" do get ":id" do
@user = User.find(params[:id]) @user = User.find(params[:id])
if current_user.is_admin? if current_user && current_user.is_admin?
present @user, with: Entities::UserFull present @user, with: Entities::UserFull
else elsif can?(current_user, :read_user, @user)
present @user, with: Entities::User present @user, with: Entities::User
else
render_api_error!("User not found.", 404)
end end
end end
......
...@@ -4,12 +4,12 @@ module Ci ...@@ -4,12 +4,12 @@ module Ci
DEFAULT_STAGES = %w(build test deploy) DEFAULT_STAGES = %w(build test deploy)
DEFAULT_STAGE = 'test' DEFAULT_STAGE = 'test'
ALLOWED_YAML_KEYS = [:before_script, :image, :services, :types, :stages, :variables, :cache] ALLOWED_YAML_KEYS = [:before_script, :after_script, :image, :services, :types, :stages, :variables, :cache]
ALLOWED_JOB_KEYS = [:tags, :script, :only, :except, :type, :image, :services, ALLOWED_JOB_KEYS = [:tags, :script, :only, :except, :type, :image, :services,
:allow_failure, :type, :stage, :when, :artifacts, :cache, :allow_failure, :type, :stage, :when, :artifacts, :cache,
:dependencies, :variables] :dependencies, :before_script, :after_script, :variables]
attr_reader :before_script, :image, :services, :path, :cache attr_reader :before_script, :after_script, :image, :services, :path, :cache
def initialize(config, path = nil) def initialize(config, path = nil)
@config = YAML.safe_load(config, [Symbol], [], true) @config = YAML.safe_load(config, [Symbol], [], true)
...@@ -55,6 +55,7 @@ module Ci ...@@ -55,6 +55,7 @@ module Ci
def initial_parsing def initial_parsing
@before_script = @config[:before_script] || [] @before_script = @config[:before_script] || []
@after_script = @config[:after_script]
@image = @config[:image] @image = @config[:image]
@services = @config[:services] @services = @config[:services]
@stages = @config[:stages] || @config[:types] @stages = @config[:stages] || @config[:types]
...@@ -83,7 +84,7 @@ module Ci ...@@ -83,7 +84,7 @@ module Ci
{ {
stage_idx: stages.index(job[:stage]), stage_idx: stages.index(job[:stage]),
stage: job[:stage], stage: job[:stage],
commands: "#{@before_script.join("\n")}\n#{normalize_script(job[:script])}", commands: [job[:before_script] || @before_script, job[:script]].flatten.join("\n"),
tag_list: job[:tags] || [], tag_list: job[:tags] || [],
name: name, name: name,
only: job[:only], only: job[:only],
...@@ -96,23 +97,32 @@ module Ci ...@@ -96,23 +97,32 @@ module Ci
artifacts: job[:artifacts], artifacts: job[:artifacts],
cache: job[:cache] || @cache, cache: job[:cache] || @cache,
dependencies: job[:dependencies], dependencies: job[:dependencies],
after_script: job[:after_script] || @after_script,
}.compact }.compact
} }
end end
def normalize_script(script) def validate!
if script.is_a? Array validate_global!
script.join("\n")
else @jobs.each do |name, job|
script validate_job!(name, job)
end end
true
end end
def validate! private
def validate_global!
unless validate_array_of_strings(@before_script) unless validate_array_of_strings(@before_script)
raise ValidationError, "before_script should be an array of strings" raise ValidationError, "before_script should be an array of strings"
end end
unless @after_script.nil? || validate_array_of_strings(@after_script)
raise ValidationError, "after_script should be an array of strings"
end
unless @image.nil? || @image.is_a?(String) unless @image.nil? || @image.is_a?(String)
raise ValidationError, "image should be a string" raise ValidationError, "image should be a string"
end end
...@@ -129,31 +139,28 @@ module Ci ...@@ -129,31 +139,28 @@ module Ci
raise ValidationError, "variables should be a map of key-value strings" raise ValidationError, "variables should be a map of key-value strings"
end end
if @cache validate_global_cache! if @cache
if @cache[:key] && !validate_string(@cache[:key]) end
raise ValidationError, "cache:key parameter should be a string"
end
if @cache[:untracked] && !validate_boolean(@cache[:untracked])
raise ValidationError, "cache:untracked parameter should be an boolean"
end
if @cache[:paths] && !validate_array_of_strings(@cache[:paths]) def validate_global_cache!
raise ValidationError, "cache:paths parameter should be an array of strings" if @cache[:key] && !validate_string(@cache[:key])
end raise ValidationError, "cache:key parameter should be a string"
end end
@jobs.each do |name, job| if @cache[:untracked] && !validate_boolean(@cache[:untracked])
validate_job!(name, job) raise ValidationError, "cache:untracked parameter should be an boolean"
end end
true if @cache[:paths] && !validate_array_of_strings(@cache[:paths])
raise ValidationError, "cache:paths parameter should be an array of strings"
end
end end
def validate_job!(name, job) def validate_job!(name, job)
validate_job_name!(name) validate_job_name!(name)
validate_job_keys!(name, job) validate_job_keys!(name, job)
validate_job_types!(name, job) validate_job_types!(name, job)
validate_job_script!(name, job)
validate_job_stage!(name, job) if job[:stage] validate_job_stage!(name, job) if job[:stage]
validate_job_variables!(name, job) if job[:variables] validate_job_variables!(name, job) if job[:variables]
...@@ -162,8 +169,6 @@ module Ci ...@@ -162,8 +169,6 @@ module Ci
validate_job_dependencies!(name, job) if job[:dependencies] validate_job_dependencies!(name, job) if job[:dependencies]
end end
private
def validate_job_name!(name) def validate_job_name!(name)
if name.blank? || !validate_string(name) if name.blank? || !validate_string(name)
raise ValidationError, "job name should be non-empty string" raise ValidationError, "job name should be non-empty string"
...@@ -179,10 +184,6 @@ module Ci ...@@ -179,10 +184,6 @@ module Ci
end end
def validate_job_types!(name, job) def validate_job_types!(name, job)
if !validate_string(job[:script]) && !validate_array_of_strings(job[:script])
raise ValidationError, "#{name} job: script should be a string or an array of a strings"
end
if job[:image] && !validate_string(job[:image]) if job[:image] && !validate_string(job[:image])
raise ValidationError, "#{name} job: image should be a string" raise ValidationError, "#{name} job: image should be a string"
end end
...@@ -212,6 +213,20 @@ module Ci ...@@ -212,6 +213,20 @@ module Ci
end end
end end
def validate_job_script!(name, job)
if !validate_string(job[:script]) && !validate_array_of_strings(job[:script])
raise ValidationError, "#{name} job: script should be a string or an array of a strings"
end
if job[:before_script] && !validate_array_of_strings(job[:before_script])
raise ValidationError, "#{name} job: before_script should be an array of strings"
end
if job[:after_script] && !validate_array_of_strings(job[:after_script])
raise ValidationError, "#{name} job: after_script should be an array of strings"
end
end
def validate_job_stage!(name, job) def validate_job_stage!(name, job)
unless job[:stage].is_a?(String) && job[:stage].in?(stages) unless job[:stage].is_a?(String) && job[:stage].in?(stages)
raise ValidationError, "#{name} job: stage parameter should be #{stages.join(", ")}" raise ValidationError, "#{name} job: stage parameter should be #{stages.join(", ")}"
......
...@@ -16,7 +16,8 @@ module Gitlab ...@@ -16,7 +16,8 @@ module Gitlab
end end
def execute def execute
import_issues && import_pull_requests && import_wiki import_labels && import_milestones && import_issues &&
import_pull_requests && import_wiki
end end
private private
...@@ -25,6 +26,26 @@ module Gitlab ...@@ -25,6 +26,26 @@ module Gitlab
@import_data_credentials ||= project.import_data.credentials if project.import_data @import_data_credentials ||= project.import_data.credentials if project.import_data
end end
def import_labels
client.labels(project.import_source).each do |raw_data|
Label.create!(LabelFormatter.new(project, raw_data).attributes)
end
true
rescue ActiveRecord::RecordInvalid => e
raise Projects::ImportService::Error, e.message
end
def import_milestones
client.list_milestones(project.import_source, state: :all).each do |raw_data|
Milestone.create!(MilestoneFormatter.new(project, raw_data).attributes)
end
true
rescue ActiveRecord::RecordInvalid => e
raise Projects::ImportService::Error, e.message
end
def import_issues def import_issues
client.list_issues(project.import_source, state: :all, client.list_issues(project.import_source, state: :all,
sort: :created, sort: :created,
...@@ -33,6 +54,7 @@ module Gitlab ...@@ -33,6 +54,7 @@ module Gitlab
if gh_issue.valid? if gh_issue.valid?
issue = Issue.create!(gh_issue.attributes) issue = Issue.create!(gh_issue.attributes)
apply_labels(gh_issue.number, issue)
if gh_issue.has_comments? if gh_issue.has_comments?
import_comments(gh_issue.number, issue) import_comments(gh_issue.number, issue)
...@@ -55,6 +77,7 @@ module Gitlab ...@@ -55,6 +77,7 @@ module Gitlab
merge_request = MergeRequest.new(pull_request.attributes) merge_request = MergeRequest.new(pull_request.attributes)
if merge_request.save if merge_request.save
apply_labels(pull_request.number, merge_request)
import_comments(pull_request.number, merge_request) import_comments(pull_request.number, merge_request)
import_comments_on_diff(pull_request.number, merge_request) import_comments_on_diff(pull_request.number, merge_request)
end end
...@@ -66,6 +89,18 @@ module Gitlab ...@@ -66,6 +89,18 @@ module Gitlab
raise Projects::ImportService::Error, e.message raise Projects::ImportService::Error, e.message
end end
def apply_labels(number, issuable)
issue = client.issue(project.import_source, number)
if issue.labels.count > 0
label_ids = issue.labels.map do |raw|
Label.find_by(LabelFormatter.new(project, raw).attributes).try(:id)
end
issuable.update_attribute(:label_ids, label_ids)
end
end
def import_comments(issue_number, noteable) def import_comments(issue_number, noteable)
comments = client.issue_comments(project.import_source, issue_number) comments = client.issue_comments(project.import_source, issue_number)
create_comments(comments, noteable) create_comments(comments, noteable)
......
...@@ -3,7 +3,9 @@ module Gitlab ...@@ -3,7 +3,9 @@ module Gitlab
class IssueFormatter < BaseFormatter class IssueFormatter < BaseFormatter
def attributes def attributes
{ {
iid: number,
project: project, project: project,
milestone: milestone,
title: raw_data.title, title: raw_data.title,
description: description, description: description,
state: state, state: state,
...@@ -54,6 +56,12 @@ module Gitlab ...@@ -54,6 +56,12 @@ module Gitlab
@formatter.author_line(author) + body @formatter.author_line(author) + body
end end
def milestone
if raw_data.milestone.present?
project.milestones.find_by(iid: raw_data.milestone.number)
end
end
def state def state
raw_data.state == 'closed' ? 'closed' : 'opened' raw_data.state == 'closed' ? 'closed' : 'opened'
end end
......
module Gitlab
module GithubImport
class LabelFormatter < BaseFormatter
def attributes
{
project: project,
title: title,
color: color
}
end
private
def color
"##{raw_data.color}"
end
def title
raw_data.name
end
end
end
end
module Gitlab
module GithubImport
class MilestoneFormatter < BaseFormatter
def attributes
{
iid: number,
project: project,
title: title,
description: description,
due_date: due_date,
state: state,
created_at: created_at,
updated_at: updated_at
}
end
private
def number
raw_data.number
end
def title
raw_data.title
end
def description
raw_data.description
end
def due_date
raw_data.due_on
end
def state
raw_data.state == 'closed' ? 'closed' : 'active'
end
def created_at
raw_data.created_at
end
def updated_at
state == 'closed' ? raw_data.closed_at : raw_data.updated_at
end
end
end
end
...@@ -3,6 +3,7 @@ module Gitlab ...@@ -3,6 +3,7 @@ module Gitlab
class PullRequestFormatter < BaseFormatter class PullRequestFormatter < BaseFormatter
def attributes def attributes
{ {
iid: number,
title: raw_data.title, title: raw_data.title,
description: description, description: description,
source_project: source_project, source_project: source_project,
...@@ -10,6 +11,7 @@ module Gitlab ...@@ -10,6 +11,7 @@ module Gitlab
target_project: target_project, target_project: target_project,
target_branch: target_branch.name, target_branch: target_branch.name,
state: state, state: state,
milestone: milestone,
author_id: author_id, author_id: author_id,
assignee_id: assignee_id, assignee_id: assignee_id,
created_at: raw_data.created_at, created_at: raw_data.created_at,
...@@ -57,6 +59,12 @@ module Gitlab ...@@ -57,6 +59,12 @@ module Gitlab
formatter.author_line(author) + body formatter.author_line(author) + body
end end
def milestone
if raw_data.milestone.present?
project.milestones.find_by(iid: raw_data.milestone.number)
end
end
def source_project def source_project
project project
end end
......
...@@ -36,11 +36,12 @@ module Gitlab ...@@ -36,11 +36,12 @@ module Gitlab
commit.hook_attrs(with_changed_files: true) commit.hook_attrs(with_changed_files: true)
end end
type = Gitlab::Git.tag_ref?(ref) ? "tag_push" : "push" type = Gitlab::Git.tag_ref?(ref) ? 'tag_push' : 'push'
# Hash to be passed as post_receive_data # Hash to be passed as post_receive_data
data = { data = {
object_kind: type, object_kind: type,
event_name: type,
before: oldrev, before: oldrev,
after: newrev, after: newrev,
ref: ref, ref: ref,
......
...@@ -11,7 +11,7 @@ retry() { ...@@ -11,7 +11,7 @@ retry() {
return 1 return 1
} }
if [ -f /.dockerinit ]; then if [ -f /.dockerenv ] || [ -f ./dockerinit ]; then
mkdir -p vendor mkdir -p vendor
# Install phantomjs package # Install phantomjs package
......
require 'spec_helper'
describe Groups::GroupMembersController do
let(:user) { create(:user) }
let(:group) { create(:group) }
context "index" do
before do
group.add_owner(user)
stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC])
end
it 'renders index with group members' do
get :index, group_id: group.path
expect(response.status).to eq(200)
expect(response).to render_template(:index)
end
end
end
require 'spec_helper'
describe Projects::GroupLinksController do
let(:project) { create(:project, :private) }
let(:group) { create(:group, :private) }
let(:user) { create(:user) }
before do
project.team << [user, :master]
sign_in(user)
end
describe '#create' do
shared_context 'link project to group' do
before do
post(:create, namespace_id: project.namespace.to_param,
project_id: project.to_param,
link_group_id: group.id,
link_group_access: ProjectGroupLink.default_access)
end
end
context 'when user has access to group he want to link project to' do
before { group.add_developer(user) }
include_context 'link project to group'
it 'links project with selected group' do
expect(group.shared_projects).to include project
end
it 'redirects to project group links page'do
expect(response).to redirect_to(
namespace_project_group_links_path(project.namespace, project)
)
end
end
context 'when user doers not have access to group he want to link to' do
include_context 'link project to group'
it 'renders 404' do
expect(response.status).to eq 404
end
it 'does not share project with that group' do
expect(group.shared_projects).to_not include project
end
end
end
end
...@@ -33,7 +33,30 @@ describe UsersController do ...@@ -33,7 +33,30 @@ describe UsersController do
it 'renders the show template' do it 'renders the show template' do
get :show, username: user.username get :show, username: user.username
expect(response).to be_success expect(response.status).to eq(200)
expect(response).to render_template('show')
end
end
end
context 'when public visibility level is restricted' do
before do
stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC])
end
context 'when logged out' do
it 'renders 404' do
get :show, username: user.username
expect(response.status).to eq(404)
end
end
context 'when logged in' do
before { sign_in(user) }
it 'renders show' do
get :show, username: user.username
expect(response.status).to eq(200)
expect(response).to render_template('show') expect(response).to render_template('show')
end end
end end
......
...@@ -80,6 +80,22 @@ describe "Runners" do ...@@ -80,6 +80,22 @@ describe "Runners" do
end end
end end
describe "shared runners description" do
let(:shared_runners_text) { 'custom **shared** runners description' }
let(:shared_runners_html) { 'custom shared runners description' }
before do
stub_application_setting(shared_runners_text: shared_runners_text)
project = FactoryGirl.create :empty_project, shared_runners_enabled: false
project.team << [user, :master]
visit runners_path(project)
end
it "sees shared runners description" do
expect(page.find(".shared-runners-description")).to have_content(shared_runners_html)
end
end
describe "show page" do describe "show page" do
before do before do
@project = FactoryGirl.create :empty_project @project = FactoryGirl.create :empty_project
......
...@@ -286,6 +286,81 @@ module Ci ...@@ -286,6 +286,81 @@ module Ci
end end
end end
describe "Scripts handling" do
let(:config_data) { YAML.dump(config) }
let(:config_processor) { GitlabCiYamlProcessor.new(config_data, path) }
subject { config_processor.builds_for_stage_and_ref("test", "master").first }
describe "before_script" do
context "in global context" do
let(:config) do
{
before_script: ["global script"],
test: { script: ["script"] }
}
end
it "return commands with scripts concencaced" do
expect(subject[:commands]).to eq("global script\nscript")
end
end
context "overwritten in local context" do
let(:config) do
{
before_script: ["global script"],
test: { before_script: ["local script"], script: ["script"] }
}
end
it "return commands with scripts concencaced" do
expect(subject[:commands]).to eq("local script\nscript")
end
end
end
describe "script" do
let(:config) do
{
test: { script: ["script"] }
}
end
it "return commands with scripts concencaced" do
expect(subject[:commands]).to eq("script")
end
end
describe "after_script" do
context "in global context" do
let(:config) do
{
after_script: ["after_script"],
test: { script: ["script"] }
}
end
it "return after_script in options" do
expect(subject[:options][:after_script]).to eq(["after_script"])
end
end
context "overwritten in local context" do
let(:config) do
{
after_script: ["local after_script"],
test: { after_script: ["local after_script"], script: ["script"] }
}
end
it "return after_script in options" do
expect(subject[:options][:after_script]).to eq(["local after_script"])
end
end
end
end
describe "Image and service handling" do describe "Image and service handling" do
it "returns image and service when defined" do it "returns image and service when defined" do
...@@ -592,7 +667,7 @@ module Ci ...@@ -592,7 +667,7 @@ module Ci
stage_idx: 1, stage_idx: 1,
name: :normal_job, name: :normal_job,
only: nil, only: nil,
commands: "\ntest", commands: "test",
tag_list: [], tag_list: [],
options: {}, options: {},
when: "on_success", when: "on_success",
...@@ -619,7 +694,7 @@ EOT ...@@ -619,7 +694,7 @@ EOT
stage_idx: 1, stage_idx: 1,
name: :job1, name: :job1,
only: nil, only: nil,
commands: "\nexecute-script-for-job", commands: "execute-script-for-job",
tag_list: [], tag_list: [],
options: {}, options: {},
when: "on_success", when: "on_success",
...@@ -631,7 +706,7 @@ EOT ...@@ -631,7 +706,7 @@ EOT
stage_idx: 1, stage_idx: 1,
name: :job2, name: :job2,
only: nil, only: nil,
commands: "\nexecute-script-for-job", commands: "execute-script-for-job",
tag_list: [], tag_list: [],
options: {}, options: {},
when: "on_success", when: "on_success",
...@@ -663,6 +738,27 @@ EOT ...@@ -663,6 +738,27 @@ EOT
end.to raise_error(GitlabCiYamlProcessor::ValidationError, "before_script should be an array of strings") end.to raise_error(GitlabCiYamlProcessor::ValidationError, "before_script should be an array of strings")
end end
it "returns errors if job before_script parameter is not an array of strings" do
config = YAML.dump({ rspec: { script: "test", before_script: [10, "test"] } })
expect do
GitlabCiYamlProcessor.new(config, path)
end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: before_script should be an array of strings")
end
it "returns errors if after_script parameter is invalid" do
config = YAML.dump({ after_script: "bundle update", rspec: { script: "test" } })
expect do
GitlabCiYamlProcessor.new(config, path)
end.to raise_error(GitlabCiYamlProcessor::ValidationError, "after_script should be an array of strings")
end
it "returns errors if job after_script parameter is not an array of strings" do
config = YAML.dump({ rspec: { script: "test", after_script: [10, "test"] } })
expect do
GitlabCiYamlProcessor.new(config, path)
end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: after_script should be an array of strings")
end
it "returns errors if image parameter is invalid" do it "returns errors if image parameter is invalid" do
config = YAML.dump({ image: ["test"], rspec: { script: "test" } }) config = YAML.dump({ image: ["test"], rspec: { script: "test" } })
expect do expect do
......
...@@ -2,13 +2,14 @@ require 'spec_helper' ...@@ -2,13 +2,14 @@ require 'spec_helper'
describe Gitlab::GithubImport::IssueFormatter, lib: true do describe Gitlab::GithubImport::IssueFormatter, lib: true do
let!(:project) { create(:project, namespace: create(:namespace, path: 'octocat')) } let!(:project) { create(:project, namespace: create(:namespace, path: 'octocat')) }
let(:octocat) { OpenStruct.new(id: 123456, login: 'octocat') } let(:octocat) { double(id: 123456, login: 'octocat') }
let(:created_at) { DateTime.strptime('2011-01-26T19:01:12Z') } let(:created_at) { DateTime.strptime('2011-01-26T19:01:12Z') }
let(:updated_at) { DateTime.strptime('2011-01-27T19:01:12Z') } let(:updated_at) { DateTime.strptime('2011-01-27T19:01:12Z') }
let(:base_data) do let(:base_data) do
{ {
number: 1347, number: 1347,
milestone: nil,
state: 'open', state: 'open',
title: 'Found a bug', title: 'Found a bug',
body: "I'm having a problem with this.", body: "I'm having a problem with this.",
...@@ -26,11 +27,13 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do ...@@ -26,11 +27,13 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do
describe '#attributes' do describe '#attributes' do
context 'when issue is open' do context 'when issue is open' do
let(:raw_data) { OpenStruct.new(base_data.merge(state: 'open')) } let(:raw_data) { double(base_data.merge(state: 'open')) }
it 'returns formatted attributes' do it 'returns formatted attributes' do
expected = { expected = {
iid: 1347,
project: project, project: project,
milestone: nil,
title: 'Found a bug', title: 'Found a bug',
description: "*Created by: octocat*\n\nI'm having a problem with this.", description: "*Created by: octocat*\n\nI'm having a problem with this.",
state: 'opened', state: 'opened',
...@@ -46,11 +49,13 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do ...@@ -46,11 +49,13 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do
context 'when issue is closed' do context 'when issue is closed' do
let(:closed_at) { DateTime.strptime('2011-01-28T19:01:12Z') } let(:closed_at) { DateTime.strptime('2011-01-28T19:01:12Z') }
let(:raw_data) { OpenStruct.new(base_data.merge(state: 'closed', closed_at: closed_at)) } let(:raw_data) { double(base_data.merge(state: 'closed', closed_at: closed_at)) }
it 'returns formatted attributes' do it 'returns formatted attributes' do
expected = { expected = {
iid: 1347,
project: project, project: project,
milestone: nil,
title: 'Found a bug', title: 'Found a bug',
description: "*Created by: octocat*\n\nI'm having a problem with this.", description: "*Created by: octocat*\n\nI'm having a problem with this.",
state: 'closed', state: 'closed',
...@@ -65,7 +70,7 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do ...@@ -65,7 +70,7 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do
end end
context 'when it is assigned to someone' do context 'when it is assigned to someone' do
let(:raw_data) { OpenStruct.new(base_data.merge(assignee: octocat)) } let(:raw_data) { double(base_data.merge(assignee: octocat)) }
it 'returns nil as assignee_id when is not a GitLab user' do it 'returns nil as assignee_id when is not a GitLab user' do
expect(issue.attributes.fetch(:assignee_id)).to be_nil expect(issue.attributes.fetch(:assignee_id)).to be_nil
...@@ -78,8 +83,23 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do ...@@ -78,8 +83,23 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do
end end
end end
context 'when it has a milestone' do
let(:milestone) { double(number: 45) }
let(:raw_data) { double(base_data.merge(milestone: milestone)) }
it 'returns nil when milestone does not exist' do
expect(issue.attributes.fetch(:milestone)).to be_nil
end
it 'returns milestone when it exists' do
milestone = create(:milestone, project: project, iid: 45)
expect(issue.attributes.fetch(:milestone)).to eq milestone
end
end
context 'when author is a GitLab user' do context 'when author is a GitLab user' do
let(:raw_data) { OpenStruct.new(base_data.merge(user: octocat)) } let(:raw_data) { double(base_data.merge(user: octocat)) }
it 'returns project#creator_id as author_id when is not a GitLab user' do it 'returns project#creator_id as author_id when is not a GitLab user' do
expect(issue.attributes.fetch(:author_id)).to eq project.creator_id expect(issue.attributes.fetch(:author_id)).to eq project.creator_id
...@@ -95,7 +115,7 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do ...@@ -95,7 +115,7 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do
describe '#has_comments?' do describe '#has_comments?' do
context 'when number of comments is greater than zero' do context 'when number of comments is greater than zero' do
let(:raw_data) { OpenStruct.new(base_data.merge(comments: 1)) } let(:raw_data) { double(base_data.merge(comments: 1)) }
it 'returns true' do it 'returns true' do
expect(issue.has_comments?).to eq true expect(issue.has_comments?).to eq true
...@@ -103,7 +123,7 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do ...@@ -103,7 +123,7 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do
end end
context 'when number of comments is equal to zero' do context 'when number of comments is equal to zero' do
let(:raw_data) { OpenStruct.new(base_data.merge(comments: 0)) } let(:raw_data) { double(base_data.merge(comments: 0)) }
it 'returns false' do it 'returns false' do
expect(issue.has_comments?).to eq false expect(issue.has_comments?).to eq false
...@@ -112,7 +132,7 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do ...@@ -112,7 +132,7 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do
end end
describe '#number' do describe '#number' do
let(:raw_data) { OpenStruct.new(base_data.merge(number: 1347)) } let(:raw_data) { double(base_data.merge(number: 1347)) }
it 'returns pull request number' do it 'returns pull request number' do
expect(issue.number).to eq 1347 expect(issue.number).to eq 1347
...@@ -121,7 +141,7 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do ...@@ -121,7 +141,7 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do
describe '#valid?' do describe '#valid?' do
context 'when mention a pull request' do context 'when mention a pull request' do
let(:raw_data) { OpenStruct.new(base_data.merge(pull_request: OpenStruct.new)) } let(:raw_data) { double(base_data.merge(pull_request: double)) }
it 'returns false' do it 'returns false' do
expect(issue.valid?).to eq false expect(issue.valid?).to eq false
...@@ -129,7 +149,7 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do ...@@ -129,7 +149,7 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do
end end
context 'when does not mention a pull request' do context 'when does not mention a pull request' do
let(:raw_data) { OpenStruct.new(base_data.merge(pull_request: nil)) } let(:raw_data) { double(base_data.merge(pull_request: nil)) }
it 'returns true' do it 'returns true' do
expect(issue.valid?).to eq true expect(issue.valid?).to eq true
......
require 'spec_helper'
describe Gitlab::GithubImport::LabelFormatter, lib: true do
describe '#attributes' do
it 'returns formatted attributes' do
project = create(:project)
raw = double(name: 'improvements', color: 'e6e6e6')
formatter = described_class.new(project, raw)
expect(formatter.attributes).to eq({
project: project,
title: 'improvements',
color: '#e6e6e6'
})
end
end
end
require 'spec_helper'
describe Gitlab::GithubImport::MilestoneFormatter, lib: true do
let(:project) { create(:empty_project) }
let(:created_at) { DateTime.strptime('2011-01-26T19:01:12Z') }
let(:updated_at) { DateTime.strptime('2011-01-27T19:01:12Z') }
let(:base_data) do
{
number: 1347,
state: 'open',
title: '1.0',
description: 'Version 1.0',
due_on: nil,
created_at: created_at,
updated_at: updated_at,
closed_at: nil
}
end
subject(:formatter) { described_class.new(project, raw_data)}
describe '#attributes' do
context 'when milestone is open' do
let(:raw_data) { double(base_data.merge(state: 'open')) }
it 'returns formatted attributes' do
expected = {
iid: 1347,
project: project,
title: '1.0',
description: 'Version 1.0',
state: 'active',
due_date: nil,
created_at: created_at,
updated_at: updated_at
}
expect(formatter.attributes).to eq(expected)
end
end
context 'when milestone is closed' do
let(:closed_at) { DateTime.strptime('2011-01-28T19:01:12Z') }
let(:raw_data) { double(base_data.merge(state: 'closed', closed_at: closed_at)) }
it 'returns formatted attributes' do
expected = {
iid: 1347,
project: project,
title: '1.0',
description: 'Version 1.0',
state: 'closed',
due_date: nil,
created_at: created_at,
updated_at: closed_at
}
expect(formatter.attributes).to eq(expected)
end
end
context 'when milestone has a due date' do
let(:due_date) { DateTime.strptime('2011-01-28T19:01:12Z') }
let(:raw_data) { double(base_data.merge(due_on: due_date)) }
it 'returns formatted attributes' do
expected = {
iid: 1347,
project: project,
title: '1.0',
description: 'Version 1.0',
state: 'active',
due_date: due_date,
created_at: created_at,
updated_at: updated_at
}
expect(formatter.attributes).to eq(expected)
end
end
end
end
...@@ -2,17 +2,18 @@ require 'spec_helper' ...@@ -2,17 +2,18 @@ require 'spec_helper'
describe Gitlab::GithubImport::PullRequestFormatter, lib: true do describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
let(:project) { create(:project) } let(:project) { create(:project) }
let(:repository) { OpenStruct.new(id: 1, fork: false) } let(:repository) { double(id: 1, fork: false) }
let(:source_repo) { repository } let(:source_repo) { repository }
let(:source_branch) { OpenStruct.new(ref: 'feature', repo: source_repo) } let(:source_branch) { double(ref: 'feature', repo: source_repo) }
let(:target_repo) { repository } let(:target_repo) { repository }
let(:target_branch) { OpenStruct.new(ref: 'master', repo: target_repo) } let(:target_branch) { double(ref: 'master', repo: target_repo) }
let(:octocat) { OpenStruct.new(id: 123456, login: 'octocat') } let(:octocat) { double(id: 123456, login: 'octocat') }
let(:created_at) { DateTime.strptime('2011-01-26T19:01:12Z') } let(:created_at) { DateTime.strptime('2011-01-26T19:01:12Z') }
let(:updated_at) { DateTime.strptime('2011-01-27T19:01:12Z') } let(:updated_at) { DateTime.strptime('2011-01-27T19:01:12Z') }
let(:base_data) do let(:base_data) do
{ {
number: 1347, number: 1347,
milestone: nil,
state: 'open', state: 'open',
title: 'New feature', title: 'New feature',
body: 'Please pull these awesome changes', body: 'Please pull these awesome changes',
...@@ -31,10 +32,11 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do ...@@ -31,10 +32,11 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
describe '#attributes' do describe '#attributes' do
context 'when pull request is open' do context 'when pull request is open' do
let(:raw_data) { OpenStruct.new(base_data.merge(state: 'open')) } let(:raw_data) { double(base_data.merge(state: 'open')) }
it 'returns formatted attributes' do it 'returns formatted attributes' do
expected = { expected = {
iid: 1347,
title: 'New feature', title: 'New feature',
description: "*Created by: octocat*\n\nPlease pull these awesome changes", description: "*Created by: octocat*\n\nPlease pull these awesome changes",
source_project: project, source_project: project,
...@@ -42,6 +44,7 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do ...@@ -42,6 +44,7 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
target_project: project, target_project: project,
target_branch: 'master', target_branch: 'master',
state: 'opened', state: 'opened',
milestone: nil,
author_id: project.creator_id, author_id: project.creator_id,
assignee_id: nil, assignee_id: nil,
created_at: created_at, created_at: created_at,
...@@ -54,10 +57,11 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do ...@@ -54,10 +57,11 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
context 'when pull request is closed' do context 'when pull request is closed' do
let(:closed_at) { DateTime.strptime('2011-01-28T19:01:12Z') } let(:closed_at) { DateTime.strptime('2011-01-28T19:01:12Z') }
let(:raw_data) { OpenStruct.new(base_data.merge(state: 'closed', closed_at: closed_at)) } let(:raw_data) { double(base_data.merge(state: 'closed', closed_at: closed_at)) }
it 'returns formatted attributes' do it 'returns formatted attributes' do
expected = { expected = {
iid: 1347,
title: 'New feature', title: 'New feature',
description: "*Created by: octocat*\n\nPlease pull these awesome changes", description: "*Created by: octocat*\n\nPlease pull these awesome changes",
source_project: project, source_project: project,
...@@ -65,6 +69,7 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do ...@@ -65,6 +69,7 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
target_project: project, target_project: project,
target_branch: 'master', target_branch: 'master',
state: 'closed', state: 'closed',
milestone: nil,
author_id: project.creator_id, author_id: project.creator_id,
assignee_id: nil, assignee_id: nil,
created_at: created_at, created_at: created_at,
...@@ -77,10 +82,11 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do ...@@ -77,10 +82,11 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
context 'when pull request is merged' do context 'when pull request is merged' do
let(:merged_at) { DateTime.strptime('2011-01-28T13:01:12Z') } let(:merged_at) { DateTime.strptime('2011-01-28T13:01:12Z') }
let(:raw_data) { OpenStruct.new(base_data.merge(state: 'closed', merged_at: merged_at)) } let(:raw_data) { double(base_data.merge(state: 'closed', merged_at: merged_at)) }
it 'returns formatted attributes' do it 'returns formatted attributes' do
expected = { expected = {
iid: 1347,
title: 'New feature', title: 'New feature',
description: "*Created by: octocat*\n\nPlease pull these awesome changes", description: "*Created by: octocat*\n\nPlease pull these awesome changes",
source_project: project, source_project: project,
...@@ -88,6 +94,7 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do ...@@ -88,6 +94,7 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
target_project: project, target_project: project,
target_branch: 'master', target_branch: 'master',
state: 'merged', state: 'merged',
milestone: nil,
author_id: project.creator_id, author_id: project.creator_id,
assignee_id: nil, assignee_id: nil,
created_at: created_at, created_at: created_at,
...@@ -99,7 +106,7 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do ...@@ -99,7 +106,7 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
end end
context 'when it is assigned to someone' do context 'when it is assigned to someone' do
let(:raw_data) { OpenStruct.new(base_data.merge(assignee: octocat)) } let(:raw_data) { double(base_data.merge(assignee: octocat)) }
it 'returns nil as assignee_id when is not a GitLab user' do it 'returns nil as assignee_id when is not a GitLab user' do
expect(pull_request.attributes.fetch(:assignee_id)).to be_nil expect(pull_request.attributes.fetch(:assignee_id)).to be_nil
...@@ -113,7 +120,7 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do ...@@ -113,7 +120,7 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
end end
context 'when author is a GitLab user' do context 'when author is a GitLab user' do
let(:raw_data) { OpenStruct.new(base_data.merge(user: octocat)) } let(:raw_data) { double(base_data.merge(user: octocat)) }
it 'returns project#creator_id as author_id when is not a GitLab user' do it 'returns project#creator_id as author_id when is not a GitLab user' do
expect(pull_request.attributes.fetch(:author_id)).to eq project.creator_id expect(pull_request.attributes.fetch(:author_id)).to eq project.creator_id
...@@ -125,10 +132,25 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do ...@@ -125,10 +132,25 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
expect(pull_request.attributes.fetch(:author_id)).to eq gl_user.id expect(pull_request.attributes.fetch(:author_id)).to eq gl_user.id
end end
end end
context 'when it has a milestone' do
let(:milestone) { double(number: 45) }
let(:raw_data) { double(base_data.merge(milestone: milestone)) }
it 'returns nil when milestone does not exists' do
expect(pull_request.attributes.fetch(:milestone)).to be_nil
end
it 'returns milestone when is exists' do
milestone = create(:milestone, project: project, iid: 45)
expect(pull_request.attributes.fetch(:milestone)).to eq milestone
end
end
end end
describe '#number' do describe '#number' do
let(:raw_data) { OpenStruct.new(base_data.merge(number: 1347)) } let(:raw_data) { double(base_data.merge(number: 1347)) }
it 'returns pull request number' do it 'returns pull request number' do
expect(pull_request.number).to eq 1347 expect(pull_request.number).to eq 1347
...@@ -136,11 +158,11 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do ...@@ -136,11 +158,11 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
end end
describe '#valid?' do describe '#valid?' do
let(:invalid_branch) { OpenStruct.new(ref: 'invalid-branch') } let(:invalid_branch) { double(ref: 'invalid-branch').as_null_object }
context 'when source, and target repositories are the same' do context 'when source, and target repositories are the same' do
context 'and source and target branches exists' do context 'and source and target branches exists' do
let(:raw_data) { OpenStruct.new(base_data.merge(head: source_branch, base: target_branch)) } let(:raw_data) { double(base_data.merge(head: source_branch, base: target_branch)) }
it 'returns true' do it 'returns true' do
expect(pull_request.valid?).to eq true expect(pull_request.valid?).to eq true
...@@ -148,7 +170,7 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do ...@@ -148,7 +170,7 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
end end
context 'and source branch doesn not exists' do context 'and source branch doesn not exists' do
let(:raw_data) { OpenStruct.new(base_data.merge(head: invalid_branch, base: target_branch)) } let(:raw_data) { double(base_data.merge(head: invalid_branch, base: target_branch)) }
it 'returns false' do it 'returns false' do
expect(pull_request.valid?).to eq false expect(pull_request.valid?).to eq false
...@@ -156,7 +178,7 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do ...@@ -156,7 +178,7 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
end end
context 'and target branch doesn not exists' do context 'and target branch doesn not exists' do
let(:raw_data) { OpenStruct.new(base_data.merge(head: source_branch, base: invalid_branch)) } let(:raw_data) { double(base_data.merge(head: source_branch, base: invalid_branch)) }
it 'returns false' do it 'returns false' do
expect(pull_request.valid?).to eq false expect(pull_request.valid?).to eq false
...@@ -165,8 +187,8 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do ...@@ -165,8 +187,8 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
end end
context 'when source repo is a fork' do context 'when source repo is a fork' do
let(:source_repo) { OpenStruct.new(id: 2, fork: true) } let(:source_repo) { double(id: 2, fork: true) }
let(:raw_data) { OpenStruct.new(base_data) } let(:raw_data) { double(base_data) }
it 'returns false' do it 'returns false' do
expect(pull_request.valid?).to eq false expect(pull_request.valid?).to eq false
...@@ -174,8 +196,8 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do ...@@ -174,8 +196,8 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
end end
context 'when target repo is a fork' do context 'when target repo is a fork' do
let(:target_repo) { OpenStruct.new(id: 2, fork: true) } let(:target_repo) { double(id: 2, fork: true) }
let(:raw_data) { OpenStruct.new(base_data) } let(:raw_data) { double(base_data) }
it 'returns false' do it 'returns false' do
expect(pull_request.valid?).to eq false expect(pull_request.valid?).to eq false
......
...@@ -14,11 +14,11 @@ describe Gitlab::PushDataBuilder, lib: true do ...@@ -14,11 +14,11 @@ describe Gitlab::PushDataBuilder, lib: true do
it { expect(data[:ref]).to eq('refs/heads/master') } it { expect(data[:ref]).to eq('refs/heads/master') }
it { expect(data[:commits].size).to eq(3) } it { expect(data[:commits].size).to eq(3) }
it { expect(data[:total_commits_count]).to eq(3) } it { expect(data[:total_commits_count]).to eq(3) }
it { expect(data[:commits].first[:added]).to eq(["gitlab-grack"]) } it { expect(data[:commits].first[:added]).to eq(['gitlab-grack']) }
it { expect(data[:commits].first[:modified]).to eq([".gitmodules"]) } it { expect(data[:commits].first[:modified]).to eq(['.gitmodules']) }
it { expect(data[:commits].first[:removed]).to eq([]) } it { expect(data[:commits].first[:removed]).to eq([]) }
include_examples 'project hook data' include_examples 'project hook data with deprecateds'
include_examples 'deprecated repository hook data' include_examples 'deprecated repository hook data'
end end
...@@ -34,9 +34,18 @@ describe Gitlab::PushDataBuilder, lib: true do ...@@ -34,9 +34,18 @@ describe Gitlab::PushDataBuilder, lib: true do
it { expect(data[:checkout_sha]).to eq('5937ac0a7beb003549fc5fd26fc247adbce4a52e') } it { expect(data[:checkout_sha]).to eq('5937ac0a7beb003549fc5fd26fc247adbce4a52e') }
it { expect(data[:after]).to eq('8a2a6eb295bb170b34c24c76c49ed0e9b2eaf34b') } it { expect(data[:after]).to eq('8a2a6eb295bb170b34c24c76c49ed0e9b2eaf34b') }
it { expect(data[:ref]).to eq('refs/tags/v1.1.0') } it { expect(data[:ref]).to eq('refs/tags/v1.1.0') }
it { expect(data[:user_id]).to eq(user.id) }
it { expect(data[:user_name]).to eq(user.name) }
it { expect(data[:user_email]).to eq(user.email) }
it { expect(data[:user_avatar]).to eq(user.avatar_url) }
it { expect(data[:project_id]).to eq(project.id) }
it { expect(data[:project]).to be_a(Hash) }
it { expect(data[:commits]).to be_empty } it { expect(data[:commits]).to be_empty }
it { expect(data[:total_commits_count]).to be_zero } it { expect(data[:total_commits_count]).to be_zero }
include_examples 'project hook data with deprecateds'
include_examples 'deprecated repository hook data'
it 'does not raise an error when given nil commits' do it 'does not raise an error when given nil commits' do
expect { described_class.build(spy, spy, spy, spy, spy, nil) }. expect { described_class.build(spy, spy, spy, spy, spy, nil) }.
not_to raise_error not_to raise_error
......
...@@ -15,7 +15,7 @@ describe RepositoryCheckMailer do ...@@ -15,7 +15,7 @@ describe RepositoryCheckMailer do
it 'mentions the number of failed checks' do it 'mentions the number of failed checks' do
mail = described_class.notify(3) mail = described_class.notify(3)
expect(mail).to have_subject '3 projects failed their last repository check' expect(mail).to have_subject 'GitLab Admin | 3 projects failed their last repository check'
end end
end end
end end
...@@ -20,6 +20,24 @@ describe API::API, api: true do ...@@ -20,6 +20,24 @@ describe API::API, api: true do
end end
context "when authenticated" do context "when authenticated" do
#These specs are written just in case API authentication is not required anymore
context "when public level is restricted" do
before do
stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC])
allow_any_instance_of(API::Helpers).to receive(:authenticate!).and_return(true)
end
it "renders 403" do
get api("/users")
expect(response.status).to eq(403)
end
it "renders 404" do
get api("/users/#{user.id}")
expect(response.status).to eq(404)
end
end
it "should return an array of users" do it "should return an array of users" do
get api("/users", user) get api("/users", user)
expect(response.status).to eq(200) expect(response.status).to eq(200)
......
...@@ -5,19 +5,17 @@ describe GitTagPushService, services: true do ...@@ -5,19 +5,17 @@ describe GitTagPushService, services: true do
let(:user) { create :user } let(:user) { create :user }
let(:project) { create :project } let(:project) { create :project }
let(:service) { GitTagPushService.new } let(:service) { GitTagPushService.new(project, user, oldrev: oldrev, newrev: newrev, ref: ref) }
before do let(:oldrev) { Gitlab::Git::BLANK_SHA }
@oldrev = Gitlab::Git::BLANK_SHA let(:newrev) { "8a2a6eb295bb170b34c24c76c49ed0e9b2eaf34b" } # gitlab-test: git rev-parse refs/tags/v1.1.0
@newrev = "8a2a6eb295bb170b34c24c76c49ed0e9b2eaf34b" # gitlab-test: git rev-parse refs/tags/v1.1.0 let(:ref) { 'refs/tags/v1.1.0' }
@ref = 'refs/tags/v1.1.0'
end
describe "Git Tag Push Data" do describe "Git Tag Push Data" do
before do before do
service.execute(project, user, @oldrev, @newrev, @ref) service.execute
@push_data = service.push_data @push_data = service.push_data
@tag_name = Gitlab::Git.ref_name(@ref) @tag_name = Gitlab::Git.ref_name(ref)
@tag = project.repository.find_tag(@tag_name) @tag = project.repository.find_tag(@tag_name)
@commit = project.commit(@tag.target) @commit = project.commit(@tag.target)
end end
...@@ -25,9 +23,9 @@ describe GitTagPushService, services: true do ...@@ -25,9 +23,9 @@ describe GitTagPushService, services: true do
subject { @push_data } subject { @push_data }
it { is_expected.to include(object_kind: 'tag_push') } it { is_expected.to include(object_kind: 'tag_push') }
it { is_expected.to include(ref: @ref) } it { is_expected.to include(ref: ref) }
it { is_expected.to include(before: @oldrev) } it { is_expected.to include(before: oldrev) }
it { is_expected.to include(after: @newrev) } it { is_expected.to include(after: newrev) }
it { is_expected.to include(message: @tag.message) } it { is_expected.to include(message: @tag.message) }
it { is_expected.to include(user_id: user.id) } it { is_expected.to include(user_id: user.id) }
it { is_expected.to include(user_name: user.name) } it { is_expected.to include(user_name: user.name) }
...@@ -80,9 +78,11 @@ describe GitTagPushService, services: true do ...@@ -80,9 +78,11 @@ describe GitTagPushService, services: true do
describe "Webhooks" do describe "Webhooks" do
context "execute webhooks" do context "execute webhooks" do
let(:service) { GitTagPushService.new(project, user, oldrev: 'oldrev', newrev: 'newrev', ref: 'refs/tags/v1.0.0') }
it "when pushing tags" do it "when pushing tags" do
expect(project).to receive(:execute_hooks) expect(project).to receive(:execute_hooks)
service.execute(project, user, 'oldrev', 'newrev', 'refs/tags/v1.0.0') service.execute
end end
end end
end end
......
RSpec.shared_examples 'project hook data' do |project_key: :project| RSpec.shared_examples 'project hook data with deprecateds' do |project_key: :project|
it 'contains project data' do it 'contains project data' do
expect(data[project_key][:name]).to eq(project.name) expect(data[project_key][:name]).to eq(project.name)
expect(data[project_key][:description]).to eq(project.description) expect(data[project_key][:description]).to eq(project.description)
...@@ -17,6 +17,21 @@ RSpec.shared_examples 'project hook data' do |project_key: :project| ...@@ -17,6 +17,21 @@ RSpec.shared_examples 'project hook data' do |project_key: :project|
end end
end end
RSpec.shared_examples 'project hook data' do |project_key: :project|
it 'contains project data' do
expect(data[project_key][:name]).to eq(project.name)
expect(data[project_key][:description]).to eq(project.description)
expect(data[project_key][:web_url]).to eq(project.web_url)
expect(data[project_key][:avatar_url]).to eq(project.avatar_url)
expect(data[project_key][:git_http_url]).to eq(project.http_url_to_repo)
expect(data[project_key][:git_ssh_url]).to eq(project.ssh_url_to_repo)
expect(data[project_key][:namespace]).to eq(project.namespace.name)
expect(data[project_key][:visibility_level]).to eq(project.visibility_level)
expect(data[project_key][:path_with_namespace]).to eq(project.path_with_namespace)
expect(data[project_key][:default_branch]).to eq(project.default_branch)
end
end
RSpec.shared_examples 'deprecated repository hook data' do |project_key: :project| RSpec.shared_examples 'deprecated repository hook data' do |project_key: :project|
it 'contains deprecated repository data' do it 'contains deprecated repository data' do
expect(data[:repository][:name]).to eq(project.name) expect(data[:repository][:name]).to eq(project.name)
......
require 'spec_helper'
require 'fileutils'
describe RepositoryCheck::SingleRepositoryWorker do
subject { described_class.new }
it 'fails if the wiki repository is broken' do
project = create(:project_empty_repo, wiki_enabled: true)
project.create_wiki
# Test sanity: everything should be fine before the wiki repo is broken
subject.perform(project.id)
expect(project.reload.last_repository_check_failed).to eq(false)
destroy_wiki(project)
subject.perform(project.id)
expect(project.reload.last_repository_check_failed).to eq(true)
end
it 'skips wikis when disabled' do
project = create(:project_empty_repo, wiki_enabled: false)
# Make sure the test would fail if it checked the wiki repo
destroy_wiki(project)
subject.perform(project.id)
expect(project.reload.last_repository_check_failed).to eq(false)
end
def destroy_wiki(project)
FileUtils.rm_rf(project.wiki.repository.path_to_repo)
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