summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDouwe Maan <douwe@gitlab.com>2016-08-10 16:52:56 +0000
committerDouwe Maan <douwe@gitlab.com>2016-08-10 16:52:56 +0000
commitae63f152c3b1135500577e0b7e6528c607ebc1f7 (patch)
tree0aed9b1bbca3491dd821d3ef557351633c9088d4
parent34d5426f0e17a9d0a2d2330b472114e7e457ae05 (diff)
parent3756bbe12c922ad23dd5ab6302cf64b7bafe84ba (diff)
downloadgitlab-ce-ae63f152c3b1135500577e0b7e6528c607ebc1f7.tar.gz
Merge branch 'feature/svg-badge-template' into 'master'
Use badge image template instead of using separate images ## What does this MR do? Makes it possible to use template for badge instead of having multiple files. ## Are there points in the code the reviewer needs to double check? We also have a deprecated badge in `controllers/ci/projects_controller.rb`. We decided to leave it until 9.0, so we still have images in `public/ci/` until 9.0. ## Why was this MR needed? We are going to implement build coverage badge, and we do not want to store 101 SVG images for each percentage value. ## What are the relevant issue numbers? #3714 ## Screenshots (if relevant) ![new_build_badge](/uploads/f1d4ed5e34278eb01f48994b5b0579f1/new_build_badge.png) ## Does this MR meet the acceptance criteria? - [ ] ~~[CHANGELOG](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CHANGELOG) entry added~~ (refactoring) - [ ] ~~[Documentation created/updated](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/development/doc_styleguide.md)~~ - [ ] ~~API support added~~ - Tests - [x] Added for this feature/bug - [x] All builds are passing - [x] Conform by the [style guides](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#style-guides) - [x] Branch has no merge conflicts with `master` (if you do - rebase it please) - [ ] ~~[Squashed related commits together](https://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits)~~ (refactoring) See merge request !5520
-rw-r--r--app/controllers/projects/badges_controller.rb3
-rw-r--r--app/controllers/projects/pipelines_settings_controller.rb2
-rw-r--r--app/views/projects/badges/badge.svg.erb36
-rw-r--r--features/steps/project/badges/build.rb2
-rw-r--r--lib/gitlab/badge/build.rb40
-rw-r--r--lib/gitlab/badge/build/metadata.rb36
-rw-r--r--lib/gitlab/badge/build/template.rb63
-rw-r--r--spec/lib/gitlab/badge/build/metadata_spec.rb37
-rw-r--r--spec/lib/gitlab/badge/build/template_spec.rb76
-rw-r--r--spec/lib/gitlab/badge/build_spec.rb120
10 files changed, 315 insertions, 100 deletions
diff --git a/app/controllers/projects/badges_controller.rb b/app/controllers/projects/badges_controller.rb
index a9f482c8787..d0f5071d2cc 100644
--- a/app/controllers/projects/badges_controller.rb
+++ b/app/controllers/projects/badges_controller.rb
@@ -8,8 +8,9 @@ class Projects::BadgesController < Projects::ApplicationController
respond_to do |format|
format.html { render_404 }
+
format.svg do
- send_data(badge.data, type: badge.type, disposition: 'inline')
+ render 'badge', locals: { badge: badge.template }
end
end
end
diff --git a/app/controllers/projects/pipelines_settings_controller.rb b/app/controllers/projects/pipelines_settings_controller.rb
index 85ba706e5cd..75dd3648e45 100644
--- a/app/controllers/projects/pipelines_settings_controller.rb
+++ b/app/controllers/projects/pipelines_settings_controller.rb
@@ -3,7 +3,7 @@ class Projects::PipelinesSettingsController < Projects::ApplicationController
def show
@ref = params[:ref] || @project.default_branch || 'master'
- @build_badge = Gitlab::Badge::Build.new(@project, @ref)
+ @build_badge = Gitlab::Badge::Build.new(@project, @ref).metadata
end
def update
diff --git a/app/views/projects/badges/badge.svg.erb b/app/views/projects/badges/badge.svg.erb
new file mode 100644
index 00000000000..a5fef4fc56f
--- /dev/null
+++ b/app/views/projects/badges/badge.svg.erb
@@ -0,0 +1,36 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="<%= badge.width %>" height="20">
+ <linearGradient id="b" x2="0" y2="100%">
+ <stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
+ <stop offset="1" stop-opacity=".1"/>
+ </linearGradient>
+
+ <mask id="a">
+ <rect width="<%= badge.width %>" height="20" rx="3" fill="#fff"/>
+ </mask>
+
+ <g mask="url(#a)">
+ <path fill="<%= badge.key_color %>"
+ d="M0 0 h<%= badge.key_width %> v20 H0 z"/>
+ <path fill="<%= badge.value_color %>"
+ d="M<%= badge.key_width %> 0 h<%= badge.value_width %> v20 H<%= badge.key_width %> z"/>
+ <path fill="url(#b)"
+ d="M0 0 h<%= badge.width %> v20 H0 z"/>
+ </g>
+
+ <g fill="#fff" text-anchor="middle">
+ <g font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
+ <text x="<%= badge.key_text_anchor %>" y="15" fill="#010101" fill-opacity=".3">
+ <%= badge.key_text %>
+ </text>
+ <text x="<%= badge.key_text_anchor %>" y="14">
+ <%= badge.key_text %>
+ </text>
+ <text x="<%= badge.value_text_anchor %>" y="15" fill="#010101" fill-opacity=".3">
+ <%= badge.value_text %>
+ </text>
+ <text x="<%= badge.value_text_anchor %>" y="14">
+ <%= badge.value_text %>
+ </text>
+ </g>
+ </g>
+</svg>
diff --git a/features/steps/project/badges/build.rb b/features/steps/project/badges/build.rb
index 66a48a176e5..96c59322f9b 100644
--- a/features/steps/project/badges/build.rb
+++ b/features/steps/project/badges/build.rb
@@ -26,7 +26,7 @@ class Spinach::Features::ProjectBadgesBuild < Spinach::FeatureSteps
def expect_badge(status)
svg = Nokogiri::XML.parse(page.body)
- expect(page.response_headers).to include('Content-Type' => 'image/svg+xml')
+ expect(page.response_headers['Content-Type']).to include('image/svg+xml')
expect(svg.at(%Q{text:contains("#{status}")})).to be_truthy
end
end
diff --git a/lib/gitlab/badge/build.rb b/lib/gitlab/badge/build.rb
index e5e9fab3f5c..1de721a2269 100644
--- a/lib/gitlab/badge/build.rb
+++ b/lib/gitlab/badge/build.rb
@@ -4,42 +4,26 @@ module Gitlab
# Build badge
#
class Build
- include Gitlab::Application.routes.url_helpers
- include ActionView::Helpers::AssetTagHelper
- include ActionView::Helpers::UrlHelper
+ delegate :key_text, :value_text, to: :template
def initialize(project, ref)
- @project, @ref = project, ref
- @image = ::Ci::ImageForBuildService.new.execute(project, ref: ref)
+ @project = project
+ @ref = ref
+ @sha = @project.commit(@ref).try(:sha)
end
- def type
- 'image/svg+xml'
+ def status
+ @project.pipelines
+ .where(sha: @sha, ref: @ref)
+ .status || 'unknown'
end
- def data
- File.read(@image[:path])
+ def metadata
+ @metadata ||= Build::Metadata.new(@project, @ref)
end
- def to_s
- @image[:name].sub(/\.svg$/, '')
- end
-
- def to_html
- link_to(image_tag(image_url, alt: 'build status'), link_url)
- end
-
- def to_markdown
- "[![build status](#{image_url})](#{link_url})"
- end
-
- def image_url
- build_namespace_project_badges_url(@project.namespace,
- @project, @ref, format: :svg)
- end
-
- def link_url
- namespace_project_commits_url(@project.namespace, @project, id: @ref)
+ def template
+ @template ||= Build::Template.new(status)
end
end
end
diff --git a/lib/gitlab/badge/build/metadata.rb b/lib/gitlab/badge/build/metadata.rb
new file mode 100644
index 00000000000..553ef8d7b16
--- /dev/null
+++ b/lib/gitlab/badge/build/metadata.rb
@@ -0,0 +1,36 @@
+module Gitlab
+ module Badge
+ class Build
+ ##
+ # Class that describes build badge metadata
+ #
+ class Metadata
+ include Gitlab::Application.routes.url_helpers
+ include ActionView::Helpers::AssetTagHelper
+ include ActionView::Helpers::UrlHelper
+
+ def initialize(project, ref)
+ @project = project
+ @ref = ref
+ end
+
+ def to_html
+ link_to(image_tag(image_url, alt: 'build status'), link_url)
+ end
+
+ def to_markdown
+ "[![build status](#{image_url})](#{link_url})"
+ end
+
+ def image_url
+ build_namespace_project_badges_url(@project.namespace,
+ @project, @ref, format: :svg)
+ end
+
+ def link_url
+ namespace_project_commits_url(@project.namespace, @project, id: @ref)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/badge/build/template.rb b/lib/gitlab/badge/build/template.rb
new file mode 100644
index 00000000000..deba3b669b3
--- /dev/null
+++ b/lib/gitlab/badge/build/template.rb
@@ -0,0 +1,63 @@
+module Gitlab
+ module Badge
+ class Build
+ ##
+ # Class that represents a build badge template.
+ #
+ # Template object will be passed to badge.svg.erb template.
+ #
+ class Template
+ STATUS_COLOR = {
+ success: '#4c1',
+ failed: '#e05d44',
+ running: '#dfb317',
+ pending: '#dfb317',
+ canceled: '#9f9f9f',
+ skipped: '#9f9f9f',
+ unknown: '#9f9f9f'
+ }
+
+ def initialize(status)
+ @status = status
+ end
+
+ def key_text
+ 'build'
+ end
+
+ def value_text
+ @status
+ end
+
+ def key_width
+ 38
+ end
+
+ def value_width
+ 54
+ end
+
+ def key_color
+ '#555'
+ end
+
+ def value_color
+ STATUS_COLOR[@status.to_sym] ||
+ STATUS_COLOR[:unknown]
+ end
+
+ def key_text_anchor
+ key_width / 2
+ end
+
+ def value_text_anchor
+ key_width + (value_width / 2)
+ end
+
+ def width
+ key_width + value_width
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/badge/build/metadata_spec.rb b/spec/lib/gitlab/badge/build/metadata_spec.rb
new file mode 100644
index 00000000000..ad5388215c2
--- /dev/null
+++ b/spec/lib/gitlab/badge/build/metadata_spec.rb
@@ -0,0 +1,37 @@
+require 'spec_helper'
+
+describe Gitlab::Badge::Build::Metadata do
+ let(:project) { create(:project) }
+ let(:branch) { 'master' }
+ let(:badge) { described_class.new(project, branch) }
+
+ describe '#to_html' do
+ let(:html) { Nokogiri::HTML.parse(badge.to_html) }
+ let(:a_href) { html.at('a') }
+
+ it 'points to link' do
+ expect(a_href[:href]).to eq badge.link_url
+ end
+
+ it 'contains clickable image' do
+ expect(a_href.children.first.name).to eq 'img'
+ end
+ end
+
+ describe '#to_markdown' do
+ subject { badge.to_markdown }
+
+ it { is_expected.to include badge.image_url }
+ it { is_expected.to include badge.link_url }
+ end
+
+ describe '#image_url' do
+ subject { badge.image_url }
+ it { is_expected.to include "badges/#{branch}/build.svg" }
+ end
+
+ describe '#link_url' do
+ subject { badge.link_url }
+ it { is_expected.to include "commits/#{branch}" }
+ end
+end
diff --git a/spec/lib/gitlab/badge/build/template_spec.rb b/spec/lib/gitlab/badge/build/template_spec.rb
new file mode 100644
index 00000000000..86dead3c54e
--- /dev/null
+++ b/spec/lib/gitlab/badge/build/template_spec.rb
@@ -0,0 +1,76 @@
+require 'spec_helper'
+
+describe Gitlab::Badge::Build::Template do
+ let(:status) { 'success' }
+ let(:template) { described_class.new(status) }
+
+ describe '#key_text' do
+ it 'is always says build' do
+ expect(template.key_text).to eq 'build'
+ end
+ end
+
+ describe '#value_text' do
+ it 'is status value' do
+ expect(template.value_text).to eq 'success'
+ end
+ end
+
+ describe 'widths and text anchors' do
+ it 'has fixed width and text anchors' do
+ expect(template.width).to eq 92
+ expect(template.key_width).to eq 38
+ expect(template.value_width).to eq 54
+ expect(template.key_text_anchor).to eq 19
+ expect(template.value_text_anchor).to eq 65
+ end
+ end
+
+ describe '#key_color' do
+ it 'is always the same' do
+ expect(template.key_color).to eq '#555'
+ end
+ end
+
+ describe '#value_color' do
+ context 'when status is success' do
+ let(:status) { 'success' }
+
+ it 'has expected color' do
+ expect(template.value_color).to eq '#4c1'
+ end
+ end
+
+ context 'when status is failed' do
+ let(:status) { 'failed' }
+
+ it 'has expected color' do
+ expect(template.value_color).to eq '#e05d44'
+ end
+ end
+
+ context 'when status is running' do
+ let(:status) { 'running' }
+
+ it 'has expected color' do
+ expect(template.value_color).to eq '#dfb317'
+ end
+ end
+
+ context 'when status is unknown' do
+ let(:status) { 'unknown' }
+
+ it 'has expected color' do
+ expect(template.value_color).to eq '#9f9f9f'
+ end
+ end
+
+ context 'when status does not match any known statuses' do
+ let(:status) { 'invalid status' }
+
+ it 'has expected color' do
+ expect(template.value_color).to eq '#9f9f9f'
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/badge/build_spec.rb b/spec/lib/gitlab/badge/build_spec.rb
index f3b522a02f5..ef9d9e7fef4 100644
--- a/spec/lib/gitlab/badge/build_spec.rb
+++ b/spec/lib/gitlab/badge/build_spec.rb
@@ -6,39 +6,17 @@ describe Gitlab::Badge::Build do
let(:branch) { 'master' }
let(:badge) { described_class.new(project, branch) }
- describe '#type' do
- subject { badge.type }
- it { is_expected.to eq 'image/svg+xml' }
- end
-
- describe '#to_html' do
- let(:html) { Nokogiri::HTML.parse(badge.to_html) }
- let(:a_href) { html.at('a') }
-
- it 'points to link' do
- expect(a_href[:href]).to eq badge.link_url
- end
-
- it 'contains clickable image' do
- expect(a_href.children.first.name).to eq 'img'
+ describe '#metadata' do
+ it 'returns badge metadata' do
+ expect(badge.metadata.image_url)
+ .to include 'badges/master/build.svg'
end
end
- describe '#to_markdown' do
- subject { badge.to_markdown }
-
- it { is_expected.to include badge.image_url }
- it { is_expected.to include badge.link_url }
- end
-
- describe '#image_url' do
- subject { badge.image_url }
- it { is_expected.to include "badges/#{branch}/build.svg" }
- end
-
- describe '#link_url' do
- subject { badge.link_url }
- it { is_expected.to include "commits/#{branch}" }
+ describe '#key_text' do
+ it 'always says build' do
+ expect(badge.key_text).to eq 'build'
+ end
end
context 'build exists' do
@@ -47,16 +25,15 @@ describe Gitlab::Badge::Build do
context 'build success' do
before { build.success! }
- describe '#to_s' do
- subject { badge.to_s }
- it { is_expected.to eq 'build-success' }
+ describe '#status' do
+ it 'is successful' do
+ expect(badge.status).to eq 'success'
+ end
end
- describe '#data' do
- let(:data) { badge.data }
-
- it 'contains information about success' do
- expect(status_node(data, 'success')).to be_truthy
+ describe '#value_text' do
+ it 'returns correct value text' do
+ expect(badge.value_text).to eq 'success'
end
end
end
@@ -64,47 +41,57 @@ describe Gitlab::Badge::Build do
context 'build failed' do
before { build.drop! }
- describe '#to_s' do
- subject { badge.to_s }
- it { is_expected.to eq 'build-failed' }
+ describe '#status' do
+ it 'failed' do
+ expect(badge.status).to eq 'failed'
+ end
end
- describe '#data' do
- let(:data) { badge.data }
-
- it 'contains information about failure' do
- expect(status_node(data, 'failed')).to be_truthy
+ describe '#value_text' do
+ it 'has correct value text' do
+ expect(badge.value_text).to eq 'failed'
end
end
end
- end
- context 'build does not exist' do
- describe '#to_s' do
- subject { badge.to_s }
- it { is_expected.to eq 'build-unknown' }
+ context 'when outdated pipeline for given ref exists' do
+ before do
+ build.success!
+
+ old_build = create_build(project, '11eeffdd', branch)
+ old_build.drop!
+ end
+
+ it 'does not take outdated pipeline into account' do
+ expect(badge.status).to eq 'success'
+ end
end
- describe '#data' do
- let(:data) { badge.data }
+ context 'when multiple pipelines exist for given sha' do
+ before do
+ build.drop!
+
+ new_build = create_build(project, sha, branch)
+ new_build.success!
+ end
- it 'contains infromation about unknown build' do
- expect(status_node(data, 'unknown')).to be_truthy
+ it 'reports the compound status' do
+ expect(badge.status).to eq 'failed'
end
end
end
- context 'when outdated pipeline for given ref exists' do
- before do
- build = create_build(project, sha, branch)
- build.success!
-
- old_build = create_build(project, '11eeffdd', branch)
- old_build.drop!
+ context 'build does not exist' do
+ describe '#status' do
+ it 'is unknown' do
+ expect(badge.status).to eq 'unknown'
+ end
end
- it 'does not take outdated pipeline into account' do
- expect(badge.to_s).to eq 'build-success'
+ describe '#value_text' do
+ it 'has correct value text' do
+ expect(badge.value_text).to eq 'unknown'
+ end
end
end
@@ -115,9 +102,4 @@ describe Gitlab::Badge::Build do
create(:ci_build, pipeline: pipeline, stage: 'notify')
end
-
- def status_node(data, status)
- xml = Nokogiri::XML.parse(data)
- xml.at(%Q{text:contains("#{status}")})
- end
end