Commit ef44138c authored by Douwe Maan's avatar Douwe Maan

Merge branch 'master' into rs-redactor-filter

parents 528d2823 123669a5
...@@ -18,6 +18,7 @@ v 8.1.0 (unreleased) ...@@ -18,6 +18,7 @@ v 8.1.0 (unreleased)
- Add first and last to pagination (Zeger-Jan van de Weg) - Add first and last to pagination (Zeger-Jan van de Weg)
- Added Commit Status API - Added Commit Status API
- Show CI status on commit page - Show CI status on commit page
- Added CI_BUILD_TAG, _STAGE, _NAME and _TRIGGERED to CI builds
- Show CI status on Your projects page and Starred projects page - Show CI status on Your projects page and Starred projects page
- Remove "Continuous Integration" page from dashboard - Remove "Continuous Integration" page from dashboard
- Add notes and SSL verification entries to hook APIs (Ben Boeckel) - Add notes and SSL verification entries to hook APIs (Ben Boeckel)
...@@ -27,6 +28,7 @@ v 8.1.0 (unreleased) ...@@ -27,6 +28,7 @@ v 8.1.0 (unreleased)
- Move CI triggers page to project settings area - Move CI triggers page to project settings area
- Move CI project settings page to CE project settings area - Move CI project settings page to CE project settings area
- Fix bug when removed file was not appearing in merge request diff - Fix bug when removed file was not appearing in merge request diff
- Show warning when build cannot be served by any of the available CI runners
- Note the original location of a moved project when notifying users of the move - Note the original location of a moved project when notifying users of the move
- Improve error message when merging fails - Improve error message when merging fails
- Add support of multibyte characters in LDAP UID (Roman Petrov) - Add support of multibyte characters in LDAP UID (Roman Petrov)
......
...@@ -6,7 +6,7 @@ module Ci ...@@ -6,7 +6,7 @@ module Ci
@runners = Ci::Runner.order('id DESC') @runners = Ci::Runner.order('id DESC')
@runners = @runners.search(params[:search]) if params[:search].present? @runners = @runners.search(params[:search]) if params[:search].present?
@runners = @runners.page(params[:page]).per(30) @runners = @runners.page(params[:page]).per(30)
@active_runners_cnt = Ci::Runner.where("contacted_at > ?", 1.minutes.ago).count @active_runners_cnt = Ci::Runner.online.count
end end
def show def show
...@@ -66,7 +66,7 @@ module Ci ...@@ -66,7 +66,7 @@ module Ci
end end
def runner_params def runner_params
params.require(:runner).permit(:token, :description, :tag_list, :contacted_at, :active) params.require(:runner).permit(:token, :description, :tag_list, :active)
end end
end end
end end
...@@ -55,7 +55,7 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -55,7 +55,7 @@ class Projects::IssuesController < Projects::ApplicationController
end end
def show def show
@participants = @issue.participants(current_user, @project) @participants = @issue.participants(current_user)
@note = @project.notes.new(noteable: @issue) @note = @project.notes.new(noteable: @issue)
@notes = @issue.notes.inc_author.fresh @notes = @issue.notes.inc_author.fresh
@noteable = @issue @noteable = @issue
......
...@@ -246,7 +246,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -246,7 +246,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end end
def define_show_vars def define_show_vars
@participants = @merge_request.participants(current_user, @project) @participants = @merge_request.participants(current_user)
# Build a note object for comment form # Build a note object for comment form
@note = @project.notes.new(noteable: @merge_request) @note = @project.notes.new(noteable: @merge_request)
......
...@@ -60,6 +60,6 @@ class Projects::RunnersController < Projects::ApplicationController ...@@ -60,6 +60,6 @@ class Projects::RunnersController < Projects::ApplicationController
end end
def runner_params def runner_params
params.require(:runner).permit(:description, :tag_list, :contacted_at, :active) params.require(:runner).permit(:description, :tag_list, :active)
end end
end end
module RunnersHelper module RunnersHelper
def runner_status_icon(runner) def runner_status_icon(runner)
unless runner.contacted_at status = runner.status
return content_tag :i, nil, case status
class: "fa fa-warning-sign", when :not_connected
content_tag :i, nil,
class: "fa fa-warning",
title: "New runner. Has not connected yet" title: "New runner. Has not connected yet"
end
status =
if runner.active?
runner.contacted_at > 3.hour.ago ? :online : :offline
else
:paused
end
when :online, :offline, :paused
content_tag :i, nil, content_tag :i, nil,
class: "fa fa-circle runner-status-#{status}", class: "fa fa-circle runner-status-#{status}",
title: "Runner is #{status}, last contact was #{time_ago_in_words(runner.contacted_at)} ago" title: "Runner is #{status}, last contact was #{time_ago_in_words(runner.contacted_at)} ago"
end end
end
end end
...@@ -119,7 +119,7 @@ module Ci ...@@ -119,7 +119,7 @@ module Ci
end end
def variables def variables
yaml_variables + project_variables + trigger_variables predefined_variables + yaml_variables + project_variables + trigger_variables
end end
def project def project
...@@ -231,6 +231,18 @@ module Ci ...@@ -231,6 +231,18 @@ module Ci
end end
end end
def can_be_served?(runner)
(tag_list - runner.tag_list).empty?
end
def any_runners_online?
project.any_runners? { |runner| runner.active? && runner.online? && can_be_served?(runner) }
end
def show_warning?
pending? && !any_runners_online?
end
private private
def yaml_variables def yaml_variables
...@@ -258,5 +270,14 @@ module Ci ...@@ -258,5 +270,14 @@ module Ci
[] []
end end
end end
def predefined_variables
variables = []
variables << { key: :CI_BUILD_TAG, value: ref, public: true } if tag?
variables << { key: :CI_BUILD_NAME, value: name, public: true }
variables << { key: :CI_BUILD_STAGE, value: stage, public: true }
variables << { key: :CI_BUILD_TRIGGERED, value: 'true', public: true } if trigger_request
variables
end
end end
end end
...@@ -115,12 +115,12 @@ module Ci ...@@ -115,12 +115,12 @@ module Ci
web_url web_url
end end
def any_runners? def any_runners?(&block)
if runners.active.any? if runners.active.any?(&block)
return true return true
end end
shared_runners_enabled && Ci::Runner.shared.active.any? shared_runners_enabled && Ci::Runner.shared.active.any?(&block)
end end
def set_default_values def set_default_values
......
...@@ -21,6 +21,8 @@ module Ci ...@@ -21,6 +21,8 @@ module Ci
class Runner < ActiveRecord::Base class Runner < ActiveRecord::Base
extend Ci::Model extend Ci::Model
LAST_CONTACT_TIME = 5.minutes.ago
has_many :builds, class_name: 'Ci::Build' has_many :builds, class_name: 'Ci::Build'
has_many :runner_projects, dependent: :destroy, class_name: 'Ci::RunnerProject' has_many :runner_projects, dependent: :destroy, class_name: 'Ci::RunnerProject'
has_many :projects, through: :runner_projects, class_name: 'Ci::Project' has_many :projects, through: :runner_projects, class_name: 'Ci::Project'
...@@ -33,6 +35,7 @@ module Ci ...@@ -33,6 +35,7 @@ module Ci
scope :shared, ->() { where(is_shared: true) } scope :shared, ->() { where(is_shared: true) }
scope :active, ->() { where(active: true) } scope :active, ->() { where(active: true) }
scope :paused, ->() { where(active: false) } scope :paused, ->() { where(active: false) }
scope :online, ->() { where('contacted_at > ?', LAST_CONTACT_TIME) }
acts_as_taggable acts_as_taggable
...@@ -65,6 +68,20 @@ module Ci ...@@ -65,6 +68,20 @@ module Ci
is_shared is_shared
end end
def online?
contacted_at && contacted_at > LAST_CONTACT_TIME
end
def status
if contacted_at.nil?
:not_connected
elsif active?
online? ? :online : :offline
else
:paused
end
end
def belongs_to_one_project? def belongs_to_one_project?
runner_projects.count == 1 runner_projects.count == 1
end end
......
...@@ -88,4 +88,8 @@ class CommitStatus < ActiveRecord::Base ...@@ -88,4 +88,8 @@ class CommitStatus < ActiveRecord::Base
def retry_url def retry_url
nil nil
end end
def show_warning?
false
end
end end
...@@ -43,67 +43,57 @@ module Mentionable ...@@ -43,67 +43,57 @@ module Mentionable
self self
end end
# Determine whether or not a cross-reference Note has already been created between this Mentionable and def all_references(current_user = self.author, text = nil, load_lazy_references: true)
# the specified target.
def has_mentioned?(target)
SystemNoteService.cross_reference_exists?(target, local_reference)
end
def mentioned_users(current_user = nil, load_lazy_references: true)
# TODO: Douwe: Will be simplified when the "Simplify ..." MR is merged.
ext = Gitlab::ReferenceExtractor.new(self.project, current_user, load_lazy_references: load_lazy_references) ext = Gitlab::ReferenceExtractor.new(self.project, current_user, load_lazy_references: load_lazy_references)
self.class.mentionable_attrs.each do |attr, options|
text = send(attr)
cache_key = [self, attr] if options[:cache]
ext.analyze(text, cache_key: cache_key, pipeline: options[:pipeline])
end
ext.users
end
# Extract GFM references to other Mentionables from this Mentionable. Always excludes its #local_reference.
def references(p = project, current_user = self.author, text = nil, load_lazy_references: true)
return [] if text.blank?
ext = Gitlab::ReferenceExtractor.new(p, current_user, load_lazy_references: load_lazy_references)
if text if text
ext.analyze(text) ext.analyze(text)
else else
self.class.mentionable_attrs.each do |attr, options| self.class.mentionable_attrs.each do |attr, options|
text = send(attr) text = send(attr)
cache_key = [self, attr] if options[:cache] options[:cache_key] = [self, attr] if options.delete(:cache)
ext.analyze(text, cache_key: cache_key) ext.analyze(text, options)
end
end
ext
end end
def mentioned_users(current_user = nil, load_lazy_references: true)
all_references(current_user).users
end end
(ext.issues + ext.merge_requests + ext.commits) - [local_reference] # Extract GFM references to other Mentionables from this Mentionable. Always excludes its #local_reference.
def referenced_mentionables(current_user = self.author, text = nil)
refs = all_references(current_user, text)
(refs.issues + refs.merge_requests + refs.commits) - [local_reference]
end end
# Create a cross-reference Note for each GFM reference to another Mentionable found in +mentionable_text+. # Create a cross-reference Note for each GFM reference to another Mentionable found in the +mentionable_attrs+.
def create_cross_references!(p = project, a = author, without = []) def create_cross_references!(author = self.author, without = [], text = nil)
refs = references(p) refs = referenced_mentionables(author, text)
# We're using this method instead of Array diffing because that requires # We're using this method instead of Array diffing because that requires
# both of the object's `hash` values to be the same, which may not be the # both of the object's `hash` values to be the same, which may not be the
# case for otherwise identical Commit objects. # case for otherwise identical Commit objects.
refs.reject! { |ref| without.include?(ref) } refs.reject! { |ref| without.include?(ref) || cross_reference_exists?(ref) }
refs.each do |ref| refs.each do |ref|
SystemNoteService.cross_reference(ref, local_reference, a) SystemNoteService.cross_reference(ref, local_reference, author)
end end
end end
# When a mentionable field is changed, creates cross-reference notes that # When a mentionable field is changed, creates cross-reference notes that
# don't already exist # don't already exist
def create_new_cross_references!(p = project, a = author) def create_new_cross_references!(author = self.author)
changes = detect_mentionable_changes changes = detect_mentionable_changes
return if changes.empty? return if changes.empty?
original_text = changes.collect { |_, vals| vals.first }.join(' ') original_text = changes.collect { |_, vals| vals.first }.join(' ')
preexisting = references(p, self.author, original_text) preexisting = referenced_mentionables(author, original_text)
create_cross_references!(p, a, preexisting) create_cross_references!(author, preexisting)
end end
private private
...@@ -125,4 +115,10 @@ module Mentionable ...@@ -125,4 +115,10 @@ module Mentionable
# Only include changed fields that are mentionable # Only include changed fields that are mentionable
source.select { |key, val| mentionable.include?(key) } source.select { |key, val| mentionable.include?(key) }
end end
# Determine whether or not a cross-reference Note has already been created between this Mentionable and
# the specified target.
def cross_reference_exists?(target)
SystemNoteService.cross_reference_exists?(target, local_reference)
end
end end
...@@ -37,7 +37,8 @@ module Participable ...@@ -37,7 +37,8 @@ module Participable
# Be aware that this method makes a lot of sql queries. # Be aware that this method makes a lot of sql queries.
# Save result into variable if you are going to reuse it inside same request # Save result into variable if you are going to reuse it inside same request
def participants(current_user = self.author, project = self.project, load_lazy_references: true) <<<<<<< HEAD
def participants(current_user = self.author, load_lazy_references: true)
participants = self.class.participant_attrs.flat_map do |attr| participants = self.class.participant_attrs.flat_map do |attr|
value = value =
if attr.respond_to?(:call) if attr.respond_to?(:call)
...@@ -46,32 +47,30 @@ module Participable ...@@ -46,32 +47,30 @@ module Participable
send(attr) send(attr)
end end
participants_for(value, current_user, project) participants_for(value, current_user)
end.compact.uniq end.compact.uniq
if load_lazy_references if load_lazy_references
participants = Gitlab::Markdown::ReferenceFilter::LazyReference.load(participants).uniq participants = Gitlab::Markdown::ReferenceFilter::LazyReference.load(participants).uniq
if project
participants.select! do |user| participants.select! do |user|
user.can?(:read_project, project) user.can?(:read_project, project)
end end
end end
end
participants participants
end end
private private
def participants_for(value, current_user = nil, project = nil) def participants_for(value, current_user = nil)
case value case value
when User, Gitlab::Markdown::ReferenceFilter::LazyReference when User, Gitlab::Markdown::ReferenceFilter::LazyReference
[value] [value]
when Enumerable, ActiveRecord::Relation when Enumerable, ActiveRecord::Relation
value.flat_map { |v| participants_for(v, current_user, project) } value.flat_map { |v| participants_for(v, current_user) }
when Participable when Participable
value.participants(current_user, project, load_lazy_references: false) value.participants(current_user, load_lazy_references: false)
end end
end end
end end
...@@ -62,7 +62,6 @@ class Note < ActiveRecord::Base ...@@ -62,7 +62,6 @@ class Note < ActiveRecord::Base
serialize :st_diff serialize :st_diff
before_create :set_diff, if: ->(n) { n.line_code.present? } before_create :set_diff, if: ->(n) { n.line_code.present? }
after_update :set_references
class << self class << self
def discussions_from_notes(notes) def discussions_from_notes(notes)
...@@ -333,15 +332,13 @@ class Note < ActiveRecord::Base ...@@ -333,15 +332,13 @@ class Note < ActiveRecord::Base
end end
def noteable_type_name def noteable_type_name
if noteable_type.present? noteable_type.downcase if noteable_type.present?
noteable_type.downcase
end
end end
# FIXME: Hack for polymorphic associations with STI # FIXME: Hack for polymorphic associations with STI
# For more information visit http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#label-Polymorphic+Associations # For more information visit http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#label-Polymorphic+Associations
def noteable_type=(sType) def noteable_type=(noteable_type)
super(sType.to_s.classify.constantize.base_class.to_s) super(noteable_type.to_s.classify.constantize.base_class.to_s)
end end
# Reset notes events cache # Reset notes events cache
...@@ -357,10 +354,6 @@ class Note < ActiveRecord::Base ...@@ -357,10 +354,6 @@ class Note < ActiveRecord::Base
Event.reset_event_cache_for(self) Event.reset_event_cache_for(self)
end end
def set_references
create_new_cross_references!(project, author)
end
def system? def system?
read_attribute(:system) read_attribute(:system)
end end
......
...@@ -17,7 +17,7 @@ module Ci ...@@ -17,7 +17,7 @@ module Ci
builds = builds.order('created_at ASC') builds = builds.order('created_at ASC')
build = builds.find do |build| build = builds.find do |build|
(build.tag_list - current_runner.tag_list).empty? build.can_be_served?(current_runner)
end end
......
...@@ -74,48 +74,30 @@ class GitPushService ...@@ -74,48 +74,30 @@ class GitPushService
def process_commit_messages(ref) def process_commit_messages(ref)
is_default_branch = is_default_branch?(ref) is_default_branch = is_default_branch?(ref)
@push_commits.each do |commit| authors = Hash.new do |hash, commit|
# Close issues if these commits were pushed to the project's default branch and the commit message matches the email = commit.author_email
# closing regex. Exclude any mentioned Issues from cross-referencing even if the commits are being pushed to return hash[email] if hash.has_key?(email)
# a different branch.
issues_to_close = commit.closes_issues(user)
# Load commit author only if needed. hash[email] = commit_user(commit)
# For push with 1k commits it prevents 900+ requests in database end
author = nil
@push_commits.each do |commit|
# Keep track of the issues that will be actually closed because they are on a default branch. # Keep track of the issues that will be actually closed because they are on a default branch.
# Hence, when creating cross-reference notes, the not-closed issues (on non-default branches) # Hence, when creating cross-reference notes, the not-closed issues (on non-default branches)
# will also have cross-reference. # will also have cross-reference.
actually_closed_issues = [] closed_issues = []
if issues_to_close.present? && is_default_branch
author ||= commit_user(commit)
actually_closed_issues = issues_to_close
issues_to_close.each do |issue|
Issues::CloseService.new(project, author, {}).execute(issue, commit)
end
end
if project.default_issues_tracker? if is_default_branch
create_cross_reference_notes(commit, actually_closed_issues) # Close issues if these commits were pushed to the project's default branch and the commit message matches the
end # closing regex. Exclude any mentioned Issues from cross-referencing even if the commits are being pushed to
# a different branch.
closed_issues = commit.closes_issues(user)
closed_issues.each do |issue|
Issues::CloseService.new(project, authors[commit], {}).execute(issue, commit)
end end
end end
def create_cross_reference_notes(commit, issues_to_close) commit.create_cross_references!(authors[commit], closed_issues)
# Create cross-reference notes for any other references than those given in issues_to_close.
# Omit any issues that were referenced in an issue-closing phrase, or have already been
# mentioned from this commit (probably from this commit being pushed to a different branch).
refs = commit.references(project, user) - issues_to_close
refs.reject! { |r| commit.has_mentioned?(r) }
if refs.present?
author ||= commit_user(commit)
refs.each do |r|
SystemNoteService.cross_reference(r, commit, author)
end
end end
end end
......
...@@ -10,7 +10,7 @@ module Issues ...@@ -10,7 +10,7 @@ module Issues
issue.update_attributes(label_ids: label_params) issue.update_attributes(label_ids: label_params)
notification_service.new_issue(issue, current_user) notification_service.new_issue(issue, current_user)
event_service.open_issue(issue, current_user) event_service.open_issue(issue, current_user)
issue.create_cross_references!(issue.project, current_user) issue.create_cross_references!(current_user)
execute_hooks(issue, 'open') execute_hooks(issue, 'open')
end end
......
...@@ -35,7 +35,7 @@ module Issues ...@@ -35,7 +35,7 @@ module Issues
create_title_change_note(issue, issue.previous_changes['title'].first) create_title_change_note(issue, issue.previous_changes['title'].first)
end end
issue.create_new_cross_references!(issue.project, current_user) issue.create_new_cross_references!
execute_hooks(issue, 'update') execute_hooks(issue, 'update')
end end
......
...@@ -18,7 +18,7 @@ module MergeRequests ...@@ -18,7 +18,7 @@ module MergeRequests
merge_request.update_attributes(label_ids: label_params) merge_request.update_attributes(label_ids: label_params)
event_service.open_mr(merge_request, current_user) event_service.open_mr(merge_request, current_user)
notification_service.new_merge_request(merge_request, current_user) notification_service.new_merge_request(merge_request, current_user)
merge_request.create_cross_references!(merge_request.project, current_user) merge_request.create_cross_references!(current_user)
execute_hooks(merge_request) execute_hooks(merge_request)
end end
......
...@@ -59,7 +59,7 @@ module MergeRequests ...@@ -59,7 +59,7 @@ module MergeRequests
merge_request.mark_as_unchecked merge_request.mark_as_unchecked
end end
merge_request.create_new_cross_references!(merge_request.project, current_user) merge_request.create_new_cross_references!
execute_hooks(merge_request, 'update') execute_hooks(merge_request, 'update')
end end
......
...@@ -11,13 +11,7 @@ module Notes ...@@ -11,13 +11,7 @@ module Notes
# Skip system notes, like status changes and cross-references. # Skip system notes, like status changes and cross-references.
unless note.system unless note.system
event_service.leave_note(note, note.author) event_service.leave_note(note, note.author)
note.create_cross_references!
# Create a cross-reference note if this Note contains GFM that names an
# issue, merge request, or commit.
note.references.each do |mentioned|
SystemNoteService.cross_reference(mentioned, note.noteable, note.author)
end
execute_hooks(note) execute_hooks(note)
end end
end end
......
...@@ -4,7 +4,7 @@ module Notes ...@@ -4,7 +4,7 @@ module Notes
return note unless note.editable? return note unless note.editable?
note.update_attributes(params.merge(updated_by: current_user)) note.update_attributes(params.merge(updated_by: current_user))
note.create_new_cross_references!
note.reset_events_cache note.reset_events_cache
note note
......
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
%a %a
Build ##{@build.id} Build ##{@build.id}
&middot; &middot;
%i.fa.fa-warning-sign %i.fa.fa-warning
This build was retried. This build was retried.
.gray-content-block.second-block .gray-content-block.second-block
...@@ -39,6 +39,27 @@ ...@@ -39,6 +39,27 @@
.pull-right .pull-right
= @build.updated_at.stamp('19:00 Aug 27') = @build.updated_at.stamp('19:00 Aug 27')
- if @build.show_warning?
- unless @build.any_runners_online?
.bs-callout.bs-callout-warning
%p
- if no_runners_for_project?(@build.project)
This build is stuck, because the project doesn't have runners assigned.
- elsif @build.tags.any?
This build is stuck.
%br
This build is stuck, because you don't have any active runners online with these tags assigned to the project:
- @build.tags.each do |tag|
%span.label.label-primary
= tag
- else
This build is stuck, because you don't have any active runners online that can run this build.
%br
Go to
= link_to namespace_project_runners_path(@build.gl_project.namespace, @build.gl_project) do
Runners page
.row.prepend-top-default .row.prepend-top-default
.col-md-9 .col-md-9
.clearfix .clearfix
......
...@@ -9,6 +9,9 @@ ...@@ -9,6 +9,9 @@
- else - else
%strong Build ##{commit_status.id} %strong Build ##{commit_status.id}
- if commit_status.show_warning?
%i.fa.fa-warning.text-warning
%td %td
= commit_status.ref = commit_status.ref
......
...@@ -15,21 +15,27 @@ The API_TOKEN will take the Secure Variable value: `SECURE`. ...@@ -15,21 +15,27 @@ The API_TOKEN will take the Secure Variable value: `SECURE`.
### Predefined variables (Environment Variables) ### Predefined variables (Environment Variables)
| Variable | Description | | Variable | Runner | Description |
|-------------------------|-------------| |-------------------------|-------------|
| **CI** | Mark that build is executed in CI environment | | **CI** | 0.4 | Mark that build is executed in CI environment |
| **GITLAB_CI** | Mark that build is executed in GitLab CI environment | | **GITLAB_CI** | all | Mark that build is executed in GitLab CI environment |
| **CI_SERVER** | Mark that build is executed in CI environment | | **CI_SERVER** | all | Mark that build is executed in CI environment |
| **CI_SERVER_NAME** | CI server that is used to coordinate builds | | **CI_SERVER_NAME** | all | CI server that is used to coordinate builds |
| **CI_SERVER_VERSION** | Not yet defined | | **CI_SERVER_VERSION** | all | Not yet defined |
| **CI_SERVER_REVISION** | Not yet defined | | **CI_SERVER_REVISION** | all | Not yet defined |
| **CI_BUILD_REF** | The commit revision for which project is built | | **CI_BUILD_REF** | all | The commit revision for which project is built |
| **CI_BUILD_BEFORE_SHA** | The first commit that were included in push request | | **CI_BUILD_TAG** | 0.5 | The commit tag name. Present only when building tags. |
| **CI_BUILD_REF_NAME** | The branch or tag name for which project is built | | **CI_BUILD_NAME** | 0.5 | The name of the build as defined in `.gitlab-ci.yml` |
| **CI_BUILD_ID** | The unique id of the current build that GitLab CI uses internally | | **CI_BUILD_STAGE** | 0.5 | The name of the stage as defined in `.gitlab-ci.yml` |
| **CI_BUILD_REPO** | The URL to clone the Git repository | | **CI_BUILD_BEFORE_SHA** | all | The first commit that were included in push request |
| **CI_PROJECT_ID** | The unique id of the current project that GitLab CI uses internally | | **CI_BUILD_REF_NAME** | all | The branch or tag name for which project is built |
| **CI_PROJECT_DIR** | The full path where the repository is cloned and where the build is ran | | **CI_BUILD_ID** | all | The unique id of the current build that GitLab CI uses internally |
| **CI_BUILD_REPO** | all | The URL to clone the Git repository |
| **CI_BUILD_TRIGGERED** | 0.5 | The flag to indicate that build was triggered |
| **CI_PROJECT_ID** | all | The unique id of the current project that GitLab CI uses internally |
| **CI_PROJECT_DIR** | all | The full path where the repository is cloned and where the build is ran |
**Some of the variables are only available when using runner with at least defined version.**
Example values: Example values:
...@@ -39,6 +45,10 @@ export CI_BUILD_ID="50" ...@@ -39,6 +45,10 @@ export CI_BUILD_ID="50"
export CI_BUILD_REF="1ecfd275763eff1d6b4844ea3168962458c9f27a" export CI_BUILD_REF="1ecfd275763eff1d6b4844ea3168962458c9f27a"
export CI_BUILD_REF_NAME="master" export CI_BUILD_REF_NAME="master"
export CI_BUILD_REPO="https://gitlab.com/gitlab-org/gitlab-ce.git" export CI_BUILD_REPO="https://gitlab.com/gitlab-org/gitlab-ce.git"
export CI_BUILD_TAG="1.0.0"
export CI_BUILD_NAME="spec:other"
export CI_BUILD_STAGE="test"
export CI_BUILD_TRIGGERED="true"
export CI_PROJECT_DIR="/builds/gitlab-org/gitlab-ce" export CI_PROJECT_DIR="/builds/gitlab-org/gitlab-ce"
export CI_PROJECT_ID="34" export CI_PROJECT_ID="34"
export CI_SERVER="yes" export CI_SERVER="yes"
......
...@@ -12,7 +12,7 @@ describe RunnersHelper do ...@@ -12,7 +12,7 @@ describe RunnersHelper do
end end
it "returns online text" do it "returns online text" do
runner = FactoryGirl.build(:ci_runner, contacted_at: 1.hour.ago, active: true) runner = FactoryGirl.build(:ci_runner, contacted_at: 1.second.ago, active: true)
expect(runner_status_icon(runner)).to include("Runner is online") expect(runner_status_icon(runner)).to include("Runner is online")
end end
end end
...@@ -200,13 +200,34 @@ describe Ci::Build do ...@@ -200,13 +200,34 @@ describe Ci::Build do
context 'returns variables' do context 'returns variables' do
subject { build.variables } subject { build.variables }
let(:variables) do let(:predefined_variables) do
[
{ key: :CI_BUILD_NAME, value: 'test', public: true },
{ key: :CI_BUILD_STAGE, value: 'stage', public: true },
]
end
let(:yaml_variables) do
[ [
{ key: :DB_NAME, value: 'postgres', public: true } { key: :DB_NAME, value: 'postgres', public: true }
] ]
end end
it { is_expected.to eq(variables) } before { build.update_attributes(stage: 'stage') }
it { is_expected.to eq(predefined_variables + yaml_variables) }
context 'for tag' do
let(:tag_variable) do
[
{ key: :CI_BUILD_TAG, value: 'master', public: true }
]
end
before { build.update_attributes(tag: true) }
it { is_expected.to eq(tag_variable + predefined_variables + yaml_variables) }
end
context 'and secure variables' do context 'and secure variables' do
let(:secure_variables) do let(:secure_variables) do
...@@ -219,7 +240,7 @@ describe Ci::Build do ...@@ -219,7 +240,7 @@ describe Ci::Build do
build.project.variables << Ci::Variable.new(key: 'SECRET_KEY', value: 'secret_value') build.project.variables << Ci::Variable.new(key: 'SECRET_KEY', value: 'secret_value')
end end
it { is_expected.to eq(variables + secure_variables) } it { is_expected.to eq(predefined_variables + yaml_variables + secure_variables) }
context 'and trigger variables' do context 'and trigger variables' do
let(:trigger) { FactoryGirl.create :ci_trigger, project: project } let(:trigger) { FactoryGirl.create :ci_trigger, project: project }
...@@ -229,12 +250,17 @@ describe Ci::Build do ...@@ -229,12 +250,17 @@ describe Ci::Build do
{ key: :TRIGGER_KEY, value: 'TRIGGER_VALUE', public: false } { key: :TRIGGER_KEY, value: 'TRIGGER_VALUE', public: false }
] ]
end end
let(:predefined_trigger_variable) do
[
{ key: :CI_BUILD_TRIGGERED, value: 'true', public: true }
]
end
before do before do
build.trigger_request = trigger_request build.trigger_request = trigger_request
end end
it { is_expected.to eq(variables + secure_variables + trigger_variables) } it { is_expected.to eq(predefined_variables + predefined_trigger_variable + yaml_variables + secure_variables + trigger_variables) }
end end
end end
end end
...@@ -273,4 +299,105 @@ describe Ci::Build do ...@@ -273,4 +299,105 @@ describe Ci::Build do
is_expected.to eq(['rec1', pusher_email]) is_expected.to eq(['rec1', pusher_email])
end end
end end
describe :can_be_served? do
let(:runner) { FactoryGirl.create :ci_specific_runner }
before { build.project.runners << runner }
context 'runner without tags' do
it 'can handle builds without tags' do
expect(build.can_be_served?(runner)).to be_truthy
end
it 'cannot handle build with tags' do
build.tag_list = ['aa']
expect(build.can_be_served?(runner)).to be_falsey
end
end
context 'runner with tags' do
before { runner.tag_list = ['bb', 'cc'] }
it 'can handle builds without tags' do
expect(build.can_be_served?(runner)).to be_truthy
end
it 'can handle build with matching tags' do
build.tag_list = ['bb']
expect(build.can_be_served?(runner)).to be_truthy
end
it 'cannot handle build with not matching tags' do
build.tag_list = ['aa']
expect(build.can_be_served?(runner)).to be_falsey
end
end
end
describe :any_runners_online? do
subject { build.any_runners_online? }
context 'when no runners' do
it { is_expected.to be_falsey }
end
context 'if there are runner' do
let(:runner) { FactoryGirl.create :ci_specific_runner }
before do
build.project.runners << runner
runner.update_attributes(contacted_at: 1.second.ago)
end
it { is_expected.to be_truthy }
it 'that is inactive' do
runner.update_attributes(active: false)
is_expected.to be_falsey
end
it 'that is not online' do
runner.update_attributes(contacted_at: nil)
is_expected.to be_falsey
end
it 'that cannot handle build' do
expect_any_instance_of(Ci::Build).to receive(:can_be_served?).and_return(false)
is_expected.to be_falsey
end
end
end
describe :show_warning? do
subject { build.show_warning? }
%w(pending).each do |state|
context "if commit_status.status is #{state}" do
before { build.status = state }
it { is_expected.to be_truthy }
context "and there are specific runner" do
let(:runner) { FactoryGirl.create :ci_specific_runner, contacted_at: 1.second.ago }
before do
build.project.runners << runner
runner.save
end
it { is_expected.to be_falsey }
end
end
end
%w(success failed canceled running).each do |state|
context "if commit_status.status is #{state}" do
before { build.status = state }
it { is_expected.to be_falsey }
end
end
end
end end
...@@ -259,5 +259,18 @@ describe Ci::Project do ...@@ -259,5 +259,18 @@ describe Ci::Project do
FactoryGirl.create(:ci_shared_runner) FactoryGirl.create(:ci_shared_runner)
expect(project.any_runners?).to be_falsey expect(project.any_runners?).to be_falsey
end end
it "checks the presence of specific runner" do
project = FactoryGirl.create(:ci_project)
specific_runner = FactoryGirl.create(:ci_specific_runner)
project.runners << specific_runner
expect(project.any_runners? { |runner| runner == specific_runner }).to be_truthy
end
it "checks the presence of shared runner" do
project = FactoryGirl.create(:ci_project, shared_runners_enabled: true)
shared_runner = FactoryGirl.create(:ci_shared_runner)
expect(project.any_runners? { |runner| runner == shared_runner }).to be_truthy
end
end end
end end
...@@ -48,6 +48,71 @@ describe Ci::Runner do ...@@ -48,6 +48,71 @@ describe Ci::Runner do
it { expect(shared_runner.only_for?(project)).to be_truthy } it { expect(shared_runner.only_for?(project)).to be_truthy }
end end
describe :online do
subject { Ci::Runner.online }
before do
@runner1 = FactoryGirl.create(:ci_shared_runner, contacted_at: 1.year.ago)
@runner2 = FactoryGirl.create(:ci_shared_runner, contacted_at: 1.second.ago)
end
it { is_expected.to eq([@runner2])}
end
describe :online? do
let(:runner) { FactoryGirl.create(:ci_shared_runner) }
subject { runner.online? }
context 'never contacted' do
before { runner.contacted_at = nil }
it { is_expected.to be_falsey }
end
context 'contacted long time ago time' do
before { runner.contacted_at = 1.year.ago }
it { is_expected.to be_falsey }
end
context 'contacted 1s ago' do
before { runner.contacted_at = 1.second.ago }
it { is_expected.to be_truthy }
end
end
describe :status do
let(:runner) { FactoryGirl.create(:ci_shared_runner, contacted_at: 1.second.ago) }
subject { runner.status }
context 'never connected' do
before { runner.contacted_at = nil }
it { is_expected.to eq(:not_connected) }
end
context 'contacted 1s ago' do
before { runner.contacted_at = 1.second.ago }
it { is_expected.to eq(:online) }
end
context 'contacted long time ago' do
before { runner.contacted_at = 1.year.ago }
it { is_expected.to eq(:offline) }
end
context 'inactive' do
before { runner.active = false }
it { is_expected.to eq(:paused) }
end
end
describe "belongs_to_one_project?" do describe "belongs_to_one_project?" do
it "returns false if there are two projects runner assigned to" do it "returns false if there are two projects runner assigned to" do
runner = FactoryGirl.create(:ci_specific_runner) runner = FactoryGirl.create(:ci_specific_runner)
......
...@@ -89,9 +89,9 @@ eos ...@@ -89,9 +89,9 @@ eos
end end
it_behaves_like 'a mentionable' do it_behaves_like 'a mentionable' do
subject { commit } subject { create(:project).commit }
let(:author) { create(:user, email: commit.author_email) } let(:author) { create(:user, email: subject.author_email) }
let(:backref_text) { "commit #{subject.id}" } let(:backref_text) { "commit #{subject.id}" }
let(:set_mentionable_text) do let(:set_mentionable_text) do
->(txt) { allow(subject).to receive(:safe_message).and_return(txt) } ->(txt) { allow(subject).to receive(:safe_message).and_return(txt) }
......
...@@ -25,7 +25,7 @@ describe Issue, "Mentionable" do ...@@ -25,7 +25,7 @@ describe Issue, "Mentionable" do
it 'correctly removes already-mentioned Commits' do it 'correctly removes already-mentioned Commits' do
expect(SystemNoteService).not_to receive(:cross_reference) expect(SystemNoteService).not_to receive(:cross_reference)
issue.create_cross_references!(project, author, [commit2]) issue.create_cross_references!(author, [commit2])
end end
end end
......
...@@ -69,7 +69,7 @@ describe Issue do ...@@ -69,7 +69,7 @@ describe Issue do
end end
it_behaves_like 'an editable mentionable' do it_behaves_like 'an editable mentionable' do
subject { create(:issue, project: project) } subject { create(:issue) }
let(:backref_text) { "issue #{subject.to_reference}" } let(:backref_text) { "issue #{subject.to_reference}" }
let(:set_mentionable_text) { ->(txt){ subject.description = txt } } let(:set_mentionable_text) { ->(txt){ subject.description = txt } }
......
...@@ -192,10 +192,9 @@ describe Note do ...@@ -192,10 +192,9 @@ describe Note do
end end
it_behaves_like 'an editable mentionable' do it_behaves_like 'an editable mentionable' do
subject { create :note, noteable: issue, project: project } subject { create :note, noteable: issue, project: issue.project }
let(:project) { create(:project) } let(:issue) { create :issue }
let(:issue) { create :issue, project: project }
let(:backref_text) { issue.gfm_reference } let(:backref_text) { issue.gfm_reference }
let(:set_mentionable_text) { ->(txt) { subject.note = txt } } let(:set_mentionable_text) { ->(txt) { subject.note = txt } }
end end
......
...@@ -76,6 +76,8 @@ describe Ci::API::API do ...@@ -76,6 +76,8 @@ describe Ci::API::API do
expect(response.status).to eq(201) expect(response.status).to eq(201)
expect(json_response["variables"]).to eq([ expect(json_response["variables"]).to eq([
{ "key" => "CI_BUILD_NAME", "value" => "spinach", "public" => true },
{ "key" => "CI_BUILD_STAGE", "value" => "test", "public" => true },
{ "key" => "DB_NAME", "value" => "postgres", "public" => true }, { "key" => "DB_NAME", "value" => "postgres", "public" => true },
{ "key" => "SECRET_KEY", "value" => "secret_value", "public" => false }, { "key" => "SECRET_KEY", "value" => "secret_value", "public" => false },
]) ])
...@@ -93,6 +95,9 @@ describe Ci::API::API do ...@@ -93,6 +95,9 @@ describe Ci::API::API do
expect(response.status).to eq(201) expect(response.status).to eq(201)
expect(json_response["variables"]).to eq([ expect(json_response["variables"]).to eq([
{ "key" => "CI_BUILD_NAME", "value" => "spinach", "public" => true },
{ "key" => "CI_BUILD_STAGE", "value" => "test", "public" => true },
{ "key" => "CI_BUILD_TRIGGERED", "value" => "true", "public" => true },
{ "key" => "DB_NAME", "value" => "postgres", "public" => true }, { "key" => "DB_NAME", "value" => "postgres", "public" => true },
{ "key" => "SECRET_KEY", "value" => "secret_value", "public" => false }, { "key" => "SECRET_KEY", "value" => "secret_value", "public" => false },
{ "key" => "TRIGGER_KEY", "value" => "TRIGGER_VALUE", "public" => false }, { "key" => "TRIGGER_KEY", "value" => "TRIGGER_VALUE", "public" => false },
......
...@@ -155,7 +155,7 @@ describe GitPushService do ...@@ -155,7 +155,7 @@ describe GitPushService do
before do before do
allow(commit).to receive_messages( allow(commit).to receive_messages(
safe_message: "this commit \n mentions ##{issue.id}", safe_message: "this commit \n mentions #{issue.to_reference}",
references: [issue], references: [issue],
author_name: commit_author.name, author_name: commit_author.name,
author_email: commit_author.email author_email: commit_author.email
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
# - let(:set_mentionable_text) { lambda { |txt| "block that assigns txt to the subject's mentionable_text" } } # - let(:set_mentionable_text) { lambda { |txt| "block that assigns txt to the subject's mentionable_text" } }
def common_mentionable_setup def common_mentionable_setup
let(:project) { create :project } let(:project) { subject.project }
let(:author) { subject.author } let(:author) { subject.author }
let(:mentioned_issue) { create(:issue, project: project) } let(:mentioned_issue) { create(:issue, project: project) }
...@@ -67,7 +67,7 @@ shared_examples 'a mentionable' do ...@@ -67,7 +67,7 @@ shared_examples 'a mentionable' do
it "extracts references from its reference property" do it "extracts references from its reference property" do
# De-duplicate and omit itself # De-duplicate and omit itself
refs = subject.references(project) refs = subject.referenced_mentionables
expect(refs.size).to eq(6) expect(refs.size).to eq(6)
expect(refs).to include(mentioned_issue) expect(refs).to include(mentioned_issue)
expect(refs).to include(mentioned_mr) expect(refs).to include(mentioned_mr)
...@@ -86,14 +86,7 @@ shared_examples 'a mentionable' do ...@@ -86,14 +86,7 @@ shared_examples 'a mentionable' do
with(referenced, subject.local_reference, author) with(referenced, subject.local_reference, author)
end end
subject.create_cross_references!(project, author) subject.create_cross_references!
end
it 'detects existing cross-references' do
SystemNoteService.cross_reference(mentioned_issue, subject.local_reference, author)
expect(subject).to have_mentioned(mentioned_issue)
expect(subject).not_to have_mentioned(mentioned_mr)
end end
end end
...@@ -145,6 +138,6 @@ shared_examples 'an editable mentionable' do ...@@ -145,6 +138,6 @@ shared_examples 'an editable mentionable' do
end end
set_mentionable_text.call(new_text) set_mentionable_text.call(new_text)
subject.create_new_cross_references!(project, author) subject.create_new_cross_references!(author)
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