Merge remote-tracking branch 'origin/master' into issue_14904
This commit is contained in:
commit
3b39214f29
|
@ -4,6 +4,7 @@ v 8.8.0 (unreleased)
|
|||
|
||||
v 8.7.0 (unreleased)
|
||||
- The number of InfluxDB points stored per UDP packet can now be configured
|
||||
- Fix error when cross-project label reference used with non-existent project
|
||||
- Transactions for /internal/allowed now have an "action" tag set
|
||||
- Method instrumentation now uses Module#prepend instead of aliasing methods
|
||||
- Repository.clean_old_archives is now instrumented
|
||||
|
@ -50,6 +51,7 @@ v 8.7.0 (unreleased)
|
|||
- Add endpoints to archive or unarchive a project !3372
|
||||
- Fix a bug whith trailing slash in bamboo_url
|
||||
- Add links to CI setup documentation from project settings and builds pages
|
||||
- Display project members page to all members
|
||||
- Handle nil descriptions in Slack issue messages (Stan Hu)
|
||||
- Add automated repository integrity checks
|
||||
- API: Expose open_issues_count, closed_issues_count, open_merge_requests_count for labels (Robert Schilling)
|
||||
|
@ -68,6 +70,7 @@ v 8.7.0 (unreleased)
|
|||
- Hide `Create a group` help block when creating a new project in a group
|
||||
- Implement 'TODOs View' as an option for dashboard preferences !3379 (Elias W.)
|
||||
- Allow issues and merge requests to be assigned to the author !2765
|
||||
- Make Ci::Commit to group only similar builds and make it stateful (ref, tag)
|
||||
- Gracefully handle notes on deleted commits in merge requests (Stan Hu)
|
||||
- Decouple membership and notifications
|
||||
- Fix creation of merge requests for orphaned branches (Stan Hu)
|
||||
|
@ -87,10 +90,10 @@ v 8.7.0 (unreleased)
|
|||
- Fix repository cache invalidation issue when project is recreated with an empty repo (Stan Hu)
|
||||
- Fix: Allow empty recipients list for builds emails service when pushed is added (Frank Groeneveld)
|
||||
- Improved markdown forms
|
||||
- Show JavaScript errors in sentry
|
||||
- Diff design updates (colors, button styles, etc)
|
||||
- Copying and pasting a diff no longer pastes the line numbers or +/-
|
||||
- Add null check to formData when updating profile content to fix Firefox bug
|
||||
- Disable spellcheck and autocorrect for username field in admin page
|
||||
- Delete tags using Rugged for performance reasons (Robert Schilling)
|
||||
- Add Slack notifications when Wiki is edited (Sebastian Klier)
|
||||
- Diffs load at the correct point when linking from from number
|
||||
|
|
19
PROCESS.md
19
PROCESS.md
|
@ -105,6 +105,25 @@ sensitive as to how you word things. Use Emoji to express your feelings (heart,
|
|||
star, smile, etc.). Some good tips about giving feedback to merge requests is in
|
||||
the [Thoughtbot code review guide].
|
||||
|
||||
## Feature Freeze
|
||||
|
||||
5 working days before the 22nd the stable branches for the upcoming release will
|
||||
be frozen for major changes. Merge requests may still be merged into master
|
||||
during this period. By freezing the stable branches prior to a release there's
|
||||
no need to worry about last minute merge requests potentially breaking a lot of
|
||||
things.
|
||||
|
||||
What is considered to be a major change is determined on a case by case basis as
|
||||
this definition depends very much on the context of changes. For example, a 5
|
||||
line change might have a big impact on the entire application. Ultimately the
|
||||
decision will be made by those reviewing a merge request and the release
|
||||
manager.
|
||||
|
||||
During the feature freeze all merge requests that are meant to go into the next
|
||||
release should have the correct milestone assigned _and_ have the label
|
||||
~"Pick into Stable" set. Merge requests without a milestone and this label will
|
||||
not be merged into any stable branches.
|
||||
|
||||
## Copy & paste responses
|
||||
|
||||
### Improperly formatted issue
|
||||
|
|
|
@ -55,7 +55,6 @@
|
|||
#= require_tree .
|
||||
#= require fuzzaldrin-plus
|
||||
#= require cropper
|
||||
#= require raven
|
||||
|
||||
window.slugify = (text) ->
|
||||
text.replace(/[^-a-zA-Z0-9]+/g, '_').toLowerCase()
|
||||
|
|
|
@ -87,8 +87,8 @@ class @MergeRequestTabs
|
|||
if window.location.hash
|
||||
navBarHeight = $('.navbar-gitlab').outerHeight()
|
||||
|
||||
$el = $("#{container} #{window.location.hash}")
|
||||
$.scrollTo("#{container} #{window.location.hash}", offset: -navBarHeight) if $el.length
|
||||
$el = $("#{container} #{window.location.hash}:not(.match)")
|
||||
$.scrollTo("#{container} #{window.location.hash}:not(.match)", offset: -navBarHeight) if $el.length
|
||||
|
||||
# Activate a tab based on the current action
|
||||
activateTab: (action) ->
|
||||
|
@ -176,12 +176,12 @@ class @MergeRequestTabs
|
|||
|
||||
if locationHash isnt ''
|
||||
hashClassString = ".#{locationHash.replace('#', '')}"
|
||||
$diffLine = $(locationHash)
|
||||
$diffLine = $("#{locationHash}:not(.match)", $('#diffs'))
|
||||
|
||||
if $diffLine.is ':not(tr)'
|
||||
$diffLine = $("td#{locationHash}, td#{hashClassString}")
|
||||
if not $diffLine.is 'tr'
|
||||
$diffLine = $('#diffs').find("td#{locationHash}, td#{hashClassString}")
|
||||
else
|
||||
$diffLine = $('td', $diffLine)
|
||||
$diffLine = $diffLine.find('td')
|
||||
|
||||
if $diffLine.length
|
||||
$diffLine.addClass 'hll'
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
@raven =
|
||||
init: ->
|
||||
if gon.sentry_dsn?
|
||||
Raven.config(gon.sentry_dsn, {
|
||||
includePaths: [/gon.relative_url_root/]
|
||||
ignoreErrors: [
|
||||
# Random plugins/extensions
|
||||
'top.GLOBALS',
|
||||
# See: http://blog.errorception.com/2012/03/tale-of-unfindable-js-error. html
|
||||
'originalCreateNotification',
|
||||
'canvas.contentDocument',
|
||||
'MyApp_RemoveAllHighlights',
|
||||
'http://tt.epicplay.com',
|
||||
'Can\'t find variable: ZiteReader',
|
||||
'jigsaw is not defined',
|
||||
'ComboSearch is not defined',
|
||||
'http://loading.retry.widdit.com/',
|
||||
'atomicFindClose',
|
||||
# ISP "optimizing" proxy - `Cache-Control: no-transform` seems to
|
||||
# reduce this. (thanks @acdha)
|
||||
# See http://stackoverflow.com/questions/4113268
|
||||
'bmi_SafeAddOnload',
|
||||
'EBCallBackMessageReceived',
|
||||
# See http://toolbar.conduit.com/Developer/HtmlAndGadget/Methods/JSInjection.aspx
|
||||
'conduitPage'
|
||||
],
|
||||
ignoreUrls: [
|
||||
# Chrome extensions
|
||||
/extensions\//i,
|
||||
/^chrome:\/\//i,
|
||||
# Other plugins
|
||||
/127\.0\.0\.1:4001\/isrunning/i, # Cacaoweb
|
||||
/webappstoolbarba\.texthelp\.com\//i,
|
||||
/metrics\.itunes\.apple\.com\.edgesuite\.net\//i
|
||||
]
|
||||
}).install()
|
||||
|
||||
if gon.current_user_id
|
||||
Raven.setUserContext({
|
||||
id: gon.current_user_id
|
||||
})
|
||||
|
||||
$ ->
|
||||
raven.init()
|
|
@ -1,5 +1,11 @@
|
|||
class @Todos
|
||||
constructor: (@name) ->
|
||||
constructor: (opts = {}) ->
|
||||
{
|
||||
@el = $('.js-todos-options')
|
||||
} = opts
|
||||
|
||||
@perPage = @el.data('perPage')
|
||||
|
||||
@clearListeners()
|
||||
@initBtnListeners()
|
||||
|
||||
|
@ -26,6 +32,7 @@ class @Todos
|
|||
dataType: 'json'
|
||||
data: '_method': 'delete'
|
||||
success: (data) =>
|
||||
@redirectIfNeeded data.count
|
||||
@clearDone $this.closest('li')
|
||||
@updateBadges data
|
||||
|
||||
|
@ -57,6 +64,40 @@ class @Todos
|
|||
$('.todos-pending .badge, .todos-pending-count').text data.count
|
||||
$('.todos-done .badge').text data.done_count
|
||||
|
||||
getTotalPages: ->
|
||||
@el.data('totalPages')
|
||||
|
||||
getCurrentPage: ->
|
||||
@el.data('currentPage')
|
||||
|
||||
getTodosPerPage: ->
|
||||
@el.data('perPage')
|
||||
|
||||
redirectIfNeeded: (total) ->
|
||||
currPages = @getTotalPages()
|
||||
currPage = @getCurrentPage()
|
||||
|
||||
# Refresh if no remaining Todos
|
||||
if not total
|
||||
location.reload()
|
||||
return
|
||||
|
||||
# Do nothing if no pagination
|
||||
return if not currPages
|
||||
|
||||
newPages = Math.ceil(total / @getTodosPerPage())
|
||||
url = location.href # Includes query strings
|
||||
|
||||
# If new total of pages is different than we have now
|
||||
if newPages isnt currPages
|
||||
# Redirect to previous page if there's one available
|
||||
if currPages > 1 and currPage is currPages
|
||||
pageParams =
|
||||
page: currPages - 1
|
||||
url = gl.utils.mergeUrlParams(pageParams, url)
|
||||
|
||||
Turbolinks.visit(url)
|
||||
|
||||
goToTodoUrl: (e)->
|
||||
todoLink = $(this).data('url')
|
||||
return unless todoLink
|
||||
|
|
|
@ -38,12 +38,14 @@
|
|||
|
||||
.filename {
|
||||
&.old {
|
||||
display: inline-block;
|
||||
span.idiff {
|
||||
background-color: #f8cbcb;
|
||||
}
|
||||
}
|
||||
|
||||
&.new {
|
||||
display: inline-block;
|
||||
span.idiff {
|
||||
background-color: #a6f3a6;
|
||||
}
|
||||
|
|
|
@ -183,6 +183,9 @@ ul.notes {
|
|||
}
|
||||
}
|
||||
|
||||
.author_link {
|
||||
color: $gl-gray;
|
||||
}
|
||||
}
|
||||
|
||||
.note-headline-light,
|
||||
|
|
|
@ -38,13 +38,13 @@ class Projects::CommitController < Projects::ApplicationController
|
|||
end
|
||||
|
||||
def cancel_builds
|
||||
ci_commit.builds.running_or_pending.each(&:cancel)
|
||||
ci_builds.running_or_pending.each(&:cancel)
|
||||
|
||||
redirect_back_or_default default: builds_namespace_project_commit_path(project.namespace, project, commit.sha)
|
||||
end
|
||||
|
||||
def retry_builds
|
||||
ci_commit.builds.latest.failed.each do |build|
|
||||
ci_builds.latest.failed.each do |build|
|
||||
if build.retryable?
|
||||
Ci::Build.retry(build)
|
||||
end
|
||||
|
@ -99,8 +99,12 @@ class Projects::CommitController < Projects::ApplicationController
|
|||
@commit ||= @project.commit(params[:id])
|
||||
end
|
||||
|
||||
def ci_commit
|
||||
@ci_commit ||= project.ci_commit(commit.sha)
|
||||
def ci_commits
|
||||
@ci_commits ||= project.ci_commits.where(sha: commit.sha)
|
||||
end
|
||||
|
||||
def ci_builds
|
||||
@ci_builds ||= Ci::Build.where(commit: ci_commits)
|
||||
end
|
||||
|
||||
def define_show_vars
|
||||
|
@ -113,7 +117,8 @@ class Projects::CommitController < Projects::ApplicationController
|
|||
@diff_refs = [commit.parent || commit, commit]
|
||||
@notes_count = commit.notes.count
|
||||
|
||||
@statuses = ci_commit.statuses if ci_commit
|
||||
@statuses = CommitStatus.where(commit: ci_commits)
|
||||
@builds = Ci::Build.where(commit: ci_commits)
|
||||
end
|
||||
|
||||
def assign_change_commit_vars(mr_source_branch)
|
||||
|
|
|
@ -321,6 +321,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
|||
|
||||
def define_widget_vars
|
||||
@ci_commit = @merge_request.ci_commit
|
||||
@ci_commits = [@ci_commit].compact
|
||||
closes_issues
|
||||
end
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
class Projects::ProjectMembersController < Projects::ApplicationController
|
||||
# Authorize
|
||||
before_action :authorize_admin_project_member!, except: :leave
|
||||
before_action :authorize_admin_project_member!, except: [:leave, :index]
|
||||
|
||||
def index
|
||||
@project_members = @project.project_members
|
||||
|
|
|
@ -272,7 +272,6 @@ class IssuableFinder
|
|||
items = items.without_label
|
||||
else
|
||||
items = items.with_label(label_names)
|
||||
|
||||
if projects
|
||||
items = items.where(labels: { project_id: projects })
|
||||
end
|
||||
|
@ -321,7 +320,7 @@ class IssuableFinder
|
|||
end
|
||||
|
||||
def label_names
|
||||
params[:label_name].split(',')
|
||||
params[:label_name].is_a?(String) ? params[:label_name].split(',') : params[:label_name]
|
||||
end
|
||||
|
||||
def current_user_related?
|
||||
|
|
|
@ -4,14 +4,6 @@ module CiStatusHelper
|
|||
builds_namespace_project_commit_path(project.namespace, project, ci_commit.sha)
|
||||
end
|
||||
|
||||
def ci_status_icon(ci_commit)
|
||||
ci_icon_for_status(ci_commit.status)
|
||||
end
|
||||
|
||||
def ci_status_label(ci_commit)
|
||||
ci_label_for_status(ci_commit.status)
|
||||
end
|
||||
|
||||
def ci_status_with_icon(status, target = nil)
|
||||
content = ci_icon_for_status(status) + ' '.html_safe + ci_label_for_status(status)
|
||||
klass = "ci-status ci-#{status}"
|
||||
|
@ -47,10 +39,13 @@ module CiStatusHelper
|
|||
end
|
||||
|
||||
def render_ci_status(ci_commit, tooltip_placement: 'auto left')
|
||||
link_to ci_status_icon(ci_commit),
|
||||
# TODO: split this method into
|
||||
# - render_commit_status
|
||||
# - render_pipeline_status
|
||||
link_to ci_icon_for_status(ci_commit.status),
|
||||
ci_status_path(ci_commit),
|
||||
class: "ci-status-link ci-status-icon-#{ci_commit.status.dasherize}",
|
||||
title: "Build #{ci_status_label(ci_commit)}",
|
||||
title: "Build #{ci_label_for_status(ci_commit.status)}",
|
||||
data: { toggle: 'tooltip', placement: tooltip_placement }
|
||||
end
|
||||
|
||||
|
|
|
@ -25,6 +25,10 @@ module GitlabRoutingHelper
|
|||
namespace_project_commits_path(project.namespace, project, @ref || project.repository.root_ref)
|
||||
end
|
||||
|
||||
def project_pipelines_path(project, *args)
|
||||
namespace_project_pipelines_path(project.namespace, project, *args)
|
||||
end
|
||||
|
||||
def project_builds_path(project, *args)
|
||||
namespace_project_builds_path(project.namespace, project, *args)
|
||||
end
|
||||
|
|
|
@ -84,6 +84,14 @@ module PageLayoutHelper
|
|||
end
|
||||
end
|
||||
|
||||
def nav(name = nil)
|
||||
if name
|
||||
@nav = name
|
||||
else
|
||||
@nav
|
||||
end
|
||||
end
|
||||
|
||||
def fluid_layout(enabled = false)
|
||||
if @fluid_layout.nil?
|
||||
@fluid_layout = (current_user && current_user.layout == "fluid") || enabled
|
||||
|
|
|
@ -144,6 +144,10 @@ module ProjectsHelper
|
|||
nav_tabs << :settings
|
||||
end
|
||||
|
||||
if can?(current_user, :read_project_member, project)
|
||||
nav_tabs << :team
|
||||
end
|
||||
|
||||
if can?(current_user, :read_issue, project)
|
||||
nav_tabs << :issues
|
||||
end
|
||||
|
|
|
@ -110,4 +110,12 @@ module TabHelper
|
|||
'active'
|
||||
end
|
||||
end
|
||||
|
||||
def profile_tab_class
|
||||
if controller.controller_path =~ /\Aprofiles/
|
||||
return 'active'
|
||||
end
|
||||
|
||||
'active' if current_controller?('oauth/applications')
|
||||
end
|
||||
end
|
||||
|
|
|
@ -37,8 +37,6 @@
|
|||
|
||||
module Ci
|
||||
class Build < CommitStatus
|
||||
LAZY_ATTRIBUTES = ['trace']
|
||||
|
||||
belongs_to :runner, class_name: 'Ci::Runner'
|
||||
belongs_to :trigger_request, class_name: 'Ci::TriggerRequest'
|
||||
belongs_to :erased_by, class_name: 'User'
|
||||
|
@ -50,25 +48,17 @@ module Ci
|
|||
|
||||
scope :unstarted, ->() { where(runner_id: nil) }
|
||||
scope :ignore_failures, ->() { where(allow_failure: false) }
|
||||
scope :similar, ->(build) { where(ref: build.ref, tag: build.tag, trigger_request_id: build.trigger_request_id) }
|
||||
|
||||
mount_uploader :artifacts_file, ArtifactUploader
|
||||
mount_uploader :artifacts_metadata, ArtifactUploader
|
||||
|
||||
acts_as_taggable
|
||||
|
||||
# To prevent db load megabytes of data from trace
|
||||
default_scope -> { select(Ci::Build.columns_without_lazy) }
|
||||
|
||||
before_destroy { project }
|
||||
|
||||
class << self
|
||||
def columns_without_lazy
|
||||
(column_names - LAZY_ATTRIBUTES).map do |column_name|
|
||||
"#{table_name}.#{column_name}"
|
||||
end
|
||||
end
|
||||
after_create :execute_hooks
|
||||
|
||||
class << self
|
||||
def last_month
|
||||
where('created_at > ?', Date.today - 1.month)
|
||||
end
|
||||
|
@ -126,12 +116,16 @@ module Ci
|
|||
end
|
||||
|
||||
def retried?
|
||||
!self.commit.latest_statuses_for_ref(self.ref).include?(self)
|
||||
!self.commit.statuses.latest.include?(self)
|
||||
end
|
||||
|
||||
def retry
|
||||
Ci::Build.retry(self)
|
||||
end
|
||||
|
||||
def depends_on_builds
|
||||
# Get builds of the same type
|
||||
latest_builds = self.commit.builds.similar(self).latest
|
||||
latest_builds = self.commit.builds.latest
|
||||
|
||||
# Return builds from previous stages
|
||||
latest_builds.where('stage_idx < ?', stage_idx)
|
||||
|
|
|
@ -19,21 +19,28 @@
|
|||
module Ci
|
||||
class Commit < ActiveRecord::Base
|
||||
extend Ci::Model
|
||||
include Statuseable
|
||||
|
||||
belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id
|
||||
has_many :statuses, class_name: 'CommitStatus'
|
||||
has_many :builds, class_name: 'Ci::Build'
|
||||
has_many :trigger_requests, dependent: :destroy, class_name: 'Ci::TriggerRequest'
|
||||
|
||||
delegate :stages, to: :statuses
|
||||
|
||||
validates_presence_of :sha
|
||||
validates_presence_of :status
|
||||
validate :valid_commit_sha
|
||||
|
||||
# Invalidate object and save if when touched
|
||||
after_touch :update_state
|
||||
|
||||
def self.truncate_sha(sha)
|
||||
sha[0...8]
|
||||
end
|
||||
|
||||
def to_param
|
||||
sha
|
||||
def self.stages
|
||||
CommitStatus.where(commit: all).stages
|
||||
end
|
||||
|
||||
def project_id
|
||||
|
@ -68,15 +75,20 @@ module Ci
|
|||
nil
|
||||
end
|
||||
|
||||
def stage
|
||||
running_or_pending = statuses.latest.running_or_pending.ordered
|
||||
running_or_pending.first.try(:stage)
|
||||
def branch?
|
||||
!tag?
|
||||
end
|
||||
|
||||
def create_builds(ref, tag, user, trigger_request = nil)
|
||||
def retryable?
|
||||
builds.latest.any? do |build|
|
||||
build.failed? && build.retryable?
|
||||
end
|
||||
end
|
||||
|
||||
def create_builds(user, trigger_request = nil)
|
||||
return unless config_processor
|
||||
config_processor.stages.any? do |stage|
|
||||
CreateBuildsService.new.execute(self, stage, ref, tag, user, trigger_request, 'success').present?
|
||||
CreateBuildsService.new(self).execute(stage, user, 'success', trigger_request).present?
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -84,7 +96,7 @@ module Ci
|
|||
return unless config_processor
|
||||
|
||||
# don't create other builds if this one is retried
|
||||
latest_builds = builds.similar(build).latest
|
||||
latest_builds = builds.latest
|
||||
return unless latest_builds.exists?(build.id)
|
||||
|
||||
# get list of stages after this build
|
||||
|
@ -92,88 +104,21 @@ module Ci
|
|||
next_stages.delete(build.stage)
|
||||
|
||||
# get status for all prior builds
|
||||
prior_builds = latest_builds.reject { |other_build| next_stages.include?(other_build.stage) }
|
||||
status = Ci::Status.get_status(prior_builds)
|
||||
prior_builds = latest_builds.where.not(stage: next_stages)
|
||||
prior_status = prior_builds.status
|
||||
|
||||
# create builds for next stages based
|
||||
next_stages.any? do |stage|
|
||||
CreateBuildsService.new.execute(self, stage, build.ref, build.tag, build.user, build.trigger_request, status).present?
|
||||
CreateBuildsService.new(self).execute(stage, build.user, prior_status, build.trigger_request).present?
|
||||
end
|
||||
end
|
||||
|
||||
def refs
|
||||
statuses.order(:ref).pluck(:ref).uniq
|
||||
end
|
||||
|
||||
def latest_statuses
|
||||
@latest_statuses ||= statuses.latest.to_a
|
||||
end
|
||||
|
||||
def latest_statuses_for_ref(ref)
|
||||
latest_statuses.select { |status| status.ref == ref }
|
||||
end
|
||||
|
||||
def matrix_builds(build = nil)
|
||||
matrix_builds = builds.latest.ordered
|
||||
matrix_builds = matrix_builds.similar(build) if build
|
||||
matrix_builds.to_a
|
||||
end
|
||||
|
||||
def retried
|
||||
@retried ||= (statuses.order(id: :desc) - statuses.latest)
|
||||
end
|
||||
|
||||
def status
|
||||
if yaml_errors.present?
|
||||
return 'failed'
|
||||
end
|
||||
|
||||
@status ||= Ci::Status.get_status(latest_statuses)
|
||||
end
|
||||
|
||||
def pending?
|
||||
status == 'pending'
|
||||
end
|
||||
|
||||
def running?
|
||||
status == 'running'
|
||||
end
|
||||
|
||||
def success?
|
||||
status == 'success'
|
||||
end
|
||||
|
||||
def failed?
|
||||
status == 'failed'
|
||||
end
|
||||
|
||||
def canceled?
|
||||
status == 'canceled'
|
||||
end
|
||||
|
||||
def active?
|
||||
running? || pending?
|
||||
end
|
||||
|
||||
def complete?
|
||||
canceled? || success? || failed?
|
||||
end
|
||||
|
||||
def duration
|
||||
duration_array = statuses.map(&:duration).compact
|
||||
duration_array.reduce(:+).to_i
|
||||
end
|
||||
|
||||
def started_at
|
||||
@started_at ||= statuses.order('started_at ASC').first.try(:started_at)
|
||||
end
|
||||
|
||||
def finished_at
|
||||
@finished_at ||= statuses.order('finished_at DESC').first.try(:finished_at)
|
||||
end
|
||||
|
||||
def coverage
|
||||
coverage_array = latest_statuses.map(&:coverage).compact
|
||||
coverage_array = statuses.latest.map(&:coverage).compact
|
||||
if coverage_array.size >= 1
|
||||
'%.2f' % (coverage_array.reduce(:+) / coverage_array.size)
|
||||
end
|
||||
|
@ -181,23 +126,29 @@ module Ci
|
|||
|
||||
def config_processor
|
||||
return nil unless ci_yaml_file
|
||||
@config_processor ||= Ci::GitlabCiYamlProcessor.new(ci_yaml_file, project.path_with_namespace)
|
||||
rescue Ci::GitlabCiYamlProcessor::ValidationError, Psych::SyntaxError => e
|
||||
save_yaml_error(e.message)
|
||||
nil
|
||||
rescue
|
||||
save_yaml_error("Undefined error")
|
||||
nil
|
||||
return @config_processor if defined?(@config_processor)
|
||||
|
||||
@config_processor ||= begin
|
||||
Ci::GitlabCiYamlProcessor.new(ci_yaml_file, project.path_with_namespace)
|
||||
rescue Ci::GitlabCiYamlProcessor::ValidationError, Psych::SyntaxError => e
|
||||
save_yaml_error(e.message)
|
||||
nil
|
||||
rescue
|
||||
save_yaml_error("Undefined error")
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def ci_yaml_file
|
||||
return @ci_yaml_file if defined?(@ci_yaml_file)
|
||||
|
||||
@ci_yaml_file ||= begin
|
||||
blob = project.repository.blob_at(sha, '.gitlab-ci.yml')
|
||||
blob.load_all_data!(project.repository)
|
||||
blob.data
|
||||
rescue
|
||||
nil
|
||||
end
|
||||
rescue
|
||||
nil
|
||||
end
|
||||
|
||||
def skip_ci?
|
||||
|
@ -206,10 +157,23 @@ module Ci
|
|||
|
||||
private
|
||||
|
||||
def update_state
|
||||
statuses.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
|
||||
save
|
||||
end
|
||||
|
||||
def save_yaml_error(error)
|
||||
return if self.yaml_errors?
|
||||
self.yaml_errors = error
|
||||
save
|
||||
update_state
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -207,12 +207,13 @@ class Commit
|
|||
@raw.short_id(7)
|
||||
end
|
||||
|
||||
def ci_commit
|
||||
project.ci_commit(sha)
|
||||
def ci_commits
|
||||
@ci_commits ||= project.ci_commits.where(sha: sha)
|
||||
end
|
||||
|
||||
def status
|
||||
ci_commit.try(:status) || :not_found
|
||||
return @status if defined?(@status)
|
||||
@status ||= ci_commits.status
|
||||
end
|
||||
|
||||
def revert_branch_name
|
||||
|
|
|
@ -33,30 +33,23 @@
|
|||
#
|
||||
|
||||
class CommitStatus < ActiveRecord::Base
|
||||
include Statuseable
|
||||
|
||||
self.table_name = 'ci_builds'
|
||||
|
||||
belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id
|
||||
belongs_to :commit, class_name: 'Ci::Commit'
|
||||
belongs_to :commit, class_name: 'Ci::Commit', touch: true
|
||||
belongs_to :user
|
||||
|
||||
validates :commit, presence: true
|
||||
validates :status, inclusion: { in: %w(pending running failed success canceled) }
|
||||
|
||||
validates_presence_of :name
|
||||
|
||||
alias_attribute :author, :user
|
||||
|
||||
scope :running, -> { where(status: 'running') }
|
||||
scope :pending, -> { where(status: 'pending') }
|
||||
scope :success, -> { where(status: 'success') }
|
||||
scope :failed, -> { where(status: 'failed') }
|
||||
scope :running_or_pending, -> { where(status: [:running, :pending]) }
|
||||
scope :finished, -> { where(status: [:success, :failed, :canceled]) }
|
||||
scope :latest, -> { where(id: unscope(:select).select('max(id)').group(:name, :ref)) }
|
||||
scope :latest, -> { where(id: unscope(:select).select('max(id)').group(:name, :commit_id)) }
|
||||
scope :ordered, -> { order(:ref, :stage_idx, :name) }
|
||||
scope :for_ref, ->(ref) { where(ref: ref) }
|
||||
|
||||
AVAILABLE_STATUSES = ['pending', 'running', 'success', 'failed', 'canceled']
|
||||
scope :ignored, -> { where(allow_failure: true, status: [:failed, :canceled]) }
|
||||
|
||||
state_machine :status, initial: :pending do
|
||||
event :run do
|
||||
|
@ -86,31 +79,24 @@ class CommitStatus < ActiveRecord::Base
|
|||
after_transition [:pending, :running] => :success do |commit_status|
|
||||
MergeRequests::MergeWhenBuildSucceedsService.new(commit_status.commit.project, nil).trigger(commit_status)
|
||||
end
|
||||
|
||||
state :pending, value: 'pending'
|
||||
state :running, value: 'running'
|
||||
state :failed, value: 'failed'
|
||||
state :success, value: 'success'
|
||||
state :canceled, value: 'canceled'
|
||||
end
|
||||
|
||||
delegate :sha, :short_sha, to: :commit, prefix: false
|
||||
delegate :sha, :short_sha, to: :commit
|
||||
|
||||
# TODO: this should be removed with all references
|
||||
def before_sha
|
||||
Gitlab::Git::BLANK_SHA
|
||||
commit.before_sha || Gitlab::Git::BLANK_SHA
|
||||
end
|
||||
|
||||
def started?
|
||||
!pending? && !canceled? && started_at
|
||||
def self.stages
|
||||
order_by = 'max(stage_idx)'
|
||||
group('stage').order(order_by).pluck(:stage, order_by).map(&:first).compact
|
||||
end
|
||||
|
||||
def active?
|
||||
running? || pending?
|
||||
end
|
||||
|
||||
def complete?
|
||||
canceled? || success? || failed?
|
||||
def self.stages_status
|
||||
all.stages.inject({}) do |h, stage|
|
||||
h[stage] = all.where(stage: stage).status
|
||||
h
|
||||
end
|
||||
end
|
||||
|
||||
def ignored?
|
||||
|
@ -118,11 +104,13 @@ class CommitStatus < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def duration
|
||||
if started_at && finished_at
|
||||
finished_at - started_at
|
||||
elsif started_at
|
||||
Time.now - started_at
|
||||
end
|
||||
duration =
|
||||
if started_at && finished_at
|
||||
finished_at - started_at
|
||||
elsif started_at
|
||||
Time.now - started_at
|
||||
end
|
||||
duration
|
||||
end
|
||||
|
||||
def stuck?
|
||||
|
|
|
@ -37,7 +37,6 @@ module Issuable
|
|||
scope :closed, -> { with_state(:closed) }
|
||||
scope :order_milestone_due_desc, -> { joins(:milestone).reorder('milestones.due_date DESC, milestones.id DESC') }
|
||||
scope :order_milestone_due_asc, -> { joins(:milestone).reorder('milestones.due_date ASC, milestones.id ASC') }
|
||||
scope :with_label, ->(title) { joins(:labels).where(labels: { title: title }) }
|
||||
scope :without_label, -> { joins("LEFT OUTER JOIN label_links ON label_links.target_type = '#{name}' AND label_links.target_id = #{table_name}.id").where(label_links: { id: nil }) }
|
||||
|
||||
scope :join_project, -> { joins(:project) }
|
||||
|
@ -122,6 +121,14 @@ module Issuable
|
|||
|
||||
joins(join_clause).group(issuable_table[:id]).reorder("COUNT(notes.id) DESC")
|
||||
end
|
||||
|
||||
def with_label(title)
|
||||
if title.is_a?(Array) && title.count > 1
|
||||
joins(:labels).where(labels: { title: title }).group('issues.id').having("count(distinct labels.title) = #{title.count}")
|
||||
else
|
||||
joins(:labels).where(labels: { title: title })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def today?
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
module Statuseable
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
AVAILABLE_STATUSES = %w(pending running success failed canceled skipped)
|
||||
|
||||
class_methods do
|
||||
def status_sql
|
||||
builds = all.select('count(*)').to_sql
|
||||
success = all.success.select('count(*)').to_sql
|
||||
ignored = all.ignored.select('count(*)').to_sql if all.respond_to?(:ignored)
|
||||
ignored ||= '0'
|
||||
pending = all.pending.select('count(*)').to_sql
|
||||
running = all.running.select('count(*)').to_sql
|
||||
canceled = all.canceled.select('count(*)').to_sql
|
||||
skipped = all.skipped.select('count(*)').to_sql
|
||||
|
||||
deduce_status = "(CASE
|
||||
WHEN (#{builds})=0 THEN NULL
|
||||
WHEN (#{builds})=(#{success})+(#{ignored}) THEN 'success'
|
||||
WHEN (#{builds})=(#{pending}) THEN 'pending'
|
||||
WHEN (#{builds})=(#{canceled}) THEN 'canceled'
|
||||
WHEN (#{builds})=(#{skipped}) THEN 'skipped'
|
||||
WHEN (#{running})+(#{pending})>0 THEN 'running'
|
||||
ELSE 'failed'
|
||||
END)"
|
||||
|
||||
deduce_status
|
||||
end
|
||||
|
||||
def status
|
||||
all.pluck(self.status_sql).first
|
||||
end
|
||||
|
||||
def duration
|
||||
duration_array = all.map(&:duration).compact
|
||||
duration_array.reduce(:+)
|
||||
end
|
||||
|
||||
def started_at
|
||||
all.minimum(:started_at)
|
||||
end
|
||||
|
||||
def finished_at
|
||||
all.maximum(:finished_at)
|
||||
end
|
||||
end
|
||||
|
||||
included do
|
||||
validates :status, inclusion: { in: AVAILABLE_STATUSES }
|
||||
|
||||
state_machine :status, initial: :pending do
|
||||
state :pending, value: 'pending'
|
||||
state :running, value: 'running'
|
||||
state :failed, value: 'failed'
|
||||
state :success, value: 'success'
|
||||
state :canceled, value: 'canceled'
|
||||
state :skipped, value: 'skipped'
|
||||
end
|
||||
|
||||
scope :running, -> { where(status: 'running') }
|
||||
scope :pending, -> { where(status: 'pending') }
|
||||
scope :success, -> { where(status: 'success') }
|
||||
scope :failed, -> { where(status: 'failed') }
|
||||
scope :canceled, -> { where(status: 'canceled') }
|
||||
scope :skipped, -> { where(status: 'skipped') }
|
||||
scope :running_or_pending, -> { where(status: [:running, :pending]) }
|
||||
scope :finished, -> { where(status: [:success, :failed, :canceled]) }
|
||||
end
|
||||
|
||||
def started?
|
||||
!pending? && !canceled? && started_at
|
||||
end
|
||||
|
||||
def active?
|
||||
running? || pending?
|
||||
end
|
||||
|
||||
def complete?
|
||||
canceled? || success? || failed?
|
||||
end
|
||||
end
|
|
@ -586,7 +586,7 @@ class MergeRequest < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def ci_commit
|
||||
@ci_commit ||= source_project.ci_commit(last_commit.id) if last_commit && source_project
|
||||
@ci_commit ||= source_project.ci_commit(last_commit.id, source_branch) if last_commit && source_project
|
||||
end
|
||||
|
||||
def diff_refs
|
||||
|
|
|
@ -963,12 +963,12 @@ class Project < ActiveRecord::Base
|
|||
!namespace.share_with_group_lock
|
||||
end
|
||||
|
||||
def ci_commit(sha)
|
||||
ci_commits.find_by(sha: sha)
|
||||
def ci_commit(sha, ref)
|
||||
ci_commits.order(id: :desc).find_by(sha: sha, ref: ref)
|
||||
end
|
||||
|
||||
def ensure_ci_commit(sha)
|
||||
ci_commit(sha) || ci_commits.create(sha: sha)
|
||||
def ensure_ci_commit(sha, ref)
|
||||
ci_commit(sha, ref) || ci_commits.create(sha: sha, ref: ref)
|
||||
end
|
||||
|
||||
def enable_ci
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
module Ci
|
||||
class CreateBuildsService
|
||||
def execute(commit, stage, ref, tag, user, trigger_request, status)
|
||||
builds_attrs = commit.config_processor.builds_for_stage_and_ref(stage, ref, tag, trigger_request)
|
||||
def initialize(commit)
|
||||
@commit = commit
|
||||
end
|
||||
|
||||
def execute(stage, user, status, trigger_request = nil)
|
||||
builds_attrs = config_processor.builds_for_stage_and_ref(stage, @commit.ref, @commit.tag, trigger_request)
|
||||
|
||||
# check when to create next build
|
||||
builds_attrs = builds_attrs.select do |build_attrs|
|
||||
|
@ -17,7 +21,8 @@ module Ci
|
|||
|
||||
builds_attrs.map do |build_attrs|
|
||||
# don't create the same build twice
|
||||
unless commit.builds.find_by(ref: ref, tag: tag, trigger_request: trigger_request, name: build_attrs[:name])
|
||||
unless @commit.builds.find_by(ref: @commit.ref, tag: @commit.tag,
|
||||
trigger_request: trigger_request, name: build_attrs[:name])
|
||||
build_attrs.slice!(:name,
|
||||
:commands,
|
||||
:tag_list,
|
||||
|
@ -26,17 +31,21 @@ module Ci
|
|||
:stage,
|
||||
:stage_idx)
|
||||
|
||||
build_attrs.merge!(ref: ref,
|
||||
tag: tag,
|
||||
build_attrs.merge!(ref: @commit.ref,
|
||||
tag: @commit.tag,
|
||||
trigger_request: trigger_request,
|
||||
user: user,
|
||||
project: commit.project)
|
||||
project: @commit.project)
|
||||
|
||||
build = commit.builds.create!(build_attrs)
|
||||
build.execute_hooks
|
||||
build
|
||||
@commit.builds.create!(build_attrs)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def config_processor
|
||||
@config_processor ||= @commit.config_processor
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,14 +7,14 @@ module Ci
|
|||
# check if ref is tag
|
||||
tag = project.repository.find_tag(ref).present?
|
||||
|
||||
ci_commit = project.ensure_ci_commit(commit.sha)
|
||||
ci_commit = project.ci_commits.create(sha: commit.sha, ref: ref, tag: tag)
|
||||
|
||||
trigger_request = trigger.trigger_requests.create!(
|
||||
variables: variables,
|
||||
commit: ci_commit,
|
||||
)
|
||||
|
||||
if ci_commit.create_builds(ref, tag, nil, trigger_request)
|
||||
if ci_commit.create_builds(nil, trigger_request)
|
||||
trigger_request
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,8 +3,9 @@ module Ci
|
|||
def execute(project, opts)
|
||||
sha = opts[:sha] || ref_sha(project, opts[:ref])
|
||||
|
||||
commit = project.ci_commits.find_by(sha: sha)
|
||||
image_name = image_for_commit(commit)
|
||||
ci_commits = project.ci_commits.where(sha: sha)
|
||||
ci_commits = ci_commits.where(ref: opts[:ref]) if opts[:ref]
|
||||
image_name = image_for_status(ci_commits.status)
|
||||
|
||||
image_path = Rails.root.join('public/ci', image_name)
|
||||
OpenStruct.new(path: image_path, name: image_name)
|
||||
|
@ -16,9 +17,9 @@ module Ci
|
|||
project.commit(ref).try(:sha) if ref
|
||||
end
|
||||
|
||||
def image_for_commit(commit)
|
||||
return 'build-unknown.svg' unless commit
|
||||
'build-' + commit.status + ".svg"
|
||||
def image_for_status(status)
|
||||
status ||= 'unknown'
|
||||
'build-' + status + ".svg"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,6 +2,7 @@ class CreateCommitBuildsService
|
|||
def execute(project, user, params)
|
||||
return false unless project.builds_enabled?
|
||||
|
||||
before_sha = params[:checkout_sha] || params[:before]
|
||||
sha = params[:checkout_sha] || params[:after]
|
||||
origin_ref = params[:ref]
|
||||
|
||||
|
@ -10,15 +11,16 @@ class CreateCommitBuildsService
|
|||
end
|
||||
|
||||
ref = Gitlab::Git.ref_name(origin_ref)
|
||||
tag = Gitlab::Git.tag_ref?(origin_ref)
|
||||
|
||||
# Skip branch removal
|
||||
if sha == Gitlab::Git::BLANK_SHA
|
||||
return false
|
||||
end
|
||||
|
||||
commit = project.ci_commit(sha)
|
||||
commit = project.ci_commit(sha, ref)
|
||||
unless commit
|
||||
commit = project.ci_commits.new(sha: sha)
|
||||
commit = project.ci_commits.new(sha: sha, ref: ref, before_sha: before_sha, tag: tag)
|
||||
|
||||
# Skip creating ci_commit when no gitlab-ci.yml is found
|
||||
unless commit.ci_yaml_file
|
||||
|
@ -32,10 +34,10 @@ class CreateCommitBuildsService
|
|||
# Skip creating builds for commits that have [ci skip]
|
||||
unless commit.skip_ci?
|
||||
# Create builds for commit
|
||||
tag = Gitlab::Git.tag_ref?(origin_ref)
|
||||
commit.create_builds(ref, tag, user)
|
||||
commit.create_builds(user)
|
||||
end
|
||||
|
||||
commit.touch
|
||||
commit
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,17 +7,17 @@
|
|||
.form-group
|
||||
= f.label :name, class: 'control-label'
|
||||
.col-sm-10
|
||||
= f.text_field :name, required: true, autocomplete: "off", class: 'form-control'
|
||||
= f.text_field :name, required: true, autocomplete: 'off', class: 'form-control'
|
||||
%span.help-inline * required
|
||||
.form-group
|
||||
= f.label :username, class: 'control-label'
|
||||
.col-sm-10
|
||||
= f.text_field :username, required: true, autocomplete: "off", class: 'form-control'
|
||||
= f.text_field :username, required: true, autocomplete: 'off', autocorrect: 'off', autocapitalize: 'off', spellcheck: false, class: 'form-control'
|
||||
%span.help-inline * required
|
||||
.form-group
|
||||
= f.label :email, class: 'control-label'
|
||||
.col-sm-10
|
||||
= f.text_field :email, required: true, autocomplete: "off", class: 'form-control'
|
||||
= f.text_field :email, required: true, autocomplete: 'off', class: 'form-control'
|
||||
%span.help-inline * required
|
||||
|
||||
- if @user.new_record?
|
||||
|
|
|
@ -45,6 +45,7 @@
|
|||
|
||||
.prepend-top-default
|
||||
- if @todos.any?
|
||||
.js-todos-options{ data: {per_page: @todos.limit_value, current_page: @todos.current_page, total_pages: @todos.total_pages} }
|
||||
- @todos.group_by(&:project).each do |group|
|
||||
.panel.panel-default.panel-small.js-todos-list
|
||||
- project = group[0]
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
- page_title "Applications"
|
||||
- header_title page_title, applications_profile_path
|
||||
|
||||
.row.prepend-top-default
|
||||
.col-lg-3.profile-settings-sidebar
|
||||
|
|
|
@ -25,6 +25,10 @@
|
|||
.content-wrapper
|
||||
= render "layouts/flash"
|
||||
= yield :flash_message
|
||||
- if defined?(nav) && nav
|
||||
.layout-nav
|
||||
%div{ class: container_class }
|
||||
= render "layouts/nav/#{nav}"
|
||||
%div{ class: (container_class unless @no_container) }
|
||||
.content
|
||||
.clearfix
|
||||
|
|
|
@ -6,6 +6,6 @@
|
|||
= yield :scripts_body_top
|
||||
|
||||
= render "layouts/header/default", title: header_title
|
||||
= render 'layouts/page', sidebar: sidebar
|
||||
= render 'layouts/page', sidebar: sidebar, nav: nav
|
||||
|
||||
= yield :scripts_body
|
||||
|
|
|
@ -48,8 +48,7 @@
|
|||
%span
|
||||
Help
|
||||
|
||||
%li.separate-item
|
||||
= nav_link(controller: :profile) do
|
||||
= nav_link(html_options: {class: profile_tab_class}) do
|
||||
= link_to profile_path, title: 'Profile Settings', data: {placement: 'bottom'} do
|
||||
= icon('user fw')
|
||||
%span
|
||||
|
|
|
@ -1,17 +1,9 @@
|
|||
%ul.nav.nav-sidebar
|
||||
= nav_link do
|
||||
= link_to root_path, title: 'Go to dashboard', class: 'back-link' do
|
||||
= icon('caret-square-o-left fw')
|
||||
%span
|
||||
Go to dashboard
|
||||
|
||||
%li.separate-item
|
||||
|
||||
%ul.nav-links
|
||||
= nav_link(path: 'profiles#show', html_options: {class: 'home'}) do
|
||||
= link_to profile_path, title: 'Profile Settings' do
|
||||
= icon('user fw')
|
||||
%span
|
||||
Profile Settings
|
||||
Profile
|
||||
= nav_link(controller: [:accounts, :two_factor_auths]) do
|
||||
= link_to profile_account_path, title: 'Account' do
|
||||
= icon('gear fw')
|
||||
|
@ -27,7 +19,6 @@
|
|||
= icon('envelope-o fw')
|
||||
%span
|
||||
Emails
|
||||
%span.count= number_with_delimiter(current_user.emails.count + 1)
|
||||
- unless current_user.ldap_user?
|
||||
= nav_link(controller: :passwords) do
|
||||
= link_to edit_profile_password_path, title: 'Password' do
|
||||
|
@ -45,7 +36,6 @@
|
|||
= icon('key fw')
|
||||
%span
|
||||
SSH Keys
|
||||
%span.count= number_with_delimiter(current_user.keys.count)
|
||||
= nav_link(controller: :preferences) do
|
||||
= link_to profile_preferences_path, title: 'Preferences' do
|
||||
-# TODO (rspeicher): Better icon?
|
||||
|
|
|
@ -77,7 +77,7 @@
|
|||
Merge Requests
|
||||
%span.count.merge_counter= number_with_delimiter(@project.merge_requests.opened.count)
|
||||
|
||||
- if project_nav_tab? :settings
|
||||
- if project_nav_tab? :team
|
||||
= nav_link(controller: [:project_members, :teams]) do
|
||||
= link_to namespace_project_project_members_path(@project.namespace, @project), title: 'Members', class: 'team-tab tab' do
|
||||
= icon('users fw')
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
- page_title "Profile Settings"
|
||||
- header_title "Profile Settings", profile_path unless header_title
|
||||
- sidebar "profile"
|
||||
- sidebar "dashboard"
|
||||
- nav "profile"
|
||||
|
||||
= render template: "layouts/application"
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
- page_title "Account"
|
||||
- header_title page_title, profile_account_path
|
||||
|
||||
- if current_user.ldap_user?
|
||||
.alert.alert-info
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
- page_title "Audit Log"
|
||||
- header_title page_title, audit_log_profile_path
|
||||
|
||||
.row.prepend-top-default
|
||||
.col-lg-3.profile-settings-sidebar
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
- page_title "Emails"
|
||||
- header_title page_title, profile_emails_path
|
||||
|
||||
.row.prepend-top-default
|
||||
.col-lg-3.profile-settings-sidebar
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
- page_title "SSH Keys"
|
||||
- header_title page_title, profile_keys_path
|
||||
|
||||
.row.prepend-top-default
|
||||
.col-lg-3.profile-settings-sidebar
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
- page_title "Notifications"
|
||||
- header_title page_title, profile_notifications_path
|
||||
|
||||
%div
|
||||
- if @user.errors.any?
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
- page_title "Password"
|
||||
- header_title page_title, edit_profile_password_path
|
||||
|
||||
.row.prepend-top-default
|
||||
.col-lg-3.profile-settings-sidebar
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
- page_title 'Preferences'
|
||||
- header_title page_title, profile_preferences_path
|
||||
|
||||
= form_for @user, url: profile_preferences_path, remote: true, method: :put, html: {class: 'row prepend-top-default js-preferences-form'} do |f|
|
||||
.col-lg-3.profile-settings-sidebar
|
||||
|
|
|
@ -55,6 +55,9 @@
|
|||
%li
|
||||
gcovr (C/C++) -
|
||||
%code ^TOTAL.*\s+(\d+\%)$
|
||||
%li
|
||||
tap --coverage-report=text-summary (Node.js) -
|
||||
%code ^Statements\s*:\s*([^%]+)
|
||||
|
||||
.form-group
|
||||
.col-sm-offset-2.col-sm-10
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
.project-last-commit
|
||||
- ci_commit = project.ci_commit(commit.sha)
|
||||
- if ci_commit
|
||||
= link_to ci_status_path(ci_commit), class: "ci-status ci-#{ci_commit.status}" do
|
||||
= ci_status_icon(ci_commit)
|
||||
= ci_status_label(ci_commit)
|
||||
- if commit.status
|
||||
= link_to builds_namespace_project_commit_path(commit.project.namespace, commit.project, commit), class: "ci-status ci-#{commit.status}" do
|
||||
= ci_icon_for_status(commit.status)
|
||||
= ci_label_for_status(commit.status)
|
||||
|
||||
= link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit_short_id"
|
||||
= link_to_gfm commit.title, namespace_project_commit_path(project.namespace, project, commit), class: "commit-row-message"
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
- if @lines.present?
|
||||
- if @form.unfold? && @form.since != 1 && !@form.bottom?
|
||||
%tr.line_holder{ id: @form.since }
|
||||
%tr.line_holder
|
||||
= render "projects/diffs/match_line", { line: @match_line,
|
||||
line_old: @form.since, line_new: @form.since, bottom: false, new_file: false }
|
||||
|
||||
- @lines.each_with_index do |line, index|
|
||||
- line_new = index + @form.since
|
||||
- line_old = line_new - @form.offset
|
||||
%tr.line_holder
|
||||
%tr.line_holder{ id: line_old }
|
||||
%td.old_line.diff-line-num{ data: { linenumber: line_old } }
|
||||
= link_to raw(line_old), "#"
|
||||
= link_to raw(line_old), "##{line_old}"
|
||||
%td.new_line.diff-line-num{ data: { linenumber: line_old } }
|
||||
= link_to raw(line_new) , "#"
|
||||
= link_to raw(line_new) , "##{line_old}"
|
||||
%td.line_content.noteable_line==#{' ' * @form.indent}#{line}
|
||||
|
||||
- if @form.unfold? && @form.bottom? && @form.to < @blob.loc
|
||||
|
|
|
@ -58,6 +58,6 @@
|
|||
%th Coverage
|
||||
%th
|
||||
|
||||
= render @builds, commit_sha: true, stage: true, allow_retry: true, coverage: @project.build_coverage_enabled?
|
||||
= render @builds, commit_sha: true, ref: true, stage: true, allow_retry: true, coverage: @project.build_coverage_enabled?
|
||||
|
||||
= paginate @builds, theme: 'gitlab'
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
= link_to "merge request #{merge_request.to_reference}", merge_request_path(merge_request)
|
||||
|
||||
#up-build-trace
|
||||
- builds = @build.commit.matrix_builds(@build)
|
||||
- builds = @build.commit.builds.latest.to_a
|
||||
- if builds.size > 1
|
||||
%ul.nav-links.no-top.no-bottom
|
||||
- builds.each do |build|
|
||||
|
|
|
@ -19,11 +19,12 @@
|
|||
%td
|
||||
= link_to build.short_sha, namespace_project_commit_path(build.project.namespace, build.project, build.sha), class: "monospace"
|
||||
|
||||
%td
|
||||
- if build.ref
|
||||
= link_to build.ref, namespace_project_commits_path(build.project.namespace, build.project, build.ref)
|
||||
- else
|
||||
.light none
|
||||
- if defined?(ref) && ref
|
||||
%td
|
||||
- if build.ref
|
||||
= link_to build.ref, namespace_project_commits_path(build.project.namespace, build.project, build.ref)
|
||||
- else
|
||||
.light none
|
||||
|
||||
- if defined?(runner) && runner
|
||||
%td
|
||||
|
@ -48,6 +49,8 @@
|
|||
%span.label.label-info triggered
|
||||
- if build.try(:allow_failure)
|
||||
%span.label.label-danger allowed to fail
|
||||
- if defined?(retried) && retried
|
||||
%span.label.label-warning retried
|
||||
|
||||
%td.duration
|
||||
- if build.duration
|
||||
|
|
|
@ -1,67 +1,2 @@
|
|||
.gray-content-block.middle-block
|
||||
.pull-right
|
||||
- if can?(current_user, :update_build, @ci_commit.project)
|
||||
- if @ci_commit.builds.latest.failed.any?(&:retryable?)
|
||||
= link_to "Retry failed", retry_builds_namespace_project_commit_path(@ci_commit.project.namespace, @ci_commit.project, @ci_commit.sha), class: 'btn btn-grouped btn-primary', method: :post
|
||||
|
||||
- if @ci_commit.builds.running_or_pending.any?
|
||||
= link_to "Cancel running", cancel_builds_namespace_project_commit_path(@ci_commit.project.namespace, @ci_commit.project, @ci_commit.sha), data: { confirm: 'Are you sure?' }, class: 'btn btn-grouped btn-danger', method: :post
|
||||
|
||||
.oneline
|
||||
= pluralize @statuses.count(:id), "build"
|
||||
- if defined?(link_to_commit) && link_to_commit
|
||||
for commit
|
||||
= link_to @ci_commit.short_sha, namespace_project_commit_path(@ci_commit.project.namespace, @ci_commit.project, @ci_commit.sha), class: "monospace"
|
||||
- if @ci_commit.duration > 0
|
||||
in
|
||||
= time_interval_in_words @ci_commit.duration
|
||||
|
||||
- if @ci_commit.yaml_errors.present?
|
||||
.bs-callout.bs-callout-danger
|
||||
%h4 Found errors in your .gitlab-ci.yml:
|
||||
%ul
|
||||
- @ci_commit.yaml_errors.split(",").each do |error|
|
||||
%li= error
|
||||
You can also test your .gitlab-ci.yml in the #{link_to "Lint", ci_lint_path}
|
||||
|
||||
- if @ci_commit.project.builds_enabled? && !@ci_commit.ci_yaml_file
|
||||
.bs-callout.bs-callout-warning
|
||||
\.gitlab-ci.yml not found in this commit
|
||||
|
||||
.table-holder
|
||||
%table.table.builds
|
||||
%thead
|
||||
%tr
|
||||
%th Status
|
||||
%th Build ID
|
||||
%th Ref
|
||||
%th Stage
|
||||
%th Name
|
||||
%th Duration
|
||||
%th Finished at
|
||||
- if @ci_commit.project.build_coverage_enabled?
|
||||
%th Coverage
|
||||
%th
|
||||
- @ci_commit.refs.each do |ref|
|
||||
- builds = @ci_commit.statuses.for_ref(ref).latest.ordered
|
||||
= render builds, coverage: @ci_commit.project.build_coverage_enabled?, stage: true, allow_retry: true
|
||||
|
||||
- if @ci_commit.retried.any?
|
||||
.gray-content-block.second-block
|
||||
Retried builds
|
||||
|
||||
.table-holder
|
||||
%table.table.builds
|
||||
%thead
|
||||
%tr
|
||||
%th Status
|
||||
%th Build ID
|
||||
%th Ref
|
||||
%th Stage
|
||||
%th Name
|
||||
%th Duration
|
||||
%th Finished at
|
||||
- if @ci_commit.project.build_coverage_enabled?
|
||||
%th Coverage
|
||||
%th
|
||||
= render @ci_commit.retried, coverage: @ci_commit.project.build_coverage_enabled?, stage: true
|
||||
- @ci_commits.each do |ci_commit|
|
||||
= render "ci_commit", ci_commit: ci_commit
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
.gray-content-block.middle-block
|
||||
.pull-right
|
||||
- if can?(current_user, :update_build, @project)
|
||||
- if ci_commit.builds.latest.failed.any?(&:retryable?)
|
||||
= link_to "Retry failed", retry_builds_namespace_project_commit_path(@project.namespace, @project, ci_commit.sha), class: 'btn btn-grouped btn-primary', method: :post
|
||||
|
||||
- if ci_commit.builds.running_or_pending.any?
|
||||
= link_to "Cancel running", cancel_builds_namespace_project_commit_path(@project.namespace, @project, ci_commit.sha), data: { confirm: 'Are you sure?' }, class: 'btn btn-grouped btn-danger', method: :post
|
||||
|
||||
.oneline
|
||||
= pluralize ci_commit.statuses.count(:id), "build"
|
||||
- if ci_commit.ref
|
||||
for
|
||||
%span.label.label-info
|
||||
= ci_commit.ref
|
||||
- if defined?(link_to_commit) && link_to_commit
|
||||
for commit
|
||||
= link_to ci_commit.short_sha, namespace_project_commit_path(@project.namespace, @project, ci_commit.sha), class: "monospace"
|
||||
- if ci_commit.duration > 0
|
||||
in
|
||||
= time_interval_in_words ci_commit.duration
|
||||
|
||||
- if ci_commit.yaml_errors.present?
|
||||
.bs-callout.bs-callout-danger
|
||||
%h4 Found errors in your .gitlab-ci.yml:
|
||||
%ul
|
||||
- ci_commit.yaml_errors.split(",").each do |error|
|
||||
%li= error
|
||||
You can also test your .gitlab-ci.yml in the #{link_to "Lint", ci_lint_path}
|
||||
|
||||
- if @project.builds_enabled? && !ci_commit.ci_yaml_file
|
||||
.bs-callout.bs-callout-warning
|
||||
\.gitlab-ci.yml not found in this commit
|
||||
|
||||
.table-holder
|
||||
%table.table.builds
|
||||
%thead
|
||||
%tr
|
||||
%th Status
|
||||
%th Build ID
|
||||
%th Stage
|
||||
%th Name
|
||||
%th Duration
|
||||
%th Finished at
|
||||
- if @project.build_coverage_enabled?
|
||||
%th Coverage
|
||||
%th
|
||||
- builds = ci_commit.statuses.latest.ordered
|
||||
= render builds, coverage: @project.build_coverage_enabled?, stage: true, ref: false, allow_retry: true
|
||||
|
||||
- if ci_commit.retried.any?
|
||||
.gray-content-block.second-block
|
||||
Retried builds
|
||||
|
||||
.table-holder
|
||||
%table.table.builds
|
||||
%thead
|
||||
%tr
|
||||
%th Status
|
||||
%th Build ID
|
||||
%th Ref
|
||||
%th Stage
|
||||
%th Name
|
||||
%th Duration
|
||||
%th Finished at
|
||||
- if @project.build_coverage_enabled?
|
||||
%th Coverage
|
||||
%th
|
||||
= render ci_commit.retried, coverage: @project.build_coverage_enabled?, stage: true, ref: false
|
|
@ -43,12 +43,12 @@
|
|||
- @commit.parents.each do |parent|
|
||||
= link_to parent.short_id, namespace_project_commit_path(@project.namespace, @project, parent), class: "monospace"
|
||||
|
||||
- if @ci_commit
|
||||
- if @commit.status
|
||||
.pull-right
|
||||
= link_to ci_status_path(@ci_commit), class: "ci-status ci-#{@ci_commit.status}" do
|
||||
= ci_status_icon(@ci_commit)
|
||||
= link_to builds_namespace_project_commit_path(@project.namespace, @project, @commit.id), class: "ci-status ci-#{@commit.status}" do
|
||||
= ci_icon_for_status(@commit.status)
|
||||
build:
|
||||
= ci_status_label(@ci_commit)
|
||||
= ci_label_for_status(@commit.status)
|
||||
|
||||
.commit-info-row.branches
|
||||
%i.fa.fa-spinner.fa-spin
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
.prepend-top-default
|
||||
= render "commit_box"
|
||||
- if @ci_commit
|
||||
- if @commit.status
|
||||
= render "ci_menu"
|
||||
- else
|
||||
%div.block-connector
|
||||
|
|
|
@ -4,9 +4,8 @@
|
|||
- notes = commit.notes
|
||||
- note_count = notes.user.count
|
||||
|
||||
- ci_commit = project.ci_commit(commit.sha)
|
||||
- cache_key = [project.path_with_namespace, commit.id, current_application_settings, note_count]
|
||||
- cache_key.push(ci_commit.status) if ci_commit
|
||||
- cache_key.push(commit.status) if commit.status
|
||||
|
||||
= cache(cache_key) do
|
||||
%li.commit.js-toggle-container{ id: "commit-#{commit.short_id}" }
|
||||
|
@ -17,8 +16,8 @@
|
|||
%a.text-expander.js-toggle-button ...
|
||||
|
||||
.pull-right
|
||||
- if ci_commit
|
||||
= render_ci_status(ci_commit)
|
||||
- if commit.status
|
||||
= render_ci_status(commit)
|
||||
= clipboard_button(clipboard_text: commit.id)
|
||||
= link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit_short_id"
|
||||
|
||||
|
|
|
@ -15,12 +15,13 @@
|
|||
- if defined?(commit_sha) && commit_sha
|
||||
%td
|
||||
= link_to generic_commit_status.short_sha, namespace_project_commit_path(generic_commit_status.project.namespace, generic_commit_status.project, generic_commit_status.sha), class: "monospace"
|
||||
|
||||
%td
|
||||
- if generic_commit_status.ref
|
||||
= link_to generic_commit_status.ref, namespace_project_commits_path(generic_commit_status.project.namespace, generic_commit_status.project, generic_commit_status.ref)
|
||||
- else
|
||||
.light none
|
||||
|
||||
- if defined?(ref) && ref
|
||||
%td
|
||||
- if generic_commit_status.ref
|
||||
= link_to generic_commit_status.ref, namespace_project_commits_path(generic_commit_status.project.namespace, generic_commit_status.project, generic_commit_status.ref)
|
||||
- else
|
||||
.light none
|
||||
|
||||
- if defined?(runner) && runner
|
||||
%td
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
- @related_branches.each do |branch|
|
||||
%li
|
||||
- sha = @project.repository.find_branch(branch).target
|
||||
- ci_commit = @project.ci_commit(sha) if sha
|
||||
- ci_commit = @project.ci_commit(sha, branch) if sha
|
||||
- if ci_commit
|
||||
%span.related-branch-ci-status
|
||||
= render_ci_status(ci_commit)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
- page_title "#{@merge_request.title} (#{merge_request.to_reference}", "Merge Requests"
|
||||
- page_title "#{@merge_request.title} (#{@merge_request.to_reference}", "Merge Requests"
|
||||
= render "header_title"
|
||||
|
||||
.merge-request
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
= render "projects/commit/builds", link_to_commit: true
|
||||
= render "projects/commit/ci_commit", ci_commit: @ci_commit, link_to_commit: true
|
||||
|
||||
|
|
|
@ -6,9 +6,8 @@
|
|||
- css_class = '' unless local_assigns[:css_class]
|
||||
- show_last_commit_as_description = false unless local_assigns[:show_last_commit_as_description] == true && project.commit
|
||||
- css_class += " no-description" if project.description.blank? && !show_last_commit_as_description
|
||||
- ci_commit = project.ci_commit(project.commit.sha) if ci && !project.empty_repo? && project.commit
|
||||
- cache_key = [project.namespace, project, controller.controller_name, controller.action_name, current_application_settings, 'v2.3']
|
||||
- cache_key.push(ci_commit.status) if ci_commit
|
||||
- cache_key.push(project.commit.status) if project.commit.try(:status)
|
||||
|
||||
%li.project-row{ class: css_class }
|
||||
= cache(cache_key) do
|
||||
|
@ -16,9 +15,9 @@
|
|||
- if project.main_language
|
||||
%span
|
||||
= project.main_language
|
||||
- if ci_commit
|
||||
- if project.commit.try(:status)
|
||||
%span
|
||||
= render_ci_status(ci_commit)
|
||||
= render_ci_status(project.commit)
|
||||
- if forks
|
||||
%span
|
||||
= icon('code-fork')
|
||||
|
|
|
@ -19,7 +19,7 @@ class Gitlab::Seeder::Builds
|
|||
commits = @project.repository.commits('master', nil, 5)
|
||||
commits_sha = commits.map { |commit| commit.raw.id }
|
||||
commits_sha.map do |sha|
|
||||
@project.ensure_ci_commit(sha)
|
||||
@project.ensure_ci_commit(sha, 'master')
|
||||
end
|
||||
rescue
|
||||
[]
|
||||
|
|
|
@ -4,6 +4,8 @@ class AddTrigramIndexesForSearching < ActiveRecord::Migration
|
|||
def up
|
||||
return unless Gitlab::Database.postgresql?
|
||||
|
||||
create_trigrams_extension
|
||||
|
||||
unless trigrams_enabled?
|
||||
raise 'You must enable the pg_trgm extension. You can do so by running ' \
|
||||
'"CREATE EXTENSION pg_trgm;" as a PostgreSQL super user, this must be ' \
|
||||
|
@ -37,6 +39,15 @@ class AddTrigramIndexesForSearching < ActiveRecord::Migration
|
|||
row && row['enabled'] == 't' ? true : false
|
||||
end
|
||||
|
||||
def create_trigrams_extension
|
||||
# This may not work if the user doesn't have permission. We attempt in
|
||||
# case we do have permission, particularly for test/dev environments.
|
||||
begin
|
||||
enable_extension 'pg_trgm'
|
||||
rescue
|
||||
end
|
||||
end
|
||||
|
||||
def to_index
|
||||
{
|
||||
ci_runners: [:token, :description],
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
class AddFieldsToCiCommit < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :ci_commits, :status, :string
|
||||
add_column :ci_commits, :started_at, :timestamp
|
||||
add_column :ci_commits, :finished_at, :timestamp
|
||||
add_column :ci_commits, :duration, :integer
|
||||
end
|
||||
end
|
|
@ -0,0 +1,35 @@
|
|||
class UpdateCiCommit < ActiveRecord::Migration
|
||||
# This migration can be run online, but needs to be executed for the second time after restarting Unicorn workers
|
||||
# Otherwise Offline migration should be used.
|
||||
def change
|
||||
execute("UPDATE ci_commits SET status=#{status}, ref=#{ref}, tag=#{tag} WHERE status IS NULL")
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def status
|
||||
builds = '(SELECT COUNT(*) FROM ci_builds WHERE ci_builds.commit_id=ci_commits.id)'
|
||||
success = "(SELECT COUNT(*) FROM ci_builds WHERE ci_builds.commit_id=ci_commits.id AND status='success')"
|
||||
ignored = "(SELECT COUNT(*) FROM ci_builds WHERE ci_builds.commit_id=ci_commits.id AND (status='failed' OR status='canceled') AND allow_failure)"
|
||||
pending = "(SELECT COUNT(*) FROM ci_builds WHERE ci_builds.commit_id=ci_commits.id AND status='pending')"
|
||||
running = "(SELECT COUNT(*) FROM ci_builds WHERE ci_builds.commit_id=ci_commits.id AND status='running')"
|
||||
canceled = "(SELECT COUNT(*) FROM ci_builds WHERE ci_builds.commit_id=ci_commits.id AND status='canceled')"
|
||||
|
||||
"(CASE
|
||||
WHEN #{builds}=0 THEN 'skipped'
|
||||
WHEN #{builds}=#{success}+#{ignored} THEN 'success'
|
||||
WHEN #{builds}=#{pending} THEN 'pending'
|
||||
WHEN #{builds}=#{canceled} THEN 'canceled'
|
||||
WHEN #{running}+#{pending}>0 THEN 'running'
|
||||
ELSE 'failed'
|
||||
END)"
|
||||
end
|
||||
|
||||
def ref
|
||||
'(SELECT ref FROM ci_builds WHERE ci_builds.commit_id=ci_commits.id ORDER BY id DESC LIMIT 1)'
|
||||
end
|
||||
|
||||
def tag
|
||||
'(SELECT tag FROM ci_builds WHERE ci_builds.commit_id=ci_commits.id ORDER BY id DESC LIMIT 1)'
|
||||
end
|
||||
end
|
|
@ -0,0 +1,19 @@
|
|||
class AddCiCommitIndexes < ActiveRecord::Migration
|
||||
disable_ddl_transaction!
|
||||
|
||||
def change
|
||||
add_index :ci_commits, [:gl_project_id, :sha], index_options
|
||||
add_index :ci_commits, [:gl_project_id, :status], index_options
|
||||
add_index :ci_commits, [:status], index_options
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def index_options
|
||||
if Gitlab::Database.postgresql?
|
||||
{ algorithm: :concurrently }
|
||||
else
|
||||
{ }
|
||||
end
|
||||
end
|
||||
end
|
|
@ -171,14 +171,21 @@ ActiveRecord::Schema.define(version: 20160419120017) do
|
|||
t.text "yaml_errors"
|
||||
t.datetime "committed_at"
|
||||
t.integer "gl_project_id"
|
||||
t.string "status"
|
||||
t.datetime "started_at"
|
||||
t.datetime "finished_at"
|
||||
t.integer "duration"
|
||||
end
|
||||
|
||||
add_index "ci_commits", ["gl_project_id", "sha"], name: "index_ci_commits_on_gl_project_id_and_sha", using: :btree
|
||||
add_index "ci_commits", ["gl_project_id", "status"], name: "index_ci_commits_on_gl_project_id_and_status", using: :btree
|
||||
add_index "ci_commits", ["gl_project_id"], name: "index_ci_commits_on_gl_project_id", using: :btree
|
||||
add_index "ci_commits", ["project_id", "committed_at", "id"], name: "index_ci_commits_on_project_id_and_committed_at_and_id", using: :btree
|
||||
add_index "ci_commits", ["project_id", "committed_at"], name: "index_ci_commits_on_project_id_and_committed_at", using: :btree
|
||||
add_index "ci_commits", ["project_id", "sha"], name: "index_ci_commits_on_project_id_and_sha", using: :btree
|
||||
add_index "ci_commits", ["project_id"], name: "index_ci_commits_on_project_id", using: :btree
|
||||
add_index "ci_commits", ["sha"], name: "index_ci_commits_on_sha", using: :btree
|
||||
add_index "ci_commits", ["status"], name: "index_ci_commits_on_status", using: :btree
|
||||
|
||||
create_table "ci_events", force: :cascade do |t|
|
||||
t.integer "project_id"
|
||||
|
|
|
@ -22,4 +22,8 @@ class Spinach::Features::ProfileActiveTab < Spinach::FeatureSteps
|
|||
step 'the active main tab should be Audit Log' do
|
||||
ensure_active_main_tab('Audit Log')
|
||||
end
|
||||
|
||||
def ensure_active_main_tab(content)
|
||||
expect(find('.layout-nav li.active')).to have_content(content)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -519,7 +519,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
|
|||
step '"Bug NS-05" has CI status' do
|
||||
project = merge_request.source_project
|
||||
project.enable_ci
|
||||
ci_commit = create :ci_commit, project: project, sha: merge_request.last_commit.id
|
||||
ci_commit = create :ci_commit, project: project, sha: merge_request.last_commit.id, ref: merge_request.source_branch
|
||||
create :ci_build, commit: ci_commit
|
||||
end
|
||||
|
||||
|
|
|
@ -10,16 +10,16 @@ module SharedBuilds
|
|||
end
|
||||
|
||||
step 'project has a recent build' do
|
||||
@ci_commit = create(:ci_commit, project: @project, sha: @project.commit.sha)
|
||||
@ci_commit = create(:ci_commit, project: @project, sha: @project.commit.sha, ref: 'master')
|
||||
@build = create(:ci_build_with_coverage, commit: @ci_commit)
|
||||
end
|
||||
|
||||
step 'recent build is successful' do
|
||||
@build.update_column(:status, 'success')
|
||||
@build.update(status: 'success')
|
||||
end
|
||||
|
||||
step 'recent build failed' do
|
||||
@build.update_column(:status, 'failed')
|
||||
@build.update(status: 'failed')
|
||||
end
|
||||
|
||||
step 'project has another build that is running' do
|
||||
|
|
|
@ -234,7 +234,7 @@ module SharedProject
|
|||
|
||||
step 'project "Shop" has CI build' do
|
||||
project = Project.find_by(name: "Shop")
|
||||
create :ci_commit, project: project, sha: project.commit.sha
|
||||
create :ci_commit, project: project, sha: project.commit.sha, ref: 'master', status: 'skipped'
|
||||
end
|
||||
|
||||
step 'I should see last commit with CI status' do
|
||||
|
|
|
@ -21,10 +21,9 @@ module API
|
|||
authorize!(:read_commit_status, user_project)
|
||||
|
||||
not_found!('Commit') unless user_project.commit(params[:sha])
|
||||
ci_commit = user_project.ci_commit(params[:sha])
|
||||
return [] unless ci_commit
|
||||
|
||||
statuses = ci_commit.statuses
|
||||
ci_commits = user_project.ci_commits.where(sha: params[:sha])
|
||||
statuses = ::CommitStatus.where(commit: ci_commits)
|
||||
statuses = statuses.latest unless parse_boolean(params[:all])
|
||||
statuses = statuses.where(ref: params[:ref]) if params[:ref].present?
|
||||
statuses = statuses.where(stage: params[:stage]) if params[:stage].present?
|
||||
|
@ -51,7 +50,21 @@ module API
|
|||
commit = @project.commit(params[:sha])
|
||||
not_found! 'Commit' unless commit
|
||||
|
||||
ci_commit = @project.ensure_ci_commit(commit.sha)
|
||||
# Since the CommitStatus is attached to Ci::Commit (in the future Pipeline)
|
||||
# We need to always have the pipeline object
|
||||
# To have a valid pipeline object that can be attached to specific MR
|
||||
# Other CI service needs to send `ref`
|
||||
# If we don't receive it, we will attach the CommitStatus to
|
||||
# the first found branch on that commit
|
||||
|
||||
ref = params[:ref]
|
||||
unless ref
|
||||
branches = @project.repository.branch_names_contains(commit.sha)
|
||||
not_found! 'References for commit' if branches.none?
|
||||
ref = branches.first
|
||||
end
|
||||
|
||||
ci_commit = @project.ensure_ci_commit(commit.sha, ref)
|
||||
|
||||
name = params[:name] || params[:context]
|
||||
status = GenericCommitStatus.running_or_pending.find_by(commit: ci_commit, name: name, ref: params[:ref])
|
||||
|
|
|
@ -18,9 +18,7 @@ module Banzai
|
|||
|
||||
def references_in(text, pattern = Label.reference_pattern)
|
||||
text.gsub(pattern) do |match|
|
||||
project = project_from_ref($~[:project])
|
||||
params = label_params($~[:label_id].to_i, $~[:label_name])
|
||||
label = project.labels.find_by(params)
|
||||
label = find_label($~[:project], $~[:label_id], $~[:label_name])
|
||||
|
||||
if label
|
||||
yield match, label.id, $~[:project], $~
|
||||
|
@ -30,6 +28,29 @@ module Banzai
|
|||
end
|
||||
end
|
||||
|
||||
def find_label(project_ref, label_id, label_name)
|
||||
project = project_from_ref(project_ref)
|
||||
return unless project
|
||||
|
||||
label_params = label_params(label_id, label_name)
|
||||
project.labels.find_by(label_params)
|
||||
end
|
||||
|
||||
# Parameters to pass to `Label.find_by` based on the given arguments
|
||||
#
|
||||
# id - Integer ID to pass. If present, returns {id: id}
|
||||
# name - String name to pass. If `id` is absent, finds by name without
|
||||
# surrounding quotes.
|
||||
#
|
||||
# Returns a Hash.
|
||||
def label_params(id, name)
|
||||
if name
|
||||
{ name: name.tr('"', '') }
|
||||
else
|
||||
{ id: id.to_i }
|
||||
end
|
||||
end
|
||||
|
||||
def url_for_object(label, project)
|
||||
h = Gitlab::Routing.url_helpers
|
||||
h.namespace_project_issues_url(project.namespace, project, label_name: label.name,
|
||||
|
@ -43,21 +64,6 @@ module Banzai
|
|||
LabelsHelper.render_colored_cross_project_label(object)
|
||||
end
|
||||
end
|
||||
|
||||
# Parameters to pass to `Label.find_by` based on the given arguments
|
||||
#
|
||||
# id - Integer ID to pass. If present, returns {id: id}
|
||||
# name - String name to pass. If `id` is absent, finds by name without
|
||||
# surrounding quotes.
|
||||
#
|
||||
# Returns a Hash.
|
||||
def label_params(id, name)
|
||||
if name
|
||||
{ name: name.tr('"', '') }
|
||||
else
|
||||
{ id: id }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
module Ci
|
||||
class Status
|
||||
def self.get_status(statuses)
|
||||
if statuses.none?
|
||||
'skipped'
|
||||
elsif statuses.all? { |status| status.success? || status.ignored? }
|
||||
'success'
|
||||
elsif statuses.all?(&:pending?)
|
||||
'pending'
|
||||
elsif statuses.any?(&:running?) || statuses.any?(&:pending?)
|
||||
'running'
|
||||
elsif statuses.all?(&:canceled?)
|
||||
'canceled'
|
||||
else
|
||||
'failed'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -8,7 +8,6 @@ module Gitlab
|
|||
gon.relative_url_root = Gitlab.config.gitlab.relative_url_root
|
||||
gon.shortcuts_path = help_shortcuts_path
|
||||
gon.user_color_scheme = Gitlab::ColorSchemes.for_user(current_user).css_class
|
||||
gon.sentry_dsn = ApplicationSetting.current.sentry_dsn if Rails.env.production?
|
||||
|
||||
if current_user
|
||||
gon.current_user_id = current_user.id
|
||||
|
|
|
@ -29,8 +29,8 @@ module Gitlab
|
|||
"in #{GRACE_TIME} seconds"
|
||||
sleep(GRACE_TIME)
|
||||
|
||||
Sidekiq.logger.warn "sending SIGUSR1 to PID #{Process.pid}"
|
||||
Process.kill('SIGUSR1', Process.pid)
|
||||
Sidekiq.logger.warn "sending SIGTERM to PID #{Process.pid}"
|
||||
Process.kill('SIGTERM', Process.pid)
|
||||
|
||||
Sidekiq.logger.warn "waiting #{SHUTDOWN_WAIT} seconds before sending "\
|
||||
"#{SHUTDOWN_SIGNAL} to PID #{Process.pid}"
|
||||
|
|
|
@ -46,4 +46,20 @@ describe Projects::ProjectMembersController do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#index' do
|
||||
let(:project) { create(:project, :private) }
|
||||
|
||||
context 'when user is member' do
|
||||
let(:member) { create(:user) }
|
||||
|
||||
before do
|
||||
project.team << [member, :guest]
|
||||
sign_in(member)
|
||||
get :index, namespace_id: project.namespace.to_param, project_id: project.to_param
|
||||
end
|
||||
|
||||
it { expect(response.status).to eq(200) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,26 +1,26 @@
|
|||
require 'rails_helper'
|
||||
|
||||
feature 'Issue filtering by Labels', feature: true do
|
||||
include WaitForAjax
|
||||
|
||||
let(:project) { create(:project, :public) }
|
||||
let!(:user) { create(:user)}
|
||||
let!(:label) { create(:label, project: project) }
|
||||
|
||||
before do
|
||||
['bug', 'feature', 'enhancement'].each do |title|
|
||||
create(:label,
|
||||
project: project,
|
||||
title: title)
|
||||
end
|
||||
bug = create(:label, project: project, title: 'bug')
|
||||
feature = create(:label, project: project, title: 'feature')
|
||||
enhancement = create(:label, project: project, title: 'enhancement')
|
||||
|
||||
issue1 = create(:issue, title: "Bugfix1", project: project)
|
||||
issue1.labels << project.labels.find_by(title: 'bug')
|
||||
issue1.labels << bug
|
||||
|
||||
issue2 = create(:issue, title: "Bugfix2", project: project)
|
||||
issue2.labels << project.labels.find_by(title: 'bug')
|
||||
issue2.labels << project.labels.find_by(title: 'enhancement')
|
||||
issue2.labels << bug
|
||||
issue2.labels << enhancement
|
||||
|
||||
issue3 = create(:issue, title: "Feature1", project: project)
|
||||
issue3.labels << project.labels.find_by(title: 'feature')
|
||||
issue3.labels << feature
|
||||
|
||||
project.team << [user, :master]
|
||||
login_as(user)
|
||||
|
@ -31,10 +31,10 @@ feature 'Issue filtering by Labels', feature: true do
|
|||
context 'filter by label bug', js: true do
|
||||
before do
|
||||
page.find('.js-label-select').click
|
||||
sleep 0.5
|
||||
wait_for_ajax
|
||||
execute_script("$('.dropdown-menu-labels li:contains(\"bug\") a').click()")
|
||||
page.first('.labels-filter .dropdown-title .dropdown-menu-close-icon').click
|
||||
sleep 2
|
||||
wait_for_ajax
|
||||
end
|
||||
|
||||
it 'should show issue "Bugfix1" and "Bugfix2" in issues list' do
|
||||
|
@ -59,10 +59,10 @@ feature 'Issue filtering by Labels', feature: true do
|
|||
context 'filter by label feature', js: true do
|
||||
before do
|
||||
page.find('.js-label-select').click
|
||||
sleep 0.5
|
||||
wait_for_ajax
|
||||
execute_script("$('.dropdown-menu-labels li:contains(\"feature\") a').click()")
|
||||
page.first('.labels-filter .dropdown-title .dropdown-menu-close-icon').click
|
||||
sleep 2
|
||||
wait_for_ajax
|
||||
end
|
||||
|
||||
it 'should show issue "Feature1" in issues list' do
|
||||
|
@ -87,10 +87,10 @@ feature 'Issue filtering by Labels', feature: true do
|
|||
context 'filter by label enhancement', js: true do
|
||||
before do
|
||||
page.find('.js-label-select').click
|
||||
sleep 0.5
|
||||
wait_for_ajax
|
||||
execute_script("$('.dropdown-menu-labels li:contains(\"enhancement\") a').click()")
|
||||
page.first('.labels-filter .dropdown-title .dropdown-menu-close-icon').click
|
||||
sleep 2
|
||||
wait_for_ajax
|
||||
end
|
||||
|
||||
it 'should show issue "Bugfix2" in issues list' do
|
||||
|
@ -115,20 +115,16 @@ feature 'Issue filtering by Labels', feature: true do
|
|||
context 'filter by label enhancement or feature', js: true do
|
||||
before do
|
||||
page.find('.js-label-select').click
|
||||
sleep 0.5
|
||||
wait_for_ajax
|
||||
execute_script("$('.dropdown-menu-labels li:contains(\"enhancement\") a').click()")
|
||||
execute_script("$('.dropdown-menu-labels li:contains(\"feature\") a').click()")
|
||||
page.first('.labels-filter .dropdown-title .dropdown-menu-close-icon').click
|
||||
sleep 2
|
||||
wait_for_ajax
|
||||
end
|
||||
|
||||
it 'should show issue "Bugfix2" or "Feature1" in issues list' do
|
||||
expect(page).to have_content "Bugfix2"
|
||||
expect(page).to have_content "Feature1"
|
||||
end
|
||||
|
||||
it 'should not show "Bugfix1" in issues list' do
|
||||
it 'should not show "Bugfix1" or "Feature1" in issues list' do
|
||||
expect(page).not_to have_content "Bugfix1"
|
||||
expect(page).not_to have_content "Feature1"
|
||||
end
|
||||
|
||||
it 'should show label "enhancement" and "feature" in filtered-labels' do
|
||||
|
@ -141,19 +137,18 @@ feature 'Issue filtering by Labels', feature: true do
|
|||
end
|
||||
end
|
||||
|
||||
context 'filter by label enhancement or bug in issues list', js: true do
|
||||
context 'filter by label enhancement and bug in issues list', js: true do
|
||||
before do
|
||||
page.find('.js-label-select').click
|
||||
sleep 0.5
|
||||
wait_for_ajax
|
||||
execute_script("$('.dropdown-menu-labels li:contains(\"enhancement\") a').click()")
|
||||
execute_script("$('.dropdown-menu-labels li:contains(\"bug\") a').click()")
|
||||
page.first('.labels-filter .dropdown-title .dropdown-menu-close-icon').click
|
||||
sleep 2
|
||||
wait_for_ajax
|
||||
end
|
||||
|
||||
it 'should show issue "Bugfix2" or "Bugfix1" in issues list' do
|
||||
it 'should show issue "Bugfix2" in issues list' do
|
||||
expect(page).to have_content "Bugfix2"
|
||||
expect(page).to have_content "Bugfix1"
|
||||
end
|
||||
|
||||
it 'should not show "Feature1"' do
|
||||
|
|
|
@ -101,12 +101,12 @@ describe "Internal Project Access", feature: true do
|
|||
it { is_expected.to be_allowed_for :admin }
|
||||
it { is_expected.to be_allowed_for owner }
|
||||
it { is_expected.to be_allowed_for master }
|
||||
it { is_expected.to be_denied_for developer }
|
||||
it { is_expected.to be_denied_for reporter }
|
||||
it { is_expected.to be_denied_for guest }
|
||||
it { is_expected.to be_denied_for :user }
|
||||
it { is_expected.to be_denied_for :external }
|
||||
it { is_expected.to be_allowed_for developer }
|
||||
it { is_expected.to be_allowed_for reporter }
|
||||
it { is_expected.to be_allowed_for guest }
|
||||
it { is_expected.to be_allowed_for :user }
|
||||
it { is_expected.to be_denied_for :visitor }
|
||||
it { is_expected.to be_denied_for :external }
|
||||
end
|
||||
|
||||
describe "GET /:project_path/blob" do
|
||||
|
|
|
@ -101,9 +101,9 @@ describe "Private Project Access", feature: true do
|
|||
it { is_expected.to be_allowed_for :admin }
|
||||
it { is_expected.to be_allowed_for owner }
|
||||
it { is_expected.to be_allowed_for master }
|
||||
it { is_expected.to be_denied_for developer }
|
||||
it { is_expected.to be_denied_for reporter }
|
||||
it { is_expected.to be_denied_for guest }
|
||||
it { is_expected.to be_allowed_for developer }
|
||||
it { is_expected.to be_allowed_for reporter }
|
||||
it { is_expected.to be_allowed_for guest }
|
||||
it { is_expected.to be_denied_for :user }
|
||||
it { is_expected.to be_denied_for :external }
|
||||
it { is_expected.to be_denied_for :visitor }
|
||||
|
|
|
@ -101,12 +101,12 @@ describe "Public Project Access", feature: true do
|
|||
it { is_expected.to be_allowed_for :admin }
|
||||
it { is_expected.to be_allowed_for owner }
|
||||
it { is_expected.to be_allowed_for master }
|
||||
it { is_expected.to be_denied_for developer }
|
||||
it { is_expected.to be_denied_for reporter }
|
||||
it { is_expected.to be_denied_for guest }
|
||||
it { is_expected.to be_denied_for :user }
|
||||
it { is_expected.to be_denied_for :external }
|
||||
it { is_expected.to be_denied_for :visitor }
|
||||
it { is_expected.to be_allowed_for developer }
|
||||
it { is_expected.to be_allowed_for reporter }
|
||||
it { is_expected.to be_allowed_for guest }
|
||||
it { is_expected.to be_allowed_for :user }
|
||||
it { is_expected.to be_allowed_for :visitor }
|
||||
it { is_expected.to be_allowed_for :external }
|
||||
end
|
||||
|
||||
describe "GET /:project_path/builds" do
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe 'Dashboard Todos', feature: true do
|
||||
let(:user){ create(:user) }
|
||||
let(:author){ create(:user) }
|
||||
let(:project){ create(:project) }
|
||||
let(:issue){ create(:issue) }
|
||||
let(:todos_per_page){ Todo.default_per_page }
|
||||
let(:todos_total){ todos_per_page + 1 }
|
||||
|
||||
describe 'GET /dashboard/todos' do
|
||||
context 'User does not have todos' do
|
||||
before do
|
||||
login_as(user)
|
||||
visit dashboard_todos_path
|
||||
end
|
||||
it 'shows "All done" message' do
|
||||
expect(page).to have_content "You're all done!"
|
||||
end
|
||||
end
|
||||
|
||||
context 'User has a todo', js: true do
|
||||
before do
|
||||
create(:todo, :mentioned, user: user, project: project, target: issue, author: author)
|
||||
login_as(user)
|
||||
visit dashboard_todos_path
|
||||
end
|
||||
|
||||
it 'todo is present' do
|
||||
expect(page).to have_selector('.todos-list .todo', count: 1)
|
||||
end
|
||||
|
||||
describe 'deleting the todo' do
|
||||
before do
|
||||
first('.done-todo').click
|
||||
end
|
||||
|
||||
it 'is removed from the list' do
|
||||
expect(page).not_to have_selector('.todos-list .todo')
|
||||
end
|
||||
|
||||
it 'shows "All done" message' do
|
||||
expect(page).to have_content("You're all done!")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'User has multiple pages of Todos' do
|
||||
let(:todo_total_pages){ (todos_total.to_f/todos_per_page).ceil }
|
||||
|
||||
before do
|
||||
todos_total.times do
|
||||
create(:todo, :mentioned, user: user, project: project, target: issue, author: author)
|
||||
end
|
||||
|
||||
login_as(user)
|
||||
visit dashboard_todos_path
|
||||
end
|
||||
|
||||
it 'is paginated' do
|
||||
expect(page).to have_selector('.gl-pagination')
|
||||
end
|
||||
|
||||
it 'is has the right number of pages' do
|
||||
expect(page).to have_selector('.gl-pagination .page', count: todo_total_pages)
|
||||
end
|
||||
|
||||
describe 'deleting last todo from last page', js: true do
|
||||
it 'redirects to the previous page' do
|
||||
page.within('.gl-pagination') do
|
||||
click_link todo_total_pages.to_s
|
||||
end
|
||||
first('.done-todo').click
|
||||
expect(page).to have_content(Todo.last.body)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -6,8 +6,8 @@ describe CiStatusHelper do
|
|||
let(:success_commit) { double("Ci::Commit", status: 'success') }
|
||||
let(:failed_commit) { double("Ci::Commit", status: 'failed') }
|
||||
|
||||
describe 'ci_status_icon' do
|
||||
it { expect(helper.ci_status_icon(success_commit)).to include('fa-check') }
|
||||
it { expect(helper.ci_status_icon(failed_commit)).to include('fa-close') }
|
||||
describe 'ci_icon_for_status' do
|
||||
it { expect(helper.ci_icon_for_status(success_commit.status)).to include('fa-check') }
|
||||
it { expect(helper.ci_icon_for_status(failed_commit.status)).to include('fa-close') }
|
||||
end
|
||||
end
|
||||
|
|
|
@ -178,27 +178,37 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
|
|||
end
|
||||
|
||||
describe 'cross project label references' do
|
||||
let(:another_project) { create(:empty_project, :public) }
|
||||
let(:project_name) { another_project.name_with_namespace }
|
||||
let(:label) { create(:label, project: another_project, color: '#00ff00') }
|
||||
let(:reference) { label.to_reference(project) }
|
||||
context 'valid project referenced' do
|
||||
let(:another_project) { create(:empty_project, :public) }
|
||||
let(:project_name) { another_project.name_with_namespace }
|
||||
let(:label) { create(:label, project: another_project, color: '#00ff00') }
|
||||
let(:reference) { label.to_reference(project) }
|
||||
|
||||
let!(:result) { reference_filter("See #{reference}") }
|
||||
let!(:result) { reference_filter("See #{reference}") }
|
||||
|
||||
it 'points to referenced project issues page' do
|
||||
expect(result.css('a').first.attr('href'))
|
||||
.to eq urls.namespace_project_issues_url(another_project.namespace,
|
||||
another_project,
|
||||
label_name: label.name)
|
||||
it 'points to referenced project issues page' do
|
||||
expect(result.css('a').first.attr('href'))
|
||||
.to eq urls.namespace_project_issues_url(another_project.namespace,
|
||||
another_project,
|
||||
label_name: label.name)
|
||||
end
|
||||
|
||||
it 'has valid color' do
|
||||
expect(result.css('a span').first.attr('style'))
|
||||
.to match /background-color: #00ff00/
|
||||
end
|
||||
|
||||
it 'contains cross project content' do
|
||||
expect(result.css('a').first.text).to eq "#{label.name} in #{project_name}"
|
||||
end
|
||||
end
|
||||
|
||||
it 'has valid color' do
|
||||
expect(result.css('a span').first.attr('style'))
|
||||
.to match /background-color: #00ff00/
|
||||
end
|
||||
context 'project that does not exist referenced' do
|
||||
let(:result) { reference_filter('aaa/bbb~ccc') }
|
||||
|
||||
it 'contains cross project content' do
|
||||
expect(result.css('a').first.text).to eq "#{label.name} in #{project_name}"
|
||||
it 'does not link reference' do
|
||||
expect(result.to_html).to eq 'aaa/bbb~ccc'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -42,7 +42,7 @@ describe Gitlab::Badge::Build do
|
|||
end
|
||||
|
||||
context 'build exists' do
|
||||
let(:ci_commit) { create(:ci_commit, project: project, sha: sha) }
|
||||
let(:ci_commit) { create(:ci_commit, project: project, sha: sha, ref: branch) }
|
||||
let!(:build) { create(:ci_build, commit: ci_commit) }
|
||||
|
||||
|
||||
|
@ -57,7 +57,7 @@ describe Gitlab::Badge::Build do
|
|||
describe '#data' do
|
||||
let(:data) { badge.data }
|
||||
|
||||
it 'contains infromation about success' do
|
||||
it 'contains information about success' do
|
||||
expect(status_node(data, 'success')).to be_truthy
|
||||
end
|
||||
end
|
||||
|
@ -74,7 +74,7 @@ describe Gitlab::Badge::Build do
|
|||
describe '#data' do
|
||||
let(:data) { badge.data }
|
||||
|
||||
it 'contains infromation about failure' do
|
||||
it 'contains information about failure' do
|
||||
expect(status_node(data, 'failed')).to be_truthy
|
||||
end
|
||||
end
|
||||
|
|
|
@ -27,6 +27,8 @@ describe Ci::Commit, models: true do
|
|||
it { is_expected.to have_many(:trigger_requests) }
|
||||
it { is_expected.to have_many(:builds) }
|
||||
it { is_expected.to validate_presence_of :sha }
|
||||
it { is_expected.to validate_presence_of :status }
|
||||
it { is_expected.to delegate_method(:stages).to(:statuses) }
|
||||
|
||||
it { is_expected.to respond_to :git_author_name }
|
||||
it { is_expected.to respond_to :git_author_email }
|
||||
|
@ -52,57 +54,9 @@ describe Ci::Commit, models: true do
|
|||
it { expect(commit.sha).to start_with(subject) }
|
||||
end
|
||||
|
||||
describe :stage do
|
||||
subject { commit.stage }
|
||||
|
||||
before do
|
||||
@second = FactoryGirl.create :commit_status, commit: commit, name: 'deploy', stage: 'deploy', stage_idx: 1, status: 'pending'
|
||||
@first = FactoryGirl.create :commit_status, commit: commit, name: 'test', stage: 'test', stage_idx: 0, status: 'pending'
|
||||
end
|
||||
|
||||
it 'returns first running stage' do
|
||||
is_expected.to eq('test')
|
||||
end
|
||||
|
||||
context 'first build succeeded' do
|
||||
before do
|
||||
@first.success
|
||||
end
|
||||
|
||||
it 'returns last running stage' do
|
||||
is_expected.to eq('deploy')
|
||||
end
|
||||
end
|
||||
|
||||
context 'all builds succeeded' do
|
||||
before do
|
||||
@first.success
|
||||
@second.success
|
||||
end
|
||||
|
||||
it 'returns nil' do
|
||||
is_expected.to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe :create_next_builds do
|
||||
end
|
||||
|
||||
describe :refs do
|
||||
subject { commit.refs }
|
||||
|
||||
before do
|
||||
FactoryGirl.create :commit_status, commit: commit, name: 'deploy'
|
||||
FactoryGirl.create :commit_status, commit: commit, name: 'deploy', ref: 'develop'
|
||||
FactoryGirl.create :commit_status, commit: commit, name: 'deploy', ref: 'master'
|
||||
end
|
||||
|
||||
it 'returns all refs' do
|
||||
is_expected.to contain_exactly('master', 'develop', nil)
|
||||
end
|
||||
end
|
||||
|
||||
describe :retried do
|
||||
subject { commit.retried }
|
||||
|
||||
|
@ -117,10 +71,10 @@ describe Ci::Commit, models: true do
|
|||
end
|
||||
|
||||
describe :create_builds do
|
||||
let!(:commit) { FactoryGirl.create :ci_commit, project: project }
|
||||
let!(:commit) { FactoryGirl.create :ci_commit, project: project, ref: 'master', tag: false }
|
||||
|
||||
def create_builds(trigger_request = nil)
|
||||
commit.create_builds('master', false, nil, trigger_request)
|
||||
commit.create_builds(nil, trigger_request)
|
||||
end
|
||||
|
||||
def create_next_builds
|
||||
|
@ -143,67 +97,6 @@ describe Ci::Commit, models: true do
|
|||
expect(create_next_builds).to be_falsey
|
||||
end
|
||||
|
||||
context 'for different ref' do
|
||||
def create_develop_builds
|
||||
commit.create_builds('develop', false, nil, nil)
|
||||
end
|
||||
|
||||
it 'creates builds' do
|
||||
expect(create_builds).to be_truthy
|
||||
commit.builds.update_all(status: "success")
|
||||
expect(commit.builds.count(:all)).to eq(2)
|
||||
|
||||
expect(create_develop_builds).to be_truthy
|
||||
commit.builds.update_all(status: "success")
|
||||
expect(commit.builds.count(:all)).to eq(4)
|
||||
expect(commit.refs.size).to eq(2)
|
||||
expect(commit.builds.pluck(:name).uniq.size).to eq(2)
|
||||
end
|
||||
end
|
||||
|
||||
context 'for build triggers' do
|
||||
let(:trigger) { FactoryGirl.create :ci_trigger, project: project }
|
||||
let(:trigger_request) { FactoryGirl.create :ci_trigger_request, commit: commit, trigger: trigger }
|
||||
|
||||
it 'creates builds' do
|
||||
expect(create_builds(trigger_request)).to be_truthy
|
||||
expect(commit.builds.count(:all)).to eq(2)
|
||||
end
|
||||
|
||||
it 'rebuilds commit' do
|
||||
expect(create_builds).to be_truthy
|
||||
expect(commit.builds.count(:all)).to eq(2)
|
||||
|
||||
expect(create_builds(trigger_request)).to be_truthy
|
||||
expect(commit.builds.count(:all)).to eq(4)
|
||||
end
|
||||
|
||||
it 'creates next builds' do
|
||||
expect(create_builds(trigger_request)).to be_truthy
|
||||
expect(commit.builds.count(:all)).to eq(2)
|
||||
commit.builds.update_all(status: "success")
|
||||
|
||||
expect(create_next_builds).to be_truthy
|
||||
expect(commit.builds.count(:all)).to eq(4)
|
||||
end
|
||||
|
||||
context 'for [ci skip]' do
|
||||
before do
|
||||
allow(commit).to receive(:git_commit_message) { 'message [ci skip]' }
|
||||
end
|
||||
|
||||
it 'rebuilds commit' do
|
||||
expect(commit.status).to eq('skipped')
|
||||
expect(create_builds).to be_truthy
|
||||
|
||||
# since everything in Ci::Commit is cached we need to fetch a new object
|
||||
new_commit = Ci::Commit.find_by_id(commit.id)
|
||||
expect(new_commit.status).to eq('pending')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
context 'custom stage with first job allowed to fail' do
|
||||
let(:yaml) do
|
||||
{
|
||||
|
@ -284,6 +177,7 @@ describe Ci::Commit, models: true do
|
|||
commit.builds.running_or_pending.each(&:success)
|
||||
|
||||
expect(commit.builds.pluck(:status)).to contain_exactly('success', 'success', 'success', 'success')
|
||||
commit.reload
|
||||
expect(commit.status).to eq('success')
|
||||
end
|
||||
|
||||
|
@ -306,6 +200,7 @@ describe Ci::Commit, models: true do
|
|||
commit.builds.running_or_pending.each(&:success)
|
||||
|
||||
expect(commit.builds.pluck(:status)).to contain_exactly('success', 'failed', 'success', 'success')
|
||||
commit.reload
|
||||
expect(commit.status).to eq('failed')
|
||||
end
|
||||
|
||||
|
@ -329,6 +224,7 @@ describe Ci::Commit, models: true do
|
|||
|
||||
expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test', 'test_failure', 'cleanup')
|
||||
expect(commit.builds.pluck(:status)).to contain_exactly('success', 'failed', 'failed', 'success')
|
||||
commit.reload
|
||||
expect(commit.status).to eq('failed')
|
||||
end
|
||||
|
||||
|
@ -351,6 +247,7 @@ describe Ci::Commit, models: true do
|
|||
commit.builds.running_or_pending.each(&:success)
|
||||
|
||||
expect(commit.builds.pluck(:status)).to contain_exactly('success', 'success', 'failed', 'success')
|
||||
commit.reload
|
||||
expect(commit.status).to eq('failed')
|
||||
end
|
||||
end
|
||||
|
@ -402,4 +299,98 @@ describe Ci::Commit, models: true do
|
|||
expect(commit.coverage).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
describe '#retryable?' do
|
||||
subject { commit.retryable? }
|
||||
|
||||
context 'no failed builds' do
|
||||
before do
|
||||
FactoryGirl.create :ci_build, name: "rspec", commit: commit, status: 'success'
|
||||
end
|
||||
|
||||
it 'be not retryable' do
|
||||
is_expected.to be_falsey
|
||||
end
|
||||
end
|
||||
|
||||
context 'with failed builds' do
|
||||
before do
|
||||
FactoryGirl.create :ci_build, name: "rspec", commit: commit, status: 'running'
|
||||
FactoryGirl.create :ci_build, name: "rubocop", commit: commit, status: 'failed'
|
||||
end
|
||||
|
||||
it 'be retryable' do
|
||||
is_expected.to be_truthy
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#stages' do
|
||||
let(:commit2) { FactoryGirl.create :ci_commit, project: project }
|
||||
subject { CommitStatus.where(commit: [commit, commit2]).stages }
|
||||
|
||||
before do
|
||||
FactoryGirl.create :ci_build, commit: commit2, stage: 'test', stage_idx: 1
|
||||
FactoryGirl.create :ci_build, commit: commit, stage: 'build', stage_idx: 0
|
||||
end
|
||||
|
||||
it 'return all stages' do
|
||||
is_expected.to eq(%w(build test))
|
||||
end
|
||||
end
|
||||
|
||||
describe '#update_state' do
|
||||
it 'execute update_state after touching object' do
|
||||
expect(commit).to receive(:update_state).and_return(true)
|
||||
commit.touch
|
||||
end
|
||||
|
||||
context 'dependent objects' do
|
||||
let(:commit_status) { build :commit_status, commit: commit }
|
||||
|
||||
it 'execute update_state after saving dependent object' do
|
||||
expect(commit).to receive(:update_state).and_return(true)
|
||||
commit_status.save
|
||||
end
|
||||
end
|
||||
|
||||
context 'update state' do
|
||||
let(:current) { Time.now.change(usec: 0) }
|
||||
let(:build) { FactoryGirl.create :ci_build, :success, commit: commit, started_at: current - 120, finished_at: current - 60 }
|
||||
|
||||
before do
|
||||
build
|
||||
end
|
||||
|
||||
[:status, :started_at, :finished_at, :duration].each do |param|
|
||||
it "update #{param}" do
|
||||
expect(commit.send(param)).to eq(build.send(param))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#branch?' do
|
||||
subject { commit.branch? }
|
||||
|
||||
context 'is not a tag' do
|
||||
before do
|
||||
commit.tag = false
|
||||
end
|
||||
|
||||
it 'return true when tag is set to false' do
|
||||
is_expected.to be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
context 'is not a tag' do
|
||||
before do
|
||||
commit.tag = true
|
||||
end
|
||||
|
||||
it 'return false when tag is set to true' do
|
||||
is_expected.to be_falsey
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -163,4 +163,12 @@ eos
|
|||
it { expect(commit.reverts_commit?(another_commit)).to be_truthy }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#ci_commits' do
|
||||
# TODO: kamil
|
||||
end
|
||||
|
||||
describe '#status' do
|
||||
# TODO: kamil
|
||||
end
|
||||
end
|
||||
|
|
|
@ -163,21 +163,7 @@ describe CommitStatus, models: true do
|
|||
end
|
||||
|
||||
it 'return unique statuses' do
|
||||
is_expected.to eq([@commit2, @commit3, @commit4, @commit5])
|
||||
end
|
||||
end
|
||||
|
||||
describe :for_ref do
|
||||
subject { CommitStatus.for_ref('bb').order(:id) }
|
||||
|
||||
before do
|
||||
@commit1 = FactoryGirl.create :commit_status, commit: commit, name: 'aa', ref: 'bb', status: 'running'
|
||||
@commit2 = FactoryGirl.create :commit_status, commit: commit, name: 'cc', ref: 'cc', status: 'pending'
|
||||
@commit3 = FactoryGirl.create :commit_status, commit: commit, name: 'aa', ref: nil, status: 'success'
|
||||
end
|
||||
|
||||
it 'return statuses with equal and nil ref set' do
|
||||
is_expected.to eq([@commit1])
|
||||
is_expected.to eq([@commit4, @commit5])
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -196,4 +182,54 @@ describe CommitStatus, models: true do
|
|||
is_expected.to eq([@commit1, @commit2])
|
||||
end
|
||||
end
|
||||
|
||||
describe '#before_sha' do
|
||||
subject { commit_status.before_sha }
|
||||
|
||||
context 'when no before_sha is set for ci::commit' do
|
||||
before { commit.before_sha = nil }
|
||||
|
||||
it 'return blank sha' do
|
||||
is_expected.to eq(Gitlab::Git::BLANK_SHA)
|
||||
end
|
||||
end
|
||||
|
||||
context 'for before_sha set for ci::commit' do
|
||||
let(:value) { '1234' }
|
||||
before { commit.before_sha = value }
|
||||
|
||||
it 'return the set value' do
|
||||
is_expected.to eq(value)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#stages' do
|
||||
before do
|
||||
FactoryGirl.create :commit_status, commit: commit, stage: 'build', stage_idx: 0, status: 'success'
|
||||
FactoryGirl.create :commit_status, commit: commit, stage: 'build', stage_idx: 0, status: 'failed'
|
||||
FactoryGirl.create :commit_status, commit: commit, stage: 'deploy', stage_idx: 2, status: 'running'
|
||||
FactoryGirl.create :commit_status, commit: commit, stage: 'test', stage_idx: 1, status: 'success'
|
||||
end
|
||||
|
||||
context 'stages list' do
|
||||
subject { CommitStatus.where(commit: commit).stages }
|
||||
|
||||
it 'return ordered list of stages' do
|
||||
is_expected.to eq(%w(build test deploy))
|
||||
end
|
||||
end
|
||||
|
||||
context 'stages with statuses' do
|
||||
subject { CommitStatus.where(commit: commit).stages_status }
|
||||
|
||||
it 'return list of stages with statuses' do
|
||||
is_expected.to eq({
|
||||
'build' => 'failed',
|
||||
'test' => 'success',
|
||||
'deploy' => 'running'
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -212,4 +212,34 @@ describe Issue, "Issuable" do
|
|||
expect(issue.downvotes).to eq(1)
|
||||
end
|
||||
end
|
||||
|
||||
describe ".with_label" do
|
||||
let(:project) { create(:project, :public) }
|
||||
let(:bug) { create(:label, project: project, title: 'bug') }
|
||||
let(:feature) { create(:label, project: project, title: 'feature') }
|
||||
let(:enhancement) { create(:label, project: project, title: 'enhancement') }
|
||||
let(:issue1) { create(:issue, title: "Bugfix1", project: project) }
|
||||
let(:issue2) { create(:issue, title: "Bugfix2", project: project) }
|
||||
let(:issue3) { create(:issue, title: "Feature1", project: project) }
|
||||
|
||||
before(:each) do
|
||||
issue1.labels << bug
|
||||
issue1.labels << feature
|
||||
issue2.labels << bug
|
||||
issue2.labels << enhancement
|
||||
issue3.labels << feature
|
||||
end
|
||||
|
||||
it 'finds the correct issue containing just enhancement label' do
|
||||
expect(Issue.with_label(enhancement.title)).to match_array([issue2])
|
||||
end
|
||||
|
||||
it 'finds the correct issues containing the same label' do
|
||||
expect(Issue.with_label(bug.title)).to match_array([issue1, issue2])
|
||||
end
|
||||
|
||||
it 'finds the correct issues containing only both labels' do
|
||||
expect(Issue.with_label([bug.title, enhancement.title])).to match_array([issue2])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,8 +1,17 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Ci::Status do
|
||||
describe '.get_status' do
|
||||
subject { described_class.get_status(statuses) }
|
||||
describe Statuseable do
|
||||
before do
|
||||
@object = Object.new
|
||||
@object.extend(Statuseable::ClassMethods)
|
||||
end
|
||||
|
||||
describe '.status' do
|
||||
before do
|
||||
allow(@object).to receive(:all).and_return(CommitStatus.where(id: statuses))
|
||||
end
|
||||
|
||||
subject { @object.status }
|
||||
|
||||
shared_examples 'build status summary' do
|
||||
context 'all successful' do
|
|
@ -404,12 +404,12 @@ describe MergeRequest, models: true do
|
|||
describe 'when the source project exists' do
|
||||
it 'returns the latest commit' do
|
||||
commit = double(:commit, id: '123abc')
|
||||
ci_commit = double(:ci_commit)
|
||||
ci_commit = double(:ci_commit, ref: 'master')
|
||||
|
||||
allow(subject).to receive(:last_commit).and_return(commit)
|
||||
|
||||
expect(subject.source_project).to receive(:ci_commit).
|
||||
with('123abc').
|
||||
with('123abc', 'master').
|
||||
and_return(ci_commit)
|
||||
|
||||
expect(subject.ci_commit).to eq(ci_commit)
|
||||
|
|
|
@ -441,9 +441,22 @@ describe Project, models: true do
|
|||
|
||||
describe :ci_commit do
|
||||
let(:project) { create :project }
|
||||
let(:commit) { create :ci_commit, project: project }
|
||||
let(:commit) { create :ci_commit, project: project, ref: 'master' }
|
||||
|
||||
it { expect(project.ci_commit(commit.sha)).to eq(commit) }
|
||||
subject { project.ci_commit(commit.sha, 'master') }
|
||||
|
||||
it { is_expected.to eq(commit) }
|
||||
|
||||
context 'return latest' do
|
||||
let(:commit2) { create :ci_commit, project: project, ref: 'master' }
|
||||
|
||||
before do
|
||||
commit
|
||||
commit2
|
||||
end
|
||||
|
||||
it { is_expected.to eq(commit2) }
|
||||
end
|
||||
end
|
||||
|
||||
describe :builds_enabled do
|
||||
|
|
|
@ -59,7 +59,7 @@ describe API::API, api: true do
|
|||
|
||||
describe 'GET /projects/:id/repository/commits/:sha/builds' do
|
||||
before do
|
||||
project.ensure_ci_commit(commit.sha)
|
||||
project.ensure_ci_commit(commit.sha, 'master')
|
||||
get api("/projects/#{project.id}/repository/commits/#{commit.sha}/builds", api_user)
|
||||
end
|
||||
|
||||
|
|
|
@ -16,7 +16,8 @@ describe API::CommitStatus, api: true do
|
|||
let(:get_url) { "/projects/#{project.id}/repository/commits/#{sha}/statuses" }
|
||||
|
||||
context 'ci commit exists' do
|
||||
let!(:ci_commit) { project.ensure_ci_commit(commit.id) }
|
||||
let!(:master) { project.ci_commits.create(sha: commit.id, ref: 'master') }
|
||||
let!(:develop) { project.ci_commits.create(sha: commit.id, ref: 'develop') }
|
||||
|
||||
it_behaves_like 'a paginated resources' do
|
||||
let(:request) { get api(get_url, reporter) }
|
||||
|
@ -25,16 +26,16 @@ describe API::CommitStatus, api: true do
|
|||
context "reporter user" do
|
||||
let(:statuses_id) { json_response.map { |status| status['id'] } }
|
||||
|
||||
def create_status(opts = {})
|
||||
create(:commit_status, { commit: ci_commit }.merge(opts))
|
||||
def create_status(commit, opts = {})
|
||||
create(:commit_status, { commit: commit, ref: commit.ref }.merge(opts))
|
||||
end
|
||||
|
||||
let!(:status1) { create_status(status: 'running') }
|
||||
let!(:status2) { create_status(name: 'coverage', status: 'pending') }
|
||||
let!(:status3) { create_status(ref: 'develop', status: 'running', allow_failure: true) }
|
||||
let!(:status4) { create_status(name: 'coverage', status: 'success') }
|
||||
let!(:status5) { create_status(name: 'coverage', ref: 'develop', status: 'success') }
|
||||
let!(:status6) { create_status(status: 'success') }
|
||||
let!(:status1) { create_status(master, status: 'running') }
|
||||
let!(:status2) { create_status(master, name: 'coverage', status: 'pending') }
|
||||
let!(:status3) { create_status(develop, status: 'running', allow_failure: true) }
|
||||
let!(:status4) { create_status(master, name: 'coverage', status: 'success') }
|
||||
let!(:status5) { create_status(develop, name: 'coverage', status: 'success') }
|
||||
let!(:status6) { create_status(master, status: 'success') }
|
||||
|
||||
context 'latest commit statuses' do
|
||||
before { get api(get_url, reporter) }
|
||||
|
|
|
@ -48,14 +48,14 @@ describe API::API, api: true do
|
|||
expect(response.status).to eq(404)
|
||||
end
|
||||
|
||||
it "should return not_found for CI status" do
|
||||
it "should return nil for commit without CI" do
|
||||
get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}", user)
|
||||
expect(response.status).to eq(200)
|
||||
expect(json_response['status']).to eq('not_found')
|
||||
expect(json_response['status']).to be_nil
|
||||
end
|
||||
|
||||
it "should return status for CI" do
|
||||
ci_commit = project.ensure_ci_commit(project.repository.commit.sha)
|
||||
ci_commit = project.ensure_ci_commit(project.repository.commit.sha, 'master')
|
||||
get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}", user)
|
||||
expect(response.status).to eq(200)
|
||||
expect(json_response['status']).to eq(ci_commit.status)
|
||||
|
|
|
@ -20,8 +20,8 @@ describe Ci::API::API do
|
|||
|
||||
describe "POST /builds/register" do
|
||||
it "should start a build" do
|
||||
commit = FactoryGirl.create(:ci_commit, project: project)
|
||||
commit.create_builds('master', false, nil)
|
||||
commit = FactoryGirl.create(:ci_commit, project: project, ref: 'master')
|
||||
commit.create_builds(nil)
|
||||
build = commit.builds.first
|
||||
|
||||
post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin }
|
||||
|
@ -56,8 +56,8 @@ describe Ci::API::API do
|
|||
end
|
||||
|
||||
it "returns options" do
|
||||
commit = FactoryGirl.create(:ci_commit, project: project)
|
||||
commit.create_builds('master', false, nil)
|
||||
commit = FactoryGirl.create(:ci_commit, project: project, ref: 'master')
|
||||
commit.create_builds(nil)
|
||||
|
||||
post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin }
|
||||
|
||||
|
@ -66,8 +66,8 @@ describe Ci::API::API do
|
|||
end
|
||||
|
||||
it "returns variables" do
|
||||
commit = FactoryGirl.create(:ci_commit, project: project)
|
||||
commit.create_builds('master', false, nil)
|
||||
commit = FactoryGirl.create(:ci_commit, project: project, ref: 'master')
|
||||
commit.create_builds(nil)
|
||||
project.variables << Ci::Variable.new(key: "SECRET_KEY", value: "secret_value")
|
||||
|
||||
post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin }
|
||||
|
@ -83,10 +83,10 @@ describe Ci::API::API do
|
|||
|
||||
it "returns variables for triggers" do
|
||||
trigger = FactoryGirl.create(:ci_trigger, project: project)
|
||||
commit = FactoryGirl.create(:ci_commit, project: project)
|
||||
commit = FactoryGirl.create(:ci_commit, project: project, ref: 'master')
|
||||
|
||||
trigger_request = FactoryGirl.create(:ci_trigger_request_with_variables, commit: commit, trigger: trigger)
|
||||
commit.create_builds('master', false, nil, trigger_request)
|
||||
commit.create_builds(nil, trigger_request)
|
||||
project.variables << Ci::Variable.new(key: "SECRET_KEY", value: "secret_value")
|
||||
|
||||
post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin }
|
||||
|
@ -103,8 +103,8 @@ describe Ci::API::API do
|
|||
end
|
||||
|
||||
it "returns dependent builds" do
|
||||
commit = FactoryGirl.create(:ci_commit, project: project)
|
||||
commit.create_builds('master', false, nil, nil)
|
||||
commit = FactoryGirl.create(:ci_commit, project: project, ref: 'master')
|
||||
commit.create_builds(nil, nil)
|
||||
commit.builds.where(stage: 'test').each(&:success)
|
||||
|
||||
post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin }
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Ci::CreateBuildsService, services: true do
|
||||
let(:commit) { create(:ci_commit) }
|
||||
let(:commit) { create(:ci_commit, ref: 'master') }
|
||||
let(:user) { create(:user) }
|
||||
|
||||
describe '#execute' do
|
||||
|
@ -9,7 +9,7 @@ describe Ci::CreateBuildsService, services: true do
|
|||
#
|
||||
|
||||
subject do
|
||||
described_class.new.execute(commit, 'test', 'master', nil, user, nil, status)
|
||||
described_class.new(commit).execute(commit, nil, user, status)
|
||||
end
|
||||
|
||||
context 'next builds available' do
|
||||
|
|
|
@ -5,7 +5,7 @@ module Ci
|
|||
let(:service) { ImageForBuildService.new }
|
||||
let(:project) { FactoryGirl.create(:empty_project) }
|
||||
let(:commit_sha) { '01234567890123456789' }
|
||||
let(:commit) { project.ensure_ci_commit(commit_sha) }
|
||||
let(:commit) { project.ensure_ci_commit(commit_sha, 'master') }
|
||||
let(:build) { FactoryGirl.create(:ci_build, commit: commit) }
|
||||
|
||||
describe :execute do
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue