diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-01-21 15:08:26 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-01-21 15:08:26 +0000 |
commit | c859c3bfd242288065fe5e2d887f7204f09e2335 (patch) | |
tree | 10febaf8774a3ea6ab3773c0dd97658d673fb280 | |
parent | 28ce39a3e0e7b47e53939a15fb823af9c433327a (diff) | |
download | gitlab-ce-c859c3bfd242288065fe5e2d887f7204f09e2335.tar.gz |
Add latest changes from gitlab-org/gitlab@master
43 files changed, 468 insertions, 111 deletions
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_checking.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_checking.vue index cf26003d038..a5e3115397a 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_checking.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_checking.vue @@ -12,7 +12,7 @@ export default { <div class="mr-widget-body media"> <status-icon :show-disabled-button="true" status="loading" /> <div class="media-body space-children"> - <span class="bold"> {{ s__('mrWidget|Checking ability to merge automatically') }} </span> + <span class="bold"> {{ s__('mrWidget|Checking ability to merge automatically…') }} </span> </div> </div> </template> diff --git a/app/assets/javascripts/vue_merge_request_widget/stores/get_state_key.js b/app/assets/javascripts/vue_merge_request_widget/stores/get_state_key.js index 3ab229567f6..a298331c1fc 100644 --- a/app/assets/javascripts/vue_merge_request_widget/stores/get_state_key.js +++ b/app/assets/javascripts/vue_merge_request_widget/stores/get_state_key.js @@ -7,7 +7,7 @@ export default function deviseState(data) { return stateKey.missingBranch; } else if (!data.commits_count) { return stateKey.nothingToMerge; - } else if (this.mergeStatus === 'unchecked') { + } else if (this.mergeStatus === 'unchecked' || this.mergeStatus === 'checking') { return stateKey.checking; } else if (data.has_conflicts) { return stateKey.conflicts; diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 17025670488..22de8dd4109 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -45,7 +45,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo def show close_merge_request_if_no_source_project - @merge_request.check_mergeability + @merge_request.check_mergeability(async: true) respond_to do |format| format.html do diff --git a/app/helpers/milestones_helper.rb b/app/helpers/milestones_helper.rb index b12b39073ef..494c0bee8b8 100644 --- a/app/helpers/milestones_helper.rb +++ b/app/helpers/milestones_helper.rb @@ -26,7 +26,7 @@ module MilestonesHelper end end - def milestones_label_path(opts = {}) + def milestones_issues_path(opts = {}) if @project project_issues_path(@project, opts) elsif @group @@ -281,6 +281,26 @@ module MilestonesHelper can?(current_user, :admin_milestone, @project.group) end end + + def display_issues_count_warning? + milestone_visible_issues_count > Milestone::DISPLAY_ISSUES_LIMIT + end + + def milestone_issues_count_message + total_count = milestone_visible_issues_count + limit = Milestone::DISPLAY_ISSUES_LIMIT + + message = _('Showing %{limit} of %{total_count} issues. ') % { limit: limit, total_count: total_count } + message += link_to(_('View all issues'), milestones_issues_path) + + message.html_safe + end + + private + + def milestone_visible_issues_count + @milestone_visible_issues_count ||= @milestone.issues_visible_to_user(current_user).size + end end MilestonesHelper.prepend_if_ee('EE::MilestonesHelper') diff --git a/app/models/concerns/milestoneish.rb b/app/models/concerns/milestoneish.rb index 88e752e51e7..9ff60003406 100644 --- a/app/models/concerns/milestoneish.rb +++ b/app/models/concerns/milestoneish.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true module Milestoneish + DISPLAY_ISSUES_LIMIT = 20 + def total_issues_count(user) count_issues_by_state(user).values.sum end @@ -53,7 +55,11 @@ module Milestoneish end def sorted_issues(user) - issues_visible_to_user(user).preload_associated_models.sort_by_attribute('label_priority') + # This method is used on milestone view to filter opened assigned, opened unassigned and closed issues columns. + # We want a limit of DISPLAY_ISSUES_LIMIT for total issues present on all columns. + limited_ids = issues_visible_to_user(user).limit(DISPLAY_ISSUES_LIMIT).select(:id) + + Issue.where(id: limited_ids).preload_associated_models.sort_by_attribute('label_priority') end def sorted_merge_requests(user) diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 7162ba08a76..4ed73d979ce 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -160,20 +160,25 @@ class MergeRequest < ApplicationRecord state_machine :merge_status, initial: :unchecked do event :mark_as_unchecked do - transition [:can_be_merged, :unchecked] => :unchecked + transition [:can_be_merged, :checking, :unchecked] => :unchecked transition [:cannot_be_merged, :cannot_be_merged_recheck] => :cannot_be_merged_recheck end + event :mark_as_checking do + transition [:unchecked, :cannot_be_merged_recheck] => :checking + end + event :mark_as_mergeable do - transition [:unchecked, :cannot_be_merged_recheck] => :can_be_merged + transition [:unchecked, :cannot_be_merged_recheck, :checking] => :can_be_merged end event :mark_as_unmergeable do - transition [:unchecked, :cannot_be_merged_recheck] => :cannot_be_merged + transition [:unchecked, :cannot_be_merged_recheck, :checking] => :cannot_be_merged end state :unchecked state :cannot_be_merged_recheck + state :checking state :can_be_merged state :cannot_be_merged @@ -191,7 +196,7 @@ class MergeRequest < ApplicationRecord # rubocop: enable CodeReuse/ServiceClass def check_state?(merge_status) - [:unchecked, :cannot_be_merged_recheck].include?(merge_status.to_sym) + [:unchecked, :cannot_be_merged_recheck, :checking].include?(merge_status.to_sym) end end @@ -812,10 +817,16 @@ class MergeRequest < ApplicationRecord MergeRequests::ReloadDiffsService.new(self, current_user).execute end - def check_mergeability + def check_mergeability(async: false) return if Feature.enabled?(:merge_requests_conditional_mergeability_check, default_enabled: true) && !recheck_merge_status? - MergeRequests::MergeabilityCheckService.new(self).execute(retry_lease: false) + check_service = MergeRequests::MergeabilityCheckService.new(self) + + if async && Feature.enabled?(:async_merge_request_check_mergeability, project) + check_service.async_execute + else + check_service.execute(retry_lease: false) + end end # rubocop: enable CodeReuse/ServiceClass diff --git a/app/services/concerns/akismet_methods.rb b/app/services/concerns/akismet_methods.rb index 1cbcf0d47b9..508a165b065 100644 --- a/app/services/concerns/akismet_methods.rb +++ b/app/services/concerns/akismet_methods.rb @@ -2,16 +2,7 @@ module AkismetMethods def spammable_owner - @user ||= User.find(spammable_owner_id) - end - - def spammable_owner_id - @owner_id ||= - if spammable.respond_to?(:author_id) - spammable.author_id - elsif spammable.respond_to?(:creator_id) - spammable.creator_id - end + @user ||= User.find(spammable.author_id) end def akismet diff --git a/app/services/merge_requests/mergeability_check_service.rb b/app/services/merge_requests/mergeability_check_service.rb index 962e2327b3e..5b79e4d01f2 100644 --- a/app/services/merge_requests/mergeability_check_service.rb +++ b/app/services/merge_requests/mergeability_check_service.rb @@ -12,6 +12,13 @@ module MergeRequests @merge_request = merge_request end + def async_execute + return service_error if service_error + return unless merge_request.mark_as_checking + + MergeRequestMergeabilityCheckWorker.perform_async(merge_request.id) + end + # Updates the MR merge_status. Whenever it switches to a can_be_merged state, # the merge-ref is refreshed. # @@ -30,8 +37,7 @@ module MergeRequests # and the merge-ref is synced. Success in case of being/becoming mergeable, # error otherwise. def execute(recheck: false, retry_lease: true) - return ServiceResponse.error(message: 'Invalid argument') unless merge_request - return ServiceResponse.error(message: 'Unsupported operation') if Gitlab::Database.read_only? + return service_error if service_error return check_mergeability(recheck) unless merge_ref_auto_sync_lock_enabled? in_write_lock(retry_lease: retry_lease) do |retried| @@ -155,5 +161,15 @@ module MergeRequests def merge_ref_auto_sync_lock_enabled? Feature.enabled?(:merge_ref_auto_sync_lock, project, default_enabled: true) end + + def service_error + strong_memoize(:service_error) do + if !merge_request + ServiceResponse.error(message: 'Invalid argument') + elsif Gitlab::Database.read_only? + ServiceResponse.error(message: 'Unsupported operation') + end + end + end end end diff --git a/app/services/pages_domains/create_acme_order_service.rb b/app/services/pages_domains/create_acme_order_service.rb index 8eab5c52432..c600f497fa5 100644 --- a/app/services/pages_domains/create_acme_order_service.rb +++ b/app/services/pages_domains/create_acme_order_service.rb @@ -3,9 +3,6 @@ module PagesDomains class CreateAcmeOrderService attr_reader :pages_domain - # TODO: remove this hack after https://gitlab.com/gitlab-org/gitlab/issues/30146 is implemented - # This makes GitLab automatically retry the certificate obtaining process every 2 hours if process wasn't finished - SHORT_EXPIRATION_DELAY = 2.hours def initialize(pages_domain) @pages_domain = pages_domain @@ -20,7 +17,7 @@ module PagesDomains private_key = OpenSSL::PKey::RSA.new(4096) saved_order = pages_domain.acme_orders.create!( url: order.url, - expires_at: [order.expires, SHORT_EXPIRATION_DELAY.from_now].min, + expires_at: order.expires, private_key: private_key.to_pem, challenge_token: challenge.token, diff --git a/app/services/spam_service.rb b/app/services/spam_service.rb index ba9b812a01c..242c0ede57a 100644 --- a/app/services/spam_service.rb +++ b/app/services/spam_service.rb @@ -53,7 +53,7 @@ class SpamService def create_spam_log(api) @spam_log = SpamLog.create!( { - user_id: spammable_owner_id, + user_id: spammable.author_id, title: spammable.spam_title, description: spammable.spam_description, source_ip: options[:ip_address], diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml index 379ba976040..2efb304b397 100644 --- a/app/views/layouts/nav/_dashboard.html.haml +++ b/app/views/layouts/nav/_dashboard.html.haml @@ -76,7 +76,7 @@ - if Feature.enabled?(:user_mode_in_session) - if header_link?(:admin_mode) = nav_link(controller: 'admin/sessions', html_options: { class: "d-none d-lg-block d-xl-block"}) do - = link_to destroy_admin_session_path, title: _('Leave Admin Mode'), aria: { label: _('Leave Admin Mode') }, data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do + = link_to destroy_admin_session_path, method: :post, title: _('Leave Admin Mode'), aria: { label: _('Leave Admin Mode') }, data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do = sprite_icon('lock-open', size: 18) - elsif current_user.admin? = nav_link(controller: 'admin/sessions', html_options: { class: "d-none d-lg-block d-xl-block"}) do diff --git a/app/views/shared/_auto_devops_implicitly_enabled_banner.html.haml b/app/views/shared/_auto_devops_implicitly_enabled_banner.html.haml index 3670e19c240..d378e6cb22c 100644 --- a/app/views/shared/_auto_devops_implicitly_enabled_banner.html.haml +++ b/app/views/shared/_auto_devops_implicitly_enabled_banner.html.haml @@ -10,4 +10,4 @@ - unless Gitlab.config.registry.enabled %div = icon('exclamation-triangle') - = _('Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for AutoDevOps to work.') + = _('Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work.') diff --git a/app/views/shared/milestones/_issues_tab.html.haml b/app/views/shared/milestones/_issues_tab.html.haml index a8db7f8a556..52ce0482cd0 100644 --- a/app/views/shared/milestones/_issues_tab.html.haml +++ b/app/views/shared/milestones/_issues_tab.html.haml @@ -1,6 +1,11 @@ - args = { show_project_name: local_assigns.fetch(:show_project_name, false), show_full_project_name: local_assigns.fetch(:show_full_project_name, false) } +- if display_issues_count_warning? + .flash-container + .flash-warning#milestone-issue-count-warning + = milestone_issues_count_message + .row.prepend-top-default .col-md-4 = render 'shared/milestones/issuables', args.merge(title: 'Unstarted Issues (open and unassigned)', issuables: issues.opened.unassigned, id: 'unassigned', show_counter: true) diff --git a/app/views/shared/milestones/_labels_tab.html.haml b/app/views/shared/milestones/_labels_tab.html.haml index ecab037e378..cdea15bf13e 100644 --- a/app/views/shared/milestones/_labels_tab.html.haml +++ b/app/views/shared/milestones/_labels_tab.html.haml @@ -5,12 +5,12 @@ %li.no-border %span.label-row %span.label-name - = render_label(label, tooltip: false, link: milestones_label_path(options)) + = render_label(label, tooltip: false, link: milestones_issues_path(options)) %span.prepend-description-left = markdown_field(label, :description) .float-right.d-none.d-lg-block.d-xl-block - = link_to milestones_label_path(options.merge(state: 'opened')), class: 'btn btn-transparent btn-action' do + = link_to milestones_issues_path(options.merge(state: 'opened')), class: 'btn btn-transparent btn-action' do - pluralize milestone_issues_by_label_count(@milestone, label, state: :opened), 'open issue' - = link_to milestones_label_path(options.merge(state: 'closed')), class: 'btn btn-transparent btn-action' do + = link_to milestones_issues_path(options.merge(state: 'closed')), class: 'btn btn-transparent btn-action' do - pluralize milestone_issues_by_label_count(@milestone, label, state: :closed), 'closed issue' diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml index 502e7976ef2..f26a2201550 100644 --- a/app/workers/all_queues.yml +++ b/app/workers/all_queues.yml @@ -191,3 +191,4 @@ - group_export - self_monitoring_project_create - self_monitoring_project_delete +- merge_request_mergeability_check diff --git a/app/workers/merge_request_mergeability_check_worker.rb b/app/workers/merge_request_mergeability_check_worker.rb new file mode 100644 index 00000000000..ed35284b66c --- /dev/null +++ b/app/workers/merge_request_mergeability_check_worker.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +class MergeRequestMergeabilityCheckWorker + include ApplicationWorker + + feature_category :source_code_management + + def perform(merge_request_id) + merge_request = MergeRequest.find_by_id(merge_request_id) + + unless merge_request + logger.error("Failed to find merge request with ID: #{merge_request_id}") + return + end + + result = + ::MergeRequests::MergeabilityCheckService + .new(merge_request) + .execute(recheck: false, retry_lease: false) + + logger.error("Failed to check mergeability of merge request (#{merge_request_id}): #{result.message}") if result.error? + end +end diff --git a/changelogs/unreleased/29984-asynchronous-mr-mergeability-check.yml b/changelogs/unreleased/29984-asynchronous-mr-mergeability-check.yml new file mode 100644 index 00000000000..c1192390dce --- /dev/null +++ b/changelogs/unreleased/29984-asynchronous-mr-mergeability-check.yml @@ -0,0 +1,5 @@ +--- +title: Check mergeability of MR asynchronously +merge_request: 21026 +author: +type: performance diff --git a/changelogs/unreleased/acme-order-short-expiration.yml b/changelogs/unreleased/acme-order-short-expiration.yml deleted file mode 100644 index d2246cfa88a..00000000000 --- a/changelogs/unreleased/acme-order-short-expiration.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Retry obtaining Let's Encrypt certificates every 2 hours if it wasn't successful -merge_request: 22336 -author: -type: fixed diff --git a/changelogs/unreleased/fix-disable-admin-mode-link-in-dashboard.yml b/changelogs/unreleased/fix-disable-admin-mode-link-in-dashboard.yml new file mode 100644 index 00000000000..a5a65b7df57 --- /dev/null +++ b/changelogs/unreleased/fix-disable-admin-mode-link-in-dashboard.yml @@ -0,0 +1,5 @@ +--- +title: Fix POST method in dashboard link for disabling admin mode +merge_request: 23363 +author: Diego Louzán +type: fixed diff --git a/changelogs/unreleased/issue_39453.yml b/changelogs/unreleased/issue_39453.yml new file mode 100644 index 00000000000..c82444009ed --- /dev/null +++ b/changelogs/unreleased/issue_39453.yml @@ -0,0 +1,5 @@ +--- +title: Limits issues displayed on milestones +merge_request: 23102 +author: +type: performance diff --git a/config/sidekiq_queues.yml b/config/sidekiq_queues.yml index 06f7f01b83d..e341c91899b 100644 --- a/config/sidekiq_queues.yml +++ b/config/sidekiq_queues.yml @@ -102,6 +102,7 @@ - [self_monitoring_project_create, 2] - [self_monitoring_project_delete, 2] - [error_tracking_issue_link, 2] + - [merge_request_mergeability_check, 5] # EE-specific queues - [analytics, 1] diff --git a/db/migrate/20200116175538_update_timestamp_softwarelicensespolicy.rb b/db/migrate/20200116175538_update_timestamp_softwarelicensespolicy.rb new file mode 100644 index 00000000000..5ed797e33a3 --- /dev/null +++ b/db/migrate/20200116175538_update_timestamp_softwarelicensespolicy.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class UpdateTimestampSoftwarelicensespolicy < ActiveRecord::Migration[5.2] + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + disable_ddl_transaction! + + def up + time = Time.zone.now + + update_column_in_batches(:software_license_policies, :created_at, time) do |table, query| + query.where(table[:created_at].eq(nil)) + end + + update_column_in_batches(:software_license_policies, :updated_at, time) do |table, query| + query.where(table[:updated_at].eq(nil)) + end + end + + def down + # no-op + end +end diff --git a/db/migrate/20200121132641_update_timestamp_softwarelicensespolicy_not_null.rb b/db/migrate/20200121132641_update_timestamp_softwarelicensespolicy_not_null.rb new file mode 100644 index 00000000000..1da96b470ef --- /dev/null +++ b/db/migrate/20200121132641_update_timestamp_softwarelicensespolicy_not_null.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class UpdateTimestampSoftwarelicensespolicyNotNull < ActiveRecord::Migration[5.2] + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + def change + change_column_null(:software_license_policies, :created_at, false) + change_column_null(:software_license_policies, :updated_at, false) + end +end diff --git a/db/schema.rb b/db/schema.rb index 8ee1f2ffea6..39ada44b5aa 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2020_01_17_112554) do +ActiveRecord::Schema.define(version: 2020_01_21_132641) do # These are extensions that must be enabled in order to support this database enable_extension "pg_trgm" @@ -3857,8 +3857,8 @@ ActiveRecord::Schema.define(version: 2020_01_17_112554) do t.integer "project_id", null: false t.integer "software_license_id", null: false t.integer "classification", default: 0, null: false - t.datetime_with_timezone "created_at" - t.datetime_with_timezone "updated_at" + t.datetime_with_timezone "created_at", null: false + t.datetime_with_timezone "updated_at", null: false t.index ["project_id", "software_license_id"], name: "index_software_license_policies_unique_per_project", unique: true t.index ["software_license_id"], name: "index_software_license_policies_on_software_license_id" end diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md index d85310de159..3a00e862094 100644 --- a/doc/api/merge_requests.md +++ b/doc/api/merge_requests.md @@ -61,6 +61,12 @@ Parameters: | `in` | string | no | Modify the scope of the `search` attribute. `title`, `description`, or a string joining them with comma. Default is `title,description` | | `wip` | string | no | Filter merge requests against their `wip` status. `yes` to return *only* WIP merge requests, `no` to return *non* WIP merge requests | +NOTE: **Note:** +[Starting in GitLab 12.8](https://gitlab.com/gitlab-org/gitlab/issues/29984), +the mergeability (`merge_status`) of each merge request will be checked +asynchronously when a request is made to this endpoint. Poll this API endpoint +to get updated status. + ```json [ { @@ -526,6 +532,12 @@ Parameters: - `include_diverged_commits_count` (optional) - If `true` response includes the commits behind the target branch - `include_rebase_in_progress` (optional) - If `true` response includes whether a rebase operation is in progress +NOTE: **Note:** +[Starting in GitLab 12.8](https://gitlab.com/gitlab-org/gitlab/issues/29984), +the mergeability (`merge_status`) of a merge request will be checked +asynchronously when a request is made to this endpoint. Poll this API endpoint +to get updated status. + ```json { "id": 1, diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index aafbe4c9189..6f2ad341ce2 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -2103,7 +2103,7 @@ The `codequality` report collects [CodeQuality issues](../../user/project/merge_ as artifacts. The collected Code Quality report will be uploaded to GitLab as an artifact and will -be automatically shown in merge requests. +be summarized in merge requests. It is not available for download through the web interface. ##### `artifacts:reports:sast` **(ULTIMATE)** @@ -2113,8 +2113,8 @@ The `sast` report collects [SAST vulnerabilities](../../user/application_securit as artifacts. The collected SAST report will be uploaded to GitLab as an artifact and will -be automatically shown in merge requests, pipeline view and provide data for security -dashboards. +be summarized in the merge requests and pipeline view. It is also used to provide data for security +dashboards. It is not available for download through the web interface. ##### `artifacts:reports:dependency_scanning` **(ULTIMATE)** @@ -2124,8 +2124,8 @@ The `dependency_scanning` report collects [Dependency Scanning vulnerabilities]( as artifacts. The collected Dependency Scanning report will be uploaded to GitLab as an artifact and will -be automatically shown in merge requests, pipeline view and provide data for security -dashboards. +be summarized in the merge requests and pipeline view. It is also used to provide data for security +dashboards. It is not available for download through the web interface. ##### `artifacts:reports:container_scanning` **(ULTIMATE)** @@ -2135,8 +2135,8 @@ The `container_scanning` report collects [Container Scanning vulnerabilities](.. as artifacts. The collected Container Scanning report will be uploaded to GitLab as an artifact and will -be automatically shown in merge requests, pipeline view and provide data for security -dashboards. +be summarized in the merge requests and pipeline view. It is also used to provide data for security +dashboards. It is not available for download through the web interface. ##### `artifacts:reports:dast` **(ULTIMATE)** @@ -2146,8 +2146,8 @@ The `dast` report collects [DAST vulnerabilities](../../user/application_securit as artifacts. The collected DAST report will be uploaded to GitLab as an artifact and will -be automatically shown in merge requests, pipeline view and provide data for security -dashboards. +be summarized in the merge requests and pipeline view. It is also used to provide data for security +dashboards. It is not available for download through the web interface. ##### `artifacts:reports:license_management` **(ULTIMATE)** @@ -2157,8 +2157,8 @@ The `license_management` report collects [Licenses](../../user/application_secur as artifacts. The collected License Compliance report will be uploaded to GitLab as an artifact and will -be automatically shown in merge requests, pipeline view and provide data for security -dashboards. +be summarized in the merge requests and pipeline view. It is also used to provide data for security +dashboards. It is not available for download through the web interface. ##### `artifacts:reports:performance` **(PREMIUM)** @@ -2168,7 +2168,7 @@ The `performance` report collects [Performance metrics](../../user/project/merge as artifacts. The collected Performance report will be uploaded to GitLab as an artifact and will -be automatically shown in merge requests. +be automatically shown in merge requests. It is not available for download through the web interface. ##### `artifacts:reports:metrics` **(PREMIUM)** @@ -2178,7 +2178,7 @@ The `metrics` report collects [Metrics](../../ci/metrics_reports.md) as artifacts. The collected Metrics report will be uploaded to GitLab as an artifact and will -be automatically shown in merge requests. +be automatically shown in merge requests. It is not available for download through the web interface. ### `dependencies` diff --git a/doc/user/project/repository/git_blame.md b/doc/user/project/repository/git_blame.md index 4b645e4c4bc..1c63d73acdb 100644 --- a/doc/user/project/repository/git_blame.md +++ b/doc/user/project/repository/git_blame.md @@ -23,10 +23,14 @@ noted information: If you hover over a commit in the UI, you'll see a precise date and time for that commit. -![Blame previous commit](img/file_blame_previous_commit_v12_7.png "Blame previous commit") +## Blame previous commit + +> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/19299) in GitLab 12.7. To see earlier revisions of a specific line, click **View blame prior to this change** -until you've found the changes you're interested in viewing. +until you've found the changes you're interested in viewing: + +![Blame previous commit](img/file_blame_previous_commit_v12_7.png "Blame previous commit") ## Associated `git` command diff --git a/doc/user/project/service_desk.md b/doc/user/project/service_desk.md index e9fce474040..d0c32bafe19 100644 --- a/doc/user/project/service_desk.md +++ b/doc/user/project/service_desk.md @@ -69,7 +69,7 @@ Follow these steps to do so: have access to your GitLab instance. We recommend **putting this behind an alias** so it can be changed if needed, and **[enabling Akismet](../../integration/akismet.md)** on your GitLab instance to add spam checking to this service. Unblocked email spam would result in many spam - issues being created, and may disrupt your GitLab service. + issues being created. If you have [templates](description_templates.md) in your repository, you can optionally select one from the selector menu to append it to all Service Desk issues. diff --git a/lib/api/entities.rb b/lib/api/entities.rb index dfd0e676586..97be90030c8 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -811,7 +811,7 @@ module API # See https://gitlab.com/gitlab-org/gitlab-foss/issues/42344 for more # information. expose :merge_status do |merge_request| - merge_request.check_mergeability + merge_request.check_mergeability(async: true) merge_request.merge_status end expose :diff_head_sha, as: :sha diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 8d3ab24f84b..5ae8d8b23fe 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -4951,7 +4951,7 @@ msgstr "" msgid "Container registry images" msgstr "" -msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for AutoDevOps to work." +msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work." msgstr "" msgid "ContainerRegistry|Automatically remove extra images that aren't designed to be kept." @@ -5759,13 +5759,22 @@ msgstr "" msgid "CycleAnalytics|Days to completion" msgstr "" +msgid "CycleAnalytics|Display chart filters" +msgstr "" + msgid "CycleAnalytics|No stages selected" msgstr "" msgid "CycleAnalytics|Number of tasks" msgstr "" -msgid "CycleAnalytics|Showing %{subject} and %{selectedLabelsCount} labels" +msgid "CycleAnalytics|Select labels" +msgstr "" + +msgid "CycleAnalytics|Show" +msgstr "" + +msgid "CycleAnalytics|Showing %{subjectFilterText} and %{selectedLabelsCount} labels" msgstr "" msgid "CycleAnalytics|Showing data for group '%{groupName}' and %{selectedProjectCount} projects from %{startDate} to %{endDate}" @@ -17051,6 +17060,9 @@ msgid_plural "Showing %d events" msgstr[0] "" msgstr[1] "" +msgid "Showing %{limit} of %{total_count} issues. " +msgstr "" + msgid "Showing %{pageSize} of %{total} issues" msgstr "" @@ -20671,6 +20683,9 @@ msgstr "" msgid "View Documentation" msgstr "" +msgid "View all issues" +msgstr "" + msgid "View blame prior to this change" msgstr "" @@ -22442,7 +22457,7 @@ msgstr "" msgid "mrWidget|Check out branch" msgstr "" -msgid "mrWidget|Checking ability to merge automatically" +msgid "mrWidget|Checking ability to merge automatically…" msgstr "" msgid "mrWidget|Cherry-pick" diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb index d5b1bfe0ac4..df2727d13cf 100644 --- a/spec/controllers/projects/merge_requests_controller_spec.rb +++ b/spec/controllers/projects/merge_requests_controller_spec.rb @@ -44,6 +44,21 @@ describe Projects::MergeRequestsController do get :show, params: params.merge(extra_params) end + context 'when merge request is unchecked' do + before do + merge_request.mark_as_unchecked! + end + + it 'checks mergeability asynchronously' do + expect_next_instance_of(MergeRequests::MergeabilityCheckService) do |service| + expect(service).not_to receive(:execute) + expect(service).to receive(:async_execute) + end + + go + end + end + describe 'as html' do context 'when diff files were cleaned' do render_views diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb index 99a6165cfc9..b31c5e30fc0 100644 --- a/spec/features/admin/admin_settings_spec.rb +++ b/spec/features/admin/admin_settings_spec.rb @@ -9,7 +9,7 @@ describe 'Admin updates settings', :clean_gitlab_redis_shared_state, :do_not_moc let(:admin) { create(:admin) } - context 'feature flag :user_mode_in_session is enabled' do + context 'feature flag :user_mode_in_session is enabled', :request_store do before do stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false') sign_in(admin) @@ -486,10 +486,24 @@ describe 'Admin updates settings', :clean_gitlab_redis_shared_state, :do_not_moc end end - it 'can leave admin mode' do + it 'can leave admin mode using main dashboard link', :js do page.within('.navbar-sub-nav') do - # Select first, link is also included in mobile view list - click_on 'Leave Admin Mode', match: :first + click_on 'Leave Admin Mode' + + expect(page).to have_link(href: new_admin_session_path) + end + end + + it 'can leave admin mode using dropdown menu on smaller screens', :js do + resize_screen_xs + visit root_dashboard_path + + find('.header-more').click + + page.within '.navbar-sub-nav' do + click_on 'Leave Admin Mode' + + find('.header-more').click expect(page).to have_link(href: new_admin_session_path) end diff --git a/spec/features/merge_request/user_sees_merge_widget_spec.rb b/spec/features/merge_request/user_sees_merge_widget_spec.rb index 098f41f120d..17754400b91 100644 --- a/spec/features/merge_request/user_sees_merge_widget_spec.rb +++ b/spec/features/merge_request/user_sees_merge_widget_spec.rb @@ -19,7 +19,7 @@ describe 'Merge request > User sees merge widget', :js do sign_in(user) end - context 'new merge request' do + context 'new merge request', :sidekiq_might_not_need_inline do before do visit project_new_merge_request_path( project, diff --git a/spec/features/merge_requests/user_squashes_merge_request_spec.rb b/spec/features/merge_requests/user_squashes_merge_request_spec.rb index a9b96c5bbf5..4115907d074 100644 --- a/spec/features/merge_requests/user_squashes_merge_request_spec.rb +++ b/spec/features/merge_requests/user_squashes_merge_request_spec.rb @@ -67,7 +67,7 @@ describe 'User squashes a merge request', :js do end end - context 'when squash is enabled on merge request creation' do + context 'when squash is enabled on merge request creation', :sidekiq_might_not_need_inline do before do visit project_new_merge_request_path(project, merge_request: { target_branch: 'master', source_branch: source_branch }) check 'merge_request[squash]' @@ -97,7 +97,7 @@ describe 'User squashes a merge request', :js do end end - context 'when squash is not enabled on merge request creation' do + context 'when squash is not enabled on merge request creation', :sidekiq_might_not_need_inline do before do visit project_new_merge_request_path(project, merge_request: { target_branch: 'master', source_branch: source_branch }) click_on 'Submit merge request' diff --git a/spec/features/milestones/user_views_milestone_spec.rb b/spec/features/milestones/user_views_milestone_spec.rb index 71abb195ad1..b1c2a87ef94 100644 --- a/spec/features/milestones/user_views_milestone_spec.rb +++ b/spec/features/milestones/user_views_milestone_spec.rb @@ -25,6 +25,37 @@ describe "User views milestone" do expect { visit_milestone }.not_to exceed_query_limit(control) end + context 'limiting milestone issues' do + before_all do + 2.times do + create(:issue, milestone: milestone, project: project) + create(:issue, milestone: milestone, project: project, assignees: [user]) + create(:issue, milestone: milestone, project: project, state: :closed) + end + end + + context 'when issues on milestone are over DISPLAY_ISSUES_LIMIT' do + it "limits issues to display and shows warning" do + stub_const('Milestoneish::DISPLAY_ISSUES_LIMIT', 3) + + visit(project_milestone_path(project, milestone)) + + expect(page).to have_selector('.issuable-row', count: 3) + expect(page).to have_selector('#milestone-issue-count-warning', text: 'Showing 3 of 6 issues. View all issues') + expect(page).to have_link('View all issues', href: project_issues_path(project)) + end + end + + context 'when issues on milestone are below DISPLAY_ISSUES_LIMIT' do + it 'does not display warning' do + visit(project_milestone_path(project, milestone)) + + expect(page).not_to have_selector('#milestone-issue-count-warning', text: 'Showing 3 of 6 issues. View all issues') + expect(page).to have_selector('.issuable-row', count: 6) + end + end + end + private def visit_milestone diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_checking_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_checking_spec.js index 4f6451473e8..efccd507fe2 100644 --- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_checking_spec.js +++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_checking_spec.js @@ -25,7 +25,7 @@ describe('MRWidgetChecking', () => { it('renders information about merging', () => { expect(vm.$el.querySelector('.media-body').textContent.trim()).toEqual( - 'Checking ability to merge automatically', + 'Checking ability to merge automatically…', ); }); }); diff --git a/spec/migrations/update_timestamp_softwarelicensespolicy_spec.rb b/spec/migrations/update_timestamp_softwarelicensespolicy_spec.rb new file mode 100644 index 00000000000..539da8ac92a --- /dev/null +++ b/spec/migrations/update_timestamp_softwarelicensespolicy_spec.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +require 'spec_helper' + +require Rails.root.join('db', 'migrate', '20200116175538_update_timestamp_softwarelicensespolicy.rb') + +describe UpdateTimestampSoftwarelicensespolicy, :migration do + let(:software_licenses_policy) { table(:software_license_policies) } + let(:projects) { table(:projects) } + let(:licenses) { table(:software_licenses) } + + before do + projects.create!(name: 'gitlab', path: 'gitlab-org/gitlab-ce', namespace_id: 1) + licenses.create!(name: 'MIT') + software_licenses_policy.create!(project_id: projects.first.id, software_license_id: licenses.first.id, created_at: nil, updated_at: nil) + end + + it 'creates timestamps' do + migrate! + + expect(software_licenses_policy.first.created_at).to be_present + expect(software_licenses_policy.first.updated_at).to be_present + end +end diff --git a/spec/models/concerns/milestoneish_spec.rb b/spec/models/concerns/milestoneish_spec.rb index d46c9747845..e39cbedde68 100644 --- a/spec/models/concerns/milestoneish_spec.rb +++ b/spec/models/concerns/milestoneish_spec.rb @@ -33,17 +33,32 @@ describe Milestone, 'Milestoneish' do end describe '#sorted_issues' do - it 'sorts issues by label priority' do + before do issue.labels << label_1 security_issue_1.labels << label_2 closed_issue_1.labels << label_3 + end + it 'sorts issues by label priority' do issues = milestone.sorted_issues(member) expect(issues.first).to eq(issue) expect(issues.second).to eq(security_issue_1) expect(issues.third).not_to eq(closed_issue_1) end + + it 'limits issue count' do + stub_const('Milestoneish::DISPLAY_ISSUES_LIMIT', 4) + + issues = milestone.sorted_issues(member) + + # Cannot use issues.count here because it is sorting + # by a virtual column 'highest_priority' and it will break + # the query. + total_issues_count = issues.opened.unassigned.length + issues.opened.assigned.length + issues.closed.length + expect(issues.length).to eq(4) + expect(total_issues_count).to eq(4) + end end context 'attributes visibility' do diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index c6894c04385..9f256719248 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -277,6 +277,7 @@ describe MergeRequest do describe 'respond to' do it { is_expected.to respond_to(:unchecked?) } + it { is_expected.to respond_to(:checking?) } it { is_expected.to respond_to(:can_be_merged?) } it { is_expected.to respond_to(:cannot_be_merged?) } it { is_expected.to respond_to(:merge_params) } @@ -2084,43 +2085,75 @@ describe MergeRequest do describe '#check_mergeability' do let(:mergeability_service) { double } + subject { create(:merge_request, merge_status: 'unchecked') } + before do allow(MergeRequests::MergeabilityCheckService).to receive(:new) do mergeability_service end end - context 'if the merge status is unchecked' do - before do - subject.mark_as_unchecked! - end - + shared_examples_for 'method that executes MergeabilityCheckService' do it 'executes MergeabilityCheckService' do expect(mergeability_service).to receive(:execute) subject.check_mergeability end + + context 'when async is true' do + context 'and async_merge_request_check_mergeability feature flag is enabled' do + it 'executes MergeabilityCheckService asynchronously' do + expect(mergeability_service).to receive(:async_execute) + + subject.check_mergeability(async: true) + end + end + + context 'and async_merge_request_check_mergeability feature flag is disabled' do + before do + stub_feature_flags(async_merge_request_check_mergeability: false) + end + + it 'executes MergeabilityCheckService' do + expect(mergeability_service).to receive(:execute) + + subject.check_mergeability(async: true) + end + end + end + end + + context 'if the merge status is unchecked' do + it_behaves_like 'method that executes MergeabilityCheckService' + end + + context 'if the merge status is checking' do + before do + subject.mark_as_checking! + end + + it_behaves_like 'method that executes MergeabilityCheckService' end context 'if the merge status is checked' do - context 'and feature flag is enabled' do - it 'executes MergeabilityCheckService' do - expect(mergeability_service).not_to receive(:execute) + before do + subject.mark_as_mergeable! + end + + context 'and merge_requests_conditional_mergeability_check feature flag is enabled' do + it 'does not call MergeabilityCheckService' do + expect(MergeRequests::MergeabilityCheckService).not_to receive(:new) subject.check_mergeability end end - context 'and feature flag is disabled' do + context 'and merge_requests_conditional_mergeability_check feature flag is disabled' do before do stub_feature_flags(merge_requests_conditional_mergeability_check: false) end - it 'does not execute MergeabilityCheckService' do - expect(mergeability_service).to receive(:execute) - - subject.check_mergeability - end + it_behaves_like 'method that executes MergeabilityCheckService' end end end @@ -3145,7 +3178,7 @@ describe MergeRequest do describe 'check_state?' do it 'indicates whether MR is still checking for mergeability' do state_machine = described_class.state_machines[:merge_status] - check_states = [:unchecked, :cannot_be_merged_recheck] + check_states = [:unchecked, :cannot_be_merged_recheck, :checking] check_states.each do |merge_status| expect(state_machine.check_state?(merge_status)).to be true diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index ae0596bea98..c3d92a90d11 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -65,6 +65,21 @@ describe API::MergeRequests do end.not_to exceed_query_limit(control) end + context 'when merge request is unchecked' do + before do + merge_request.mark_as_unchecked! + end + + it 'checks mergeability asynchronously' do + expect_next_instance_of(MergeRequests::MergeabilityCheckService) do |service| + expect(service).not_to receive(:execute) + expect(service).to receive(:async_execute) + end + + get api(endpoint_path, user) + end + end + context 'with labels' do include_context 'with labels' @@ -1003,6 +1018,21 @@ describe API::MergeRequests do expect(json_response['user']['can_merge']).to be_falsy end + + context 'when merge request is unchecked' do + before do + merge_request.mark_as_unchecked! + end + + it 'checks mergeability asynchronously' do + expect_next_instance_of(MergeRequests::MergeabilityCheckService) do |service| + expect(service).not_to receive(:execute) + expect(service).to receive(:async_execute) + end + + get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}", user) + end + end end describe 'GET /projects/:id/merge_requests/:merge_request_iid/participants' do diff --git a/spec/services/merge_requests/mergeability_check_service_spec.rb b/spec/services/merge_requests/mergeability_check_service_spec.rb index a864da0a6fb..8f17e8083e3 100644 --- a/spec/services/merge_requests/mergeability_check_service_spec.rb +++ b/spec/services/merge_requests/mergeability_check_service_spec.rb @@ -53,9 +53,42 @@ describe MergeRequests::MergeabilityCheckService, :clean_gitlab_redis_shared_sta end end + let(:project) { create(:project, :repository) } + let(:merge_request) { create(:merge_request, merge_status: :unchecked, source_project: project, target_project: project) } + + describe '#async_execute' do + shared_examples_for 'no job is enqueued' do + it 'does not enqueue MergeRequestMergeabilityCheckWorker' do + expect(MergeRequestMergeabilityCheckWorker).not_to receive(:perform_async) + + described_class.new(merge_request).async_execute + end + end + + it 'enqueues MergeRequestMergeabilityCheckWorker' do + expect(MergeRequestMergeabilityCheckWorker).to receive(:perform_async) + + described_class.new(merge_request).async_execute + end + + context 'when read only DB' do + before do + allow(Gitlab::Database).to receive(:read_only?) { true } + end + + it_behaves_like 'no job is enqueued' + end + + context 'when merge_status is already checking' do + before do + merge_request.mark_as_checking + end + + it_behaves_like 'no job is enqueued' + end + end + describe '#execute' do - let(:project) { create(:project, :repository) } - let(:merge_request) { create(:merge_request, merge_status: :unchecked, source_project: project, target_project: project) } let(:repo) { project.repository } subject { described_class.new(merge_request).execute } diff --git a/spec/services/pages_domains/create_acme_order_service_spec.rb b/spec/services/pages_domains/create_acme_order_service_spec.rb index 154b3fd5600..d59aa9b979e 100644 --- a/spec/services/pages_domains/create_acme_order_service_spec.rb +++ b/spec/services/pages_domains/create_acme_order_service_spec.rb @@ -45,34 +45,12 @@ describe PagesDomains::CreateAcmeOrderService do expect { OpenSSL::PKey::RSA.new(saved_order.private_key) }.not_to raise_error end - it 'properly saves order url' do + it 'properly saves order attributes' do service.execute saved_order = PagesDomainAcmeOrder.last expect(saved_order.url).to eq(order_double.url) - end - - context 'when order expires in 2 days' do - it 'sets expiration time in 2 hours' do - Timecop.freeze do - service.execute - - saved_order = PagesDomainAcmeOrder.last - expect(saved_order.expires_at).to be_like_time(2.hours.from_now) - end - end - end - - context 'when order expires in an hour' do - it 'sets expiration time accordingly to order' do - Timecop.freeze do - allow(order_double).to receive(:expires).and_return(1.hour.from_now) - service.execute - - saved_order = PagesDomainAcmeOrder.last - expect(saved_order.expires_at).to be_like_time(1.hour.from_now) - end - end + expect(saved_order.expires_at).to be_like_time(order_double.expires) end it 'properly saves challenge attributes' do diff --git a/spec/workers/merge_request_mergeability_check_worker_spec.rb b/spec/workers/merge_request_mergeability_check_worker_spec.rb new file mode 100644 index 00000000000..2331664215f --- /dev/null +++ b/spec/workers/merge_request_mergeability_check_worker_spec.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe MergeRequestMergeabilityCheckWorker do + subject { described_class.new } + + describe '#perform' do + context 'when merge request does not exist' do + it 'does not execute MergeabilityCheckService' do + expect(MergeRequests::MergeabilityCheckService).not_to receive(:new) + + subject.perform(1) + end + end + + context 'when merge request exists' do + let(:merge_request) { create(:merge_request) } + + it 'executes MergeabilityCheckService' do + expect_next_instance_of(MergeRequests::MergeabilityCheckService, merge_request) do |service| + expect(service).to receive(:execute).and_return(double(error?: false)) + end + + subject.perform(merge_request.id) + end + end + end +end |