Commit 1840b860 authored by Ruben Davila's avatar Ruben Davila

Merge remote-tracking branch 'ce/8-11-stable' into 8-11-stable-ee

parents d9e44d8f 070a518f
......@@ -101,6 +101,7 @@ v 8.11.0 (unreleased)
- Make error pages responsive (Takuya Noguchi)
- The performance of the project dropdown used for moving issues has been improved
- Fix skip_repo parameter being ignored when destroying a namespace
- Add all builds into stage/job dropdowns on builds page
- Change requests_profiles resource constraint to catch virtually any file
- Bump gitlab_git to lazy load compare commits
- Reduce number of queries made for merge_requests/:id/diffs
......@@ -113,16 +114,20 @@ v 8.11.0 (unreleased)
- Speed up and reduce memory usage of Commit#repo_changes, Repository#expire_avatar_cache and IrkerWorker
- Add unfold links for Side-by-Side view. !5415 (Tim Masliuchenko)
- Adds support for pending invitation project members importing projects
- Add pipeline visualization/graph on pipeline page
- Update devise initializer to turn on changed password notification emails. !5648 (tombell)
- Avoid to show the original password field when password is automatically set. !5712 (duduribeiro)
- Fix importing GitLab projects with an invalid MR source project
- Sort folders with submodules in Files view !5521
- Each `File::exists?` replaced to `File::exist?` because of deprecate since ruby version 2.2.0
- Add auto-completition in pipeline (Katarzyna Kobierska Ula Budziszewska)
- Add pipelines tab to merge requests
- Fix a memory leak caused by Banzai::Filter::SanitizationFilter
- Speed up todos queries by limiting the projects set we join with
- Ensure file editing in UI does not overwrite commited changes without warning user
- Eliminate unneeded calls to Repository#blob_at when listing commits with no path
- Update gitlab_git gem to 10.4.7
- Simplify SQL queries of marking a todo as done
v 8.10.6 (unreleased)
- Fix import/export configuration missing some included attributes
......
......@@ -57,7 +57,7 @@ gem 'browser', '~> 2.2'
# Extracting information from a git repository
# Provide access to Gitlab::Git library
gem 'gitlab_git', '~> 10.4.5'
gem 'gitlab_git', '~> 10.4.7'
# LDAP Auth
# GitLab fork with several improvements to original library. For full list of changes
......
......@@ -300,7 +300,7 @@ GEM
mime-types (>= 1.16, < 3)
posix-spawn (~> 0.3)
gitlab-license (1.0.0)
gitlab_git (10.4.5)
gitlab_git (10.4.7)
activesupport (~> 4.0)
charlock_holmes (~> 0.7.3)
github-linguist (~> 4.7.0)
......@@ -887,8 +887,12 @@ DEPENDENCIES
github-markup (~> 1.4)
gitlab-elasticsearch-git (~> 0.0.17)
gitlab-flowdock-git-hook (~> 1.0.1)
<<<<<<< HEAD
gitlab-license (~> 1.0)
gitlab_git (~> 10.4.5)
=======
gitlab_git (~> 10.4.7)
>>>>>>> ce/8-11-stable
gitlab_meta (= 7.0)
gitlab_omniauth-ldap (~> 1.2.1)
gollum-lib (~> 4.2)
......
......@@ -6,19 +6,26 @@
Build.state = null;
function Build(page_url, build_url, build_status, state1) {
this.page_url = page_url;
this.build_url = build_url;
this.build_status = build_status;
this.state = state1;
function Build(options) {
this.page_url = options.page_url;
this.build_url = options.build_url;
this.build_status = options.build_status;
this.state = options.state1;
this.build_stage = options.build_stage;
this.hideSidebar = bind(this.hideSidebar, this);
this.toggleSidebar = bind(this.toggleSidebar, this);
this.updateDropdown = bind(this.updateDropdown, this);
clearInterval(Build.interval);
this.bp = Breakpoints.get();
this.hideSidebar();
$('.js-build-sidebar').niceScroll();
this.populateJobs(this.build_stage);
this.updateStageDropdownText(this.build_stage);
this.hideSidebar();
$(document).off('click', '.js-sidebar-build-toggle').on('click', '.js-sidebar-build-toggle', this.toggleSidebar);
$(window).off('resize.build').on('resize.build', this.hideSidebar);
$(document).off('click', '.stage-item').on('click', '.stage-item', this.updateDropdown);
this.updateArtifactRemoveDate();
if ($('#build-trace').length) {
this.getInitialBuildTrace();
......@@ -132,6 +139,22 @@
}
};
Build.prototype.populateJobs = function(stage) {
$('.build-job').hide();
$('.build-job[data-stage="' + stage + '"]').show();
};
Build.prototype.updateStageDropdownText = function(stage) {
$('.stage-selection').text(stage);
};
Build.prototype.updateDropdown = function(e) {
e.preventDefault();
var stage = e.currentTarget.text;
this.updateStageDropdownText(stage);
this.populateJobs(stage);
};
return Build;
})();
......
......@@ -33,7 +33,7 @@
this.render = bind(this.render, this);
this.VIEW_TYPE = $('input#view[type=hidden]').val();
debounce = _.debounce(this.render, DEBOUNCE_TIMEOUT_DURATION);
$(document).off('mouseover', LINE_COLUMN_CLASSES).off('mouseleave', LINE_COLUMN_CLASSES).on('mouseover', LINE_COLUMN_CLASSES, debounce).on('mouseleave', LINE_COLUMN_CLASSES, this.destroy);
$(this.filesContainerElement).off('mouseover', LINE_COLUMN_CLASSES).off('mouseleave', LINE_COLUMN_CLASSES).on('mouseover', LINE_COLUMN_CLASSES, debounce).on('mouseleave', LINE_COLUMN_CLASSES, this.destroy);
}
FilesCommentButton.prototype.render = function(e) {
......
(function() {
function toggleGraph() {
const $pipelineBtn = $(this).closest('.toggle-pipeline-btn');
const $pipelineGraph = $(this).closest('.row-content-block').next('.pipeline-graph');
const $btnText = $(this).find('.toggle-btn-text');
$($pipelineBtn).add($pipelineGraph).toggleClass('graph-collapsed');
const graphCollapsed = $pipelineGraph.hasClass('graph-collapsed');
graphCollapsed ? $btnText.text('Expand') : $btnText.text('Hide')
}
$(document).on('click', '.toggle-pipeline-btn', toggleGraph);
})();
......@@ -222,3 +222,7 @@ header.header-pinned-nav {
padding-right: $sidebar_collapsed_width;
}
}
.right-sidebar {
border-left: 1px solid $border-color;
}
......@@ -53,14 +53,6 @@
left: 70px;
}
}
.nav-links {
svg {
position: relative;
top: 2px;
margin-right: 3px;
}
}
}
.build-header {
......@@ -108,24 +100,98 @@
}
.right-sidebar.build-sidebar {
padding-top: $gl-padding;
padding-bottom: $gl-padding;
padding: $gl-padding 0;
&.right-sidebar-collapsed {
display: none;
}
.blocks-container {
padding: $gl-padding;
}
.block {
width: 100%;
}
.build-sidebar-header {
padding-top: 0;
padding: 0 $gl-padding $gl-padding;
.gutter-toggle {
margin-top: 0;
}
}
.stage-item {
cursor: pointer;
&:hover {
color: $gl-text-color;
}
}
.build-dropdown {
padding: 0 $gl-padding;
.dropdown-menu-toggle {
margin-top: 8px;
}
.dropdown-menu {
right: $gl-padding;
left: $gl-padding;
width: auto;
}
}
.builds-container {
margin-top: $gl-padding;
background-color: $white-light;
border-top: 1px solid $border-color;
border-bottom: 1px solid $border-color;
max-height: 300px;
overflow: scroll;
svg {
position: relative;
top: 2px;
margin-right: 3px;
height: 13px;
}
a {
display: block;
padding: $gl-padding 10px $gl-padding 40px;
width: 270px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
&:hover {
background-color: $row-hover;
color: $gl-text-color;
}
}
.build-job {
position: relative;
.fa {
position: absolute;
left: 15px;
top: 20px;
display: none;
}
&.active {
font-weight: bold;
.fa {
display: block;
}
}
}
}
}
.build-detail-row {
......
......@@ -230,6 +230,187 @@
}
}
// Pipeline visualization
.toggle-pipeline-btn {
background-color: $gray-dark;
.caret {
border-top: none;
border-bottom: 4px solid;
}
&.graph-collapsed {
background-color: $white-light;
.caret {
border-bottom: none;
border-top: 4px solid;
}
}
}
.pipeline-graph {
width: 100%;
overflow: auto;
white-space: nowrap;
max-height: 500px;
transition: max-height 0.3s, padding 0.3s;
&.graph-collapsed {
max-height: 0;
padding: 0 16px;
}
}
.pipeline-visualization {
position: relative;
min-width: 1220px;
ul {
padding: 0;
}
}
.stage-column {
display: inline-block;
vertical-align: top;
margin-right: 50px;
li {
list-style: none;
}
.stage-name {
margin-bottom: 15px;
font-weight: bold;
width: 150px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.build {
border: 1px solid $border-color;
position: relative;
padding: 6px 10px;
border-radius: 30px;
width: 150px;
margin-bottom: 10px;
&.playable {
background-color: $gray-light;
}
.build-content {
width: 130px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
a {
color: $layout-link-gray;
}
}
svg {
position: relative;
top: 2px;
margin-right: 5px;
}
.fa {
font-size: 13px;
}
// Connect first build in each stage with right horizontal line
&:first-child {
&::after {
content: '';
position: absolute;
top: 50%;
right: -54px;
border-top: 2px solid $border-color;
width: 54px;
height: 1px;
}
}
// Connect each build (except for first) with curved lines
&:not(:first-child) {
&::after, &::before {
content: '';
top: -47px;
position: absolute;
border-bottom: 2px solid $border-color;
width: 20px;
height: 65px;
}
// Right connecting curves
&::after {
right: -20px;
border-right: 2px solid $border-color;
border-radius: 0 0 50px;
}
// Left connecting curves
&::before {
left: -20px;
border-left: 2px solid $border-color;
border-radius: 0 0 0 50px;
}
}
// Connect second build to first build with smaller curved line
&:nth-child(2) {
&::after, &::before {
height: 45px;
top: -26px;
}
}
}
&:last-child {
.build {
// Remove right connecting horizontal line from first build in last stage
&:first-child {
&::after, &::before {
border: none;
}
}
// Remove right curved connectors from all builds in last stage
&:not(:first-child) {
&::after {
border: none;
}
}
}
}
&:first-child {
.build {
// Remove left curved connectors from all builds in first stage
&:not(:first-child) {
&::before {
border: none;
}
}
}
}
}
.pipeline-actions {
border-bottom: none;
}
.toggle-pipeline-btn {
.fa {
color: $dropdown-header-color;
}
}
.pipelines.tab-pane {
.content-list.pipelines {
......
......@@ -6,7 +6,7 @@ class Dashboard::TodosController < Dashboard::ApplicationController
end
def destroy
TodoService.new.mark_todos_as_done([todo], current_user)
TodoService.new.mark_todos_as_done_by_ids([params[:id]], current_user)
respond_to do |format|
format.html { redirect_to dashboard_todos_path, notice: 'Todo was successfully marked as done.' }
......@@ -27,10 +27,6 @@ class Dashboard::TodosController < Dashboard::ApplicationController
private
def todo
@todo ||= find_todos.find(params[:id])
end
def find_todos
@todos ||= TodosFinder.new(current_user, params).execute
end
......
......@@ -38,6 +38,10 @@ module CiStatusHelper
'icon_status_pending'
when 'running'
'icon_status_running'
when 'play'
return icon('play fw')
when 'created'
'icon_status_pending'
else
'icon_status_cancel'
end
......@@ -48,13 +52,13 @@ module CiStatusHelper
def render_commit_status(commit, tooltip_placement: 'auto left')
project = commit.project
path = builds_namespace_project_commit_path(project.namespace, project, commit)
render_status_with_link('commit', commit.status, path, tooltip_placement)
render_status_with_link('commit', commit.status, path, tooltip_placement: tooltip_placement)
end
def render_pipeline_status(pipeline, tooltip_placement: 'auto left')
project = pipeline.project
path = namespace_project_pipeline_path(project.namespace, project, pipeline)
render_status_with_link('pipeline', pipeline.status, path, tooltip_placement)
render_status_with_link('pipeline', pipeline.status, path, tooltip_placement: tooltip_placement)
end
def no_runners_for_project?(project)
......@@ -62,13 +66,17 @@ module CiStatusHelper
Ci::Runner.shared.blank?
end
private
def render_status_with_link(type, status, path = nil, tooltip_placement: 'auto left', cssclass: '')
klass = "ci-status-link ci-status-icon-#{status.dasherize} #{cssclass}"
title = "#{type.titleize}: #{ci_label_for_status(status)}"
data = { toggle: 'tooltip', placement: tooltip_placement }
def render_status_with_link(type, status, path, tooltip_placement, cssclass: '')
link_to ci_icon_for_status(status),
path,
class: "ci-status-link ci-status-icon-#{status.dasherize} #{cssclass}",
title: "#{type.titleize}: #{ci_label_for_status(status)}",
data: { toggle: 'tooltip', placement: tooltip_placement }
if path
link_to ci_icon_for_status(status), path,
class: klass, title: title, data: data
else
content_tag :span, ci_icon_for_status(status),
class: klass, title: title, data: data
end
end
end
......@@ -97,7 +97,7 @@ module Ci
end
def playable?
project.builds_enabled? && commands.present? && manual?
project.builds_enabled? && commands.present? && manual? && skipped?
end
def play(current_user = nil)
......
......@@ -72,6 +72,10 @@ module Ci
CommitStatus.where(pipeline: pluck(:id)).stages
end
def stages_with_latest_statuses
statuses.latest.order(:stage_idx).group_by(&:stage)
end
def project_id
project.id
end
......
......@@ -150,7 +150,11 @@ class TodoService
# When user marks some todos as done
def mark_todos_as_done(todos, current_user)
todos = current_user.todos.where(id: todos.map(&:id)) unless todos.respond_to?(:update_all)
mark_todos_as_done_by_ids(todos.select(&:id), current_user)
end
def mark_todos_as_done_by_ids(ids, current_user)
todos = current_user.todos.where(id: ids)
marked_todos = todos.update_all(state: :done)
current_user.update_todos_count_cache
......
......@@ -11,98 +11,133 @@
%p.build-detail-row
#{@build.coverage}%
- if can?(current_user, :read_build, @project) && (@build.artifacts? || @build.artifacts_expired?)
.block{ class: ("block-first" if !@build.coverage) }
.title
Build artifacts
- if @build.artifacts_expired?
%p.build-detail-row
The artifacts were removed
#{time_ago_with_tooltip(@build.artifacts_expire_at)}
- elsif @build.artifacts_expire_at
%p.build-detail-row
The artifacts will be removed in
%span.js-artifacts-remove= @build.artifacts_expire_at
- builds = @build.pipeline.builds.latest.to_a
- statuses = ["failed", "pending", "running", "canceled", "success", "skipped"]
- if builds.size > 1
.dropdown.build-dropdown
.build-light-text Stage
%button.dropdown-menu-toggle{type: 'button', 'data-toggle' => 'dropdown'}
%span.stage-selection More
= icon('caret-down')
%ul.dropdown-menu
- builds.map(&:stage).uniq.each do |stage|
%li
%a.stage-item= stage
- if @build.artifacts?
.btn-group.btn-group-justified{ role: :group }
- if @build.artifacts_expire_at
= link_to keep_namespace_project_build_artifacts_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-default', method: :post do
Keep
.builds-container
- statuses.each do |build_status|
- builds.select{|build| build.status == build_status}.each do |build|
.build-job{class: ('active' if build == @build), data: {stage: build.stage}}
= link_to namespace_project_build_path(@project.namespace, @project, build) do
= icon('check')
= ci_icon_for_status(build.status)
%span
- if build.name
= build.name
- else
= build.id
= link_to download_namespace_project_build_artifacts_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-default' do
Download
- if @build.retried?
%li.active
%a
Build ##{@build.id}
&middot;
%i.fa.fa-warning
This build was retried.
- if @build.artifacts_metadata?
= link_to browse_namespace_project_build_artifacts_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-default' do
Browse
.blocks-container
- if can?(current_user, :read_build, @project) && (@build.artifacts? || @build.artifacts_expired?)
.block{ class: ("block-first" if !@build.coverage) }
.title
Build artifacts
- if @build.artifacts_expired?
%p.build-detail-row
The artifacts were removed
#{time_ago_with_tooltip(@build.artifacts_expire_at)}
- elsif @build.artifacts_expire_at
%p.build-detail-row
The artifacts will be removed in
%span.js-artifacts-remove= @build.artifacts_expire_at
.block{ class: ("block-first" if !@build.coverage && !(can?(current_user, :read_build, @project) && (@build.artifacts? || @build.artifacts_expired?))) }
.title
Build details
- if can?(current_user, :update_build, @build) && @build.retryable?
= link_to "Retry", retry_namespace_project_build_path(@project.namespace, @project, @build), class: 'pull-right', method: :post
- if @build.merge_request
%p.build-detail-row
%span.build-light-text Merge Request:
= link_to "#{@build.merge_request.to_reference}", merge_request_path(@build.merge_request)
- if @build.duration
%p.build-detail-row
%span.build-light-text Duration:
= time_interval_in_words(@build.duration)
- if @build.finished_at
%p.build-detail-row
%span.build-light-text Finished:
#{time_ago_with_tooltip(@build.finished_at)}
- if @build.erased_at
%p.build-detail-row
%span.build-light-text Erased:
#{time_ago_with_tooltip(@build.erased_at)}
%p.build-detail-row
%span.build-light-text Runner:
- if @build.runner && current_user && current_user.admin
= link_to "##{@build.runner.id}", admin_runner_path(@build.runner.id)
- elsif @build.runner
\##{@build.runner.id}
.btn-group.btn-group-justified{ role: :group }
- if @build.has_trace?
= link_to 'Raw', raw_namespace_project_build_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-default'
- if @build.active?
= link_to "Cancel", cancel_namespace_project_build_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-default', method: :post
- if can?(current_user, :update_build, @project) && @build.erasable?
= link_to erase_namespace_project_build_path(@project.namespace, @project, @build),
class: "btn btn-sm btn-default", method: :post,
data: { confirm: "Are you sure you want to erase this build?" } do
Erase
- if @build.artifacts?
.btn-group.btn-group-justified{ role: :group }
- if @build.artifacts_expire_at
= link_to keep_namespace_project_build_artifacts_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-default', method: :post do
Keep
= link_to download_namespace_project_build_artifacts_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-default' do
Download
- if @build.trigger_request
.build-widget
%h4.title
Trigger
- if @build.artifacts_metadata?
= link_to browse_namespace_project_build_artifacts_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-default' do
Browse
%p
%span.build-light-text Token:
#{@build.trigger_request.trigger.short_token}
.block{ class: ("block-first" if !@build.coverage && !(can?(current_user, :read_build, @project) && (@build.artifacts? || @build.artifacts_expired?))) }
.title
Build details
- if can?(current_user, :update_build, @build) && @build.retryable?
= link_to "Retry", retry_namespace_project_build_path(@project.namespace, @project, @build), class: 'pull-right', method: :post
- if @build.merge_request
%p.build-detail-row
%span.build-light-text Merge Request:
= link_to "#{@build.merge_request.to_reference}", merge_request_path(@build.merge_request)
- if @build.duration
%p.build-detail-row
%span.build-light-text Duration:
= time_interval_in_words(@build.duration)
- if @build.finished_at
%p.build-detail-row
%span.build-light-text Finished:
#{time_ago_with_tooltip(@build.finished_at)}
- if @build.erased_at
%p.build-detail-row
%span.build-light-text Erased:
#{time_ago_with_tooltip(@build.erased_at)}
%p.build-detail-row
%span.build-light-text Runner:
- if @build.runner && current_user && current_user.admin
= link_to "##{@build.runner.id}", admin_runner_path(@build.runner.id)
- elsif @build.runner
\##{@build.runner.id}
.btn-group.btn-group-justified{ role: :group }
- if @build.has_trace?
= link_to 'Raw', raw_namespace_project_build_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-default'
- if @build.active?
= link_to "Cancel", cancel_namespace_project_build_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-default', method: :post
- if can?(current_user, :update_build, @project) && @build.erasable?
= link_to erase_namespace_project_build_path(@project.namespace, @project, @build),
class: "btn btn-sm btn-default", method: :post,
data: { confirm: "Are you sure you want to erase this build?" } do
Erase
- if @build.trigger_request
.build-widget
%h4.title
Trigger
- if @build.trigger_request.variables
%p
%span.build-light-text Variables:
%span.build-light-text Token:
#{@build.trigger_request.trigger.short_token}
- if @build.trigger_request.variables
%p
%span.build-light-text Variables:
- @build.trigger_request.variables.each do |key, value|
%code
#{key}=#{value}
.block
.title
Commit title
%p.build-light-text.append-bottom-0
#{@build.pipeline.git_commit_title}
- @build.trigger_request.variables.each do |key, value|
%code
#{key}=#{value}
- if @build.tags.any?
.block
.title
Tags
- @build.tag_list.each do |tag|
%span.label.label-primary
= tag
Commit title
%p.build-light-text.append-bottom-0
#{@build.pipeline.git_commit_title}
- if @build.tags.any?
.block
.title
Tags
- @build.tag_list.each do |tag|
%span.label.label-primary
= tag
......@@ -5,26 +5,6 @@
.build-page
= render "header"
- builds = @build.pipeline.builds.latest.to_a
- if builds.size > 1
%ul.nav-links.no-top.no-bottom
- builds.each do |build|
%li{class: ('active' if build == @build) }
= link_to namespace_project_build_path(@project.namespace, @project, build) do
= ci_icon_for_status(build.status)
%span
- if build.name
= build.name
- else
= build.id
- if @build.retried?
%li.active
%a
Build ##{@build.id}
&middot;
%i.fa.fa-warning
This build was retried.
- if @build.stuck?
- unless @build.any_runners_online?
.bs-callout.bs-callout-warning
......@@ -67,4 +47,10 @@
= render "sidebar"
:javascript
new Build("#{namespace_project_build_url(@project.namespace, @project, @build)}", "#{namespace_project_build_url(@project.namespace, @project, @build, :json)}", "#{@build.status}", "#{trace_with_state[:state]}")
new Build({
page_url: "#{namespace_project_build_url(@project.namespace, @project, @build)}",
build_url: "#{namespace_project_build_url(@project.namespace, @project, @build, :json)}",
build_status: "#{@build.status}",
build_stage: "#{@build.stage}",
state1: "#{trace_with_state[:state]}"
})
- is_playable = subject.playable? && can?(current_user, :update_build, @project)
%li.build{class: ("playable" if is_playable)}
.build-content
- if is_playable
= link_to play_namespace_project_build_path(subject.project.namespace, subject.project, subject, return_to: request.original_url), method: :post, title: 'Play' do
= render_status_with_link('build', 'play')
= subject.name
- elsif can?(current_user, :read_build, @project)
= link_to namespace_project_build_path(subject.project.namespace, subject.project, subject) do
= render_status_with_link('build', subject.status)
= subject.name
- else
= render_status_with_link('build', subject.status)
= ci_icon_for_status(subject.status)
.row-content-block.build-content.middle-block
.row-content-block.build-content.middle-block.pipeline-actions
.pull-right
.btn.btn-grouped.btn-white.toggle-pipeline-btn
%span.toggle-btn-text Hide
%span pipeline graph
%span.caret
- if can?(current_user, :update_pipeline, pipeline.project)
- if pipeline.builds.latest.failed.any?(&:retryable?)
= link_to "Retry failed", retry_namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id), class: 'btn btn-grouped btn-primary', method: :post
......@@ -23,6 +27,22 @@
in
= time_interval_in_words pipeline.duration
.row-content-block.build-content.middle-block.pipeline-graph
.pipeline-visualization
%ul.stage-column-list
- stages = pipeline.stages_with_latest_statuses
- stages.each do |stage, statuses|
%li.stage-column
.stage-name
%a{name: stage}
- if stage
= stage.titleize
.builds-container
%ul
- statuses.each do |status|
= render "projects/#{status.to_partial_path}_pipeline", subject: status
- if pipeline.yaml_errors.present?
.bs-callout.bs-callout-danger
%h4 Found errors in your .gitlab-ci.yml:
......
%li.build
.build-content
- if subject.target_url
- link_to subject.target_url do
= render_status_with_link('commit status', subject.status)
= subject.name
- else
= render_status_with_link('commit status', subject.status)
= subject.name
......@@ -39,7 +39,9 @@
= weight
.pull-right
- if controller.controller_name == 'boards' && can?(current_user, :admin_list, @project)
- if controller.controller_name != 'boards'
= render 'shared/sort_dropdown'
- if can?(current_user, :admin_list, @project)
.dropdown
%button.btn.btn-create.js-new-board-list{ type: "button", data: { toggle: "dropdown", labels: labels_filter_path, project_id: @project.try(:id) } }
Create new list
......@@ -48,8 +50,6 @@
- if can?(current_user, :admin_label, @project)
= render partial: "shared/issuable/label_page_create"
= dropdown_loading
- else
= render 'shared/sort_dropdown'
- if controller.controller_name == 'issues'
.issues_bulk_update.hide
......
class Gitlab::Seeder::Builds
STAGES = %w[build notify_build test notify_test deploy notify_deploy]
STAGES = %w[build test deploy notify]
BUILDS = [
{ name: 'build:linux', stage: 'build', status: :success },
{ name: 'build:osx', stage: 'build', status: :success },
{ name: 'slack post build', stage: 'notify_build', status: :success },
{ name: 'rspec:linux', stage: 'test', status: :success },
{ name: 'rspec:windows', stage: 'test', status: :success },
{ name: 'rspec:windows', stage: 'test', status: :success },
......@@ -12,9 +11,9 @@ class Gitlab::Seeder::Builds
{ name: 'spinach:osx', stage: 'test', status: :canceled },
{ name: 'cucumber:linux', stage: 'test', status: :running },
{ name: 'cucumber:osx', stage: 'test', status: :failed },
{ name: 'slack post test', stage: 'notify_test', status: :success },
{ name: 'staging', stage: 'deploy', environment: 'staging', status: :success },
{ name: 'production', stage: 'deploy', environment: 'production', when: 'manual', status: :success },
{ name: 'production', stage: 'deploy', environment: 'production', when: 'manual', status: :skipped },
{ name: 'slack', stage: 'notify', when: 'manual', status: :created },
]
def initialize(project)
......@@ -25,7 +24,7 @@ class Gitlab::Seeder::Builds
pipelines.each do |pipeline|
begin
BUILDS.each { |opts| build_create!(pipeline, opts) }
commit_status_create!(pipeline, name: 'jenkins', status: :success)
commit_status_create!(pipeline, name: 'jenkins', stage: 'test', status: :success)
print '.'
rescue ActiveRecord::RecordInvalid
print 'F'
......
......@@ -45,6 +45,8 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps
step 'I click link "All"' do
click_link "All"
# Waits for load
expect(find('.issues-state-filters > .active')).to have_content 'All'
end
step 'I click link "Release 0.4"' do
......@@ -297,7 +299,7 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps
end
step 'I fill in issue search with \'Rock and roll\'' do
filter_issue 'Description for issue'
filter_issue 'Rock and roll'
end
step 'I should see \'Bugfix1\' in issues' do
......@@ -354,8 +356,6 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps
end
def filter_issue(text)
sleep 1
fill_in 'issue_search', with: text
sleep 1
end
end
......@@ -22,6 +22,8 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
step 'I click link "All"' do
click_link "All"
# Waits for load
expect(find('.issues-state-filters > .active')).to have_content 'All'
end
step 'I click link "Merged"' do
......@@ -509,7 +511,6 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
end
step 'I fill in merge request search with "Fe"' do
sleep 1
fill_in 'issue_search', with: "Fe"
end
......
......@@ -193,7 +193,11 @@ describe "Pipelines" do
end
context 'playing manual build' do
before { click_link('Play') }
before do
within '.pipeline-holder' do
click_link('Play')
end
end
it { expect(@manual.reload).to be_pending }
end
......
......@@ -194,12 +194,12 @@ describe TodoService, services: true do
end
end
describe '#mark_todos_as_done' do
it 'marks related todos for the user as done' do
first_todo = create(:todo, :assigned, user: john_doe, project: project, target: issue, author: author)
second_todo = create(:todo, :assigned, user: john_doe, project: project, target: issue, author: author)
shared_examples 'marking todos as done' do |meth|
let!(:first_todo) { create(:todo, :assigned, user: john_doe, project: project, target: issue, author: author) }
let!(:second_todo) { create(:todo, :assigned, user: john_doe, project: project, target: issue, author: author) }
service.mark_todos_as_done([first_todo, second_todo], john_doe)
it 'marks related todos for the user as done' do
service.send(meth, collection, john_doe)
expect(first_todo.reload).to be_done
expect(second_todo.reload).to be_done
......@@ -207,20 +207,30 @@ describe TodoService, services: true do
describe 'cached counts' do
it 'updates when todos change' do
todo = create(:todo, :assigned, user: john_doe, project: project, target: issue, author: author)
expect(john_doe.todos_done_count).to eq(0)
expect(john_doe.todos_pending_count).to eq(1)
expect(john_doe.todos_pending_count).to eq(2)
expect(john_doe).to receive(:update_todos_count_cache).and_call_original
service.mark_todos_as_done([todo], john_doe)
service.send(meth, collection, john_doe)
expect(john_doe.todos_done_count).to eq(1)
expect(john_doe.todos_done_count).to eq(2)
expect(john_doe.todos_pending_count).to eq(0)
end
end
end
describe '#mark_todos_as_done' do
it_behaves_like 'marking todos as done', :mark_todos_as_done do
let(:collection) { [first_todo, second_todo] }
end
end
describe '#mark_todos_as_done_by_ids' do
it_behaves_like 'marking todos as done', :mark_todos_as_done_by_ids do
let(:collection) { [first_todo, second_todo].map(&:id) }
end
end
describe '#new_note' do
let!(:first_todo) { create(:todo, :assigned, user: john_doe, project: project, target: issue, author: author) }
let!(:second_todo) { create(:todo, :assigned, user: john_doe, project: project, target: issue, author: author) }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment