GitLab wurde erfolgreich aktualisiert. Dank regelmäßiger Updates bleibt das THM GitLab sicher und Sie profitieren von den neuesten Funktionen. Danke für Ihre Geduld.

Commit 32bba60c authored by Phil Hughes's avatar Phil Hughes

Merge branch '50904-vuex-show-block' into 'master'

Uses Vue app to render part of job show page

See merge request gitlab-org/gitlab-ce!22028
parents 88c1cf67 9128e784
......@@ -12,12 +12,16 @@
type: Object,
required: true,
},
iconStatus: {
type: Object,
required: true,
},
},
computed: {
environment() {
let environmentText;
switch (this.deploymentStatus.status) {
case 'latest':
case 'last':
environmentText = sprintf(
__('This job is the most recent deployment to %{link}.'),
{ link: this.environmentLink },
......@@ -32,7 +36,7 @@
),
{
environmentLink: this.environmentLink,
deploymentLink: this.deploymentLink,
deploymentLink: this.deploymentLink(`#${this.lastDeployment.iid}`),
},
false,
);
......@@ -56,11 +60,11 @@
if (this.hasLastDeployment) {
environmentText = sprintf(
__(
'This job is creating a deployment to %{environmentLink} and will overwrite the last %{deploymentLink}.',
'This job is creating a deployment to %{environmentLink} and will overwrite the %{deploymentLink}.',
),
{
environmentLink: this.environmentLink,
deploymentLink: this.deploymentLink,
deploymentLink: this.deploymentLink(__('latest deployment')),
},
false,
);
......@@ -78,41 +82,57 @@
return environmentText;
},
environmentLink() {
return sprintf(
'%{startLink}%{name}%{endLink}',
{
startLink: `<a href="${this.deploymentStatus.environment.path}">`,
name: _.escape(this.deploymentStatus.environment.name),
endLink: '</a>',
},
false,
);
if (this.hasEnvironment) {
return sprintf(
'%{startLink}%{name}%{endLink}',
{
startLink: `<a href="${
this.deploymentStatus.environment.environment_path
}" class="js-environment-link">`,
name: _.escape(this.deploymentStatus.environment.name),
endLink: '</a>',
},
false,
);
}
return '';
},
deploymentLink() {
hasLastDeployment() {
return this.hasEnvironment && this.deploymentStatus.environment.last_deployment;
},
lastDeployment() {
return this.hasLastDeployment ? this.deploymentStatus.environment.last_deployment : {};
},
hasEnvironment() {
return !_.isEmpty(this.deploymentStatus.environment);
},
lastDeploymentPath() {
return !_.isEmpty(this.lastDeployment.deployable) ? this.lastDeployment.deployable.build_path : '';
},
},
methods: {
deploymentLink(name) {
return sprintf(
'%{startLink}%{name}%{endLink}',
{
startLink: `<a href="${this.lastDeployment.path}">`,
name: _.escape(this.lastDeployment.name),
startLink: `<a href="${this.lastDeploymentPath}" class="js-job-deployment-link">`,
name,
endLink: '</a>',
},
false,
);
},
hasLastDeployment() {
return this.deploymentStatus.environment.last_deployment;
},
lastDeployment() {
return this.deploymentStatus.environment.last_deployment;
},
},
};
</script>
<template>
<div class="prepend-top-default js-environment-container">
<div class="environment-information">
<ci-icon :status="deploymentStatus.icon" />
<p v-html="environment"></p>
<ci-icon :status="iconStatus"/>
<p
class="inline append-bottom-0"
v-html="environment"
></p>
</div>
</div>
</template>
<script>
import ciHeader from '../../vue_shared/components/header_ci_component.vue';
import callout from '../../vue_shared/components/callout.vue';
export default {
name: 'JobHeaderSection',
components: {
ciHeader,
callout,
},
props: {
job: {
type: Object,
required: true,
},
isLoading: {
type: Boolean,
required: true,
},
},
data() {
return {
actions: this.getActions(),
};
},
computed: {
status() {
return this.job && this.job.status;
},
shouldRenderContent() {
return !this.isLoading && Object.keys(this.job).length;
},
shouldRenderReason() {
return !!(this.job.status && this.job.callout_message);
},
/**
* When job has not started the key will be `false`
* When job started the key will be a string with a date.
*/
jobStarted() {
return !this.job.started === false;
},
headerTime() {
return this.jobStarted ? this.job.started : this.job.created_at;
},
},
watch: {
job() {
this.actions = this.getActions();
},
},
methods: {
getActions() {
const actions = [];
if (this.job.new_issue_path) {
actions.push({
label: 'New issue',
path: this.job.new_issue_path,
cssClass: 'js-new-issue btn btn-success btn-inverted d-none d-md-block d-lg-block d-xl-block',
type: 'link',
});
}
return actions;
},
},
};
</script>
<template>
<header>
<div class="js-build-header build-header top-area">
<ci-header
v-if="shouldRenderContent"
:status="status"
:item-id="job.id"
:time="headerTime"
:user="job.user"
:actions="actions"
:has-sidebar-button="true"
:should-render-triggered-label="jobStarted"
item-name="Job"
/>
<gl-loading-icon
v-if="isLoading"
:size="2"
class="prepend-top-default append-bottom-default"
/>
</div>
<callout
v-if="shouldRenderReason"
:message="job.callout_message"
/>
</header>
</template>
<script>
import { mapGetters, mapState } from 'vuex';
import CiHeader from '~/vue_shared/components/header_ci_component.vue';
import Callout from '~/vue_shared/components/callout.vue';
import EnvironmentsBlock from './environments_block.vue';
import ErasedBlock from './erased_block.vue';
import StuckBlock from './stuck_block.vue';
export default {
name: 'JobPageApp',
components: {
CiHeader,
Callout,
EnvironmentsBlock,
ErasedBlock,
StuckBlock,
},
props: {
runnerHelpUrl: {
type: String,
required: false,
default: null,
},
},
computed: {
...mapState(['isLoading', 'job']),
...mapGetters([
'headerActions',
'headerTime',
'shouldRenderCalloutMessage',
'jobHasStarted',
'hasEnvironment',
'isJobStuck',
]),
},
};
</script>
<template>
<div>
<gl-loading-icon
v-if="isLoading"
:size="2"
class="prepend-top-20"
/>
<template v-else>
<!-- Header Section -->
<header>
<div class="js-build-header build-header top-area">
<ci-header
:status="job.status"
:item-id="job.id"
:time="headerTime"
:user="job.user"
:actions="headerActions"
:has-sidebar-button="true"
:should-render-triggered-label="jobHasStarted"
:item-name="__('Job')"
/>
</div>
<callout
v-if="shouldRenderCalloutMessage"
:message="job.callout_message"
/>
</header>
<!-- EO Header Section -->
<!-- Body Section -->
<stuck-block
v-if="isJobStuck"
class="js-job-stuck"
:has-no-runners-for-project="job.runners.available"
:tags="job.tags"
:runners-path="runnerHelpUrl"
/>
<environments-block
v-if="hasEnvironment"
:deployment-status="job.deployment_status"
:icon-status="job.status"
/>
<erased-block
v-if="job.erased"
:user="job.erased_by"
:erased-at="job.erased_at"
/>
<!--job log -->
<!-- EO job log -->
<!--empty state -->
<!-- EO empty state -->
<!-- EO Body Section -->
</template>
</div>
</template>
......@@ -2,7 +2,7 @@ import _ from 'underscore';
import { mapState, mapActions } from 'vuex';
import Vue from 'vue';
import Job from '../job';
import JobHeader from './components/header.vue';
import JobApp from './components/job_app.vue';
import Sidebar from './components/sidebar.vue';
import createStore from './store';
......@@ -22,17 +22,18 @@ export default () => {
new Vue({
el: '#js-build-header-vue',
components: {
JobHeader,
JobApp,
},
store,
computed: {
...mapState(['job', 'isLoading']),
},
render(createElement) {
return createElement('job-header', {
return createElement('job-app', {
props: {
isLoading: this.isLoading,
job: this.job,
runnerHelpUrl: dataset.runnerHelpUrl,
},
});
},
......
import _ from 'underscore';
import { __ } from '~/locale';
export const headerActions = state => {
if (state.job.new_issue_path) {
return [
{
label: __('New issue'),
path: state.job.new_issue_path,
cssClass:
'js-new-issue btn btn-success btn-inverted d-none d-md-block d-lg-block d-xl-block',
type: 'link',
},
];
}
return [];
};
export const headerTime = state => (state.job.started ? state.job.started : state.job.created_at);
export const shouldRenderCalloutMessage = state =>
!_.isEmpty(state.job.status) && !_.isEmpty(state.job.callout_message);
/**
* When job has not started the key will be `false`
* When job started the key will be a string with a date.
*/
export const jobHasStarted = state => !(state.job.started === false);
export const hasEnvironment = state => !_.isEmpty(state.job.deployment_status);
/**
* When the job is pending and there are no available runners
* we need to render the stuck block;
*
* @returns {Boolean}
*/
export const isJobStuck = state =>
state.job.status.group === 'pending' && state.job.runners && state.job.runners.available === false;
// prevent babel-plugin-rewire from generating an invalid default during karma tests
export default () => {};
......@@ -2,6 +2,7 @@ import Vue from 'vue';
import Vuex from 'vuex';
import state from './state';
import * as actions from './actions';
import * as getters from './getters';
import mutations from './mutations';
Vue.use(Vuex);
......@@ -9,5 +10,6 @@ Vue.use(Vuex);
export default () => new Vuex.Store({
actions,
mutations,
getters,
state: state(),
});
......@@ -9,54 +9,6 @@
%div{ class: container_class }
.build-page.js-build-page
#js-build-header-vue
- if @build.stuck?
- unless @build.any_runners_online?
.bs-callout.bs-callout-warning.js-build-stuck
%p
- if @project.any_runners?
This job is stuck, because the project doesn't have any runners online assigned to it.
- elsif @build.tags.any?
This job is stuck, because you don't have any active runners online with any of these tags assigned to them:
- @build.tags.each do |tag|
%span.badge.badge-primary
= tag
- else
This job is stuck, because you don't have any active runners that can run this job.
%br
Go to
= link_to project_runners_path(@build.project, anchor: 'js-runners-settings') do
Runners page
- if @build.starts_environment?
.prepend-top-default.js-environment-container
.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 job is the most recent deployment to #{environment_link_for_build(@build.project, @build)}.
- else
This job is an out-of-date deployment to #{environment_link_for_build(@build.project, @build)}.
View the most recent deployment #{deployment_link(environment.last_deployment)}.
- elsif @build.complete? && !@build.success?
The deployment of this job to #{environment_link_for_build(@build.project, @build)} did not succeed.
- else
This job is creating a deployment to #{environment_link_for_build(@build.project, @build)}
- if environment.try(:last_deployment)
and will overwrite the #{deployment_link(environment.last_deployment, text: 'latest deployment')}
- if @build.erased?
.prepend-top-default.js-build-erased
.erased.alert.alert-warning
- if @build.erased_by_user?
Job has been erased by #{link_to(@build.erased_by_name, user_path(@build.erased_by))} #{time_ago_with_tooltip(@build.erased_at)}
- else
Job has been erased #{time_ago_with_tooltip(@build.erased_at)}
- if @build.running? || @build.has_trace?
.build-trace-container.prepend-top-default
......
---
title: Renders Job show page in new Vue app
merge_request:
author:
type: other
......@@ -6106,7 +6106,7 @@ msgstr ""
msgid "This job is an out-of-date deployment to %{environmentLink}. View the most recent deployment %{deploymentLink}."
msgstr ""
msgid "This job is creating a deployment to %{environmentLink} and will overwrite the last %{deploymentLink}."
msgid "This job is creating a deployment to %{environmentLink} and will overwrite the %{deploymentLink}."
msgstr ""
msgid "This job is creating a deployment to %{environmentLink}."
......@@ -7054,6 +7054,9 @@ msgstr ""
msgid "issue boards"
msgstr ""
msgid "latest deployment"
msgstr ""
msgid "latest version"
msgstr ""
......
......@@ -16,7 +16,9 @@
visit(project_job_path(project, build))
end
it 'erases the job log' do
it 'erases the job log', :js do
wait_for_requests
expect(page).to have_content("Job ##{build.id}")
expect(page).to have_css('#build-trace')
......@@ -29,9 +31,7 @@
expect(build.artifacts_file.exists?).to be_falsy
expect(build.artifacts_metadata.exists?).to be_falsy
page.within('.erased') do
expect(page).to have_content('Job has been erased')
end
expect(page).to have_content('Job has been erased')
end
context 'with a failed job' do
......
......@@ -369,39 +369,167 @@
end
end
context 'when job starts environment' do
let(:environment) { create(:environment, project: project) }
let(:pipeline) { create(:ci_pipeline, project: project) }
context 'when job starts environment', :js do
let(:environment) { create(:environment, name: 'production', project: project) }
context 'job is successfull and has deployment' do
let(:deployment) { create(:deployment) }
let(:job) { create(:ci_build, :success, :trace_artifact, environment: environment.name, deployments: [deployment], pipeline: pipeline) }
context 'job is successful and has deployment' do
let(:build) { create(:ci_build, :success, :trace_live, environment: environment.name, pipeline: pipeline) }
let!(:deployment) { create(:deployment, environment: environment, project: environment.project, deployable: build) }
it 'shows a link for the job' do
visit project_job_path(project, job)
before do
visit project_job_path(project, build)
wait_for_requests
# scroll to the top of the page first
execute_script "window.scrollTo(0,0)"
end
it 'shows a link for the job' do
expect(page).to have_link environment.name
end
it 'shows deployment message' do
expect(page).to have_content 'This job is the most recent deployment'
expect(find('.js-environment-link')['href']).to match("environments/#{environment.id}")
end
end
context 'job is complete and not successful' do
let(:job) { create(:ci_build, :failed, :trace_artifact, environment: environment.name, pipeline: pipeline) }
let(:build) { create(:ci_build, :failed, :trace_artifact, environment: environment.name, pipeline: pipeline) }
it 'shows a link for the job' do
visit project_job_path(project, job)
visit project_job_path(project, build)
wait_for_requests
# scroll to the top of the page first
execute_script "window.scrollTo(0,0)"
expect(page).to have_link environment.name
expect(find('.js-environment-link')['href']).to match("environments/#{environment.id}")
end
end
context 'job creates a new deployment' do
let!(:deployment) { create(:deployment, environment: environment, sha: project.commit.id) }
let(:job) { create(:ci_build, :success, :trace_artifact, environment: environment.name, pipeline: pipeline) }
context 'deployment still not finished' do
let(:build) { create(:ci_build, :success, environment: environment.name, pipeline: pipeline) }
it 'shows a link to latest deployment' do
visit project_job_path(project, job)
visit project_job_path(project, build)
wait_for_all_requests
# scroll to the top of the page first
execute_script "window.scrollTo(0,0)"
expect(page).to have_link environment.name
expect(page).to have_content 'This job is creating a deployment'
expect(find('.js-environment-link')['href']).to match("environments/#{environment.id}")
end
end
end
describe 'environment info in job view', :js do
before do
visit project_job_path(project, job)
wait_for_requests
# scroll to the top of the page first
execute_script "window.scrollTo(0,0)"
end
context 'job with outdated deployment' do
let(:job) { create(:ci_build, :success, :trace_artifact, environment: 'staging', pipeline: pipeline) }
let(:second_build) { create(:ci_build, :success, :trace_artifact, environment: 'staging', pipeline: pipeline) }
let(:environment) { create(:environment, name: 'staging', project: project) }
let!(:first_deployment) { create(:deployment, environment: environment, deployable: job) }
let!(:second_deployment) { create(:deployment, environment: environment, deployable: second_build) }
it 'shows deployment message' do
expected_text = 'This job is an out-of-date deployment ' \
"to staging. View the most recent deployment ##{second_deployment.iid}."
expect(page).to have_css('.environment-information', text: expected_text)
end