summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/assets/stylesheets/framework/files.scss50
-rw-r--r--app/assets/stylesheets/framework/variables.scss7
-rw-r--r--app/helpers/blame_helper.rb21
-rw-r--r--app/models/ci/build.rb4
-rw-r--r--app/views/projects/blame/_age_map_legend.html.haml12
-rw-r--r--app/views/projects/blame/show.html.haml8
-rw-r--r--app/views/shared/issuable/form/_merge_params.html.haml3
-rw-r--r--changelogs/unreleased/23998-blame-age-map.yml4
-rw-r--r--changelogs/unreleased/disable-blocked-manual-actions.yml4
-rw-r--r--spec/helpers/blame_helper_spec.rb59
-rw-r--r--spec/models/ci/build_spec.rb16
-rw-r--r--spec/models/deployment_spec.rb4
-rw-r--r--spec/models/environment_spec.rb2
-rw-r--r--spec/spec_helper.rb1
14 files changed, 186 insertions, 9 deletions
diff --git a/app/assets/stylesheets/framework/files.scss b/app/assets/stylesheets/framework/files.scss
index d08df05fd6c..b26d8fbd5fe 100644
--- a/app/assets/stylesheets/framework/files.scss
+++ b/app/assets/stylesheets/framework/files.scss
@@ -59,6 +59,43 @@
}
}
+ .file-blame-legend {
+ background-color: $gray-light;
+ text-align: right;
+ padding: 8px $gl-padding;
+
+ @media (max-width: $screen-xs-max) {
+ text-align: left;
+ }
+
+ .left-label {
+ padding-right: 5px;
+ }
+
+ .right-label {
+ padding-left: 5px;
+ }
+
+ .legend-box {
+ display: inline-block;
+ width: 10px;
+ height: 10px;
+ padding: 0 2px;
+ }
+
+ @for $i from 0 through 5 {
+ .legend-box-#{$i} {
+ background-color: mix($blame-cyan, $blame-blue, $i / 5.0 * 100%);
+ }
+ }
+
+ @for $i from 1 through 4 {
+ .legend-box-#{$i + 5} {
+ background-color: mix($blame-gray, $blame-cyan, $i / 4.0 * 100%);
+ }
+ }
+ }
+
.file-content {
background: $white-light;
@@ -118,6 +155,19 @@
padding: 5px 10px;
min-width: 400px;
background: $gray-light;
+ border-left: 3px solid;
+ }
+
+ @for $i from 0 through 5 {
+ td.blame-commit-age-#{$i} {
+ border-left-color: mix($blame-cyan, $blame-blue, $i / 5.0 * 100%);
+ }
+ }
+
+ @for $i from 1 through 4 {
+ td.blame-commit-age-#{$i + 5} {
+ border-left-color: mix($blame-gray, $blame-cyan, $i / 4.0 * 100%);
+ }
}
td.line-numbers {
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index 4114a050d9a..49ba0108228 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -365,6 +365,13 @@ $avatar-border: rgba(0, 0, 0, .1);
$gl-avatar-size: 40px;
/*
+* Blame
+*/
+$blame-gray: #ededed;
+$blame-cyan: #acd5f2;
+$blame-blue: #254e77;
+
+/*
* Builds
*/
$builds-trace-bg: #111;
diff --git a/app/helpers/blame_helper.rb b/app/helpers/blame_helper.rb
new file mode 100644
index 00000000000..d1dc4d94560
--- /dev/null
+++ b/app/helpers/blame_helper.rb
@@ -0,0 +1,21 @@
+module BlameHelper
+ def age_map_duration(blame_groups, project)
+ now = Time.zone.now
+ start_date = blame_groups.map { |blame_group| blame_group[:commit].committed_date }
+ .append(project.created_at).min
+
+ {
+ now: now,
+ started_days_ago: (now - start_date).to_i / 1.day
+ }
+ end
+
+ def age_map_class(commit_date, duration)
+ commit_date_days_ago = (duration[:now] - commit_date).to_i / 1.day
+ # Numbers 0 to 10 come from this calculation, but only commits on the oldest
+ # day get number 10 (all other numbers can be multiple days), so the range
+ # is normalized to 0-9
+ age_group = [(10 * commit_date_days_ago) / duration[:started_days_ago], 9].min
+ "blame-commit-age-#{age_group}"
+ end
+end
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index cec1ca89a6a..58758f7ca8a 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -33,7 +33,7 @@ module Ci
scope :with_artifacts_not_expired, ->() { with_artifacts.where('artifacts_expire_at IS NULL OR artifacts_expire_at > ?', Time.now) }
scope :with_expired_artifacts, ->() { with_artifacts.where('artifacts_expire_at < ?', Time.now) }
scope :last_month, ->() { where('created_at > ?', Date.today - 1.month) }
- scope :manual_actions, ->() { where(when: :manual).relevant }
+ scope :manual_actions, ->() { where(when: :manual, status: COMPLETED_STATUSES + [:manual]) }
mount_uploader :artifacts_file, ArtifactUploader
mount_uploader :artifacts_metadata, ArtifactUploader
@@ -109,7 +109,7 @@ module Ci
end
def playable?
- action? && manual?
+ action? && (manual? || complete?)
end
def action?
diff --git a/app/views/projects/blame/_age_map_legend.html.haml b/app/views/projects/blame/_age_map_legend.html.haml
new file mode 100644
index 00000000000..533dc20ffb3
--- /dev/null
+++ b/app/views/projects/blame/_age_map_legend.html.haml
@@ -0,0 +1,12 @@
+%span.left-label Newer
+%span.legend-box.legend-box-0
+%span.legend-box.legend-box-1
+%span.legend-box.legend-box-2
+%span.legend-box.legend-box-3
+%span.legend-box.legend-box-4
+%span.legend-box.legend-box-5
+%span.legend-box.legend-box-6
+%span.legend-box.legend-box-7
+%span.legend-box.legend-box-8
+%span.legend-box.legend-box-9
+%span.right-label Older
diff --git a/app/views/projects/blame/show.html.haml b/app/views/projects/blame/show.html.haml
index a6ee2b2f7b8..ce937ee1842 100644
--- a/app/views/projects/blame/show.html.haml
+++ b/app/views/projects/blame/show.html.haml
@@ -1,4 +1,5 @@
- @no_container = true
+- project_duration = age_map_duration(@blame_groups, @project)
- page_title "Annotate", @blob.path, @ref
= render "projects/commits/head"
@@ -8,15 +9,16 @@
.file-holder
= render "projects/blob/header", blob: @blob, blame: true
-
+ .file-blame-legend
+ = render 'age_map_legend'
.table-responsive.file-content.blame.code.js-syntax-highlight
%table
- current_line = 1
- @blame_groups.each do |blame_group|
%tr
- %td.blame-commit
+ - commit = blame_group[:commit]
+ %td.blame-commit{ class: age_map_class(commit.committed_date, project_duration) }
.commit
- - commit = blame_group[:commit]
= author_avatar(commit, size: 36)
.commit-row-title
%strong
diff --git a/app/views/shared/issuable/form/_merge_params.html.haml b/app/views/shared/issuable/form/_merge_params.html.haml
index 271150ed318..bfa91629e1e 100644
--- a/app/views/shared/issuable/form/_merge_params.html.haml
+++ b/app/views/shared/issuable/form/_merge_params.html.haml
@@ -3,7 +3,8 @@
- return unless issuable.is_a?(MergeRequest)
- return if issuable.closed_without_fork?
--# This check is duplicated below, to avoid conflicts with EE.
+-# This check is duplicated below to avoid CE -> EE merge conflicts.
+-# This comment and the following line should only exist in CE.
- return unless issuable.can_remove_source_branch?(current_user)
.form-group
diff --git a/changelogs/unreleased/23998-blame-age-map.yml b/changelogs/unreleased/23998-blame-age-map.yml
new file mode 100644
index 00000000000..26a38f0939c
--- /dev/null
+++ b/changelogs/unreleased/23998-blame-age-map.yml
@@ -0,0 +1,4 @@
+---
+title: Add blame view age mapping
+merge_request: 7198
+author: Jeff Stubler
diff --git a/changelogs/unreleased/disable-blocked-manual-actions.yml b/changelogs/unreleased/disable-blocked-manual-actions.yml
new file mode 100644
index 00000000000..a640f61a7dd
--- /dev/null
+++ b/changelogs/unreleased/disable-blocked-manual-actions.yml
@@ -0,0 +1,4 @@
+---
+title: disable blocked manual actions
+merge_request:
+author:
diff --git a/spec/helpers/blame_helper_spec.rb b/spec/helpers/blame_helper_spec.rb
new file mode 100644
index 00000000000..b4368516d83
--- /dev/null
+++ b/spec/helpers/blame_helper_spec.rb
@@ -0,0 +1,59 @@
+require 'spec_helper'
+
+describe BlameHelper do
+ describe '#get_age_map_start_date' do
+ let(:dates) do
+ [Time.zone.local(2014, 3, 17, 0, 0, 0),
+ Time.zone.local(2011, 11, 2, 0, 0, 0),
+ Time.zone.local(2015, 7, 9, 0, 0, 0),
+ Time.zone.local(2013, 2, 24, 0, 0, 0),
+ Time.zone.local(2010, 9, 22, 0, 0, 0)]
+ end
+ let(:blame_groups) do
+ [
+ { commit: double(committed_date: dates[0]) },
+ { commit: double(committed_date: dates[1]) },
+ { commit: double(committed_date: dates[2]) }
+ ]
+ end
+
+ it 'returns the earliest date from a blame group' do
+ project = double(created_at: dates[3])
+
+ duration = helper.age_map_duration(blame_groups, project)
+
+ expect(duration[:started_days_ago]).to eq((duration[:now] - dates[1]).to_i / 1.day)
+ end
+
+ it 'returns the earliest date from a project' do
+ project = double(created_at: dates[4])
+
+ duration = helper.age_map_duration(blame_groups, project)
+
+ expect(duration[:started_days_ago]).to eq((duration[:now] - dates[4]).to_i / 1.day)
+ end
+ end
+
+ describe '#age_map_class' do
+ let(:dates) do
+ [Time.zone.local(2014, 3, 17, 0, 0, 0)]
+ end
+ let(:blame_groups) do
+ [
+ { commit: double(committed_date: dates[0]) }
+ ]
+ end
+ let(:duration) do
+ project = double(created_at: dates[0])
+ helper.age_map_duration(blame_groups, project)
+ end
+
+ it 'returns blame-commit-age-9 when oldest' do
+ expect(helper.age_map_class(dates[0], duration)).to eq 'blame-commit-age-9'
+ end
+
+ it 'returns blame-commit-age-0 class when newest' do
+ expect(helper.age_map_class(duration[:now], duration)).to eq 'blame-commit-age-0'
+ end
+ end
+end
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index 975a8b01e9e..3816422fec6 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -21,6 +21,18 @@ describe Ci::Build, :models do
it { is_expected.to respond_to(:has_trace?) }
it { is_expected.to respond_to(:trace) }
+ describe '.manual_actions' do
+ let!(:manual_but_created) { create(:ci_build, :manual, status: :created, pipeline: pipeline) }
+ let!(:manual_but_succeeded) { create(:ci_build, :manual, status: :success, pipeline: pipeline) }
+ let!(:manual_action) { create(:ci_build, :manual, pipeline: pipeline) }
+
+ subject { described_class.manual_actions }
+
+ it { is_expected.to include(manual_action) }
+ it { is_expected.to include(manual_but_succeeded) }
+ it { is_expected.not_to include(manual_but_created) }
+ end
+
describe '#actionize' do
context 'when build is a created' do
before do
@@ -938,6 +950,10 @@ describe Ci::Build, :models do
context 'when other build is retried' do
let!(:retried_build) { Ci::Build.retry(other_build, user) }
+ before do
+ retried_build.success
+ end
+
it 'returns a retried build' do
is_expected.to contain_exactly(retried_build)
end
diff --git a/spec/models/deployment_spec.rb b/spec/models/deployment_spec.rb
index 6f0d2db23c7..aad215d5f41 100644
--- a/spec/models/deployment_spec.rb
+++ b/spec/models/deployment_spec.rb
@@ -102,7 +102,7 @@ describe Deployment, models: true do
end
context 'with other actions' do
- let!(:close_action) { create(:ci_build, pipeline: build.pipeline, name: 'close_app', when: :manual) }
+ let!(:close_action) { create(:ci_build, :manual, pipeline: build.pipeline, name: 'close_app') }
context 'when matching action is defined' do
let(:deployment) { FactoryGirl.build(:deployment, deployable: build, on_stop: 'close_other_app') }
@@ -130,7 +130,7 @@ describe Deployment, models: true do
context 'when matching action is defined' do
let(:build) { create(:ci_build) }
let(:deployment) { FactoryGirl.build(:deployment, deployable: build, on_stop: 'close_app') }
- let!(:close_action) { create(:ci_build, pipeline: build.pipeline, name: 'close_app', when: :manual) }
+ let!(:close_action) { create(:ci_build, :manual, pipeline: build.pipeline, name: 'close_app') }
it { is_expected.to be_truthy }
end
diff --git a/spec/models/environment_spec.rb b/spec/models/environment_spec.rb
index fe69c8e351d..f8123cb518e 100644
--- a/spec/models/environment_spec.rb
+++ b/spec/models/environment_spec.rb
@@ -170,7 +170,7 @@ describe Environment, models: true do
context 'when matching action is defined' do
let(:build) { create(:ci_build) }
let!(:deployment) { create(:deployment, environment: environment, deployable: build, on_stop: 'close_app') }
- let!(:close_action) { create(:ci_build, pipeline: build.pipeline, name: 'close_app', when: :manual) }
+ let!(:close_action) { create(:ci_build, :manual, pipeline: build.pipeline, name: 'close_app') }
context 'when environment is available' do
before do
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index a81d3573f8d..01ac3cbd3f6 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -56,6 +56,7 @@ RSpec.configure do |config|
config.include StubGitlabCalls
config.include StubGitlabData
config.include ApiHelpers, :api
+ config.include Rails.application.routes.url_helpers, type: :routing
config.include MigrationsHelpers, :migration
config.infer_spec_type_from_file_location!