summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-07-17 21:09:23 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-07-17 21:09:23 +0000
commit1d84a028b42a1a3aed36a0f3a6cae970c8df8e69 (patch)
tree1ebd0249d20169a0b8be47dc541e9c2676d62648 /app
parentec72da1833d94bb1556af94193ccf2a93c9cb939 (diff)
downloadgitlab-ce-1d84a028b42a1a3aed36a0f3a6cae970c8df8e69.tar.gz
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/issuable_form.js25
-rw-r--r--app/assets/javascripts/logs/stores/mutations.js4
-rw-r--r--app/assets/javascripts/monitoring/components/refresh_button.vue6
-rw-r--r--app/assets/javascripts/profile/account/components/delete_account_modal.vue1
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/work_in_progress.vue44
-rw-r--r--app/finders/group_members_finder.rb2
-rw-r--r--app/services/merge_requests/merge_service.rb2
-rw-r--r--app/services/merge_requests/post_merge_service.rb25
-rw-r--r--app/services/merge_requests/squash_service.rb10
-rw-r--r--app/views/layouts/_flash.html.haml5
-rw-r--r--app/views/layouts/nav/sidebar/_profile.html.haml2
-rw-r--r--app/views/profiles/accounts/show.html.haml2
-rw-r--r--app/views/shared/issuable/form/_title.html.haml24
-rw-r--r--app/workers/all_queues.yml2
-rw-r--r--app/workers/merge_worker.rb1
15 files changed, 93 insertions, 62 deletions
diff --git a/app/assets/javascripts/issuable_form.js b/app/assets/javascripts/issuable_form.js
index cf780556c8d..2dcf5e6a0d6 100644
--- a/app/assets/javascripts/issuable_form.js
+++ b/app/assets/javascripts/issuable_form.js
@@ -48,7 +48,19 @@ export default class IssuableForm {
this.renderWipExplanation = this.renderWipExplanation.bind(this);
this.resetAutosave = this.resetAutosave.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
- this.wipRegex = /^\s*(\[WIP\]\s*|WIP:\s*|WIP\s+)+\s*/i;
+ /* eslint-disable @gitlab/require-i18n-strings */
+ this.wipRegex = new RegExp(
+ '^\\s*(' + // Line start, then any amount of leading whitespace
+ 'draft\\s-\\s' + // Draft_-_ where "_" are *exactly* one whitespace
+ '|\\[(draft|wip)\\]\\s*' + // [Draft] or [WIP] and any following whitespace
+ '|(draft|wip):\\s*' + // Draft: or WIP: and any following whitespace
+ '|(draft|wip)\\s+' + // Draft_ or WIP_ where "_" is at least one whitespace
+ '|\\(draft\\)\\s*' + // (Draft) and any following whitespace
+ ')+' + // At least one repeated match of the preceding parenthetical
+ '\\s*', // Any amount of trailing whitespace
+ 'i', // Match any case(s)
+ );
+ /* eslint-enable @gitlab/require-i18n-strings */
this.gfmAutoComplete = new GfmAutoComplete(
gl.GfmAutoComplete && gl.GfmAutoComplete.dataSources,
@@ -131,9 +143,18 @@ export default class IssuableForm {
workInProgress() {
return this.wipRegex.test(this.titleField.val());
}
+ titlePrefixContainsDraft() {
+ const prefix = this.titleField.val().match(this.wipRegex);
+
+ return prefix && prefix[0].match(/draft/i);
+ }
renderWipExplanation() {
if (this.workInProgress()) {
+ // These strings are not "translatable" (the code is hard-coded to look for them)
+ this.$wipExplanation.find('code')[0].textContent = this.titlePrefixContainsDraft()
+ ? 'Draft' /* eslint-disable-line @gitlab/require-i18n-strings */
+ : 'WIP';
this.$wipExplanation.show();
return this.$noWipExplanation.hide();
}
@@ -156,7 +177,7 @@ export default class IssuableForm {
}
addWip() {
- this.titleField.val(`WIP: ${this.titleField.val()}`);
+ this.titleField.val(`Draft: ${this.titleField.val()}`);
}
initTargetBranchDropdown() {
diff --git a/app/assets/javascripts/logs/stores/mutations.js b/app/assets/javascripts/logs/stores/mutations.js
index be22204d88d..147f562057f 100644
--- a/app/assets/javascripts/logs/stores/mutations.js
+++ b/app/assets/javascripts/logs/stores/mutations.js
@@ -112,7 +112,9 @@ export default {
},
// Managed apps data
[types.RECEIVE_MANAGED_APPS_DATA_SUCCESS](state, apps) {
- state.managedApps.options = apps;
+ state.managedApps.options = apps.filter(
+ ({ gitlab_managed_apps_logs_path }) => gitlab_managed_apps_logs_path, // eslint-disable-line babel/camelcase
+ );
state.managedApps.isLoading = false;
},
[types.RECEIVE_MANAGED_APPS_DATA_ERROR](state) {
diff --git a/app/assets/javascripts/monitoring/components/refresh_button.vue b/app/assets/javascripts/monitoring/components/refresh_button.vue
index 31092a26048..d32a5b14476 100644
--- a/app/assets/javascripts/monitoring/components/refresh_button.vue
+++ b/app/assets/javascripts/monitoring/components/refresh_button.vue
@@ -1,6 +1,7 @@
<script>
-import { n__, __ } from '~/locale';
+import Visibility from 'visibilityjs';
import { mapActions } from 'vuex';
+import { n__, __ } from '~/locale';
import {
GlButtonGroup,
@@ -98,7 +99,8 @@ export default {
};
this.stopAutoRefresh();
- if (document.hidden) {
+
+ if (Visibility.hidden()) {
// Inactive tab? Skip fetch and schedule again
schedule();
} else {
diff --git a/app/assets/javascripts/profile/account/components/delete_account_modal.vue b/app/assets/javascripts/profile/account/components/delete_account_modal.vue
index aeb69fb1c05..605859cfb6a 100644
--- a/app/assets/javascripts/profile/account/components/delete_account_modal.vue
+++ b/app/assets/javascripts/profile/account/components/delete_account_modal.vue
@@ -100,6 +100,7 @@ Once you confirm %{deleteAccount}, it cannot be undone or recovered.`),
name="password"
class="form-control"
type="password"
+ data-qa-selector="password_confirmation_field"
aria-labelledby="input-label"
/>
<input
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/work_in_progress.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/work_in_progress.vue
index 118caac84b9..6837545c681 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/work_in_progress.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/work_in_progress.vue
@@ -1,7 +1,7 @@
<script>
import $ from 'jquery';
-import { GlDeprecatedButton } from '@gitlab/ui';
-import { __, s__ } from '~/locale';
+import { GlButton } from '@gitlab/ui';
+import { __ } from '~/locale';
import createFlash from '~/flash';
import StatusIcon from '../mr_widget_status_icon.vue';
import tooltip from '../../../vue_shared/directives/tooltip';
@@ -11,7 +11,7 @@ export default {
name: 'WorkInProgress',
components: {
StatusIcon,
- GlDeprecatedButton,
+ GlButton,
},
directives: {
tooltip,
@@ -25,13 +25,6 @@ export default {
isMakingRequest: false,
};
},
- computed: {
- wipInfoTooltip() {
- return s__(
- 'mrWidget|When this merge request is ready, remove the WIP: prefix from the title to allow it to be merged',
- );
- },
- },
methods: {
handleRemoveWIP() {
this.isMakingRequest = true;
@@ -55,28 +48,25 @@ export default {
<template>
<div class="mr-widget-body media">
<status-icon :show-disabled-button="Boolean(mr.removeWIPPath)" status="warning" />
- <div class="media-body space-children">
- <span class="bold">
- {{ __('This is a Work in Progress') }}
- <i
- v-tooltip
- class="fa fa-question-circle"
- :title="wipInfoTooltip"
- :aria-label="wipInfoTooltip"
- >
- </i>
- </span>
- <gl-deprecated-button
+ <div class="media-body">
+ <div class="gl-ml-3 float-left">
+ <span class="gl-font-weight-bold">
+ {{ __('This merge request is still a work in progress.') }}
+ </span>
+ <span class="gl-display-block text-muted">{{
+ __("Draft merge requests can't be merged.")
+ }}</span>
+ </div>
+ <gl-button
v-if="mr.removeWIPPath"
- size="sm"
- variant="default"
+ size="small"
:disabled="isMakingRequest"
:loading="isMakingRequest"
- class="js-remove-wip"
+ class="js-remove-wip gl-ml-3"
@click="handleRemoveWIP"
>
- {{ s__('mrWidget|Resolve WIP status') }}
- </gl-deprecated-button>
+ {{ s__('mrWidget|Mark as ready') }}
+ </gl-button>
</div>
</div>
</template>
diff --git a/app/finders/group_members_finder.rb b/app/finders/group_members_finder.rb
index 949af103eb3..a4b00588368 100644
--- a/app/finders/group_members_finder.rb
+++ b/app/finders/group_members_finder.rb
@@ -57,7 +57,7 @@ class GroupMembersFinder < UnionFinder
members = members.search(params[:search]) if params[:search].present?
members = members.sort_by_attribute(params[:sort]) if params[:sort].present?
- if can_manage_members && params[:two_factor].present?
+ if params[:two_factor].present? && can_manage_members
members = members.filter_by_2fa(params[:two_factor])
end
diff --git a/app/services/merge_requests/merge_service.rb b/app/services/merge_requests/merge_service.rb
index 961a7cb1ef6..e631bfc08e1 100644
--- a/app/services/merge_requests/merge_service.rb
+++ b/app/services/merge_requests/merge_service.rb
@@ -55,7 +55,7 @@ module MergeRequests
error =
if @merge_request.should_be_rebased?
'Only fast-forward merge is allowed for your project. Please update your source branch'
- elsif !@merge_request.mergeable?
+ elsif !@merge_request.merged? && !@merge_request.mergeable?
'Merge request is not mergeable'
elsif !@merge_request.squash && project.squash_always?
'This project requires squashing commits when merge requests are accepted.'
diff --git a/app/services/merge_requests/post_merge_service.rb b/app/services/merge_requests/post_merge_service.rb
index fdf8f442297..2d45f2aceb8 100644
--- a/app/services/merge_requests/post_merge_service.rb
+++ b/app/services/merge_requests/post_merge_service.rb
@@ -8,18 +8,31 @@ module MergeRequests
#
class PostMergeService < MergeRequests::BaseService
def execute(merge_request)
- merge_request.mark_as_merged
- close_issues(merge_request)
- todo_service.merge_merge_request(merge_request, current_user)
- create_event(merge_request)
- create_note(merge_request)
+ return if merge_request.merged?
+
+ # These operations need to happen transactionally
+ ActiveRecord::Base.transaction(requires_new: true) do
+ merge_request.mark_as_merged
+
+ # These options do not call external services and should be
+ # quick enough to put in a transaction
+ create_event(merge_request)
+ todo_service.merge_merge_request(merge_request, current_user)
+ end
+
notification_service.merge_mr(merge_request, current_user)
- execute_hooks(merge_request, 'merge')
+ create_note(merge_request)
+ close_issues(merge_request)
invalidate_cache_counts(merge_request, users: merge_request.assignees)
merge_request.update_project_counter_caches
delete_non_latest_diffs(merge_request)
cancel_review_app_jobs!(merge_request)
cleanup_environments(merge_request)
+
+ # Anything after this point will be executed at-most-once. Less important activity only
+ # TODO: make all the work in here a separate sidekiq job so it can go in the transaction
+ # Issue: https://gitlab.com/gitlab-org/gitlab/-/issues/228803
+ execute_hooks(merge_request, 'merge')
end
private
diff --git a/app/services/merge_requests/squash_service.rb b/app/services/merge_requests/squash_service.rb
index faa2e921581..94ce7098d3a 100644
--- a/app/services/merge_requests/squash_service.rb
+++ b/app/services/merge_requests/squash_service.rb
@@ -7,9 +7,7 @@ module MergeRequests
def execute
# If performing a squash would result in no change, then
# immediately return a success message without performing a squash
- if merge_request.commits_count < 2 && message.nil?
- return success(squash_sha: merge_request.diff_head_sha)
- end
+ return success(squash_sha: merge_request.diff_head_sha) if squash_redundant?
return error(s_('MergeRequests|This project does not allow squashing commits when merge requests are accepted.')) if squash_forbidden?
@@ -25,6 +23,12 @@ module MergeRequests
private
+ def squash_redundant?
+ return true if merge_request.merged?
+
+ merge_request.commits_count < 2 && message.nil?
+ end
+
def squash!
squash_sha = repository.squash(current_user, merge_request, message || merge_request.default_squash_commit_message)
diff --git a/app/views/layouts/_flash.html.haml b/app/views/layouts/_flash.html.haml
index 07c271be2f0..1424b9a7585 100644
--- a/app/views/layouts/_flash.html.haml
+++ b/app/views/layouts/_flash.html.haml
@@ -8,5 +8,6 @@
%div{ class: "flash-#{key} mb-2" }
= sprite_icon(icons[key], size: 16, css_class: 'align-middle mr-1') unless icons[key].nil?
%span= value
- %div{ class: "close-icon-wrapper js-close-icon" }
- = sprite_icon('close', size: 16, css_class: 'close-icon')
+ - if %w(alert notice success).include?(key)
+ %div{ class: "close-icon-wrapper js-close-icon" }
+ = sprite_icon('close', size: 16, css_class: 'close-icon')
diff --git a/app/views/layouts/nav/sidebar/_profile.html.haml b/app/views/layouts/nav/sidebar/_profile.html.haml
index 95d66786984..21b1387f620 100644
--- a/app/views/layouts/nav/sidebar/_profile.html.haml
+++ b/app/views/layouts/nav/sidebar/_profile.html.haml
@@ -18,7 +18,7 @@
%strong.fly-out-top-item-name
= _('Profile')
= nav_link(controller: [:accounts, :two_factor_auths]) do
- = link_to profile_account_path do
+ = link_to profile_account_path, data: { qa_selector: 'profile_account_link' } do
.nav-icon-container
= sprite_icon('account')
%span.nav-item-name
diff --git a/app/views/profiles/accounts/show.html.haml b/app/views/profiles/accounts/show.html.haml
index ea2f888c129..6b2692fe426 100644
--- a/app/views/profiles/accounts/show.html.haml
+++ b/app/views/profiles/accounts/show.html.haml
@@ -56,7 +56,7 @@
= render 'users/deletion_guidance', user: current_user
%button#delete-account-button.btn.btn-danger.disabled{ data: { toggle: 'modal',
- target: '#delete-account-modal' } }
+ target: '#delete-account-modal', qa_selector: 'delete_account_button' } }
= s_('Profiles|Delete account')
#delete-account-modal{ data: { action_url: user_registration_path,
diff --git a/app/views/shared/issuable/form/_title.html.haml b/app/views/shared/issuable/form/_title.html.haml
index 355a6627b8f..98c9f73fa3a 100644
--- a/app/views/shared/issuable/form/_title.html.haml
+++ b/app/views/shared/issuable/form/_title.html.haml
@@ -3,6 +3,13 @@
- form = local_assigns.fetch(:form)
- no_issuable_templates = issuable_templates(issuable).empty?
- div_class = no_issuable_templates ? 'col-sm-10' : 'col-sm-7 col-lg-8'
+- toggle_wip_link_start = '<a href="" class="js-toggle-wip">'
+- toggle_wip_link_end = '</a>'
+- draft_snippet = '<code>Draft:</code>'.html_safe
+- wip_snippet = '<code>WIP:</code>'.html_safe
+- draft_or_wip_snippet = '<code>Draft/WIP</code>'.html_safe
+- add_wip_text = (_('%{link_start}Start the title with %{draft_snippet} or %{wip_snippet}%{link_end} to prevent a merge request that is a work in progress from being merged before it\'s ready.') % { link_start: toggle_wip_link_start, link_end: toggle_wip_link_end, draft_snippet: draft_snippet, wip_snippet: wip_snippet } ).html_safe
+- remove_wip_text = (_('%{link_start}Remove the %{draft_or_wip_snippet} prefix%{link_end} from the title to allow this merge request to be merged when it\'s ready.' ) % { link_start: toggle_wip_link_start, link_end: toggle_wip_link_end, draft_or_wip_snippet: draft_or_wip_snippet } ).html_safe
%div{ class: div_class }
= form.text_field :title, required: true, maxlength: 255, autofocus: true,
@@ -11,23 +18,12 @@
- if issuable.respond_to?(:work_in_progress?)
.form-text.text-muted
.js-wip-explanation
- %a.js-toggle-wip{ href: '' }
- Remove the
- %code WIP:
- prefix from the title
- to allow this
- %strong Work In Progress
- merge request to be merged when it's ready.
+ = remove_wip_text
.js-no-wip-explanation
- if has_wip_commits
- It looks like you have some WIP commits in this branch.
+ = _('It looks like you have some draft commits in this branch.')
%br
- %a.js-toggle-wip{ href: '' }
- Start the title with
- %code WIP:
- to prevent a
- %strong Work In Progress
- merge request from being merged before it's ready.
+ = add_wip_text
- if no_issuable_templates && can?(current_user, :push_code, issuable.project)
= render 'shared/issuable/form/default_templates'
diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml
index 5148772c881..3d243cfd00c 100644
--- a/app/workers/all_queues.yml
+++ b/app/workers/all_queues.yml
@@ -1426,7 +1426,7 @@
:urgency: :high
:resource_boundary: :unknown
:weight: 5
- :idempotent:
+ :idempotent: true
:tags: []
- :name: merge_request_mergeability_check
:feature_category: :source_code_management
diff --git a/app/workers/merge_worker.rb b/app/workers/merge_worker.rb
index 270bd831f96..fbebaa925b4 100644
--- a/app/workers/merge_worker.rb
+++ b/app/workers/merge_worker.rb
@@ -7,6 +7,7 @@ class MergeWorker # rubocop:disable Scalability/IdempotentWorker
urgency :high
weight 5
loggable_arguments 2
+ idempotent!
def perform(merge_request_id, current_user_id, params)
params = params.with_indifferent_access