Commit ca162699 authored by Kamil Trzcinski's avatar Kamil Trzcinski

Merge branch 'improve-pipeline-processing' into pipeline-hooks-without-slack

# Conflicts:
#	app/models/ci/pipeline.rb
#	app/models/commit_status.rb
#	app/services/ci/create_pipeline_service.rb
#	spec/models/ci/pipeline_spec.rb
parents 2c06cf98 ad3e1edc
image: "ruby:2.1" image: "ruby:2.3"
cache: cache:
key: "ruby21" key: "ruby-23"
paths: paths:
- vendor/apt - vendor/apt
- vendor/ruby - vendor/ruby
...@@ -138,57 +138,57 @@ spinach 7 10: *spinach-knapsack ...@@ -138,57 +138,57 @@ spinach 7 10: *spinach-knapsack
spinach 8 10: *spinach-knapsack spinach 8 10: *spinach-knapsack
spinach 9 10: *spinach-knapsack spinach 9 10: *spinach-knapsack
# Execute all testing suites against Ruby 2.3 # Execute all testing suites against Ruby 2.1
.ruby-23: &ruby-23 .ruby-21: &ruby-21
image: "ruby:2.3" image: "ruby:2.1"
<<: *use-db <<: *use-db
only: only:
- master - master
cache: cache:
key: "ruby-23" key: "ruby21"
paths: paths:
- vendor/apt - vendor/apt
- vendor/ruby - vendor/ruby
.rspec-knapsack-ruby23: &rspec-knapsack-ruby23 .rspec-knapsack-ruby21: &rspec-knapsack-ruby21
<<: *rspec-knapsack <<: *rspec-knapsack
<<: *ruby-23 <<: *ruby-21
.spinach-knapsack-ruby23: &spinach-knapsack-ruby23 .spinach-knapsack-ruby21: &spinach-knapsack-ruby21
<<: *spinach-knapsack <<: *spinach-knapsack
<<: *ruby-23 <<: *ruby-21
rspec 0 20 ruby23: *rspec-knapsack-ruby23 rspec 0 20 ruby21: *rspec-knapsack-ruby21
rspec 1 20 ruby23: *rspec-knapsack-ruby23 rspec 1 20 ruby21: *rspec-knapsack-ruby21
rspec 2 20 ruby23: *rspec-knapsack-ruby23 rspec 2 20 ruby21: *rspec-knapsack-ruby21
rspec 3 20 ruby23: *rspec-knapsack-ruby23 rspec 3 20 ruby21: *rspec-knapsack-ruby21
rspec 4 20 ruby23: *rspec-knapsack-ruby23 rspec 4 20 ruby21: *rspec-knapsack-ruby21
rspec 5 20 ruby23: *rspec-knapsack-ruby23 rspec 5 20 ruby21: *rspec-knapsack-ruby21
rspec 6 20 ruby23: *rspec-knapsack-ruby23 rspec 6 20 ruby21: *rspec-knapsack-ruby21
rspec 7 20 ruby23: *rspec-knapsack-ruby23 rspec 7 20 ruby21: *rspec-knapsack-ruby21
rspec 8 20 ruby23: *rspec-knapsack-ruby23 rspec 8 20 ruby21: *rspec-knapsack-ruby21
rspec 9 20 ruby23: *rspec-knapsack-ruby23 rspec 9 20 ruby21: *rspec-knapsack-ruby21
rspec 10 20 ruby23: *rspec-knapsack-ruby23 rspec 10 20 ruby21: *rspec-knapsack-ruby21
rspec 11 20 ruby23: *rspec-knapsack-ruby23 rspec 11 20 ruby21: *rspec-knapsack-ruby21
rspec 12 20 ruby23: *rspec-knapsack-ruby23 rspec 12 20 ruby21: *rspec-knapsack-ruby21
rspec 13 20 ruby23: *rspec-knapsack-ruby23 rspec 13 20 ruby21: *rspec-knapsack-ruby21
rspec 14 20 ruby23: *rspec-knapsack-ruby23 rspec 14 20 ruby21: *rspec-knapsack-ruby21
rspec 15 20 ruby23: *rspec-knapsack-ruby23 rspec 15 20 ruby21: *rspec-knapsack-ruby21
rspec 16 20 ruby23: *rspec-knapsack-ruby23 rspec 16 20 ruby21: *rspec-knapsack-ruby21
rspec 17 20 ruby23: *rspec-knapsack-ruby23 rspec 17 20 ruby21: *rspec-knapsack-ruby21
rspec 18 20 ruby23: *rspec-knapsack-ruby23 rspec 18 20 ruby21: *rspec-knapsack-ruby21
rspec 19 20 ruby23: *rspec-knapsack-ruby23 rspec 19 20 ruby21: *rspec-knapsack-ruby21
spinach 0 10 ruby23: *spinach-knapsack-ruby23 spinach 0 10 ruby21: *spinach-knapsack-ruby21
spinach 1 10 ruby23: *spinach-knapsack-ruby23 spinach 1 10 ruby21: *spinach-knapsack-ruby21
spinach 2 10 ruby23: *spinach-knapsack-ruby23 spinach 2 10 ruby21: *spinach-knapsack-ruby21
spinach 3 10 ruby23: *spinach-knapsack-ruby23 spinach 3 10 ruby21: *spinach-knapsack-ruby21
spinach 4 10 ruby23: *spinach-knapsack-ruby23 spinach 4 10 ruby21: *spinach-knapsack-ruby21
spinach 5 10 ruby23: *spinach-knapsack-ruby23 spinach 5 10 ruby21: *spinach-knapsack-ruby21
spinach 6 10 ruby23: *spinach-knapsack-ruby23 spinach 6 10 ruby21: *spinach-knapsack-ruby21
spinach 7 10 ruby23: *spinach-knapsack-ruby23 spinach 7 10 ruby21: *spinach-knapsack-ruby21
spinach 8 10 ruby23: *spinach-knapsack-ruby23 spinach 8 10 ruby21: *spinach-knapsack-ruby21
spinach 9 10 ruby23: *spinach-knapsack-ruby23 spinach 9 10 ruby21: *spinach-knapsack-ruby21
# Other generic tests # Other generic tests
......
...@@ -5,6 +5,8 @@ v 8.11.0 (unreleased) ...@@ -5,6 +5,8 @@ v 8.11.0 (unreleased)
- Fix don't pass a local variable called `i` to a partial. !20510 (herminiotorres) - Fix don't pass a local variable called `i` to a partial. !20510 (herminiotorres)
- Fix rename `add_users_into_project` and `projects_ids`. !20512 (herminiotorres) - Fix rename `add_users_into_project` and `projects_ids`. !20512 (herminiotorres)
- Fix the title of the toggle dropdown button. !5515 (herminiotorres) - Fix the title of the toggle dropdown button. !5515 (herminiotorres)
- Rename `markdown_preview` routes to `preview_markdown`. (Christopher Bartz)
- Update to Ruby 2.3.1. !4948
- Improve diff performance by eliminating redundant checks for text blobs - Improve diff performance by eliminating redundant checks for text blobs
- Ensure that branch names containing escapable characters (e.g. %20) aren't unescaped indiscriminately. !5770 (ewiltshi) - Ensure that branch names containing escapable characters (e.g. %20) aren't unescaped indiscriminately. !5770 (ewiltshi)
- Convert switch icon into icon font (ClemMakesApps) - Convert switch icon into icon font (ClemMakesApps)
...@@ -22,6 +24,7 @@ v 8.11.0 (unreleased) ...@@ -22,6 +24,7 @@ v 8.11.0 (unreleased)
- Cache highlighted diff lines for merge requests - Cache highlighted diff lines for merge requests
- Pre-create all builds for a Pipeline when the new Pipeline is created !5295 - Pre-create all builds for a Pipeline when the new Pipeline is created !5295
- Fix of 'Commits being passed to custom hooks are already reachable when using the UI' - Fix of 'Commits being passed to custom hooks are already reachable when using the UI'
- Show member roles to all users on members page
- Fix awardable button mutuality loading spinners (ClemMakesApps) - Fix awardable button mutuality loading spinners (ClemMakesApps)
- Add support for using RequestStore within Sidekiq tasks via SIDEKIQ_REQUEST_STORE env variable - Add support for using RequestStore within Sidekiq tasks via SIDEKIQ_REQUEST_STORE env variable
- Optimize maximum user access level lookup in loading of notes - Optimize maximum user access level lookup in loading of notes
...@@ -102,6 +105,7 @@ v 8.11.0 (unreleased) ...@@ -102,6 +105,7 @@ v 8.11.0 (unreleased)
- Fix importing GitLab projects with an invalid MR source project - Fix importing GitLab projects with an invalid MR source project
- Sort folders with submodules in Files view !5521 - Sort folders with submodules in Files view !5521
- Each `File::exists?` replaced to `File::exist?` because of deprecate since ruby version 2.2.0 - Each `File::exists?` replaced to `File::exist?` because of deprecate since ruby version 2.2.0
- Add auto-completition in pipeline (Katarzyna Kobierska Ula Budziszewska)
v 8.10.5 v 8.10.5
- Add a data migration to fix some missing timestamps in the members table. !5670 - Add a data migration to fix some missing timestamps in the members table. !5670
......
source 'https://rubygems.org' source 'https://rubygems.org'
gem 'rails', '4.2.7' gem 'rails', '4.2.7.1'
gem 'rails-deprecated_sanitizer', '~> 1.0.3' gem 'rails-deprecated_sanitizer', '~> 1.0.3'
# Responders respond_to and respond_with # Responders respond_to and respond_with
......
...@@ -3,34 +3,34 @@ GEM ...@@ -3,34 +3,34 @@ GEM
specs: specs:
RedCloth (4.3.2) RedCloth (4.3.2)
ace-rails-ap (4.0.2) ace-rails-ap (4.0.2)
actionmailer (4.2.7) actionmailer (4.2.7.1)
actionpack (= 4.2.7) actionpack (= 4.2.7.1)
actionview (= 4.2.7) actionview (= 4.2.7.1)
activejob (= 4.2.7) activejob (= 4.2.7.1)
mail (~> 2.5, >= 2.5.4) mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 1.0, >= 1.0.5) rails-dom-testing (~> 1.0, >= 1.0.5)
actionpack (4.2.7) actionpack (4.2.7.1)
actionview (= 4.2.7) actionview (= 4.2.7.1)
activesupport (= 4.2.7) activesupport (= 4.2.7.1)
rack (~> 1.6) rack (~> 1.6)
rack-test (~> 0.6.2) rack-test (~> 0.6.2)
rails-dom-testing (~> 1.0, >= 1.0.5) rails-dom-testing (~> 1.0, >= 1.0.5)
rails-html-sanitizer (~> 1.0, >= 1.0.2) rails-html-sanitizer (~> 1.0, >= 1.0.2)
actionview (4.2.7) actionview (4.2.7.1)
activesupport (= 4.2.7) activesupport (= 4.2.7.1)
builder (~> 3.1) builder (~> 3.1)
erubis (~> 2.7.0) erubis (~> 2.7.0)
rails-dom-testing (~> 1.0, >= 1.0.5) rails-dom-testing (~> 1.0, >= 1.0.5)
rails-html-sanitizer (~> 1.0, >= 1.0.2) rails-html-sanitizer (~> 1.0, >= 1.0.2)
activejob (4.2.7) activejob (4.2.7.1)
activesupport (= 4.2.7) activesupport (= 4.2.7.1)
globalid (>= 0.3.0) globalid (>= 0.3.0)
activemodel (4.2.7) activemodel (4.2.7.1)
activesupport (= 4.2.7) activesupport (= 4.2.7.1)
builder (~> 3.1) builder (~> 3.1)
activerecord (4.2.7) activerecord (4.2.7.1)
activemodel (= 4.2.7) activemodel (= 4.2.7.1)
activesupport (= 4.2.7) activesupport (= 4.2.7.1)
arel (~> 6.0) arel (~> 6.0)
activerecord-session_store (1.0.0) activerecord-session_store (1.0.0)
actionpack (>= 4.0, < 5.1) actionpack (>= 4.0, < 5.1)
...@@ -38,7 +38,7 @@ GEM ...@@ -38,7 +38,7 @@ GEM
multi_json (~> 1.11, >= 1.11.2) multi_json (~> 1.11, >= 1.11.2)
rack (>= 1.5.2, < 3) rack (>= 1.5.2, < 3)
railties (>= 4.0, < 5.1) railties (>= 4.0, < 5.1)
activesupport (4.2.7) activesupport (4.2.7.1)
i18n (~> 0.7) i18n (~> 0.7)
json (~> 1.7, >= 1.7.7) json (~> 1.7, >= 1.7.7)
minitest (~> 5.1) minitest (~> 5.1)
...@@ -289,7 +289,7 @@ GEM ...@@ -289,7 +289,7 @@ GEM
omniauth (~> 1.0) omniauth (~> 1.0)
pyu-ruby-sasl (~> 0.0.3.1) pyu-ruby-sasl (~> 0.0.3.1)
rubyntlm (~> 0.3) rubyntlm (~> 0.3)
globalid (0.3.6) globalid (0.3.7)
activesupport (>= 4.1.0) activesupport (>= 4.1.0)
gollum-grit_adapter (1.0.1) gollum-grit_adapter (1.0.1)
gitlab-grit (~> 2.7, >= 2.7.1) gitlab-grit (~> 2.7, >= 2.7.1)
...@@ -518,16 +518,16 @@ GEM ...@@ -518,16 +518,16 @@ GEM
rack rack
rack-test (0.6.3) rack-test (0.6.3)
rack (>= 1.0) rack (>= 1.0)
rails (4.2.7) rails (4.2.7.1)
actionmailer (= 4.2.7) actionmailer (= 4.2.7.1)
actionpack (= 4.2.7) actionpack (= 4.2.7.1)
actionview (= 4.2.7) actionview (= 4.2.7.1)
activejob (= 4.2.7) activejob (= 4.2.7.1)
activemodel (= 4.2.7) activemodel (= 4.2.7.1)
activerecord (= 4.2.7) activerecord (= 4.2.7.1)
activesupport (= 4.2.7) activesupport (= 4.2.7.1)
bundler (>= 1.3.0, < 2.0) bundler (>= 1.3.0, < 2.0)
railties (= 4.2.7) railties (= 4.2.7.1)
sprockets-rails sprockets-rails
rails-deprecated_sanitizer (1.0.3) rails-deprecated_sanitizer (1.0.3)
activesupport (>= 4.2.0.alpha) activesupport (>= 4.2.0.alpha)
...@@ -537,9 +537,9 @@ GEM ...@@ -537,9 +537,9 @@ GEM
rails-deprecated_sanitizer (>= 1.0.1) rails-deprecated_sanitizer (>= 1.0.1)
rails-html-sanitizer (1.0.3) rails-html-sanitizer (1.0.3)
loofah (~> 2.0) loofah (~> 2.0)
railties (4.2.7) railties (4.2.7.1)
actionpack (= 4.2.7) actionpack (= 4.2.7.1)
activesupport (= 4.2.7) activesupport (= 4.2.7.1)
rake (>= 0.8.7) rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0) thor (>= 0.18.1, < 2.0)
rainbow (2.1.0) rainbow (2.1.0)
...@@ -914,7 +914,7 @@ DEPENDENCIES ...@@ -914,7 +914,7 @@ DEPENDENCIES
rack-attack (~> 4.3.1) rack-attack (~> 4.3.1)
rack-cors (~> 0.4.0) rack-cors (~> 0.4.0)
rack-oauth2 (~> 1.2.1) rack-oauth2 (~> 1.2.1)
rails (= 4.2.7) rails (= 4.2.7.1)
rails-deprecated_sanitizer (~> 1.0.3) rails-deprecated_sanitizer (~> 1.0.3)
rainbow (~> 2.1.0) rainbow (~> 2.1.0)
rblineprof (~> 0.3.6) rblineprof (~> 0.3.6)
......
/*= require markdown_preview */ /*= require preview_markdown */
(function() { (function() {
this.DropzoneInput = (function() { this.DropzoneInput = (function() {
......
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
}; };
MarkdownPreview.prototype.renderMarkdown = function(text, success) { MarkdownPreview.prototype.renderMarkdown = function(text, success) {
if (!window.markdown_preview_path) { if (!window.preview_markdown_path) {
return; return;
} }
if (text === this.ajaxCache.text) { if (text === this.ajaxCache.text) {
...@@ -36,7 +36,7 @@ ...@@ -36,7 +36,7 @@
} }
return $.ajax({ return $.ajax({
type: 'POST', type: 'POST',
url: window.markdown_preview_path, url: window.preview_markdown_path,
data: { data: {
text: text text: text
}, },
......
...@@ -91,7 +91,7 @@ class Projects::WikisController < Projects::ApplicationController ...@@ -91,7 +91,7 @@ class Projects::WikisController < Projects::ApplicationController
) )
end end
def markdown_preview def preview_markdown
text = params[:text] text = params[:text]
ext = Gitlab::ReferenceExtractor.new(@project, current_user) ext = Gitlab::ReferenceExtractor.new(@project, current_user)
......
...@@ -238,7 +238,7 @@ class ProjectsController < Projects::ApplicationController ...@@ -238,7 +238,7 @@ class ProjectsController < Projects::ApplicationController
} }
end end
def markdown_preview def preview_markdown
text = params[:text] text = params[:text]
ext = Gitlab::ReferenceExtractor.new(@project, current_user) ext = Gitlab::ReferenceExtractor.new(@project, current_user)
......
...@@ -6,12 +6,6 @@ module MembersHelper ...@@ -6,12 +6,6 @@ module MembersHelper
"#{action}_#{member.type.underscore}".to_sym "#{action}_#{member.type.underscore}".to_sym
end end
def default_show_roles(member)
can?(current_user, action_member_permission(:update, member), member) ||
can?(current_user, action_member_permission(:destroy, member), member) ||
can?(current_user, action_member_permission(:admin, member), member.source)
end
def remove_member_message(member, user: nil) def remove_member_message(member, user: nil)
user = current_user if defined?(current_user) user = current_user if defined?(current_user)
......
...@@ -42,24 +42,25 @@ module Ci ...@@ -42,24 +42,25 @@ module Ci
end end
def retry(build, user = nil) def retry(build, user = nil)
new_build = Ci::Build.new(status: 'pending') new_build = Ci::Build.create(
new_build.ref = build.ref ref: build.ref,
new_build.tag = build.tag tag: build.tag,
new_build.options = build.options options: build.options,
new_build.commands = build.commands commands: build.commands,
new_build.tag_list = build.tag_list tag_list: build.tag_list,
new_build.project = build.project project: build.project,
new_build.pipeline = build.pipeline pipeline: build.pipeline,
new_build.name = build.name name: build.name,
new_build.allow_failure = build.allow_failure allow_failure: build.allow_failure,
new_build.stage = build.stage stage: build.stage,
new_build.stage_idx = build.stage_idx stage_idx: build.stage_idx,
new_build.trigger_request = build.trigger_request trigger_request: build.trigger_request,
new_build.yaml_variables = build.yaml_variables yaml_variables: build.yaml_variables,
new_build.when = build.when when: build.when,
new_build.user = user user: user,
new_build.environment = build.environment environment: build.environment,
new_build.save status_event: 'queue'
)
MergeRequests::AddTodoWhenBuildFailsService.new(build.project, nil).close(new_build) MergeRequests::AddTodoWhenBuildFailsService.new(build.project, nil).close(new_build)
new_build new_build
end end
......
...@@ -21,6 +21,49 @@ module Ci ...@@ -21,6 +21,49 @@ module Ci
delegate :stages, to: :statuses delegate :stages, to: :statuses
state_machine :status, initial: :created do
event :queue do
transition :created => :pending
transition any - [:created, :pending] => :running
end
event :run do
transition any => :running
end
event :skip do
transition any => :skipped
end
event :drop do
transition any => :failed
end
event :succeed do
transition any => :success
end
event :cancel do
transition any => :canceled
end
before_transition [:created, :pending] => :running do |pipeline|
pipeline.started_at = Time.now
end
before_transition any => [:success, :failed, :canceled] do |pipeline|
pipeline.finished_at = Time.now
end
before_transition do |pipeline|
pipeline.update_duration
end
after_transition do |pipeline, transition|
pipeline.execute_hooks unless transition.loopback?
end
end
# ref can't be HEAD or SHA, can only be branch/tag name # ref can't be HEAD or SHA, can only be branch/tag name
scope :latest_successful_for, ->(ref = default_branch) do scope :latest_successful_for, ->(ref = default_branch) do
where(ref: ref).success.order(id: :desc).limit(1) where(ref: ref).success.order(id: :desc).limit(1)
...@@ -91,16 +134,12 @@ module Ci ...@@ -91,16 +134,12 @@ module Ci
def cancel_running def cancel_running
builds.running_or_pending.each(&:cancel) builds.running_or_pending.each(&:cancel)
reload_status!
end end
def retry_failed(user) def retry_failed(user)
builds.latest.failed.select(&:retryable?).each do |build| builds.latest.failed.select(&:retryable?).each do |build|
Ci::Build.retry(build, user) Ci::Build.retry(build, user)
end end
reload_status!
end end
def latest? def latest?
...@@ -187,8 +226,23 @@ module Ci ...@@ -187,8 +226,23 @@ module Ci
def process! def process!
Ci::ProcessPipelineService.new(project, user).execute(self) Ci::ProcessPipelineService.new(project, user).execute(self)
end
reload_status! def build_updated
case latest_builds_status
when 'pending'
queue
when 'running'
run
when 'success'
succeed
when 'failed'
drop
when 'canceled'
cancel
when 'skipped'
skip
end
end end
def predefined_variables def predefined_variables
...@@ -197,21 +251,8 @@ module Ci ...@@ -197,21 +251,8 @@ module Ci
] ]
end end
def reload_status! def update_duration
reload
self.status =
if yaml_errors.blank?
statuses.latest.status || 'skipped'
else
'failed'
end
self.started_at = statuses.started_at
self.finished_at = statuses.finished_at
self.duration = statuses.latest.duration self.duration = statuses.latest.duration
should_execute_hooks = status_changed?
save
execute_hooks if should_execute_hooks
end end
private private
...@@ -225,6 +266,12 @@ module Ci ...@@ -225,6 +266,12 @@ module Ci
Gitlab::DataBuilder::Pipeline.build(self) Gitlab::DataBuilder::Pipeline.build(self)
end end
def latest_builds_status
return 'failed' unless yaml_errors.blank?
statuses.latest.status || 'skipped'
end
def keep_around_commits def keep_around_commits
return unless project return unless project
......
...@@ -62,25 +62,23 @@ class CommitStatus < ActiveRecord::Base ...@@ -62,25 +62,23 @@ class CommitStatus < ActiveRecord::Base
commit_status.update_attributes finished_at: Time.now commit_status.update_attributes finished_at: Time.now
end end
after_transition [:created, :pending, :running] => :success do |commit_status|
MergeRequests::MergeWhenBuildSucceedsService.new(commit_status.pipeline.project, nil).trigger(commit_status)
end
after_transition any => :failed do |commit_status|
MergeRequests::AddTodoWhenBuildFailsService.new(commit_status.pipeline.project, nil).execute(commit_status)
end
# We use around_transition to process pipeline on next stages as soon as possible, before the `after_*` is executed # We use around_transition to process pipeline on next stages as soon as possible, before the `after_*` is executed
around_transition any => [:success, :failed, :canceled] do |commit_status, block| around_transition any => [:success, :failed, :canceled] do |commit_status, block|
block.call block.call
commit_status.pipeline.process! if commit_status.pipeline commit_status.pipeline.try(:process!)
end end
around_transition any => [:pending, :running] do |commit_status, block| after_transition do |commit_status, transition|
block.call commit_status.pipeline.try(:build_updated) unless transition.loopback?
end
after_transition [:created, :pending, :running] => :success do |commit_status|
MergeRequests::MergeWhenBuildSucceedsService.new(commit_status.pipeline.project, nil).trigger(commit_status)
end
commit_status.pipeline.reload_status! if commit_status.pipeline after_transition any => :failed do |commit_status|
MergeRequests::AddTodoWhenBuildFailsService.new(commit_status.pipeline.project, nil).execute(commit_status)
end end
end end
......
...@@ -104,6 +104,7 @@ class MergeRequest < ActiveRecord::Base ...@@ -104,6 +104,7 @@ class MergeRequest < ActiveRecord::Base
scope :from_project, ->(project) { where(source_project_id: project.id) } scope :from_project, ->(project) { where(source_project_id: project.id) }
scope :merged, -> { with_state(:merged) } scope :merged, -> { with_state(:merged) }
scope :closed_and_merged, -> { with_states(:closed, :merged) } scope :closed_and_merged, -> { with_states(:closed, :merged) }
scope :from_source_branches, ->(branches) { where(source_branch: branches) }
scope :join_project, -> { joins(:target_project) } scope :join_project, -> { joins(:target_project) }
scope :references_project, -> { references(:target_project) } scope :references_project, -> { references(:target_project) }
......
...@@ -37,7 +37,8 @@ module Ci ...@@ -37,7 +37,8 @@ module Ci
end end
if !ignore_skip_ci && skip_ci? if !ignore_skip_ci && skip_ci?
return error('Creation of pipeline is skipped', save: save_on_errors) pipeline.skip if save_on_errors
return pipeline
end end
unless pipeline.config_builds_attributes.present? unless pipeline.config_builds_attributes.present?
...@@ -93,10 +94,7 @@ module Ci ...@@ -93,10 +94,7 @@ module Ci
def error(message, save: false) def error(message, save: false)
pipeline.errors.add(:base, message) pipeline.errors.add(:base, message)
if save pipeline.drop if save
pipeline.save
pipeline.reload_status!
end
pipeline pipeline
end end
end end
......
module MergeRequests
class GetUrlsService < BaseService
attr_reader :project
def initialize(project)
@project = project
end
def execute(changes)
branches = get_branches(changes)
merge_requests_map = opened_merge_requests_from_source_branches(branches)
branches.map do |branch|
existing_merge_request = merge_requests_map[branch]
if existing_merge_request
url_for_existing_merge_request(existing_merge_request)
else
url_for_new_merge_request(branch)
end
end
end
private
def opened_merge_requests_from_source_branches(branches)
merge_requests = MergeRequest.from_project(project).opened.from_source_branches(branches)
merge_requests.inject({}) do |hash, mr|
hash[mr.source_branch] = mr
hash
end
end
def get_branches(changes)
changes_list = Gitlab::ChangesList.new(changes)
changes_list.map do |change|
next unless Gitlab::Git.branch_ref?(change[:ref])
Gitlab::Git.branch_name(change[:ref])
end.compact
end
def url_for_new_merge_request(branch_name)
merge_request_params = { source_branch: branch_name }
url = Gitlab::Routing.url_helpers.new_namespace_project_merge_request_url(project.namespace, project, merge_request: merge_request_params)
{ branch_name: branch_name, url: url, new_merge_request: true }
end
def url_for_existing_merge_request(merge_request)
target_project = merge_request.target_project
url = Gitlab::Routing.url_helpers.namespace_project_merge_request_url(target_project.namespace, target_project, merge_request)
{ branch_name: merge_request.source_branch, url: url, new_merge_request: false }
end
end
end
...@@ -6,13 +6,13 @@ ...@@ -6,13 +6,13 @@
- content_for :scripts_body_top do - content_for :scripts_body_top do
- project = @target_project || @project - project = @target_project || @project
- if @project_wiki && @page - if @project_wiki && @page
- markdown_preview_path = namespace_project_wiki_markdown_preview_path(project.namespace, project, @page.slug) - preview_markdown_path = namespace_project_wiki_preview_markdown_path(project.namespace, project, @page.slug)
- else - else
- markdown_preview_path = markdown_preview_namespace_project_path(project.namespace, project) - preview_markdown_path = preview_markdown_namespace_project_path(project.namespace, project)
- if current_user - if current_user
:javascript :javascript
window.project_uploads_path = "#{namespace_project_uploads_path project.namespace,project}"; window.project_uploads_path = "#{namespace_project_uploads_path project.namespace,project}";
window.markdown_preview_path = "#{markdown_preview_path}"; window.preview_markdown_path = "#{preview_markdown_path}";
- content_for :scripts_body do - content_for :scripts_body do
= render "layouts/init_auto_complete" if current_user = render "layouts/init_auto_complete" if current_user
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
.form-group .form-group
= f.label :ref, 'Create for', class: 'control-label' = f.label :ref, 'Create for', class: 'control-label'
.col-sm-10 .col-sm-10
= f.text_field :ref, required: true, tabindex: 2, class: 'form-control' = f.text_field :ref, required: true, tabindex: 2, class: 'form-control js-branch-name ui-autocomplete-input', autocomplete: :false, id: :ref
.help-block Existing branch name, tag .help-block Existing branch name, tag
.form-actions .form-actions
= f.submit 'Create pipeline', class: 'btn btn-create', tabindex: 3 = f.submit 'Create pipeline', class: 'btn btn-create', tabindex: 3
......
- show_roles = local_assigns.fetch(:show_roles, default_show_roles(member)) - show_roles = local_assigns.fetch(:show_roles, true)
- show_controls = local_assigns.fetch(:show_controls, true) - show_controls = local_assigns.fetch(:show_controls, true)
- user = member.user - user = member.user
......
...@@ -468,7 +468,7 @@ Rails.application.routes.draw do ...@@ -468,7 +468,7 @@ Rails.application.routes.draw do
post :unarchive post :unarchive
post :housekeeping post :housekeeping
post :toggle_star post :toggle_star
post :markdown_preview post :preview_markdown
post :export post :export
post :remove_export post :remove_export
post :generate_new_export post :generate_new_export
...@@ -672,7 +672,7 @@ Rails.application.routes.draw do ...@@ -672,7 +672,7 @@ Rails.application.routes.draw do
get '/wikis/*id', to: 'wikis#show', as: 'wiki', constraints: WIKI_SLUG_ID get '/wikis/*id', to: 'wikis#show', as: 'wiki', constraints: WIKI_SLUG_ID
delete '/wikis/*id', to: 'wikis#destroy', constraints: WIKI_SLUG_ID delete '/wikis/*id', to: 'wikis#destroy', constraints: WIKI_SLUG_ID
put '/wikis/*id', to: 'wikis#update', constraints: WIKI_SLUG_ID put '/wikis/*id', to: 'wikis#update', constraints: WIKI_SLUG_ID
post '/wikis/*id/markdown_preview', to: 'wikis#markdown_preview', constraints: WIKI_SLUG_ID, as: 'wiki_markdown_preview' post '/wikis/*id/preview_markdown', to: 'wikis#preview_markdown', constraints: WIKI_SLUG_ID, as: 'wiki_preview_markdown'
end end
resource :repository, only: [:create] do resource :repository, only: [:create] do
......
...@@ -108,8 +108,7 @@ Then select 'Internet Site' and press enter to confirm the hostname. ...@@ -108,8 +108,7 @@ Then select 'Internet Site' and press enter to confirm the hostname.
## 2. Ruby ## 2. Ruby
_**Note:** The current supported Ruby version is 2.1.x. Ruby 2.2 and 2.3 are _**Note:** The current supported Ruby versions are 2.1.x and 2.3.x. 2.3.x is preferred, and support for 2.1.x will be dropped in the future.
currently not supported._
The use of Ruby version managers such as [RVM], [rbenv] or [chruby] with GitLab The use of Ruby version managers such as [RVM], [rbenv] or [chruby] with GitLab
in production, frequently leads to hard to diagnose problems. For example, in production, frequently leads to hard to diagnose problems. For example,
...@@ -124,9 +123,9 @@ Remove the old Ruby 1.8 if present: ...@@ -124,9 +123,9 @@ Remove the old Ruby 1.8 if present:
Download Ruby and compile it: Download Ruby and compile it:
mkdir /tmp/ruby && cd /tmp/ruby mkdir /tmp/ruby && cd /tmp/ruby
curl --remote-name --progress https://cache.ruby-lang.org/pub/ruby/2.1/ruby-2.1.8.tar.gz curl --remote-name --progress https://cache.ruby-lang.org/pub/ruby/2.3/ruby-2.3.1.tar.gz
echo 'c7e50159357afd87b13dc5eaf4ac486a70011149 ruby-2.1.8.tar.gz' | shasum -c - && tar xzf ruby-2.1.8.tar.gz echo 'c39b4001f7acb4e334cb60a0f4df72d434bef711 ruby-2.3.1.tar.gz' | shasum -c - && tar xzf ruby-2.3.1.tar.gz
cd ruby-2.1.8 cd ruby-2.3.1
./configure --disable-install-rdoc ./configure --disable-install-rdoc
make make
sudo make install sudo make install
...@@ -591,13 +590,13 @@ for the changes to take effect. ...@@ -591,13 +590,13 @@ for the changes to take effect.
If you'd like to connect to a Redis server on a non-standard port or on a different host, you can configure its connection string via the `config/resque.yml` file. If you'd like to connect to a Redis server on a non-standard port or on a different host, you can configure its connection string via the `config/resque.yml` file.
# example # example
production: production:
url: redis://redis.example.tld:6379 url: redis://redis.example.tld:6379
If you want to connect the Redis server via socket, then use the "unix:" URL scheme and the path to the Redis socket file in the `config/resque.yml` file. If you want to connect the Redis server via socket, then use the "unix:" URL scheme and the path to the Redis socket file in the `config/resque.yml` file.
# example # example
production: production:
url: unix:/path/to/redis/socket url: unix:/path/to/redis/socket
### Custom SSH Connection ### Custom SSH Connection
......
...@@ -6,13 +6,17 @@ You accept and agree to the following terms and conditions for Your present and ...@@ -6,13 +6,17 @@ You accept and agree to the following terms and conditions for Your present and
"You" (or "Your") shall mean the copyright owner or legal entity authorized by the copyright owner that is making this Agreement with GitLab B.V.. For legal entities, the entity making a Contribution and all other entities that control, are controlled by, or are under common control with that entity are considered to be a single Contributor. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean the copyright owner or legal entity authorized by the copyright owner that is making this Agreement with GitLab B.V.. For legal entities, the entity making a Contribution and all other entities that control, are controlled by, or are under common control with that entity are considered to be a single Contributor. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
"Contribution" shall mean the code, documentation or other original works of authorship expressly identified in Schedule B, as well as any original work of authorship, including any modifications or additions to an existing work, that is intentionally submitted by You to GitLab B.V. for inclusion in, or documentation of, any of the products owned or managed by GitLab B.V. (the "Work"). For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to GitLab B.V. or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, GitLab B.V. for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution." "Contribution" shall mean the code, documentation or other original works of authorship, including any modifications or additions to an existing work, that is submitted by You to GitLab B.V. for inclusion in, or documentation of, any of the products owned or managed by GitLab B.V. (the "Work"). For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to GitLab B.V. or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, GitLab B.V. for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution."
2. Grant of Copyright License. Subject to the terms and conditions of this Agreement, You hereby grant to GitLab B.V. and to recipients of software distributed by GitLab B.V. a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Your Contributions and such derivative works. 2. Grant of Copyright License.
3. Grant of Patent License. Subject to the terms and conditions of this Agreement, You hereby grant to GitLab B.V. and to recipients of software distributed by GitLab B.V. a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by You that are necessarily infringed by Your Contribution(s) alone or by combination of Your Contribution(s) with the Work to which such Contribution(s) was submitted. If any entity institutes patent litigation against You or any other entity (including a cross-claim or counterclaim in a lawsuit) alleging that your Contribution, or the Work to which you have contributed, constitutes direct or contributory patent infringement, then any patent licenses granted to that entity under this Agreement for that Contribution or Work shall terminate as of the date such litigation is filed. Subject to the terms and conditions of this Agreement, You hereby grant to GitLab B.V. and to recipients of software distributed by GitLab B.V. a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Your Contributions and such derivative works.
4. You represent that You are legally entitled to grant the above license. You represent further that each employee of the Corporation designated on Schedule A below (or in a subsequent written modification to that Schedule) is authorized to submit Contributions on behalf of the Corporation. 3. Grant of Patent License.
Subject to the terms and conditions of this Agreement, You hereby grant to GitLab B.V. and to recipients of software distributed by GitLab B.V. a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by You that are necessarily infringed by Your Contribution(s) alone or by combination of Your Contribution(s) with the Work to which such Contribution(s) was submitted. If any entity institutes patent litigation against You or any other entity (including a cross-claim or counterclaim in a lawsuit) alleging that your Contribution, or the Work to which you have contributed, constitutes direct or contributory patent infringement, then any patent licenses granted to that entity under this Agreement for that Contribution or Work shall terminate as of the date such litigation is filed.
4. You represent that You are legally entitled to grant the above license. You represent further that each employee of the Corporation is authorized to submit Contributions on behalf of the Corporation, but excluding employees that are designated in writing by You as "Not authorized to submit Contributions on behalf of [name of corporation here]."
5. You represent that each of Your Contributions is Your original creation (see section 7 for submissions on behalf of others). 5. You represent that each of Your Contributions is Your original creation (see section 7 for submissions on behalf of others).
...@@ -20,6 +24,6 @@ You accept and agree to the following terms and conditions for Your present and ...@@ -20,6 +24,6 @@ You accept and agree to the following terms and conditions for Your present and
7. Should You wish to submit work that is not Your original creation, You may submit it to GitLab B.V. separately from any Contribution, identifying the complete details of its source and of any license or other restriction (including, but not limited to, related patents, trademarks, and license agreements) of which you are personally aware, and conspicuously marking the work as "Submitted on behalf of a third-party: [named here]". 7. Should You wish to submit work that is not Your original creation, You may submit it to GitLab B.V. separately from any Contribution, identifying the complete details of its source and of any license or other restriction (including, but not limited to, related patents, trademarks, and license agreements) of which you are personally aware, and conspicuously marking the work as "Submitted on behalf of a third-party: [named here]".
8. It is your responsibility to notify GitLab B.V. when any change is required to the list of designated employees authorized to submit Contributions on behalf of the Corporation, or to the Corporation's Point of Contact with GitLab B.V.. 8. It is your responsibility to notify GitLab B.V. when any change is required to the designation of employees not authorized to submit Contributions on behalf of the Corporation, or to the Corporation's Point of Contact with GitLab B.V..
This text is licensed under the [Creative Commons Attribution 3.0 License](https://creativecommons.org/licenses/by/3.0/) and the original source is the Google Open Source Programs Office. This text is licensed under the [Creative Commons Attribution 3.0 License](https://creativecommons.org/licenses/by/3.0/) and the original source is the Google Open Source Programs Office.
...@@ -46,7 +46,7 @@ sudo -u git -H git checkout 8-11-stable-ee ...@@ -46,7 +46,7 @@ sudo -u git -H git checkout 8-11-stable-ee
```bash ```bash
cd /home/git/gitlab-shell cd /home/git/gitlab-shell
sudo -u git -H git fetch --all --tags sudo -u git -H git fetch --all --tags
sudo -u git -H git checkout v3.2.1 sudo -u git -H git checkout v3.3.3
``` ```
### 5. Update gitlab-workhorse ### 5. Update gitlab-workhorse
......
...@@ -24,14 +24,6 @@ Feature: Explore Groups ...@@ -24,14 +24,6 @@ Feature: Explore Groups
Then I should see project "Internal" items Then I should see project "Internal" items
And I should not see project "Enterprise" items And I should not see project "Enterprise" items
Scenario: I should see group's members as user
Given group "TestGroup" has internal project "Internal"
And "John Doe" is owner of group "TestGroup"
When I sign in as a user
And I visit group "TestGroup" members page
Then I should see group member "John Doe"
And I should not see member roles
Scenario: I should see group with private, internal and public projects as visitor Scenario: I should see group with private, internal and public projects as visitor
Given group "TestGroup" has internal project "Internal" Given group "TestGroup" has internal project "Internal"
Given group "TestGroup" has public project "Community" Given group "TestGroup" has public project "Community"
...@@ -56,14 +48,6 @@ Feature: Explore Groups ...@@ -56,14 +48,6 @@ Feature: Explore Groups
And I should not see project "Internal" items And I should not see project "Internal" items
And I should not see project "Enterprise" items And I should not see project "Enterprise" items
Scenario: I should see group's members as visitor
Given group "TestGroup" has internal project "Internal"
Given group "TestGroup" has public project "Community"
And "John Doe" is owner of group "TestGroup"
When I visit group "TestGroup" members page
Then I should see group member "John Doe"
And I should not see member roles
Scenario: I should see group with private, internal and public projects as user Scenario: I should see group with private, internal and public projects as user
Given group "TestGroup" has internal project "Internal" Given group "TestGroup" has internal project "Internal"
Given group "TestGroup" has public project "Community" Given group "TestGroup" has public project "Community"
...@@ -91,15 +75,6 @@ Feature: Explore Groups ...@@ -91,15 +75,6 @@ Feature: Explore Groups
And I should see project "Internal" items And I should see project "Internal" items
And I should not see project "Enterprise" items And I should not see project "Enterprise" items
Scenario: I should see group's members as user
Given group "TestGroup" has internal project "Internal"
Given group "TestGroup" has public project "Community"
And "John Doe" is owner of group "TestGroup"
When I sign in as a user
And I visit group "TestGroup" members page
Then I should see group member "John Doe"
And I should not see member roles
Scenario: I should see group with public project in public groups area Scenario: I should see group with public project in public groups area
Given group "TestGroup" has public project "Community" Given group "TestGroup" has public project "Community"
When I visit the public groups area When I visit the public groups area
......
...@@ -62,10 +62,6 @@ class Spinach::Features::ExploreGroups < Spinach::FeatureSteps ...@@ -62,10 +62,6 @@ class Spinach::Features::ExploreGroups < Spinach::FeatureSteps
expect(page).to have_content "John Doe" expect(page).to have_content "John Doe"
end end
step 'I should not see member roles' do
expect(body).not_to match(%r{owner|developer|reporter|guest}i)
end
protected protected
def group_has_project(groupname, projectname, visibility_level) def group_has_project(groupname, projectname, visibility_level)
......
...@@ -12,7 +12,6 @@ module SharedBuilds ...@@ -12,7 +12,6 @@ module SharedBuilds
step 'project has a recent build' do step 'project has a recent build' do
@pipeline = create(:ci_empty_pipeline, project: @project, sha: @project.commit.sha, ref: 'master') @pipeline = create(:ci_empty_pipeline, project: @project, sha: @project.commit.sha, ref: 'master')
@build = create(:ci_build_with_coverage, pipeline: @pipeline) @build = create(:ci_build_with_coverage, pipeline: @pipeline)
@pipeline.reload_status!
end end
step 'recent build is successful' do step 'recent build is successful' do
...@@ -24,8 +23,7 @@ module SharedBuilds ...@@ -24,8 +23,7 @@ module SharedBuilds
end end
step 'project has another build that is running' do step 'project has another build that is running' do
create(:ci_build, pipeline: @pipeline, name: 'second build', status: 'running') create(:ci_build, pipeline: @pipeline, name: 'second build', status_event: 'run')
@pipeline.reload_status!
end end
step 'I visit recent build details page' do step 'I visit recent build details page' do
......
...@@ -74,6 +74,10 @@ module API ...@@ -74,6 +74,10 @@ module API
response response
end end
get "/merge_request_urls" do
::MergeRequests::GetUrlsService.new(project).execute(params[:changes])
end
# #
# Discover user by ssh key # Discover user by ssh key
# #
......
module Gitlab
class ChangesList
include Enumerable
attr_reader :raw_changes
def initialize(changes)
@raw_changes = changes.kind_of?(String) ? changes.lines : changes
end
def each(&block)
changes.each(&block)
end
def changes
@changes ||= begin
@raw_changes.map do |change|
next if change.blank?
oldrev, newrev, ref = change.strip.split(' ')
{ oldrev: oldrev, newrev: newrev, ref: ref }
end.compact
end
end
end
end
...@@ -4,8 +4,8 @@ module Gitlab ...@@ -4,8 +4,8 @@ module Gitlab
attr_reader :user_access, :project attr_reader :user_access, :project
def initialize(change, user_access:, project:) def initialize(change, user_access:, project:)
@oldrev, @newrev, @ref = change.split(' ') @oldrev, @newrev, @ref = change.values_at(:oldrev, :newrev, :ref)
@branch_name = branch_name(@ref) @branch_name = Gitlab::Git.branch_name(@ref)
@user_access = user_access @user_access = user_access
@project = project @project = project
end end
...@@ -47,7 +47,7 @@ module Gitlab ...@@ -47,7 +47,7 @@ module Gitlab
end end
def tag_checks def tag_checks
tag_ref = tag_name(@ref) tag_ref = Gitlab::Git.tag_name(@ref)
if tag_ref && protected_tag?(tag_ref) && user_access.cannot_do_action?(:admin_project) if tag_ref && protected_tag?(tag_ref) && user_access.cannot_do_action?(:admin_project)
"You are not allowed to change existing tags on this project." "You are not allowed to change existing tags on this project."
...@@ -73,24 +73,6 @@ module Gitlab ...@@ -73,24 +73,6 @@ module Gitlab
def matching_merge_request? def matching_merge_request?
Checks::MatchingMergeRequest.new(@newrev, @branch_name, @project).match? Checks::MatchingMergeRequest.new(@newrev, @branch_name, @project).match?
end end
def branch_name(ref)
ref = @ref.to_s
if Gitlab::Git.branch_ref?(ref)
Gitlab::Git.ref_name(ref)
else
nil
end
end
def tag_name(ref)
ref = @ref.to_s
if Gitlab::Git.tag_ref?(ref)
Gitlab::Git.ref_name(ref)
else
nil
end
end
end end
end end
end end
...@@ -9,6 +9,24 @@ module Gitlab ...@@ -9,6 +9,24 @@ module Gitlab
ref.gsub(/\Arefs\/(tags|heads)\//, '') ref.gsub(/\Arefs\/(tags|heads)\//, '')
end end
def branch_name(ref)
ref = ref.to_s
if self.branch_ref?(ref)
self.ref_name(ref)
else
nil
end
end
def tag_name(ref)
ref = ref.to_s
if self.tag_ref?(ref)
self.ref_name(ref)
else
nil
end
end
def tag_ref?(ref) def tag_ref?(ref)
ref.start_with?(TAG_REF_PREFIX) ref.start_with?(TAG_REF_PREFIX)
end end
......
...@@ -76,10 +76,10 @@ module Gitlab ...@@ -76,10 +76,10 @@ module Gitlab
return build_status_object(false, "A repository for this project does not exist yet.") return build_status_object(false, "A repository for this project does not exist yet.")
end end
changes = changes.lines if changes.kind_of?(String) changes_list = Gitlab::ChangesList.new(changes)
# Iterate over all changes to find if user allowed all of them to be applied # Iterate over all changes to find if user allowed all of them to be applied
changes.map(&:strip).reject(&:blank?).each do |change| changes_list.each do |change|
status = change_access_check(change) status = change_access_check(change)
unless status.allowed? unless status.allowed?
# If user does not have access to make at least one change - cancel all push # If user does not have access to make at least one change - cancel all push
......
...@@ -12,7 +12,7 @@ describe "Pipelines" do ...@@ -12,7 +12,7 @@ describe "Pipelines" do
end end
describe 'GET /:project/pipelines' do describe 'GET /:project/pipelines' do
let!(:pipeline) { create(:ci_pipeline, project: project, ref: 'master', status: 'running') } let!(:pipeline) { create(:ci_empty_pipeline, project: project, ref: 'master', status: 'running') }
[:all, :running, :branches].each do |scope| [:all, :running, :branches].each do |scope|
context "displaying #{scope}" do context "displaying #{scope}" do
...@@ -31,10 +31,10 @@ describe "Pipelines" do ...@@ -31,10 +31,10 @@ describe "Pipelines" do
end end
context 'cancelable pipeline' do context 'cancelable pipeline' do
let!(:running) { create(:ci_build, :running, pipeline: pipeline, stage: 'test', commands: 'test') } let!(:build) { create(:ci_build, pipeline: pipeline, stage: 'test', commands: 'test') }
before do before do
pipeline.reload_status! build.run
visit namespace_project_pipelines_path(project.namespace, project) visit namespace_project_pipelines_path(project.namespace, project)
end end
...@@ -50,10 +50,10 @@ describe "Pipelines" do ...@@ -50,10 +50,10 @@ describe "Pipelines" do
end end
context 'retryable pipelines' do context 'retryable pipelines' do
let!(:failed) { create(:ci_build, :failed, pipeline: pipeline, stage: 'test', commands: 'test') } let!(:build) { create(:ci_build, pipeline: pipeline, stage: 'test', commands: 'test') }
before do before do
pipeline.reload_status! build.drop
visit namespace_project_pipelines_path(project.namespace, project) visit namespace_project_pipelines_path(project.namespace, project)
end end
...@@ -87,7 +87,6 @@ describe "Pipelines" do ...@@ -87,7 +87,6 @@ describe "Pipelines" do
let!(:running) { create(:generic_commit_status, status: 'running', pipeline: pipeline, stage: 'test') } let!(:running) { create(:generic_commit_status, status: 'running', pipeline: pipeline, stage: 'test') }
before do before do
pipeline.reload_status!
visit namespace_project_pipelines_path(project.namespace, project) visit namespace_project_pipelines_path(project.namespace, project)
end end
...@@ -101,10 +100,10 @@ describe "Pipelines" do ...@@ -101,10 +100,10 @@ describe "Pipelines" do
end end
context 'when failed' do context 'when failed' do
let!(:failed) { create(:generic_commit_status, status: 'failed', pipeline: pipeline, stage: 'test') } let!(:status) { create(:generic_commit_status, :pending, pipeline: pipeline, stage: 'test') }
before do before do
pipeline.reload_status! status.drop
visit namespace_project_pipelines_path(project.namespace, project) visit namespace_project_pipelines_path(project.namespace, project)
end end
...@@ -206,7 +205,7 @@ describe "Pipelines" do ...@@ -206,7 +205,7 @@ describe "Pipelines" do
before { visit new_namespace_project_pipeline_path(project.namespace, project) } before { visit new_namespace_project_pipeline_path(project.namespace, project) }
context 'for valid commit' do context 'for valid commit' do
before { fill_in('Create for', with: 'master') } before { fill_in('pipeline[ref]', with: 'master') }
context 'with gitlab-ci.yml' do context 'with gitlab-ci.yml' do
before { stub_ci_pipeline_to_return_yaml_file } before { stub_ci_pipeline_to_return_yaml_file }
...@@ -223,11 +222,37 @@ describe "Pipelines" do ...@@ -223,11 +222,37 @@ describe "Pipelines" do
context 'for invalid commit' do context 'for invalid commit' do
before do before do
fill_in('Create for', with: 'invalid-reference') fill_in('pipeline[ref]', with: 'invalid-reference')
click_on 'Create pipeline' click_on 'Create pipeline'
end end
it { expect(page).to have_content('Reference not found') } it { expect(page).to have_content('Reference not found') }
end end
end end
describe 'Create pipelines', feature: true do
let(:project) { create(:project) }
before do
visit new_namespace_project_pipeline_path(project.namespace, project)
end
describe 'new pipeline page' do
it 'has field to add a new pipeline' do
expect(page).to have_field('pipeline[ref]')
expect(page).to have_content('Create for')
end
end
describe 'find pipelines' do
it 'shows filtered pipelines', js: true do
fill_in('pipeline[ref]', with: 'fix')
find('input#ref').native.send_keys(:keydown)
within('.ui-autocomplete') do
expect(page).to have_selector('li', text: 'fix')
end
end
end
end
end end
...@@ -9,54 +9,6 @@ describe MembersHelper do ...@@ -9,54 +9,6 @@ describe MembersHelper do
it { expect(action_member_permission(:admin, group_member)).to eq :admin_group_member } it { expect(action_member_permission(:admin, group_member)).to eq :admin_group_member }
end end
describe '#default_show_roles' do
let(:user) { double }
let(:member) { build(:project_member) }
before do
allow(helper).to receive(:current_user).and_return(user)
allow(helper).to receive(:can?).with(user, :update_project_member, member).and_return(false)
allow(helper).to receive(:can?).with(user, :destroy_project_member, member).and_return(false)
allow(helper).to receive(:can?).with(user, :admin_project_member, member.source).and_return(false)
end
context 'when the current cannot update, destroy or admin the passed member' do
it 'returns false' do
expect(helper.default_show_roles(member)).to be_falsy
end
end
context 'when the current can update the passed member' do
before do
allow(helper).to receive(:can?).with(user, :update_project_member, member).and_return(true)
end
it 'returns true' do
expect(helper.default_show_roles(member)).to be_truthy
end
end
context 'when the current can destroy the passed member' do
before do
allow(helper).to receive(:can?).with(user, :destroy_project_member, member).and_return(true)
end
it 'returns true' do
expect(helper.default_show_roles(member)).to be_truthy
end
end
context 'when the current can admin the passed member source' do
before do
allow(helper).to receive(:can?).with(user, :admin_project_member, member.source).and_return(true)
end
it 'returns true' do
expect(helper.default_show_roles(member)).to be_truthy
end
end
end
describe '#remove_member_message' do describe '#remove_member_message' do
let(:requester) { build(:user) } let(:requester) { build(:user) }
let(:project) { create(:project) } let(:project) { create(:project) }
......
...@@ -2,22 +2,23 @@ require 'spec_helper' ...@@ -2,22 +2,23 @@ require 'spec_helper'
describe Ci::Charts, lib: true do describe Ci::Charts, lib: true do
context "build_times" do context "build_times" do
let(:project) { create(:empty_project) }
let(:chart) { Ci::Charts::BuildTime.new(project) }
subject { chart.build_times }
before do before do
@pipeline = FactoryGirl.create(:ci_pipeline) create(:ci_empty_pipeline, project: project, duration: 120)
FactoryGirl.create(:ci_build, pipeline: @pipeline)
@pipeline.reload_status!
end end
it 'returns build times in minutes' do it 'returns build times in minutes' do
chart = Ci::Charts::BuildTime.new(@pipeline.project) is_expected.to contain_exactly(2)
expect(chart.build_times).to eq([2])
end end
it 'handles nil build times' do it 'handles nil build times' do
create(:ci_pipeline, duration: nil, project: @pipeline.project) create(:ci_empty_pipeline, project: project, duration: nil)
chart = Ci::Charts::BuildTime.new(@pipeline.project) is_expected.to contain_exactly(2, 0)
expect(chart.build_times).to eq([2, 0])
end end
end end
end end
require "spec_helper"
describe Gitlab::ChangesList do
let(:valid_changes_string) { "\n000000 570e7b2 refs/heads/my_branch\nd14d6c 6fd24d refs/heads/master" }
let(:invalid_changes) { 1 }
context 'when changes is a valid string' do
let(:changes_list) { Gitlab::ChangesList.new(valid_changes_string) }
it 'splits elements by newline character' do
expect(changes_list).to contain_exactly({
oldrev: "000000",
newrev: "570e7b2",
ref: "refs/heads/my_branch"
}, {
oldrev: "d14d6c",
newrev: "6fd24d",
ref: "refs/heads/master"
})
end
it 'behaves like a list' do
expect(changes_list.first).to eq({
oldrev: "000000",
newrev: "570e7b2",
ref: "refs/heads/my_branch"
})
end
end
end
...@@ -2,7 +2,7 @@ require 'spec_helper' ...@@ -2,7 +2,7 @@ require 'spec_helper'
describe Ci::Pipeline, models: true do describe Ci::Pipeline, models: true do
let(:project) { FactoryGirl.create :empty_project } let(:project) { FactoryGirl.create :empty_project }
let(:pipeline) { FactoryGirl.create :ci_empty_pipeline, status: 'created', project: project } let(:pipeline) { FactoryGirl.create :ci_empty_pipeline, project: project }
it { is_expected.to belong_to(:project) } it { is_expected.to belong_to(:project) }
it { is_expected.to belong_to(:user) } it { is_expected.to belong_to(:user) }
...@@ -53,25 +53,6 @@ describe Ci::Pipeline, models: true do ...@@ -53,25 +53,6 @@ describe Ci::Pipeline, models: true do
end end
end end
describe "#finished_at" do
let(:pipeline) { FactoryGirl.create :ci_pipeline }
it "returns finished_at of latest build" do
build = FactoryGirl.create :ci_build, pipeline: pipeline, finished_at: Time.now - 60
FactoryGirl.create :ci_build, pipeline: pipeline, finished_at: Time.now - 120
pipeline.reload_status!
expect(pipeline.finished_at.to_i).to eq(build.finished_at.to_i)
end
it "returns nil if there is no finished build" do
FactoryGirl.create :ci_not_started_build, pipeline: pipeline
pipeline.reload_status!
expect(pipeline.finished_at).to be_nil
end
end
describe "coverage" do describe "coverage" do
let(:project) { FactoryGirl.create :empty_project, build_coverage_regex: "/.*/" } let(:project) { FactoryGirl.create :empty_project, build_coverage_regex: "/.*/" }
let(:pipeline) { FactoryGirl.create :ci_empty_pipeline, project: project } let(:pipeline) { FactoryGirl.create :ci_empty_pipeline, project: project }
...@@ -141,32 +122,47 @@ describe Ci::Pipeline, models: true do ...@@ -141,32 +122,47 @@ describe Ci::Pipeline, models: true do
end end
end end
describe '#reload_status!' do describe 'state machine' do
let(:pipeline) { create :ci_empty_pipeline, project: project } let(:current) { Time.now.change(usec: 0) }
let(:build) { create :ci_build, name: 'build1', pipeline: pipeline, started_at: current - 60, finished_at: current }
let(:build2) { create :ci_build, name: 'build2', pipeline: pipeline, started_at: current - 60, finished_at: current }
context 'dependent objects' do describe '#duration' do
let(:commit_status) { create :commit_status, :pending, pipeline: pipeline } before do
build.skip
build2.skip
end
it 'executes reload_status! after succeeding dependent object' do it 'matches sum of builds duration' do
expect(pipeline).to receive(:reload_status!).and_return(true) expect(pipeline.reload.duration).to eq(build.duration + build2.duration)
end
end
commit_status.success describe '#started_at' do
it 'updates on transitioning to running' do
build.run
expect(pipeline.reload.started_at).not_to be_nil
end
it 'do not update on transitioning to success' do
build.success
expect(pipeline.reload.started_at).to be_nil
end end
end end
context 'updates' do describe '#finished_at' do
let(:current) { Time.now.change(usec: 0) } it 'updates on transitioning to success' do
let(:build) { FactoryGirl.create :ci_build, pipeline: pipeline, started_at: current - 120, finished_at: current - 60 } build.success
before do expect(pipeline.reload.finished_at).not_to be_nil
build
pipeline.reload_status!
end end
[:status, :started_at, :finished_at, :duration].each do |param| it 'do not update on transitioning to running' do
it "#{param}" do build.run
expect(pipeline.send(param)).to eq(build.send(param))
end expect(pipeline.reload.finished_at).to be_nil
end end
end end
end end
......
...@@ -9,7 +9,7 @@ describe API::API, api: true do ...@@ -9,7 +9,7 @@ describe API::API, api: true do
let!(:developer) { create(:project_member, :developer, user: user, project: project) } let!(:developer) { create(:project_member, :developer, user: user, project: project) }
let(:reporter) { create(:project_member, :reporter, project: project) } let(:reporter) { create(:project_member, :reporter, project: project) }
let(:guest) { create(:project_member, :guest, project: project) } let(:guest) { create(:project_member, :guest, project: project) }
let!(:pipeline) { create(:ci_pipeline, project: project, sha: project.commit.id, ref: project.default_branch) } let!(:pipeline) { create(:ci_empty_pipeline, project: project, sha: project.commit.id, ref: project.default_branch) }
let!(:build) { create(:ci_build, pipeline: pipeline) } let!(:build) { create(:ci_build, pipeline: pipeline) }
describe 'GET /projects/:id/builds ' do describe 'GET /projects/:id/builds ' do
...@@ -174,7 +174,11 @@ describe API::API, api: true do ...@@ -174,7 +174,11 @@ describe API::API, api: true do
describe 'GET /projects/:id/artifacts/:ref_name/download?job=name' do describe 'GET /projects/:id/artifacts/:ref_name/download?job=name' do
let(:api_user) { reporter.user } let(:api_user) { reporter.user }
let(:build) { create(:ci_build, :success, :artifacts, pipeline: pipeline) } let(:build) { create(:ci_build, :artifacts, pipeline: pipeline) }
before do
build.success
end
def path_for_ref(ref = pipeline.ref, job = build.name) def path_for_ref(ref = pipeline.ref, job = build.name)
api("/projects/#{project.id}/builds/artifacts/#{ref}/download?job=#{job}", api_user) api("/projects/#{project.id}/builds/artifacts/#{ref}/download?job=#{job}", api_user)
...@@ -238,10 +242,6 @@ describe API::API, api: true do ...@@ -238,10 +242,6 @@ describe API::API, api: true do
it { expect(response.headers).to include(download_headers) } it { expect(response.headers).to include(download_headers) }
end end
before do
pipeline.reload_status!
end
context 'with regular branch' do context 'with regular branch' do
before do before do
pipeline.update(ref: 'master', pipeline.update(ref: 'master',
......
...@@ -275,6 +275,24 @@ describe API::API, api: true do ...@@ -275,6 +275,24 @@ describe API::API, api: true do
end end
end end
describe 'GET /internal/merge_request_urls' do
let(:repo_name) { "#{project.namespace.name}/#{project.path}" }
let(:changes) { URI.escape("#{Gitlab::Git::BLANK_SHA} 570e7b2abdd848b95f2f578043fc23bd6f6fd24d refs/heads/new_branch") }
before do
project.team << [user, :developer]
get api("/internal/merge_request_urls?project=#{repo_name}&changes=#{changes}"), secret_token: secret_token
end
it 'returns link to create new merge request' do
expect(json_response).to match [{
"branch_name" => "new_branch",
"url" => "http://localhost/#{project.namespace.name}/#{project.path}/merge_requests/new?merge_request%5Bsource_branch%5D=new_branch",
"new_merge_request" => true
}]
end
end
def pull(key, project, protocol = 'ssh') def pull(key, project, protocol = 'ssh')
post( post(
api("/internal/allowed"), api("/internal/allowed"),
......
...@@ -60,7 +60,7 @@ end ...@@ -60,7 +60,7 @@ end
# project GET /:id(.:format) projects#show # project GET /:id(.:format) projects#show
# PUT /:id(.:format) projects#update # PUT /:id(.:format) projects#update
# DELETE /:id(.:format) projects#destroy # DELETE /:id(.:format) projects#destroy
# markdown_preview_project POST /:id/markdown_preview(.:format) projects#markdown_preview # preview_markdown_project POST /:id/preview_markdown(.:format) projects#preview_markdown
describe ProjectsController, 'routing' do describe ProjectsController, 'routing' do
it 'to #create' do it 'to #create' do
expect(post('/projects')).to route_to('projects#create') expect(post('/projects')).to route_to('projects#create')
...@@ -91,9 +91,9 @@ describe ProjectsController, 'routing' do ...@@ -91,9 +91,9 @@ describe ProjectsController, 'routing' do
expect(delete('/gitlab/gitlabhq')).to route_to('projects#destroy', namespace_id: 'gitlab', id: 'gitlabhq') expect(delete('/gitlab/gitlabhq')).to route_to('projects#destroy', namespace_id: 'gitlab', id: 'gitlabhq')
end end
it 'to #markdown_preview' do it 'to #preview_markdown' do
expect(post('/gitlab/gitlabhq/markdown_preview')).to( expect(post('/gitlab/gitlabhq/preview_markdown')).to(
route_to('projects#markdown_preview', namespace_id: 'gitlab', id: 'gitlabhq') route_to('projects#preview_markdown', namespace_id: 'gitlab', id: 'gitlabhq')
) )
end end
end end
......
...@@ -14,7 +14,6 @@ module Ci ...@@ -14,7 +14,6 @@ module Ci
context 'branch name' do context 'branch name' do
before { allow(project).to receive(:commit).and_return(OpenStruct.new(sha: commit_sha)) } before { allow(project).to receive(:commit).and_return(OpenStruct.new(sha: commit_sha)) }
before { build.run! } before { build.run! }
before { pipeline.reload_status! }
let(:image) { service.execute(project, ref: 'master') } let(:image) { service.execute(project, ref: 'master') }
it { expect(image).to be_kind_of(OpenStruct) } it { expect(image).to be_kind_of(OpenStruct) }
...@@ -32,7 +31,6 @@ module Ci ...@@ -32,7 +31,6 @@ module Ci
context 'commit sha' do context 'commit sha' do
before { build.run! } before { build.run! }
before { pipeline.reload_status! }
let(:image) { service.execute(project, sha: build.sha) } let(:image) { service.execute(project, sha: build.sha) }
it { expect(image).to be_kind_of(OpenStruct) } it { expect(image).to be_kind_of(OpenStruct) }
......
require "spec_helper"
describe MergeRequests::GetUrlsService do
let(:project) { create(:project, :public) }
let(:service) { MergeRequests::GetUrlsService.new(project) }
let(:source_branch) { "my_branch" }
let(:new_merge_request_url) { "http://localhost/#{project.namespace.name}/#{project.path}/merge_requests/new?merge_request%5Bsource_branch%5D=#{source_branch}" }
let(:show_merge_request_url) { "http://localhost/#{project.namespace.name}/#{project.path}/merge_requests/#{merge_request.iid}" }
let(:new_branch_changes) { "#{Gitlab::Git::BLANK_SHA} 570e7b2abdd848b95f2f578043fc23bd6f6fd24d refs/heads/#{source_branch}" }
let(:existing_branch_changes) { "d14d6c0abdd253381df51a723d58691b2ee1ab08 570e7b2abdd848b95f2f578043fc23bd6f6fd24d refs/heads/#{source_branch}" }
describe "#execute" do
shared_examples 'new_merge_request_link' do
it 'returns url to create new merge request' do
result = service.execute(changes)
expect(result).to match([{
branch_name: source_branch,
url: new_merge_request_url,
new_merge_request: true
}])
end
end
shared_examples 'show_merge_request_url' do
it 'returns url to view merge request' do
result = service.execute(changes)
expect(result).to match([{
branch_name: source_branch,
url: show_merge_request_url,
new_merge_request: false
}])
end
end
context 'pushing one completely new branch' do
let(:changes) { new_branch_changes }
it_behaves_like 'new_merge_request_link'
end
context 'pushing to existing branch but no merge request' do
let(:changes) { existing_branch_changes }
it_behaves_like 'new_merge_request_link'
end
context 'pushing to existing branch and merge request opened' do
let!(:merge_request) { create(:merge_request, source_project: project, source_branch: source_branch) }
let(:changes) { existing_branch_changes }
it_behaves_like 'show_merge_request_url'
end
context 'pushing to existing branch and merge request is reopened' do
let!(:merge_request) { create(:merge_request, :reopened, source_project: project, source_branch: source_branch) }
let(:changes) { existing_branch_changes }
it_behaves_like 'show_merge_request_url'
end
context 'pushing to existing branch from forked project' do
let(:user) { create(:user) }
let!(:forked_project) { Projects::ForkService.new(project, user).execute }
let!(:merge_request) { create(:merge_request, source_project: forked_project, target_project: project, source_branch: source_branch) }
let(:changes) { existing_branch_changes }
# Source project is now the forked one
let(:service) { MergeRequests::GetUrlsService.new(forked_project) }
it_behaves_like 'show_merge_request_url'
end
context 'pushing to existing branch and merge request is closed' do
let!(:merge_request) { create(:merge_request, :closed, source_project: project, source_branch: source_branch) }
let(:changes) { existing_branch_changes }
it_behaves_like 'new_merge_request_link'
end
context 'pushing to existing branch and merge request is merged' do
let!(:merge_request) { create(:merge_request, :merged, source_project: project, source_branch: source_branch) }
let(:changes) { existing_branch_changes }
it_behaves_like 'new_merge_request_link'
end
context 'pushing new branch and existing branch (with merge request created) at once' do
let!(:merge_request) { create(:merge_request, source_project: project, source_branch: "existing_branch") }
let(:new_branch_changes) { "#{Gitlab::Git::BLANK_SHA} 570e7b2abdd848b95f2f578043fc23bd6f6fd24d refs/heads/new_branch" }
let(:existing_branch_changes) { "d14d6c0abdd253381df51a723d58691b2ee1ab08 570e7b2abdd848b95f2f578043fc23bd6f6fd24d refs/heads/existing_branch" }
let(:changes) { "#{new_branch_changes}\n#{existing_branch_changes}" }
let(:new_merge_request_url) { "http://localhost/#{project.namespace.name}/#{project.path}/merge_requests/new?merge_request%5Bsource_branch%5D=new_branch" }
it 'returns 2 urls for both creating new and showing merge request' do
result = service.execute(changes)
expect(result).to match([{
branch_name: "new_branch",
url: new_merge_request_url,
new_merge_request: true
}, {
branch_name: "existing_branch",
url: show_merge_request_url,
new_merge_request: false
}])
end
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment