diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-07-17 21:09:23 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-07-17 21:09:23 +0000 |
commit | 1d84a028b42a1a3aed36a0f3a6cae970c8df8e69 (patch) | |
tree | 1ebd0249d20169a0b8be47dc541e9c2676d62648 /app | |
parent | ec72da1833d94bb1556af94193ccf2a93c9cb939 (diff) | |
download | gitlab-ce-1d84a028b42a1a3aed36a0f3a6cae970c8df8e69.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r-- | app/assets/javascripts/issuable_form.js | 25 | ||||
-rw-r--r-- | app/assets/javascripts/logs/stores/mutations.js | 4 | ||||
-rw-r--r-- | app/assets/javascripts/monitoring/components/refresh_button.vue | 6 | ||||
-rw-r--r-- | app/assets/javascripts/profile/account/components/delete_account_modal.vue | 1 | ||||
-rw-r--r-- | app/assets/javascripts/vue_merge_request_widget/components/states/work_in_progress.vue | 44 | ||||
-rw-r--r-- | app/finders/group_members_finder.rb | 2 | ||||
-rw-r--r-- | app/services/merge_requests/merge_service.rb | 2 | ||||
-rw-r--r-- | app/services/merge_requests/post_merge_service.rb | 25 | ||||
-rw-r--r-- | app/services/merge_requests/squash_service.rb | 10 | ||||
-rw-r--r-- | app/views/layouts/_flash.html.haml | 5 | ||||
-rw-r--r-- | app/views/layouts/nav/sidebar/_profile.html.haml | 2 | ||||
-rw-r--r-- | app/views/profiles/accounts/show.html.haml | 2 | ||||
-rw-r--r-- | app/views/shared/issuable/form/_title.html.haml | 24 | ||||
-rw-r--r-- | app/workers/all_queues.yml | 2 | ||||
-rw-r--r-- | app/workers/merge_worker.rb | 1 |
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 |