gitlab-org--gitlab-foss/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue

358 lines
12 KiB
Vue
Raw Normal View History

2018-03-05 22:24:16 +00:00
<script>
2017-05-09 04:15:34 +00:00
import successSvg from 'icons/_icon_status_success.svg';
import warningSvg from 'icons/_icon_status_warning.svg';
import simplePoll from '~/lib/utils/simple_poll';
import MergeRequest from '../../../merge_request';
import Flash from '../../../flash';
import statusIcon from '../mr_widget_status_icon.vue';
2017-05-09 04:15:34 +00:00
import eventHub from '../../event_hub';
import SquashBeforeMerge from './squash_before_merge.vue';
2017-05-09 04:15:34 +00:00
export default {
2018-03-05 22:24:16 +00:00
name: 'ReadyToMerge',
components: {
statusIcon,
SquashBeforeMerge,
2018-03-05 22:24:16 +00:00
},
2017-05-09 04:15:34 +00:00
props: {
mr: { type: Object, required: true },
service: { type: Object, required: true },
},
data() {
return {
removeSourceBranch: this.mr.shouldRemoveSourceBranch,
2017-05-09 04:15:34 +00:00
mergeWhenBuildSucceeds: false,
useCommitMessageWithDescription: false,
setToMergeWhenPipelineSucceeds: false,
showCommitMessageEditor: false,
isMakingRequest: false,
isMergingImmediately: false,
commitMessage: this.mr.commitMessage,
squashBeforeMerge: this.mr.squash,
2017-05-09 04:15:34 +00:00
successSvg,
warningSvg,
};
},
computed: {
shouldShowMergeWhenPipelineSucceedsText() {
return this.mr.isPipelineActive;
},
2017-05-09 04:15:34 +00:00
commitMessageLinkTitle() {
const withDesc = 'Include description in commit message';
const withoutDesc = "Don't include description in commit message";
return this.useCommitMessageWithDescription ? withoutDesc : withDesc;
},
status() {
2017-05-09 04:15:34 +00:00
const { pipeline, isPipelineActive, isPipelineFailed, hasCI, ciStatus } = this.mr;
if (hasCI && !ciStatus) {
return 'failed';
2017-05-09 04:15:34 +00:00
} else if (!pipeline) {
return 'success';
2017-05-09 04:15:34 +00:00
} else if (isPipelineActive) {
return 'pending';
2017-05-09 04:15:34 +00:00
} else if (isPipelineFailed) {
return 'failed';
}
return 'success';
},
mergeButtonClass() {
const defaultClass = 'btn btn-sm btn-success accept-merge-request';
const failedClass = `${defaultClass} btn-danger`;
const inActionClass = `${defaultClass} btn-info`;
if (this.status === 'failed') {
2017-05-09 04:15:34 +00:00
return failedClass;
} else if (this.status === 'pending') {
return inActionClass;
2017-05-09 04:15:34 +00:00
}
return defaultClass;
},
iconClass() {
if (
this.status === 'failed' ||
!this.commitMessage.length ||
!this.mr.isMergeAllowed ||
this.mr.preventMerge
) {
2018-01-19 15:56:34 +00:00
return 'warning';
}
return 'success';
},
2017-05-09 04:15:34 +00:00
mergeButtonText() {
if (this.isMergingImmediately) {
return 'Merge in progress';
} else if (this.shouldShowMergeWhenPipelineSucceedsText) {
2017-05-09 04:15:34 +00:00
return 'Merge when pipeline succeeds';
}
return 'Merge';
},
shouldShowMergeOptionsDropdown() {
return this.mr.isPipelineActive && !this.mr.onlyAllowMergeIfPipelineSucceeds;
},
isMergeButtonDisabled() {
const { commitMessage } = this;
return Boolean(
!commitMessage.length ||
!this.shouldShowMergeControls() ||
this.isMakingRequest ||
this.mr.preventMerge,
);
2017-05-09 04:15:34 +00:00
},
isRemoveSourceBranchButtonDisabled() {
return this.isMergeButtonDisabled;
},
2017-05-09 04:15:34 +00:00
shouldShowSquashBeforeMerge() {
const { commitsCount, enableSquashBeforeMerge } = this.mr;
return enableSquashBeforeMerge && commitsCount > 1;
},
},
methods: {
shouldShowMergeControls() {
return this.mr.isMergeAllowed || this.shouldShowMergeWhenPipelineSucceedsText;
2017-05-09 04:15:34 +00:00
},
updateCommitMessage() {
const cmwd = this.mr.commitMessageWithDescription;
this.useCommitMessageWithDescription = !this.useCommitMessageWithDescription;
this.commitMessage = this.useCommitMessageWithDescription ? cmwd : this.mr.commitMessage;
},
toggleCommitMessageEditor() {
this.showCommitMessageEditor = !this.showCommitMessageEditor;
},
handleMergeButtonClick(mergeWhenBuildSucceeds, mergeImmediately) {
// TODO: Remove no-param-reassign
if (mergeWhenBuildSucceeds === undefined) {
mergeWhenBuildSucceeds = this.mr.isPipelineActive; // eslint-disable-line no-param-reassign
} else if (mergeImmediately) {
this.isMergingImmediately = true;
}
this.setToMergeWhenPipelineSucceeds = mergeWhenBuildSucceeds === true;
const options = {
sha: this.mr.sha,
commit_message: this.commitMessage,
merge_when_pipeline_succeeds: this.setToMergeWhenPipelineSucceeds,
should_remove_source_branch: this.removeSourceBranch === true,
squash: this.squashBeforeMerge,
2017-05-09 04:15:34 +00:00
};
this.isMakingRequest = true;
this.service
.merge(options)
.then(res => res.data)
.then(data => {
const hasError = data.status === 'failed' || data.status === 'hook_validation_error';
2017-05-09 04:15:34 +00:00
if (data.status === 'merge_when_pipeline_succeeds') {
2017-05-09 04:15:34 +00:00
eventHub.$emit('MRWidgetUpdateRequested');
} else if (data.status === 'success') {
2017-05-09 04:15:34 +00:00
this.initiateMergePolling();
} else if (hasError) {
eventHub.$emit('FailedToMerge', data.merge_error);
2017-05-09 04:15:34 +00:00
}
})
.catch(() => {
this.isMakingRequest = false;
new Flash('Something went wrong. Please try again.'); // eslint-disable-line
});
},
initiateMergePolling() {
simplePoll((continuePolling, stopPolling) => {
this.handleMergePolling(continuePolling, stopPolling);
});
},
handleMergePolling(continuePolling, stopPolling) {
this.service
.poll()
.then(res => res.data)
.then(data => {
if (data.state === 'merged') {
2017-05-09 04:15:34 +00:00
// If state is merged we should update the widget and stop the polling
eventHub.$emit('MRWidgetUpdateRequested');
eventHub.$emit('FetchActionsContent');
MergeRequest.setStatusBoxToMerged();
MergeRequest.hideCloseButton();
MergeRequest.decreaseCounter();
2017-05-09 04:15:34 +00:00
stopPolling();
// If user checked remove source branch and we didn't remove the branch yet
// we should start another polling for source branch remove process
if (this.removeSourceBranch && data.source_branch_exists) {
2017-05-09 04:15:34 +00:00
this.initiateRemoveSourceBranchPolling();
}
} else if (data.merge_error) {
eventHub.$emit('FailedToMerge', data.merge_error);
2017-05-09 04:15:34 +00:00
stopPolling();
} else {
// MR is not merged yet, continue polling until the state becomes 'merged'
continuePolling();
}
})
.catch(() => {
new Flash('Something went wrong while merging this merge request. Please try again.'); // eslint-disable-line
});
},
initiateRemoveSourceBranchPolling() {
// We need to show source branch is being removed spinner in another component
eventHub.$emit('SetBranchRemoveFlag', [true]);
simplePoll((continuePolling, stopPolling) => {
this.handleRemoveBranchPolling(continuePolling, stopPolling);
});
},
handleRemoveBranchPolling(continuePolling, stopPolling) {
this.service
.poll()
.then(res => res.data)
.then(data => {
2017-05-09 04:15:34 +00:00
// If source branch exists then we should continue polling
// because removing a source branch is a background task and takes time
if (data.source_branch_exists) {
2017-05-09 04:15:34 +00:00
continuePolling();
} else {
// Branch is removed. Update widget, stop polling and hide the spinner
eventHub.$emit('MRWidgetUpdateRequested', () => {
eventHub.$emit('SetBranchRemoveFlag', [false]);
});
stopPolling();
}
})
.catch(() => {
new Flash('Something went wrong while deleting the source branch. Please try again.'); // eslint-disable-line
2017-05-09 04:15:34 +00:00
});
},
},
2018-03-05 22:24:16 +00:00
};
</script>
<template>
<div class="mr-widget-body media">
<status-icon :status="iconClass" />
<div class="media-body">
<div class="mr-widget-body-controls media space-children">
2018-07-12 15:07:25 +00:00
<span class="btn-group">
2018-03-05 22:24:16 +00:00
<button
:disabled="isMergeButtonDisabled"
:class="mergeButtonClass"
type="button"
2018-06-11 09:49:47 +00:00
class="qa-merge-button"
@click="handleMergeButtonClick()"
2018-11-16 20:07:38 +00:00
>
<i v-if="isMakingRequest" class="fa fa-spinner fa-spin" aria-hidden="true"></i>
2018-03-05 22:24:16 +00:00
{{ mergeButtonText }}
</button>
<button
v-if="shouldShowMergeOptionsDropdown"
:disabled="isMergeButtonDisabled"
type="button"
class="btn btn-sm btn-info dropdown-toggle js-merge-moment"
data-toggle="dropdown"
2018-11-16 20:07:38 +00:00
aria-label="Select merge moment"
>
<i class="fa fa-chevron-down qa-merge-moment-dropdown" aria-hidden="true"></i>
2018-03-05 22:24:16 +00:00
</button>
<ul
v-if="shouldShowMergeOptionsDropdown"
class="dropdown-menu dropdown-menu-right"
2018-11-16 20:07:38 +00:00
role="menu"
>
2018-03-05 22:24:16 +00:00
<li>
<a
class="merge_when_pipeline_succeeds qa-merge-when-pipeline-succeeds-option"
2018-06-11 09:49:47 +00:00
href="#"
@click.prevent="handleMergeButtonClick(true)"
2018-11-16 20:07:38 +00:00
>
2018-03-05 22:24:16 +00:00
<span class="media">
2018-11-16 20:07:38 +00:00
<span class="merge-opt-icon" aria-hidden="true" v-html="successSvg"></span>
2018-03-05 22:24:16 +00:00
<span class="media-body merge-opt-title">Merge when pipeline succeeds</span>
</span>
</a>
</li>
<li>
<a
class="accept-merge-request qa-merge-immediately-option"
2018-06-11 09:49:47 +00:00
href="#"
@click.prevent="handleMergeButtonClick(false, true)"
2018-11-16 20:07:38 +00:00
>
2018-03-05 22:24:16 +00:00
<span class="media">
2018-11-16 20:07:38 +00:00
<span class="merge-opt-icon" aria-hidden="true" v-html="warningSvg"></span>
2018-03-05 22:24:16 +00:00
<span class="media-body merge-opt-title">Merge immediately</span>
</span>
</a>
</li>
</ul>
</span>
<div class="media-body-wrap space-children">
<template v-if="shouldShowMergeControls()">
<label v-if="mr.canRemoveSourceBranch">
<input
id="remove-source-branch-input"
v-model="removeSourceBranch"
:disabled="isRemoveSourceBranchButtonDisabled"
2018-06-11 09:49:47 +00:00
class="js-remove-source-branch-checkbox"
2018-11-16 20:07:38 +00:00
type="checkbox"
/>
Delete source branch
2018-03-05 22:24:16 +00:00
</label>
<!-- Placeholder for EE extension of this component -->
<squash-before-merge
v-if="shouldShowSquashBeforeMerge"
v-model="squashBeforeMerge"
:help-path="mr.squashBeforeMergeHelpPath"
:is-disabled="isMergeButtonDisabled"
2018-11-16 20:07:38 +00:00
/>
2018-03-05 22:24:16 +00:00
2018-11-16 20:07:38 +00:00
<span v-if="mr.ffOnlyEnabled" class="js-fast-forward-message">
2018-03-05 22:24:16 +00:00
Fast-forward merge without a merge commit
</span>
2017-08-07 02:29:37 +00:00
<button
2018-03-05 22:24:16 +00:00
v-else
2017-08-07 02:29:37 +00:00
:disabled="isMergeButtonDisabled"
2018-04-16 21:19:03 +00:00
class="js-modify-commit-message-button btn btn-default btn-sm"
2018-06-11 09:49:47 +00:00
type="button"
2018-11-16 20:07:38 +00:00
@click="toggleCommitMessageEditor"
>
2018-03-05 22:24:16 +00:00
Modify commit message
2017-08-07 02:29:37 +00:00
</button>
2018-03-05 22:24:16 +00:00
</template>
<template v-else>
<span class="bold js-resolve-mr-widget-items-message">
You can only merge once the items above are resolved
</span>
</template>
2017-08-07 02:29:37 +00:00
</div>
2018-03-05 22:24:16 +00:00
</div>
2018-11-16 20:07:38 +00:00
<div v-if="showCommitMessageEditor" class="prepend-top-default commit-message-editor">
2018-03-05 22:24:16 +00:00
<div class="form-group clearfix">
2018-11-16 20:07:38 +00:00
<label class="col-form-label" for="commit-message"> Commit message </label>
2018-03-05 22:24:16 +00:00
<div class="col-sm-10">
<div class="commit-message-container">
<div class="max-width-marker"></div>
<textarea
id="commit-message"
v-model="commitMessage"
class="form-control js-commit-message"
required="required"
rows="14"
2018-11-16 20:07:38 +00:00
name="Commit message"
></textarea>
2018-03-05 22:24:16 +00:00
</div>
<p class="hint">
Try to keep the first line under 52 characters and the others under 72
</p>
<div class="hint">
2018-11-16 20:07:38 +00:00
<a href="#" @click.prevent="updateCommitMessage"> {{ commitMessageLinkTitle }} </a>
2017-05-09 04:15:34 +00:00
</div>
</div>
</div>
2017-08-07 02:29:37 +00:00
</div>
2017-05-09 04:15:34 +00:00
</div>
2018-03-05 22:24:16 +00:00
</div>
</template>