diff options
author | Phil Hughes <me@iamphill.com> | 2017-05-05 18:58:01 +0000 |
---|---|---|
committer | Phil Hughes <me@iamphill.com> | 2017-05-05 18:58:01 +0000 |
commit | 9caa7e7a54a5e3d261f77765868b07117a282a68 (patch) | |
tree | ca3c6b9de47499fde1f02aa39edef00deaafcaaa | |
parent | 240400152242924e75ea81918b5cfdcf2441549c (diff) | |
parent | 9f3f22c8959b430811102fb790895e7edd61d3f9 (diff) | |
download | gitlab-ce-9caa7e7a54a5e3d261f77765868b07117a282a68.tar.gz |
Merge branch '24883-build-failure-summary-page' into 'master'
Build failures summary page for pipelines
Closes #24883
See merge request !10719
-rw-r--r-- | app/assets/javascripts/dispatcher.js | 1 | ||||
-rw-r--r-- | app/assets/stylesheets/pages/pipelines.scss | 26 | ||||
-rw-r--r-- | app/controllers/projects/pipelines_controller.rb | 22 | ||||
-rw-r--r-- | app/helpers/builds_helper.rb | 12 | ||||
-rw-r--r-- | app/views/projects/pipelines/_with_tabs.html.haml | 19 | ||||
-rw-r--r-- | changelogs/unreleased/24883-build-failure-summary-page.yml | 4 | ||||
-rw-r--r-- | config/routes/project.rb | 1 | ||||
-rw-r--r-- | spec/features/projects/pipelines/pipeline_spec.rb | 53 |
8 files changed, 131 insertions, 7 deletions
diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js index b87c57c38fe..b16ff2a0221 100644 --- a/app/assets/javascripts/dispatcher.js +++ b/app/assets/javascripts/dispatcher.js @@ -252,6 +252,7 @@ const ShortcutsBlob = require('./shortcuts_blob'); } break; case 'projects:pipelines:builds': + case 'projects:pipelines:failures': case 'projects:pipelines:show': const { controllerAction } = document.querySelector('.js-pipeline-container').dataset; const pipelineStatusUrl = `${document.querySelector('.js-pipeline-tab-link a').getAttribute('href')}/status.json`; diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index 9115d26c779..5cca77b5bf3 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -316,6 +316,32 @@ } } +.build-failures { + .build-state { + padding: 20px 2px; + + .build-name { + float: right; + font-weight: 500; + } + + .ci-status-icon-failed svg { + vertical-align: middle; + } + + .stage { + color: $gl-text-color-secondary; + font-weight: 500; + vertical-align: middle; + } + } + + .build-log { + border: none; + line-height: initial; + } +} + // Pipeline graph .pipeline-graph { width: 100%; diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb index 2908036607a..f9adedcb074 100644 --- a/app/controllers/projects/pipelines_controller.rb +++ b/app/controllers/projects/pipelines_controller.rb @@ -1,6 +1,6 @@ class Projects::PipelinesController < Projects::ApplicationController before_action :pipeline, except: [:index, :new, :create, :charts] - before_action :commit, only: [:show, :builds] + before_action :commit, only: [:show, :builds, :failures] before_action :authorize_read_pipeline! before_action :authorize_create_pipeline!, only: [:new, :create] before_action :authorize_update_pipeline!, only: [:retry, :cancel] @@ -69,10 +69,14 @@ class Projects::PipelinesController < Projects::ApplicationController end def builds - respond_to do |format| - format.html do - render 'show' - end + render_show + end + + def failures + if @pipeline.statuses.latest.failed.present? + render_show + else + redirect_to pipeline_path(@pipeline) end end @@ -125,6 +129,14 @@ class Projects::PipelinesController < Projects::ApplicationController private + def render_show + respond_to do |format| + format.html do + render 'show' + end + end + end + def create_params params.require(:pipeline).permit(:ref) end diff --git a/app/helpers/builds_helper.rb b/app/helpers/builds_helper.rb index 2fcb7a59fc3..2eb2c6c7389 100644 --- a/app/helpers/builds_helper.rb +++ b/app/helpers/builds_helper.rb @@ -1,4 +1,16 @@ module BuildsHelper + def build_summary(build, skip: false) + if build.has_trace? + if skip + link_to "View job trace", pipeline_build_url(build.pipeline, build) + else + build.trace.html(last_lines: 10).html_safe + end + else + "No job trace" + end + end + def sidebar_build_class(build, current_build) build_class = '' build_class += ' active' if build.id === current_build.id diff --git a/app/views/projects/pipelines/_with_tabs.html.haml b/app/views/projects/pipelines/_with_tabs.html.haml index d7cefb8613e..1aa48bf9813 100644 --- a/app/views/projects/pipelines/_with_tabs.html.haml +++ b/app/views/projects/pipelines/_with_tabs.html.haml @@ -1,3 +1,5 @@ +- failed_builds = @pipeline.statuses.latest.failed + .tabs-holder %ul.pipelines-tabs.nav-links.no-top.no-bottom %li.js-pipeline-tab-link @@ -7,8 +9,11 @@ = link_to builds_namespace_project_pipeline_path(@project.namespace, @project, @pipeline), data: {target: 'div#js-tab-builds', action: 'builds', toggle: 'tab' }, class: 'builds-tab' do Jobs %span.badge.js-builds-counter= pipeline.statuses.count - - + - if failed_builds.present? + %li.js-failures-tab-link + = link_to failures_namespace_project_pipeline_path(@project.namespace, @project, @pipeline), data: {target: 'div#js-tab-failures', action: 'failures', toggle: 'tab' }, class: 'failures-tab' do + Failed Jobs + %span.badge.js-failures-counter= failed_builds.count .tab-content #js-tab-pipeline.tab-pane @@ -39,3 +44,13 @@ %th Coverage %th = render partial: "projects/stage/stage", collection: pipeline.stages, as: :stage + - if failed_builds.present? + #js-tab-failures.build-failures.tab-pane + - failed_builds.each_with_index do |build, index| + .build-state + %span.ci-status-icon-failed= custom_icon('icon_status_failed') + %span.stage + = build.stage.titleize + %span.build-name + = link_to build.name, pipeline_build_url(pipeline, build) + %pre.build-log= build_summary(build, skip: index >= 10) diff --git a/changelogs/unreleased/24883-build-failure-summary-page.yml b/changelogs/unreleased/24883-build-failure-summary-page.yml new file mode 100644 index 00000000000..214cd3e2bc7 --- /dev/null +++ b/changelogs/unreleased/24883-build-failure-summary-page.yml @@ -0,0 +1,4 @@ +--- +title: Added build failures summary page for pipelines +merge_request: 10719 +author: diff --git a/config/routes/project.rb b/config/routes/project.rb index 085f5a24e2e..68474a44368 100644 --- a/config/routes/project.rb +++ b/config/routes/project.rb @@ -123,6 +123,7 @@ constraints(ProjectUrlConstrainer.new) do post :cancel post :retry get :builds + get :failures get :status end end diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb index 5a53e48f5f8..cfac54ef259 100644 --- a/spec/features/projects/pipelines/pipeline_spec.rb +++ b/spec/features/projects/pipelines/pipeline_spec.rb @@ -254,4 +254,57 @@ describe 'Pipeline', :feature, :js do it { expect(build_manual.reload).to be_pending } end end + + describe 'GET /:project/pipelines/:id/failures' do + let(:project) { create(:project) } + let(:pipeline) { create(:ci_pipeline, project: project, ref: 'master', sha: project.commit.id) } + let(:pipeline_failures_page) { failures_namespace_project_pipeline_path(project.namespace, project, pipeline) } + let!(:failed_build) { create(:ci_build, :failed, pipeline: pipeline) } + + context 'with failed build' do + before do + failed_build.trace.set('4 examples, 1 failure') + + visit pipeline_failures_page + end + + it 'shows jobs tab pane as active' do + expect(page).to have_content('Failed Jobs') + expect(page).to have_css('#js-tab-failures.active') + end + + it 'lists failed builds' do + expect(page).to have_content(failed_build.name) + expect(page).to have_content(failed_build.stage) + end + + it 'shows build failure logs' do + expect(page).to have_content('4 examples, 1 failure') + end + end + + context 'when missing build logs' do + before do + visit pipeline_failures_page + end + + it 'includes failed jobs' do + expect(page).to have_content('No job trace') + end + end + + context 'without failures' do + before do + failed_build.update!(status: :success) + + visit pipeline_failures_page + end + + it 'displays the pipeline graph' do + expect(current_path).to eq(pipeline_path(pipeline)) + expect(page).not_to have_content('Failed Jobs') + expect(page).to have_selector('.pipeline-visualization') + end + end + end end |