Merge branch 'master' into rs-redactor-filter
This commit is contained in:
commit
ef44138ce4
|
@ -18,6 +18,7 @@ v 8.1.0 (unreleased)
|
|||
- Add first and last to pagination (Zeger-Jan van de Weg)
|
||||
- Added Commit Status API
|
||||
- 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
|
||||
- Remove "Continuous Integration" page from dashboard
|
||||
- Add notes and SSL verification entries to hook APIs (Ben Boeckel)
|
||||
|
@ -27,6 +28,7 @@ v 8.1.0 (unreleased)
|
|||
- Move CI triggers page to 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
|
||||
- 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
|
||||
- Improve error message when merging fails
|
||||
- Add support of multibyte characters in LDAP UID (Roman Petrov)
|
||||
|
|
|
@ -6,7 +6,7 @@ module Ci
|
|||
@runners = Ci::Runner.order('id DESC')
|
||||
@runners = @runners.search(params[:search]) if params[:search].present?
|
||||
@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
|
||||
|
||||
def show
|
||||
|
@ -66,7 +66,7 @@ module Ci
|
|||
end
|
||||
|
||||
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
|
||||
|
|
|
@ -55,7 +55,7 @@ class Projects::IssuesController < Projects::ApplicationController
|
|||
end
|
||||
|
||||
def show
|
||||
@participants = @issue.participants(current_user, @project)
|
||||
@participants = @issue.participants(current_user)
|
||||
@note = @project.notes.new(noteable: @issue)
|
||||
@notes = @issue.notes.inc_author.fresh
|
||||
@noteable = @issue
|
||||
|
|
|
@ -246,7 +246,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
|||
end
|
||||
|
||||
def define_show_vars
|
||||
@participants = @merge_request.participants(current_user, @project)
|
||||
@participants = @merge_request.participants(current_user)
|
||||
|
||||
# Build a note object for comment form
|
||||
@note = @project.notes.new(noteable: @merge_request)
|
||||
|
|
|
@ -60,6 +60,6 @@ class Projects::RunnersController < Projects::ApplicationController
|
|||
end
|
||||
|
||||
def runner_params
|
||||
params.require(:runner).permit(:description, :tag_list, :contacted_at, :active)
|
||||
params.require(:runner).permit(:description, :tag_list, :active)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,20 +1,16 @@
|
|||
module RunnersHelper
|
||||
def runner_status_icon(runner)
|
||||
unless runner.contacted_at
|
||||
return content_tag :i, nil,
|
||||
class: "fa fa-warning-sign",
|
||||
title: "New runner. Has not connected yet"
|
||||
status = runner.status
|
||||
case status
|
||||
when :not_connected
|
||||
content_tag :i, nil,
|
||||
class: "fa fa-warning",
|
||||
title: "New runner. Has not connected yet"
|
||||
|
||||
when :online, :offline, :paused
|
||||
content_tag :i, nil,
|
||||
class: "fa fa-circle runner-status-#{status}",
|
||||
title: "Runner is #{status}, last contact was #{time_ago_in_words(runner.contacted_at)} ago"
|
||||
end
|
||||
|
||||
status =
|
||||
if runner.active?
|
||||
runner.contacted_at > 3.hour.ago ? :online : :offline
|
||||
else
|
||||
:paused
|
||||
end
|
||||
|
||||
content_tag :i, nil,
|
||||
class: "fa fa-circle runner-status-#{status}",
|
||||
title: "Runner is #{status}, last contact was #{time_ago_in_words(runner.contacted_at)} ago"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -119,7 +119,7 @@ module Ci
|
|||
end
|
||||
|
||||
def variables
|
||||
yaml_variables + project_variables + trigger_variables
|
||||
predefined_variables + yaml_variables + project_variables + trigger_variables
|
||||
end
|
||||
|
||||
def project
|
||||
|
@ -231,6 +231,18 @@ module Ci
|
|||
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
|
||||
|
||||
def yaml_variables
|
||||
|
@ -258,5 +270,14 @@ module Ci
|
|||
[]
|
||||
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
|
||||
|
|
|
@ -115,12 +115,12 @@ module Ci
|
|||
web_url
|
||||
end
|
||||
|
||||
def any_runners?
|
||||
if runners.active.any?
|
||||
def any_runners?(&block)
|
||||
if runners.active.any?(&block)
|
||||
return true
|
||||
end
|
||||
|
||||
shared_runners_enabled && Ci::Runner.shared.active.any?
|
||||
shared_runners_enabled && Ci::Runner.shared.active.any?(&block)
|
||||
end
|
||||
|
||||
def set_default_values
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
module Ci
|
||||
class Runner < ActiveRecord::Base
|
||||
extend Ci::Model
|
||||
|
||||
LAST_CONTACT_TIME = 5.minutes.ago
|
||||
|
||||
has_many :builds, class_name: 'Ci::Build'
|
||||
has_many :runner_projects, dependent: :destroy, class_name: 'Ci::RunnerProject'
|
||||
|
@ -33,6 +35,7 @@ module Ci
|
|||
scope :shared, ->() { where(is_shared: true) }
|
||||
scope :active, ->() { where(active: true) }
|
||||
scope :paused, ->() { where(active: false) }
|
||||
scope :online, ->() { where('contacted_at > ?', LAST_CONTACT_TIME) }
|
||||
|
||||
acts_as_taggable
|
||||
|
||||
|
@ -65,6 +68,20 @@ module Ci
|
|||
is_shared
|
||||
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?
|
||||
runner_projects.count == 1
|
||||
end
|
||||
|
|
|
@ -88,4 +88,8 @@ class CommitStatus < ActiveRecord::Base
|
|||
def retry_url
|
||||
nil
|
||||
end
|
||||
|
||||
def show_warning?
|
||||
false
|
||||
end
|
||||
end
|
||||
|
|
|
@ -43,67 +43,57 @@ module Mentionable
|
|||
self
|
||||
end
|
||||
|
||||
# Determine whether or not a cross-reference Note has already been created between this Mentionable and
|
||||
# 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.
|
||||
def all_references(current_user = self.author, text = nil, load_lazy_references: true)
|
||||
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
|
||||
ext.analyze(text)
|
||||
else
|
||||
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)
|
||||
options[:cache_key] = [self, attr] if options.delete(:cache)
|
||||
ext.analyze(text, options)
|
||||
end
|
||||
end
|
||||
|
||||
(ext.issues + ext.merge_requests + ext.commits) - [local_reference]
|
||||
ext
|
||||
end
|
||||
|
||||
# Create a cross-reference Note for each GFM reference to another Mentionable found in +mentionable_text+.
|
||||
def create_cross_references!(p = project, a = author, without = [])
|
||||
refs = references(p)
|
||||
def mentioned_users(current_user = nil, load_lazy_references: true)
|
||||
all_references(current_user).users
|
||||
end
|
||||
|
||||
# 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
|
||||
|
||||
# Create a cross-reference Note for each GFM reference to another Mentionable found in the +mentionable_attrs+.
|
||||
def create_cross_references!(author = self.author, without = [], text = nil)
|
||||
refs = referenced_mentionables(author, text)
|
||||
|
||||
# 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
|
||||
# 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|
|
||||
SystemNoteService.cross_reference(ref, local_reference, a)
|
||||
SystemNoteService.cross_reference(ref, local_reference, author)
|
||||
end
|
||||
end
|
||||
|
||||
# When a mentionable field is changed, creates cross-reference notes that
|
||||
# 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
|
||||
|
||||
return if changes.empty?
|
||||
|
||||
original_text = changes.collect { |_, vals| vals.first }.join(' ')
|
||||
|
||||
preexisting = references(p, self.author, original_text)
|
||||
create_cross_references!(p, a, preexisting)
|
||||
preexisting = referenced_mentionables(author, original_text)
|
||||
create_cross_references!(author, preexisting)
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -125,4 +115,10 @@ module Mentionable
|
|||
# Only include changed fields that are mentionable
|
||||
source.select { |key, val| mentionable.include?(key) }
|
||||
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
|
||||
|
|
|
@ -37,7 +37,8 @@ module Participable
|
|||
|
||||
# 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
|
||||
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|
|
||||
value =
|
||||
if attr.respond_to?(:call)
|
||||
|
@ -46,16 +47,14 @@ module Participable
|
|||
send(attr)
|
||||
end
|
||||
|
||||
participants_for(value, current_user, project)
|
||||
participants_for(value, current_user)
|
||||
end.compact.uniq
|
||||
|
||||
if load_lazy_references
|
||||
participants = Gitlab::Markdown::ReferenceFilter::LazyReference.load(participants).uniq
|
||||
|
||||
if project
|
||||
participants.select! do |user|
|
||||
user.can?(:read_project, project)
|
||||
end
|
||||
participants.select! do |user|
|
||||
user.can?(:read_project, project)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -64,14 +63,14 @@ module Participable
|
|||
|
||||
private
|
||||
|
||||
def participants_for(value, current_user = nil, project = nil)
|
||||
def participants_for(value, current_user = nil)
|
||||
case value
|
||||
when User, Gitlab::Markdown::ReferenceFilter::LazyReference
|
||||
[value]
|
||||
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
|
||||
value.participants(current_user, project, load_lazy_references: false)
|
||||
value.participants(current_user, load_lazy_references: false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -62,7 +62,6 @@ class Note < ActiveRecord::Base
|
|||
|
||||
serialize :st_diff
|
||||
before_create :set_diff, if: ->(n) { n.line_code.present? }
|
||||
after_update :set_references
|
||||
|
||||
class << self
|
||||
def discussions_from_notes(notes)
|
||||
|
@ -333,15 +332,13 @@ class Note < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def noteable_type_name
|
||||
if noteable_type.present?
|
||||
noteable_type.downcase
|
||||
end
|
||||
noteable_type.downcase if noteable_type.present?
|
||||
end
|
||||
|
||||
# FIXME: Hack for polymorphic associations with STI
|
||||
# For more information visit http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#label-Polymorphic+Associations
|
||||
def noteable_type=(sType)
|
||||
super(sType.to_s.classify.constantize.base_class.to_s)
|
||||
def noteable_type=(noteable_type)
|
||||
super(noteable_type.to_s.classify.constantize.base_class.to_s)
|
||||
end
|
||||
|
||||
# Reset notes events cache
|
||||
|
@ -357,10 +354,6 @@ class Note < ActiveRecord::Base
|
|||
Event.reset_event_cache_for(self)
|
||||
end
|
||||
|
||||
def set_references
|
||||
create_new_cross_references!(project, author)
|
||||
end
|
||||
|
||||
def system?
|
||||
read_attribute(:system)
|
||||
end
|
||||
|
|
|
@ -17,7 +17,7 @@ module Ci
|
|||
builds = builds.order('created_at ASC')
|
||||
|
||||
build = builds.find do |build|
|
||||
(build.tag_list - current_runner.tag_list).empty?
|
||||
build.can_be_served?(current_runner)
|
||||
end
|
||||
|
||||
|
||||
|
|
|
@ -74,48 +74,30 @@ class GitPushService
|
|||
def process_commit_messages(ref)
|
||||
is_default_branch = is_default_branch?(ref)
|
||||
|
||||
authors = Hash.new do |hash, commit|
|
||||
email = commit.author_email
|
||||
return hash[email] if hash.has_key?(email)
|
||||
|
||||
hash[email] = commit_user(commit)
|
||||
end
|
||||
|
||||
@push_commits.each do |commit|
|
||||
# Close issues if these commits were pushed to the project's default branch and the commit message matches the
|
||||
# closing regex. Exclude any mentioned Issues from cross-referencing even if the commits are being pushed to
|
||||
# a different branch.
|
||||
issues_to_close = commit.closes_issues(user)
|
||||
|
||||
# Load commit author only if needed.
|
||||
# For push with 1k commits it prevents 900+ requests in database
|
||||
author = nil
|
||||
|
||||
# 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)
|
||||
# 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)
|
||||
if is_default_branch
|
||||
# Close issues if these commits were pushed to the project's default branch and the commit message matches the
|
||||
# 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
|
||||
|
||||
if project.default_issues_tracker?
|
||||
create_cross_reference_notes(commit, actually_closed_issues)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def create_cross_reference_notes(commit, issues_to_close)
|
||||
# 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
|
||||
commit.create_cross_references!(authors[commit], closed_issues)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ module Issues
|
|||
issue.update_attributes(label_ids: label_params)
|
||||
notification_service.new_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')
|
||||
end
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ module Issues
|
|||
create_title_change_note(issue, issue.previous_changes['title'].first)
|
||||
end
|
||||
|
||||
issue.create_new_cross_references!(issue.project, current_user)
|
||||
issue.create_new_cross_references!
|
||||
execute_hooks(issue, 'update')
|
||||
end
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ module MergeRequests
|
|||
merge_request.update_attributes(label_ids: label_params)
|
||||
event_service.open_mr(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)
|
||||
end
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@ module MergeRequests
|
|||
merge_request.mark_as_unchecked
|
||||
end
|
||||
|
||||
merge_request.create_new_cross_references!(merge_request.project, current_user)
|
||||
merge_request.create_new_cross_references!
|
||||
execute_hooks(merge_request, 'update')
|
||||
end
|
||||
|
||||
|
|
|
@ -11,13 +11,7 @@ module Notes
|
|||
# Skip system notes, like status changes and cross-references.
|
||||
unless note.system
|
||||
event_service.leave_note(note, note.author)
|
||||
|
||||
# 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
|
||||
|
||||
note.create_cross_references!
|
||||
execute_hooks(note)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,7 +4,7 @@ module Notes
|
|||
return note unless note.editable?
|
||||
|
||||
note.update_attributes(params.merge(updated_by: current_user))
|
||||
|
||||
note.create_new_cross_references!
|
||||
note.reset_events_cache
|
||||
|
||||
note
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
%a
|
||||
Build ##{@build.id}
|
||||
·
|
||||
%i.fa.fa-warning-sign
|
||||
%i.fa.fa-warning
|
||||
This build was retried.
|
||||
|
||||
.gray-content-block.second-block
|
||||
|
@ -39,6 +39,27 @@
|
|||
.pull-right
|
||||
= @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
|
||||
.col-md-9
|
||||
.clearfix
|
||||
|
|
|
@ -9,6 +9,9 @@
|
|||
- else
|
||||
%strong Build ##{commit_status.id}
|
||||
|
||||
- if commit_status.show_warning?
|
||||
%i.fa.fa-warning.text-warning
|
||||
|
||||
%td
|
||||
= commit_status.ref
|
||||
|
||||
|
|
|
@ -15,21 +15,27 @@ The API_TOKEN will take the Secure Variable value: `SECURE`.
|
|||
|
||||
### Predefined variables (Environment Variables)
|
||||
|
||||
| Variable | Description |
|
||||
| Variable | Runner | Description |
|
||||
|-------------------------|-------------|
|
||||
| **CI** | Mark that build is executed in CI environment |
|
||||
| **GITLAB_CI** | Mark that build is executed in GitLab CI environment |
|
||||
| **CI_SERVER** | Mark that build is executed in CI environment |
|
||||
| **CI_SERVER_NAME** | CI server that is used to coordinate builds |
|
||||
| **CI_SERVER_VERSION** | Not yet defined |
|
||||
| **CI_SERVER_REVISION** | Not yet defined |
|
||||
| **CI_BUILD_REF** | The commit revision for which project is built |
|
||||
| **CI_BUILD_BEFORE_SHA** | The first commit that were included in push request |
|
||||
| **CI_BUILD_REF_NAME** | The branch or tag name for which project is built |
|
||||
| **CI_BUILD_ID** | The unique id of the current build that GitLab CI uses internally |
|
||||
| **CI_BUILD_REPO** | The URL to clone the Git repository |
|
||||
| **CI_PROJECT_ID** | The unique id of the current project that GitLab CI uses internally |
|
||||
| **CI_PROJECT_DIR** | The full path where the repository is cloned and where the build is ran |
|
||||
| **CI** | 0.4 | Mark that build is executed in CI environment |
|
||||
| **GITLAB_CI** | all | Mark that build is executed in GitLab CI environment |
|
||||
| **CI_SERVER** | all | Mark that build is executed in CI environment |
|
||||
| **CI_SERVER_NAME** | all | CI server that is used to coordinate builds |
|
||||
| **CI_SERVER_VERSION** | all | Not yet defined |
|
||||
| **CI_SERVER_REVISION** | all | Not yet defined |
|
||||
| **CI_BUILD_REF** | all | The commit revision for which project is built |
|
||||
| **CI_BUILD_TAG** | 0.5 | The commit tag name. Present only when building tags. |
|
||||
| **CI_BUILD_NAME** | 0.5 | The name of the build as defined in `.gitlab-ci.yml` |
|
||||
| **CI_BUILD_STAGE** | 0.5 | The name of the stage as defined in `.gitlab-ci.yml` |
|
||||
| **CI_BUILD_BEFORE_SHA** | all | The first commit that were included in push request |
|
||||
| **CI_BUILD_REF_NAME** | all | The branch or tag name for which project is built |
|
||||
| **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:
|
||||
|
||||
|
@ -39,6 +45,10 @@ export CI_BUILD_ID="50"
|
|||
export CI_BUILD_REF="1ecfd275763eff1d6b4844ea3168962458c9f27a"
|
||||
export CI_BUILD_REF_NAME="master"
|
||||
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_ID="34"
|
||||
export CI_SERVER="yes"
|
||||
|
|
|
@ -12,7 +12,7 @@ describe RunnersHelper do
|
|||
end
|
||||
|
||||
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")
|
||||
end
|
||||
end
|
||||
|
|
|
@ -200,13 +200,34 @@ describe Ci::Build do
|
|||
context 'returns variables' do
|
||||
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 }
|
||||
]
|
||||
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
|
||||
let(:secure_variables) do
|
||||
|
@ -219,7 +240,7 @@ describe Ci::Build do
|
|||
build.project.variables << Ci::Variable.new(key: 'SECRET_KEY', value: 'secret_value')
|
||||
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
|
||||
let(:trigger) { FactoryGirl.create :ci_trigger, project: project }
|
||||
|
@ -229,12 +250,17 @@ describe Ci::Build do
|
|||
{ key: :TRIGGER_KEY, value: 'TRIGGER_VALUE', public: false }
|
||||
]
|
||||
end
|
||||
let(:predefined_trigger_variable) do
|
||||
[
|
||||
{ key: :CI_BUILD_TRIGGERED, value: 'true', public: true }
|
||||
]
|
||||
end
|
||||
|
||||
before do
|
||||
build.trigger_request = trigger_request
|
||||
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
|
||||
|
@ -273,4 +299,105 @@ describe Ci::Build do
|
|||
is_expected.to eq(['rec1', pusher_email])
|
||||
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
|
||||
|
|
|
@ -259,5 +259,18 @@ describe Ci::Project do
|
|||
FactoryGirl.create(:ci_shared_runner)
|
||||
expect(project.any_runners?).to be_falsey
|
||||
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
|
||||
|
|
|
@ -48,6 +48,71 @@ describe Ci::Runner do
|
|||
it { expect(shared_runner.only_for?(project)).to be_truthy }
|
||||
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
|
||||
it "returns false if there are two projects runner assigned to" do
|
||||
runner = FactoryGirl.create(:ci_specific_runner)
|
||||
|
|
|
@ -89,9 +89,9 @@ eos
|
|||
end
|
||||
|
||||
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(:set_mentionable_text) do
|
||||
->(txt) { allow(subject).to receive(:safe_message).and_return(txt) }
|
||||
|
|
|
@ -25,7 +25,7 @@ describe Issue, "Mentionable" do
|
|||
it 'correctly removes already-mentioned Commits' do
|
||||
expect(SystemNoteService).not_to receive(:cross_reference)
|
||||
|
||||
issue.create_cross_references!(project, author, [commit2])
|
||||
issue.create_cross_references!(author, [commit2])
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -69,7 +69,7 @@ describe Issue do
|
|||
end
|
||||
|
||||
it_behaves_like 'an editable mentionable' do
|
||||
subject { create(:issue, project: project) }
|
||||
subject { create(:issue) }
|
||||
|
||||
let(:backref_text) { "issue #{subject.to_reference}" }
|
||||
let(:set_mentionable_text) { ->(txt){ subject.description = txt } }
|
||||
|
|
|
@ -192,10 +192,9 @@ describe Note do
|
|||
end
|
||||
|
||||
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, project: project }
|
||||
let(:issue) { create :issue }
|
||||
let(:backref_text) { issue.gfm_reference }
|
||||
let(:set_mentionable_text) { ->(txt) { subject.note = txt } }
|
||||
end
|
||||
|
|
|
@ -76,6 +76,8 @@ describe Ci::API::API do
|
|||
|
||||
expect(response.status).to eq(201)
|
||||
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" => "SECRET_KEY", "value" => "secret_value", "public" => false },
|
||||
])
|
||||
|
@ -93,6 +95,9 @@ describe Ci::API::API do
|
|||
|
||||
expect(response.status).to eq(201)
|
||||
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" => "SECRET_KEY", "value" => "secret_value", "public" => false },
|
||||
{ "key" => "TRIGGER_KEY", "value" => "TRIGGER_VALUE", "public" => false },
|
||||
|
|
|
@ -155,7 +155,7 @@ describe GitPushService do
|
|||
|
||||
before do
|
||||
allow(commit).to receive_messages(
|
||||
safe_message: "this commit \n mentions ##{issue.id}",
|
||||
safe_message: "this commit \n mentions #{issue.to_reference}",
|
||||
references: [issue],
|
||||
author_name: commit_author.name,
|
||||
author_email: commit_author.email
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
# - let(:set_mentionable_text) { lambda { |txt| "block that assigns txt to the subject's mentionable_text" } }
|
||||
|
||||
def common_mentionable_setup
|
||||
let(:project) { create :project }
|
||||
let(:project) { subject.project }
|
||||
let(:author) { subject.author }
|
||||
|
||||
let(:mentioned_issue) { create(:issue, project: project) }
|
||||
|
@ -67,7 +67,7 @@ shared_examples 'a mentionable' do
|
|||
|
||||
it "extracts references from its reference property" do
|
||||
# De-duplicate and omit itself
|
||||
refs = subject.references(project)
|
||||
refs = subject.referenced_mentionables
|
||||
expect(refs.size).to eq(6)
|
||||
expect(refs).to include(mentioned_issue)
|
||||
expect(refs).to include(mentioned_mr)
|
||||
|
@ -86,14 +86,7 @@ shared_examples 'a mentionable' do
|
|||
with(referenced, subject.local_reference, author)
|
||||
end
|
||||
|
||||
subject.create_cross_references!(project, author)
|
||||
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)
|
||||
subject.create_cross_references!
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -145,6 +138,6 @@ shared_examples 'an editable mentionable' do
|
|||
end
|
||||
|
||||
set_mentionable_text.call(new_text)
|
||||
subject.create_new_cross_references!(project, author)
|
||||
subject.create_new_cross_references!(author)
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue