Merge branch '23205-information-about-environments-build-page' into 'master'
Add environment info to builds page ![Screen_Shot_2016-11-02_at_5.44.01_PM](/uploads/3443d9518997147d1e6f41830e3774ff/Screen_Shot_2016-11-02_at_5.44.01_PM.png) Closes #23205 See merge request !7251
This commit is contained in:
commit
1e8cb595bf
9 changed files with 363 additions and 13 deletions
|
@ -40,6 +40,19 @@
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.environment-information {
|
||||||
|
background-color: $background-color;
|
||||||
|
border: 1px solid $border-color;
|
||||||
|
padding: 12px $gl-padding;
|
||||||
|
border-radius: $border-radius-default;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
position: relative;
|
||||||
|
top: 1px;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.build-header {
|
.build-header {
|
||||||
|
|
29
app/helpers/environment_helper.rb
Normal file
29
app/helpers/environment_helper.rb
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
module EnvironmentHelper
|
||||||
|
def environment_for_build(project, build)
|
||||||
|
return unless build.environment
|
||||||
|
|
||||||
|
project.environments.find_by(name: build.expanded_environment_name)
|
||||||
|
end
|
||||||
|
|
||||||
|
def environment_link_for_build(project, build)
|
||||||
|
environment = environment_for_build(project, build)
|
||||||
|
if environment
|
||||||
|
link_to environment.name, namespace_project_environment_path(project.namespace, project, environment)
|
||||||
|
else
|
||||||
|
content_tag :span, build.expanded_environment_name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def deployment_link(deployment)
|
||||||
|
return unless deployment
|
||||||
|
|
||||||
|
link_to "##{deployment.iid}", [deployment.project.namespace.becomes(Namespace), deployment.project, deployment.deployable]
|
||||||
|
end
|
||||||
|
|
||||||
|
def last_deployment_link_for_environment_build(project, build)
|
||||||
|
environment = environment_for_build(project, build)
|
||||||
|
return unless environment
|
||||||
|
|
||||||
|
deployment_link(environment.last_deployment)
|
||||||
|
end
|
||||||
|
end
|
|
@ -7,6 +7,8 @@ module Ci
|
||||||
belongs_to :trigger_request
|
belongs_to :trigger_request
|
||||||
belongs_to :erased_by, class_name: 'User'
|
belongs_to :erased_by, class_name: 'User'
|
||||||
|
|
||||||
|
has_many :deployments, as: :deployable
|
||||||
|
|
||||||
serialize :options
|
serialize :options
|
||||||
serialize :yaml_variables
|
serialize :yaml_variables
|
||||||
|
|
||||||
|
@ -125,6 +127,34 @@ module Ci
|
||||||
!self.pipeline.statuses.latest.include?(self)
|
!self.pipeline.statuses.latest.include?(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def expanded_environment_name
|
||||||
|
ExpandVariables.expand(environment, variables) if environment
|
||||||
|
end
|
||||||
|
|
||||||
|
def has_environment?
|
||||||
|
self.environment.present?
|
||||||
|
end
|
||||||
|
|
||||||
|
def starts_environment?
|
||||||
|
has_environment? && self.environment_action == 'start'
|
||||||
|
end
|
||||||
|
|
||||||
|
def stops_environment?
|
||||||
|
has_environment? && self.environment_action == 'stop'
|
||||||
|
end
|
||||||
|
|
||||||
|
def environment_action
|
||||||
|
self.options.fetch(:environment, {}).fetch(:action, 'start')
|
||||||
|
end
|
||||||
|
|
||||||
|
def outdated_deployment?
|
||||||
|
success? && !last_deployment.try(:last?)
|
||||||
|
end
|
||||||
|
|
||||||
|
def last_deployment
|
||||||
|
deployments.last
|
||||||
|
end
|
||||||
|
|
||||||
def depends_on_builds
|
def depends_on_builds
|
||||||
# Get builds of the same type
|
# Get builds of the same type
|
||||||
latest_builds = self.pipeline.builds.latest
|
latest_builds = self.pipeline.builds.latest
|
||||||
|
|
|
@ -26,6 +26,30 @@
|
||||||
= link_to namespace_project_runners_path(@build.project.namespace, @build.project) do
|
= link_to namespace_project_runners_path(@build.project.namespace, @build.project) do
|
||||||
Runners page
|
Runners page
|
||||||
|
|
||||||
|
- if @build.starts_environment?
|
||||||
|
.prepend-top-default
|
||||||
|
.environment-information
|
||||||
|
- if @build.outdated_deployment?
|
||||||
|
= ci_icon_for_status('success_with_warnings')
|
||||||
|
- else
|
||||||
|
= ci_icon_for_status(@build.status)
|
||||||
|
|
||||||
|
- environment = environment_for_build(@build.project, @build)
|
||||||
|
- if @build.success? && @build.last_deployment.present?
|
||||||
|
- if @build.last_deployment.last?
|
||||||
|
This build is the most recent deployment to #{environment_link_for_build(@build.project, @build)}.
|
||||||
|
- else
|
||||||
|
This build is an out-of-date deployment to #{environment_link_for_build(@build.project, @build)}.
|
||||||
|
- if environment.last_deployment
|
||||||
|
View the most recent deployment #{deployment_link(environment.last_deployment)}.
|
||||||
|
- elsif @build.complete? && !@build.success?
|
||||||
|
The deployment of this build to #{environment_link_for_build(@build.project, @build)} did not succeed.
|
||||||
|
- else
|
||||||
|
This build is creating a deployment to #{environment_link_for_build(@build.project, @build)}
|
||||||
|
- if environment.last_deployment
|
||||||
|
and will overwrite the
|
||||||
|
= link_to 'latest deployment', deployment_link(environment.last_deployment)
|
||||||
|
|
||||||
.prepend-top-default
|
.prepend-top-default
|
||||||
- if @build.erased?
|
- if @build.erased?
|
||||||
.erased.alert.alert-warning
|
.erased.alert.alert-warning
|
||||||
|
|
|
@ -4,15 +4,13 @@ class BuildSuccessWorker
|
||||||
|
|
||||||
def perform(build_id)
|
def perform(build_id)
|
||||||
Ci::Build.find_by(id: build_id).try do |build|
|
Ci::Build.find_by(id: build_id).try do |build|
|
||||||
create_deployment(build)
|
create_deployment(build) if build.has_environment?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def create_deployment(build)
|
def create_deployment(build)
|
||||||
return if build.environment.blank?
|
|
||||||
|
|
||||||
service = CreateDeploymentService.new(
|
service = CreateDeploymentService.new(
|
||||||
build.project, build.user,
|
build.project, build.user,
|
||||||
environment: build.environment,
|
environment: build.environment,
|
||||||
|
|
|
@ -1052,4 +1052,132 @@ describe Ci::Build, models: true do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#has_environment?' do
|
||||||
|
subject { build.has_environment? }
|
||||||
|
|
||||||
|
context 'when environment is defined' do
|
||||||
|
before do
|
||||||
|
build.update(environment: 'review')
|
||||||
|
end
|
||||||
|
|
||||||
|
it { is_expected.to be_truthy }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when environment is not defined' do
|
||||||
|
before do
|
||||||
|
build.update(environment: nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
it { is_expected.to be_falsey }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#starts_environment?' do
|
||||||
|
subject { build.starts_environment? }
|
||||||
|
|
||||||
|
context 'when environment is defined' do
|
||||||
|
before do
|
||||||
|
build.update(environment: 'review')
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'no action is defined' do
|
||||||
|
it { is_expected.to be_truthy }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'and start action is defined' do
|
||||||
|
before do
|
||||||
|
build.update(options: { environment: { action: 'start' } } )
|
||||||
|
end
|
||||||
|
|
||||||
|
it { is_expected.to be_truthy }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when environment is not defined' do
|
||||||
|
before do
|
||||||
|
build.update(environment: nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
it { is_expected.to be_falsey }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#stops_environment?' do
|
||||||
|
subject { build.stops_environment? }
|
||||||
|
|
||||||
|
context 'when environment is defined' do
|
||||||
|
before do
|
||||||
|
build.update(environment: 'review')
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'no action is defined' do
|
||||||
|
it { is_expected.to be_falsey }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'and stop action is defined' do
|
||||||
|
before do
|
||||||
|
build.update(options: { environment: { action: 'stop' } } )
|
||||||
|
end
|
||||||
|
|
||||||
|
it { is_expected.to be_truthy }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when environment is not defined' do
|
||||||
|
before do
|
||||||
|
build.update(environment: nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
it { is_expected.to be_falsey }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#last_deployment' do
|
||||||
|
subject { build.last_deployment }
|
||||||
|
|
||||||
|
context 'when multiple deployments are created' do
|
||||||
|
let!(:deployment1) { create(:deployment, deployable: build) }
|
||||||
|
let!(:deployment2) { create(:deployment, deployable: build) }
|
||||||
|
|
||||||
|
it 'returns the latest one' do
|
||||||
|
is_expected.to eq(deployment2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#outdated_deployment?' do
|
||||||
|
subject { build.outdated_deployment? }
|
||||||
|
|
||||||
|
context 'when build succeeded' do
|
||||||
|
let(:build) { create(:ci_build, :success) }
|
||||||
|
let!(:deployment) { create(:deployment, deployable: build) }
|
||||||
|
|
||||||
|
context 'current deployment is latest' do
|
||||||
|
it { is_expected.to be_falsey }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'current deployment is not latest on environment' do
|
||||||
|
let!(:deployment2) { create(:deployment, environment: deployment.environment) }
|
||||||
|
|
||||||
|
it { is_expected.to be_truthy }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when build failed' do
|
||||||
|
let(:build) { create(:ci_build, :failed) }
|
||||||
|
|
||||||
|
it { is_expected.to be_falsey }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#expanded_environment_name' do
|
||||||
|
subject { build.expanded_environment_name }
|
||||||
|
|
||||||
|
context 'when environment uses variables' do
|
||||||
|
let(:build) { create(:ci_build, ref: 'master', environment: 'review/$CI_BUILD_REF_NAME') }
|
||||||
|
|
||||||
|
it { is_expected.to eq('review/master') }
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,6 +4,12 @@ describe Ci::Build, models: true do
|
||||||
let(:build) { create(:ci_build) }
|
let(:build) { create(:ci_build) }
|
||||||
let(:test_trace) { 'This is a test' }
|
let(:test_trace) { 'This is a test' }
|
||||||
|
|
||||||
|
it { is_expected.to belong_to(:runner) }
|
||||||
|
it { is_expected.to belong_to(:trigger_request) }
|
||||||
|
it { is_expected.to belong_to(:erased_by) }
|
||||||
|
|
||||||
|
it { is_expected.to have_many(:deployments) }
|
||||||
|
|
||||||
describe '#trace' do
|
describe '#trace' do
|
||||||
it 'obfuscates project runners token' do
|
it 'obfuscates project runners token' do
|
||||||
allow(build).to receive(:raw_trace).and_return("Test: #{build.project.runners_token}")
|
allow(build).to receive(:raw_trace).and_return("Test: #{build.project.runners_token}")
|
||||||
|
|
|
@ -26,10 +26,11 @@ RSpec.configure do |config|
|
||||||
config.verbose_retry = true
|
config.verbose_retry = true
|
||||||
config.display_try_failure_messages = true
|
config.display_try_failure_messages = true
|
||||||
|
|
||||||
config.include Devise::Test::ControllerHelpers, type: :controller
|
config.include Devise::Test::ControllerHelpers, type: :controller
|
||||||
|
config.include Devise::Test::ControllerHelpers, type: :view
|
||||||
config.include Warden::Test::Helpers, type: :request
|
config.include Warden::Test::Helpers, type: :request
|
||||||
config.include LoginHelpers, type: :feature
|
config.include LoginHelpers, type: :feature
|
||||||
config.include SearchHelpers, type: :feature
|
config.include SearchHelpers, type: :feature
|
||||||
config.include StubConfiguration
|
config.include StubConfiguration
|
||||||
config.include EmailHelpers
|
config.include EmailHelpers
|
||||||
config.include TestEnv
|
config.include TestEnv
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
describe 'projects/builds/show' do
|
describe 'projects/builds/show', :view do
|
||||||
include Devise::Test::ControllerHelpers
|
|
||||||
|
|
||||||
let(:project) { create(:project) }
|
let(:project) { create(:project) }
|
||||||
let(:pipeline) do
|
|
||||||
create(:ci_pipeline, project: project,
|
|
||||||
sha: project.commit.id)
|
|
||||||
end
|
|
||||||
let(:build) { create(:ci_build, pipeline: pipeline) }
|
let(:build) { create(:ci_build, pipeline: pipeline) }
|
||||||
|
|
||||||
|
let(:pipeline) do
|
||||||
|
create(:ci_pipeline, project: project, sha: project.commit.id)
|
||||||
|
end
|
||||||
|
|
||||||
before do
|
before do
|
||||||
assign(:build, build)
|
assign(:build, build)
|
||||||
assign(:project, project)
|
assign(:project, project)
|
||||||
|
@ -17,6 +15,129 @@ describe 'projects/builds/show' do
|
||||||
allow(view).to receive(:can?).and_return(true)
|
allow(view).to receive(:can?).and_return(true)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'environment info in build view' do
|
||||||
|
context 'build with latest deployment' do
|
||||||
|
let(:build) do
|
||||||
|
create(:ci_build, :success, environment: 'staging')
|
||||||
|
end
|
||||||
|
|
||||||
|
before do
|
||||||
|
create(:environment, name: 'staging')
|
||||||
|
create(:deployment, deployable: build)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'shows deployment message' do
|
||||||
|
expected_text = 'This build is the most recent deployment'
|
||||||
|
render
|
||||||
|
|
||||||
|
expect(rendered).to have_css(
|
||||||
|
'.environment-information', text: expected_text)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'build with outdated deployment' do
|
||||||
|
let(:build) do
|
||||||
|
create(:ci_build, :success, environment: 'staging', pipeline: pipeline)
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:second_build) do
|
||||||
|
create(:ci_build, :success, environment: 'staging', pipeline: pipeline)
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:environment) do
|
||||||
|
create(:environment, name: 'staging', project: project)
|
||||||
|
end
|
||||||
|
|
||||||
|
let!(:first_deployment) do
|
||||||
|
create(:deployment, environment: environment, deployable: build)
|
||||||
|
end
|
||||||
|
|
||||||
|
let!(:second_deployment) do
|
||||||
|
create(:deployment, environment: environment, deployable: second_build)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'shows deployment message' do
|
||||||
|
expected_text = 'This build is an out-of-date deployment ' \
|
||||||
|
"to staging.\nView the most recent deployment ##{second_deployment.iid}."
|
||||||
|
render
|
||||||
|
|
||||||
|
expect(rendered).to have_css('.environment-information', text: expected_text)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'build failed to deploy' do
|
||||||
|
let(:build) do
|
||||||
|
create(:ci_build, :failed, environment: 'staging', pipeline: pipeline)
|
||||||
|
end
|
||||||
|
|
||||||
|
let!(:environment) do
|
||||||
|
create(:environment, name: 'staging', project: project)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'shows deployment message' do
|
||||||
|
expected_text = 'The deployment of this build to staging did not succeed.'
|
||||||
|
render
|
||||||
|
|
||||||
|
expect(rendered).to have_css(
|
||||||
|
'.environment-information', text: expected_text)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'build will deploy' do
|
||||||
|
let(:build) do
|
||||||
|
create(:ci_build, :running, environment: 'staging', pipeline: pipeline)
|
||||||
|
end
|
||||||
|
|
||||||
|
let!(:environment) do
|
||||||
|
create(:environment, name: 'staging', project: project)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'shows deployment message' do
|
||||||
|
expected_text = 'This build is creating a deployment to staging'
|
||||||
|
render
|
||||||
|
|
||||||
|
expect(rendered).to have_css(
|
||||||
|
'.environment-information', text: expected_text)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'build that failed to deploy and environment has not been created' do
|
||||||
|
let(:build) do
|
||||||
|
create(:ci_build, :failed, environment: 'staging', pipeline: pipeline)
|
||||||
|
end
|
||||||
|
|
||||||
|
let!(:environment) do
|
||||||
|
create(:environment, name: 'staging', project: project)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'shows deployment message' do
|
||||||
|
expected_text = 'The deployment of this build to staging did not succeed'
|
||||||
|
render
|
||||||
|
|
||||||
|
expect(rendered).to have_css(
|
||||||
|
'.environment-information', text: expected_text)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'build that will deploy and environment has not been created' do
|
||||||
|
let(:build) do
|
||||||
|
create(:ci_build, :running, environment: 'staging', pipeline: pipeline)
|
||||||
|
end
|
||||||
|
|
||||||
|
let!(:environment) do
|
||||||
|
create(:environment, name: 'staging', project: project)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'shows deployment message' do
|
||||||
|
expected_text = 'This build is creating a deployment to staging'
|
||||||
|
render
|
||||||
|
|
||||||
|
expect(rendered).to have_css(
|
||||||
|
'.environment-information', text: expected_text)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context 'when build is running' do
|
context 'when build is running' do
|
||||||
before do
|
before do
|
||||||
build.run!
|
build.run!
|
||||||
|
|
Loading…
Reference in a new issue