Skip to content
Snippets Groups Projects
Commit a706b373 authored by Douwe Maan's avatar Douwe Maan
Browse files

Merge branch 'master' into 'dm-document-role-maintainer'

# Conflicts:
#   doc/development/code_review.md
parents 90056ed2 68eafa50
No related branches found
No related tags found
1 merge request!10495Merge Requests - Assignee
Showing
with 518 additions and 509 deletions
<script>
import TimeagoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import timeagoMixin from '~/vue_shared/mixins/timeago';
import TimeagoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import timeagoMixin from '~/vue_shared/mixins/timeago';
 
export default {
components: {
TimeagoTooltip,
export default {
components: {
TimeagoTooltip,
},
mixins: [timeagoMixin],
props: {
artifact: {
type: Object,
required: true,
},
mixins: [
timeagoMixin,
],
props: {
artifact: {
type: Object,
required: true,
},
},
computed: {
isExpired() {
return this.artifact.expired;
},
computed: {
isExpired() {
return this.artifact.expired;
},
// Only when the key is `false` we can render this block
willExpire() {
return this.artifact.expired === false;
},
// Only when the key is `false` we can render this block
willExpire() {
return this.artifact.expired === false;
},
};
},
};
</script>
<template>
<div class="block">
Loading
Loading
<script>
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
 
export default {
components: {
ClipboardButton,
export default {
components: {
ClipboardButton,
},
props: {
commit: {
type: Object,
required: true,
},
props: {
commit: {
type: Object,
required: true,
},
mergeRequest: {
type: Object,
required: false,
default: null,
},
isLastBlock: {
type: Boolean,
required: true,
},
mergeRequest: {
type: Object,
required: false,
default: null,
},
};
isLastBlock: {
type: Boolean,
required: true,
},
},
};
</script>
<template>
<div
Loading
Loading
<script>
export default {
props: {
illustrationPath: {
type: String,
required: true,
},
illustrationSizeClass: {
type: String,
required: true,
},
title: {
type: String,
required: true,
},
content: {
type: String,
required: false,
default: null,
},
action: {
type: Object,
required: false,
default: null,
validator(value) {
return (
value === null ||
(Object.prototype.hasOwnProperty.call(value, 'path') &&
Object.prototype.hasOwnProperty.call(value, 'method') &&
Object.prototype.hasOwnProperty.call(value, 'button_title'))
);
},
export default {
props: {
illustrationPath: {
type: String,
required: true,
},
illustrationSizeClass: {
type: String,
required: true,
},
title: {
type: String,
required: true,
},
content: {
type: String,
required: false,
default: null,
},
action: {
type: Object,
required: false,
default: null,
validator(value) {
return (
value === null ||
(Object.prototype.hasOwnProperty.call(value, 'path') &&
Object.prototype.hasOwnProperty.call(value, 'method') &&
Object.prototype.hasOwnProperty.call(value, 'button_title'))
);
},
},
};
},
};
</script>
<template>
<div class="row empty-state">
Loading
Loading
<script>
import _ from 'underscore';
import CiIcon from '~/vue_shared/components/ci_icon.vue';
import { sprintf, __ } from '../../locale';
import _ from 'underscore';
import CiIcon from '~/vue_shared/components/ci_icon.vue';
import { sprintf, __ } from '../../locale';
 
export default {
components: {
CiIcon,
export default {
components: {
CiIcon,
},
props: {
deploymentStatus: {
type: Object,
required: true,
},
props: {
deploymentStatus: {
type: Object,
required: true,
},
iconStatus: {
type: Object,
required: true,
},
iconStatus: {
type: Object,
required: true,
},
computed: {
environment() {
let environmentText;
switch (this.deploymentStatus.status) {
case 'last':
},
computed: {
environment() {
let environmentText;
switch (this.deploymentStatus.status) {
case 'last':
environmentText = sprintf(
__('This job is the most recent deployment to %{link}.'),
{ link: this.environmentLink },
false,
);
break;
case 'out_of_date':
if (this.hasLastDeployment) {
environmentText = sprintf(
__('This job is the most recent deployment to %{link}.'),
{ link: this.environmentLink },
__(
'This job is an out-of-date deployment to %{environmentLink}. View the most recent deployment %{deploymentLink}.',
),
{
environmentLink: this.environmentLink,
deploymentLink: this.deploymentLink(`#${this.lastDeployment.iid}`),
},
false,
);
break;
case 'out_of_date':
if (this.hasLastDeployment) {
environmentText = sprintf(
__(
'This job is an out-of-date deployment to %{environmentLink}. View the most recent deployment %{deploymentLink}.',
),
{
environmentLink: this.environmentLink,
deploymentLink: this.deploymentLink(`#${this.lastDeployment.iid}`),
},
false,
);
} else {
environmentText = sprintf(
__('This job is an out-of-date deployment to %{environmentLink}.'),
{ environmentLink: this.environmentLink },
false,
);
}
break;
case 'failed':
} else {
environmentText = sprintf(
__('The deployment of this job to %{environmentLink} did not succeed.'),
__('This job is an out-of-date deployment to %{environmentLink}.'),
{ environmentLink: this.environmentLink },
false,
);
break;
case 'creating':
if (this.hasLastDeployment) {
environmentText = sprintf(
__(
'This job is creating a deployment to %{environmentLink} and will overwrite the %{deploymentLink}.',
),
{
environmentLink: this.environmentLink,
deploymentLink: this.deploymentLink(__('latest deployment')),
},
false,
);
} else {
environmentText = sprintf(
__('This job is creating a deployment to %{environmentLink}.'),
{ environmentLink: this.environmentLink },
false,
);
}
break;
default:
break;
}
return environmentText;
},
environmentLink() {
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>',
},
}
break;
case 'failed':
environmentText = sprintf(
__('The deployment of this job to %{environmentLink} did not succeed.'),
{ environmentLink: this.environmentLink },
false,
);
}
return '';
},
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 : '';
},
break;
case 'creating':
if (this.hasLastDeployment) {
environmentText = sprintf(
__(
'This job is creating a deployment to %{environmentLink} and will overwrite the %{deploymentLink}.',
),
{
environmentLink: this.environmentLink,
deploymentLink: this.deploymentLink(__('latest deployment')),
},
false,
);
} else {
environmentText = sprintf(
__('This job is creating a deployment to %{environmentLink}.'),
{ environmentLink: this.environmentLink },
false,
);
}
break;
default:
break;
}
return environmentText;
},
methods: {
deploymentLink(name) {
environmentLink() {
if (this.hasEnvironment) {
return sprintf(
'%{startLink}%{name}%{endLink}',
{
startLink: `<a href="${this.lastDeploymentPath}" class="js-job-deployment-link">`,
name,
startLink: `<a href="${
this.deploymentStatus.environment.environment_path
}" class="js-environment-link">`,
name: _.escape(this.deploymentStatus.environment.name),
endLink: '</a>',
},
false,
);
},
}
return '';
},
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.lastDeploymentPath}" class="js-job-deployment-link">`,
name,
endLink: '</a>',
},
false,
);
},
};
},
};
</script>
<template>
<div class="prepend-top-default js-environment-container">
Loading
Loading
<script>
import _ from 'underscore';
import TimeagoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import _ from 'underscore';
import TimeagoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
 
export default {
components: {
TimeagoTooltip,
export default {
components: {
TimeagoTooltip,
},
props: {
user: {
type: Object,
required: false,
default: () => ({}),
},
props: {
user: {
type: Object,
required: false,
default: () => ({}),
},
erasedAt: {
type: String,
required: true,
},
erasedAt: {
type: String,
required: true,
},
computed: {
isErasedByUser() {
return !_.isEmpty(this.user);
},
},
computed: {
isErasedByUser() {
return !_.isEmpty(this.user);
},
};
},
};
</script>
<template>
<div class="prepend-top-default js-build-erased">
Loading
Loading
Loading
Loading
@@ -18,7 +18,7 @@
StuckBlock,
},
props: {
runnerHelpUrl: {
runnerSettingsUrl: {
type: String,
required: false,
default: null,
Loading
Loading
@@ -30,7 +30,7 @@
'headerActions',
'headerTime',
'shouldRenderCalloutMessage',
'jobHasStarted',
'shouldRenderTriggeredLabel',
'hasEnvironment',
'isJobStuck',
'hasTrace',
Loading
Loading
@@ -58,7 +58,7 @@
:user="job.user"
:actions="headerActions"
:has-sidebar-button="true"
:should-render-triggered-label="jobHasStarted"
:should-render-triggered-label="shouldRenderTriggeredLabel"
:item-name="__('Job')"
/>
</div>
Loading
Loading
@@ -76,7 +76,7 @@
class="js-job-stuck"
:has-no-runners-for-project="job.runners.available"
:tags="job.tags"
:runners-path="runnerHelpUrl"
:runners-path="runnerSettingsUrl"
/>
 
<environments-block
Loading
Loading
@@ -87,8 +87,8 @@
/>
 
<erased-block
v-if="job.erased"
class="js-job-erased"
v-if="job.erased_at"
class="js-job-erased-block"
:user="job.erased_by"
:erased-at="job.erased_at"
/>
Loading
Loading
<script>
export default {
name: 'JobLog',
props: {
trace: {
type: String,
required: true,
},
isComplete: {
type: Boolean,
required: true,
},
export default {
name: 'JobLog',
props: {
trace: {
type: String,
required: true,
},
};
isComplete: {
type: Boolean,
required: true,
},
},
};
</script>
<template>
<pre class="build-trace">
Loading
Loading
<script>
import { polyfillSticky } from '~/lib/utils/sticky';
import Icon from '~/vue_shared/components/icon.vue';
import tooltip from '~/vue_shared/directives/tooltip';
import { numberToHumanSize } from '~/lib/utils/number_utils';
import { sprintf } from '~/locale';
import { polyfillSticky } from '~/lib/utils/sticky';
import Icon from '~/vue_shared/components/icon.vue';
import tooltip from '~/vue_shared/directives/tooltip';
import { numberToHumanSize } from '~/lib/utils/number_utils';
import { sprintf } from '~/locale';
 
export default {
components: {
Icon,
export default {
components: {
Icon,
},
directives: {
tooltip,
},
props: {
erasePath: {
type: String,
required: false,
default: null,
},
directives: {
tooltip,
size: {
type: Number,
required: true,
},
props: {
erasePath: {
type: String,
required: false,
default: null,
},
size: {
type: Number,
required: true,
},
rawPath: {
type: String,
required: false,
default: null,
},
isScrollTopDisabled: {
type: Boolean,
required: true,
},
isScrollBottomDisabled: {
type: Boolean,
required: true,
},
isScrollingDown: {
type: Boolean,
required: true,
},
isTraceSizeVisible: {
type: Boolean,
required: true,
},
rawPath: {
type: String,
required: false,
default: null,
},
computed: {
jobLogSize() {
return sprintf('Showing last %{size} of log -', {
size: numberToHumanSize(this.size),
});
},
isScrollTopDisabled: {
type: Boolean,
required: true,
},
mounted() {
polyfillSticky(this.$el);
isScrollBottomDisabled: {
type: Boolean,
required: true,
},
methods: {
handleScrollToTop() {
this.$emit('scrollJobLogTop');
},
handleScrollToBottom() {
this.$emit('scrollJobLogBottom');
},
isScrollingDown: {
type: Boolean,
required: true,
},
};
isTraceSizeVisible: {
type: Boolean,
required: true,
},
},
computed: {
jobLogSize() {
return sprintf('Showing last %{size} of log -', {
size: numberToHumanSize(this.size),
});
},
},
mounted() {
polyfillSticky(this.$el);
},
methods: {
handleScrollToTop() {
this.$emit('scrollJobLogTop');
},
handleScrollToBottom() {
this.$emit('scrollJobLogBottom');
},
},
};
</script>
<template>
<div class="top-bar">
<div class="top-bar affix js-top-bar">
<!-- truncate information -->
<div class="js-truncated-info truncated-info d-none d-sm-block float-left">
<template v-if="isTraceSizeVisible">
Loading
Loading
<script>
import _ from 'underscore';
import CiIcon from '~/vue_shared/components/ci_icon.vue';
import Icon from '~/vue_shared/components/icon.vue';
import tooltip from '~/vue_shared/directives/tooltip';
import _ from 'underscore';
import CiIcon from '~/vue_shared/components/ci_icon.vue';
import Icon from '~/vue_shared/components/icon.vue';
import tooltip from '~/vue_shared/directives/tooltip';
 
export default {
components: {
CiIcon,
Icon,
export default {
components: {
CiIcon,
Icon,
},
directives: {
tooltip,
},
props: {
jobs: {
type: Array,
required: true,
},
directives: {
tooltip,
jobId: {
type: Number,
required: true,
},
props: {
jobs: {
type: Array,
required: true,
},
jobId: {
type: Number,
required: true,
},
},
methods: {
isJobActive(currentJobId) {
return this.jobId === currentJobId;
},
methods: {
isJobActive(currentJobId) {
return this.jobId === currentJobId;
},
tooltipText(job) {
return `${_.escape(job.name)} - ${job.status.tooltip}`;
},
tooltipText(job) {
return `${_.escape(job.name)} - ${job.status.tooltip}`;
},
};
},
};
</script>
<template>
<div class="js-jobs-container builds-container">
Loading
Loading
<script>
import _ from 'underscore';
import { mapActions, mapState } from 'vuex';
import timeagoMixin from '~/vue_shared/mixins/timeago';
import { timeIntervalInWords } from '~/lib/utils/datetime_utility';
import Icon from '~/vue_shared/components/icon.vue';
import DetailRow from './sidebar_detail_row.vue';
import ArtifactsBlock from './artifacts_block.vue';
import TriggerBlock from './trigger_block.vue';
import CommitBlock from './commit_block.vue';
import StagesDropdown from './stages_dropdown.vue';
import JobsContainer from './jobs_container.vue';
import _ from 'underscore';
import { mapActions, mapState } from 'vuex';
import timeagoMixin from '~/vue_shared/mixins/timeago';
import { timeIntervalInWords } from '~/lib/utils/datetime_utility';
import Icon from '~/vue_shared/components/icon.vue';
import DetailRow from './sidebar_detail_row.vue';
import ArtifactsBlock from './artifacts_block.vue';
import TriggerBlock from './trigger_block.vue';
import CommitBlock from './commit_block.vue';
import StagesDropdown from './stages_dropdown.vue';
import JobsContainer from './jobs_container.vue';
 
export default {
name: 'JobSidebar',
components: {
ArtifactsBlock,
CommitBlock,
DetailRow,
Icon,
TriggerBlock,
StagesDropdown,
JobsContainer,
export default {
name: 'JobSidebar',
components: {
ArtifactsBlock,
CommitBlock,
DetailRow,
Icon,
TriggerBlock,
StagesDropdown,
JobsContainer,
},
mixins: [timeagoMixin],
props: {
runnerHelpUrl: {
type: String,
required: false,
default: '',
},
mixins: [timeagoMixin],
props: {
runnerHelpUrl: {
type: String,
required: false,
default: '',
},
terminalPath: {
type: String,
required: false,
default: null,
},
terminalPath: {
type: String,
required: false,
default: null,
},
computed: {
...mapState(['job', 'isLoading', 'stages', 'jobs']),
coverage() {
return `${this.job.coverage}%`;
},
duration() {
return timeIntervalInWords(this.job.duration);
},
queued() {
return timeIntervalInWords(this.job.queued);
},
runnerId() {
return `${this.job.runner.description} (#${this.job.runner.id})`;
},
retryButtonClass() {
let className =
'js-retry-button float-right btn btn-retry d-none d-md-block d-lg-block d-xl-block';
className +=
this.job.status && this.job.recoverable ? ' btn-primary' : ' btn-inverted-secondary';
return className;
},
hasTimeout() {
return this.job.metadata != null && this.job.metadata.timeout_human_readable !== null;
},
timeout() {
if (this.job.metadata == null) {
return '';
}
},
computed: {
...mapState(['job', 'isLoading', 'stages', 'jobs', 'selectedStage']),
coverage() {
return `${this.job.coverage}%`;
},
duration() {
return timeIntervalInWords(this.job.duration);
},
queued() {
return timeIntervalInWords(this.job.queued);
},
runnerId() {
return `${this.job.runner.description} (#${this.job.runner.id})`;
},
retryButtonClass() {
let className =
'js-retry-button float-right btn btn-retry d-none d-md-block d-lg-block d-xl-block';
className +=
this.job.status && this.job.recoverable ? ' btn-primary' : ' btn-inverted-secondary';
return className;
},
hasTimeout() {
return this.job.metadata != null && this.job.metadata.timeout_human_readable !== null;
},
timeout() {
if (this.job.metadata == null) {
return '';
}
 
let t = this.job.metadata.timeout_human_readable;
if (this.job.metadata.timeout_source !== '') {
t += ` (from ${this.job.metadata.timeout_source})`;
}
let t = this.job.metadata.timeout_human_readable;
if (this.job.metadata.timeout_source !== '') {
t += ` (from ${this.job.metadata.timeout_source})`;
}
 
return t;
},
renderBlock() {
return (
this.job.merge_request ||
this.job.duration ||
this.job.finished_data ||
this.job.erased_at ||
this.job.queued ||
this.job.runner ||
this.job.coverage ||
this.job.tags.length ||
this.job.cancel_path
);
},
hasArtifact() {
return !_.isEmpty(this.job.artifact);
},
hasTriggers() {
return !_.isEmpty(this.job.trigger);
},
hasStages() {
return (
(this.job &&
this.job.pipeline &&
this.job.pipeline.stages &&
this.job.pipeline.stages.length > 0) ||
false
);
},
commit() {
return this.job.pipeline.commit || {};
},
return t;
},
renderBlock() {
return (
this.job.merge_request ||
this.job.duration ||
this.job.finished_data ||
this.job.erased_at ||
this.job.queued ||
this.job.runner ||
this.job.coverage ||
this.job.tags.length ||
this.job.cancel_path
);
},
hasArtifact() {
return !_.isEmpty(this.job.artifact);
},
hasTriggers() {
return !_.isEmpty(this.job.trigger);
},
hasStages() {
return (
(this.job &&
this.job.pipeline &&
this.job.pipeline.stages &&
this.job.pipeline.stages.length > 0) ||
false
);
},
methods: {
...mapActions(['fetchJobsForStage']),
commit() {
return this.job.pipeline.commit || {};
},
};
},
methods: {
...mapActions(['fetchJobsForStage']),
},
};
</script>
<template>
<aside
class="right-sidebar right-sidebar-expanded build-sidebar"
class="js-build-sidebar right-sidebar right-sidebar-expanded build-sidebar"
data-offset-top="101"
data-spy="affix"
>
Loading
Loading
@@ -276,6 +276,7 @@
<stages-dropdown
:stages="stages"
:pipeline="job.pipeline"
:selected-stage="selectedStage"
@requestSidebarStageDropdown="fetchJobsForStage"
/>
 
Loading
Loading
<script>
export default {
name: 'SidebarDetailRow',
props: {
title: {
type: String,
required: false,
default: '',
},
value: {
type: String,
required: true,
},
helpUrl: {
type: String,
required: false,
default: '',
},
export default {
name: 'SidebarDetailRow',
props: {
title: {
type: String,
required: false,
default: '',
},
computed: {
hasTitle() {
return this.title.length > 0;
},
hasHelpURL() {
return this.helpUrl.length > 0;
},
value: {
type: String,
required: true,
},
};
helpUrl: {
type: String,
required: false,
default: '',
},
},
computed: {
hasTitle() {
return this.title.length > 0;
},
hasHelpURL() {
return this.helpUrl.length > 0;
},
},
};
</script>
<template>
<p class="build-detail-row">
Loading
Loading
<script>
import _ from 'underscore';
import CiIcon from '~/vue_shared/components/ci_icon.vue';
import Icon from '~/vue_shared/components/icon.vue';
import { __ } from '~/locale';
import _ from 'underscore';
import CiIcon from '~/vue_shared/components/ci_icon.vue';
import Icon from '~/vue_shared/components/icon.vue';
 
export default {
components: {
CiIcon,
Icon,
export default {
components: {
CiIcon,
Icon,
},
props: {
pipeline: {
type: Object,
required: true,
},
props: {
pipeline: {
type: Object,
required: true,
},
stages: {
type: Array,
required: true,
},
stages: {
type: Array,
required: true,
},
data() {
return {
selectedStage: this.stages.length > 0 ? this.stages[0].name : __('More'),
};
selectedStage: {
type: String,
required: true,
},
computed: {
hasRef() {
return !_.isEmpty(this.pipeline.ref);
},
},
watch: {
// When the component is initially mounted it may start with an empty stages array.
// Once the prop is updated, we set the first stage as the selected one
stages(newVal) {
if (newVal.length) {
this.selectedStage = newVal[0].name;
}
},
},
computed: {
hasRef() {
return !_.isEmpty(this.pipeline.ref);
},
methods: {
onStageClick(stage) {
this.$emit('requestSidebarStageDropdown', stage);
this.selectedStage = stage.name;
},
},
methods: {
onStageClick(stage) {
this.$emit('requestSidebarStageDropdown', stage);
},
};
},
};
</script>
<template>
<div class="block-last dropdown">
Loading
Loading
<script>
export default {
props: {
trigger: {
type: Object,
required: true,
},
export default {
props: {
trigger: {
type: Object,
required: true,
},
data() {
return {
areVariablesVisible: false,
};
},
data() {
return {
areVariablesVisible: false,
};
},
computed: {
hasVariables() {
return this.trigger.variables && this.trigger.variables.length > 0;
},
computed: {
hasVariables() {
return this.trigger.variables && this.trigger.variables.length > 0;
},
},
methods: {
revealVariables() {
this.areVariablesVisible = true;
},
methods: {
revealVariables() {
this.areVariablesVisible = true;
},
},
};
},
};
</script>
 
<template>
Loading
Loading
Loading
Loading
@@ -9,8 +9,7 @@ import createStore from './store';
export default () => {
const { dataset } = document.getElementById('js-job-details-vue');
 
// eslint-disable-next-line no-new
new Job();
 
const store = createStore();
store.dispatch('setJobEndpoint', dataset.endpoint);
Loading
Loading
@@ -33,7 +32,7 @@ export default () => {
props: {
isLoading: this.isLoading,
job: this.job,
runnerHelpUrl: dataset.runnerHelpUrl,
runnerSettingsUrl: dataset.runnerSettingsUrl,
},
});
},
Loading
Loading
@@ -71,4 +70,7 @@ export default () => {
});
},
});
// eslint-disable-next-line no-new
new Job();
};
Loading
Loading
@@ -139,10 +139,12 @@ export const fetchStages = ({ state, dispatch }) => {
dispatch('requestStages');
 
axios
.get(state.job.pipeline.path)
.get(`${state.job.pipeline.path}.json`)
.then(({ data }) => {
// Set selected stage
dispatch('receiveStagesSuccess', data.details.stages);
dispatch('fetchJobsForStage', data.details.stages[0]);
const selectedStage = data.details.stages.find(stage => stage.name === state.selectedStage);
dispatch('fetchJobsForStage', selectedStage);
})
.catch(() => dispatch('receiveStagesError'));
};
Loading
Loading
@@ -156,11 +158,12 @@ export const receiveStagesError = ({ commit }) => {
/**
* Jobs list on sidebar - depend on stages dropdown
*/
export const requestJobsForStage = ({ commit }) => commit(types.REQUEST_JOBS_FOR_STAGE);
export const requestJobsForStage = ({ commit }, stage) =>
commit(types.REQUEST_JOBS_FOR_STAGE, stage);
 
// On stage click, set selected stage + fetch job
export const fetchJobsForStage = ({ dispatch }, stage) => {
dispatch('requestJobsForStage');
dispatch('requestJobsForStage', stage);
 
axios
.get(stage.dropdown_path, {
Loading
Loading
Loading
Loading
@@ -22,10 +22,10 @@ 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 has not started the key will be null
* When job started the key will be a string with a date.
*/
export const jobHasStarted = state => !(state.job.started === false);
export const shouldRenderTriggeredLabel = state => _.isString(state.job.started);
 
export const hasEnvironment = state => !_.isEmpty(state.job.deployment_status);
 
Loading
Loading
Loading
Loading
@@ -7,9 +7,10 @@ import mutations from './mutations';
 
Vue.use(Vuex);
 
export default () => new Vuex.Store({
actions,
mutations,
getters,
state: state(),
});
export default () =>
new Vuex.Store({
actions,
mutations,
getters,
state: state(),
});
Loading
Loading
@@ -53,6 +53,16 @@ export default {
state.isLoading = false;
state.hasError = false;
state.job = job;
/**
* We only update it on the first request
* The dropdown can be changed by the user
* after the first request,
* and we do not want to hijack that
*/
if (state.selectedStage === 'More' && job.stage) {
state.selectedStage = job.stage;
}
},
[types.RECEIVE_JOB_ERROR](state) {
state.isLoading = false;
Loading
Loading
@@ -81,8 +91,9 @@ export default {
state.stages = [];
},
 
[types.REQUEST_JOBS_FOR_STAGE](state) {
[types.REQUEST_JOBS_FOR_STAGE](state, stage) {
state.isLoadingJobs = true;
state.selectedStage = stage.name;
},
[types.RECEIVE_JOBS_FOR_STAGE_SUCCESS](state, jobs) {
state.isLoadingJobs = false;
Loading
Loading
import { __ } from '~/locale';
export default () => ({
jobEndpoint: null,
traceEndpoint: null,
Loading
Loading
@@ -34,7 +36,7 @@ export default () => ({
// sidebar dropdown
isLoadingStages: false,
isLoadingJobs: false,
selectedStage: null,
selectedStage: __('More'),
stages: [],
jobs: [],
});
Loading
Loading
@@ -11,6 +11,7 @@ import DropdownUtils from './filtered_search/dropdown_utils';
import CreateLabelDropdown from './create_label';
import flash from './flash';
import ModalStore from './boards/stores/modal_store';
import boardsStore from './boards/stores/boards_store';
 
export default class LabelsSelect {
constructor(els, options = {}) {
Loading
Loading
@@ -378,7 +379,7 @@ export default class LabelsSelect {
}
else if ($dropdown.hasClass('js-issue-board-sidebar')) {
if ($el.hasClass('is-active')) {
gl.issueBoards.BoardsStore.detail.issue.labels.push(new ListLabel({
boardsStore.detail.issue.labels.push(new ListLabel({
id: label.id,
title: label.title,
color: label.color[0],
Loading
Loading
@@ -386,16 +387,16 @@ export default class LabelsSelect {
}));
}
else {
var { labels } = gl.issueBoards.BoardsStore.detail.issue;
var { labels } = boardsStore.detail.issue;
labels = labels.filter(function (selectedLabel) {
return selectedLabel.id !== label.id;
});
gl.issueBoards.BoardsStore.detail.issue.labels = labels;
boardsStore.detail.issue.labels = labels;
}
 
$loading.fadeIn();
 
gl.issueBoards.BoardsStore.detail.issue.update($dropdown.attr('data-issue-update'))
boardsStore.detail.issue.update($dropdown.attr('data-issue-update'))
.then(fadeOutLoader)
.catch(fadeOutLoader);
}
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment