diff options
68 files changed, 476 insertions, 332 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index a1c928aedf3..56ac809b548 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,19 @@ documentation](doc/development/changelog.md) for instructions on adding your own entry. +## 11.6.2 (2019-01-02) + +### Fixed (7 changes) + +- Hide cluster features that don't work yet with Group Clusters. !23935 +- Fix a 500 error that could occur until all migrations are done. !23939 +- Fix missing Git clone button when protocol restriction setting enabled. !24015 +- Fix clone dropdown parent inheritance issues in HAML. !24029 +- Fix content-disposition in blobs and files API endpoint. !24078 +- Fixed markdown toolbar buttons. +- Adjust line-height of blame view line numbers. + + ## 11.6.1 (2018-12-28) ### Security (15 changes) diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION index 016dac34bf9..917d38ec9f9 100644 --- a/GITLAB_SHELL_VERSION +++ b/GITLAB_SHELL_VERSION @@ -1 +1 @@ -8.4.3 +8.4.4 @@ -127,7 +127,7 @@ gem 'asciidoctor-plantuml', '0.0.8' gem 'rouge', '~> 3.1' gem 'truncato', '~> 0.7.9' gem 'bootstrap_form', '~> 2.7.0' -gem 'nokogiri', '~> 1.8.4' +gem 'nokogiri', '~> 1.8.5' gem 'escape_utils', '~> 1.1' # Calendar rendering @@ -322,7 +322,7 @@ end group :development, :test do gem 'bootsnap', '~> 1.3' gem 'bullet', '~> 5.5.0', require: !!ENV['ENABLE_BULLET'] - gem 'pry-byebug', '~> 3.4.1', platform: :mri + gem 'pry-byebug', '~> 3.5.1', platform: :mri gem 'pry-rails', '~> 0.3.4' gem 'awesome_print', require: false @@ -337,13 +337,13 @@ group :development, :test do gem 'rspec-parameterized', require: false # Prevent occasions where minitest is not bundled in packaged versions of ruby (see #3826) - gem 'minitest', '~> 5.7.0' + gem 'minitest', '~> 5.11.0' # Generate Fake data gem 'ffaker', '~> 2.10' - gem 'capybara', '~> 2.15' - gem 'capybara-screenshot', '~> 1.0.0' + gem 'capybara', '~> 2.16.1' + gem 'capybara-screenshot', '~> 1.0.18' gem 'selenium-webdriver', '~> 3.12' gem 'spring', '~> 2.0.0' diff --git a/Gemfile.lock b/Gemfile.lock index 1f7f58d59e0..4dda0bcad18 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -97,16 +97,16 @@ GEM bundler-audit (0.5.0) bundler (~> 1.2) thor (~> 0.18) - byebug (9.0.6) - capybara (2.15.1) + byebug (9.1.0) + capybara (2.16.1) addressable mini_mime (>= 0.1.3) nokogiri (>= 1.3.3) rack (>= 1.0.0) rack-test (>= 0.5.4) xpath (~> 2.0) - capybara-screenshot (1.0.14) - capybara (>= 1.0, < 3) + capybara-screenshot (1.0.22) + capybara (>= 1.0, < 4) launchy carrierwave (1.3.0) activemodel (>= 4.0.0) @@ -463,7 +463,7 @@ GEM mini_magick (4.8.0) mini_mime (1.0.1) mini_portile2 (2.3.0) - minitest (5.7.0) + minitest (5.11.3) msgpack (1.2.4) multi_json (1.13.1) multi_xml (0.6.0) @@ -595,8 +595,8 @@ GEM pry (0.11.3) coderay (~> 1.1.0) method_source (~> 0.9.0) - pry-byebug (3.4.3) - byebug (>= 9.0, < 9.1) + pry-byebug (3.5.1) + byebug (~> 9.1) pry (~> 0.10) pry-rails (0.3.6) pry (>= 0.10.4) @@ -963,8 +963,8 @@ DEPENDENCIES browser (~> 2.5) bullet (~> 5.5.0) bundler-audit (~> 0.5.0) - capybara (~> 2.15) - capybara-screenshot (~> 1.0.0) + capybara (~> 2.16.1) + capybara-screenshot (~> 1.0.18) carrierwave (~> 1.3) charlock_holmes (~> 0.7.5) chronic (~> 0.10.2) @@ -1054,12 +1054,12 @@ DEPENDENCIES method_source (~> 0.8) mimemagic (~> 0.3.2) mini_magick - minitest (~> 5.7.0) + minitest (~> 5.11.0) mysql2 (~> 0.4.10) nakayoshi_fork (~> 0.0.4) net-ldap net-ssh (~> 5.0) - nokogiri (~> 1.8.4) + nokogiri (~> 1.8.5) oauth2 (~> 1.4) octokit (~> 4.9) omniauth (~> 1.8) @@ -1087,7 +1087,7 @@ DEPENDENCIES pg (~> 0.18.2) premailer-rails (~> 1.9.7) prometheus-client-mmap (~> 0.9.4) - pry-byebug (~> 3.4.1) + pry-byebug (~> 3.5.1) pry-rails (~> 0.3.4) puma (~> 3.12) puma_worker_killer @@ -1171,4 +1171,4 @@ DEPENDENCIES wikicloth (= 0.8.1) BUNDLED WITH - 1.17.1 + 1.17.3 diff --git a/app/assets/javascripts/notes/components/note_form.vue b/app/assets/javascripts/notes/components/note_form.vue index 9b7f3d3588d..e78596f8b52 100644 --- a/app/assets/javascripts/notes/components/note_form.vue +++ b/app/assets/javascripts/notes/components/note_form.vue @@ -226,7 +226,7 @@ export default { <button :disabled="isDisabled" type="button" - class="js-vue-issue-save btn btn-success js-comment-button" + class="js-vue-issue-save btn btn-success js-comment-button qa-reply-comment-button" @click="handleUpdate();" > {{ saveButtonTitle }} diff --git a/app/assets/javascripts/notes/components/toggle_replies_widget.vue b/app/assets/javascripts/notes/components/toggle_replies_widget.vue index 72a8ff28466..f1b0b12bdce 100644 --- a/app/assets/javascripts/notes/components/toggle_replies_widget.vue +++ b/app/assets/javascripts/notes/components/toggle_replies_widget.vue @@ -57,7 +57,7 @@ export default { tooltip-placement="bottom" /> </div> - <button class="btn btn-link js-replies-text" type="button" @click="toggle"> + <button class="btn btn-link js-replies-text qa-expand-replies" type="button" @click="toggle"> {{ replies.length }} {{ n__('reply', 'replies', replies.length) }} </button> {{ __('Last reply by') }} @@ -66,7 +66,11 @@ export default { </a> <time-ago-tooltip :time="lastReply.created_at" tooltip-placement="bottom" /> </template> - <span v-else class="collapse-replies-btn js-collapse-replies" @click="toggle"> + <span + v-else + class="collapse-replies-btn js-collapse-replies qa-collapse-replies" + @click="toggle" + > <icon name="chevron-down" /> {{ s__('Notes|Collapse replies') }} </span> </li> diff --git a/app/assets/stylesheets/framework/issue_box.scss b/app/assets/stylesheets/framework/issue_box.scss index a66604e56ff..e51f230a680 100644 --- a/app/assets/stylesheets/framework/issue_box.scss +++ b/app/assets/stylesheets/framework/issue_box.scss @@ -45,9 +45,4 @@ &.status-box-upcoming { background: $gl-text-color-secondary; } - - &.status-box-milestone { - color: $gl-text-color; - background: $gray-darker; - } } diff --git a/app/assets/stylesheets/pages/milestone.scss b/app/assets/stylesheets/pages/milestone.scss index 1e92582d6d9..94bf32945fc 100644 --- a/app/assets/stylesheets/pages/milestone.scss +++ b/app/assets/stylesheets/pages/milestone.scss @@ -1,3 +1,5 @@ +$status-box-line-height: 26px; + .issues-sortable-list .str-truncated { max-width: 90%; } @@ -38,6 +40,7 @@ font-size: $tooltip-font-size; margin-top: 0; margin-right: $gl-padding-4; + line-height: $status-box-line-height; @include media-breakpoint-down(xs) { line-height: unset; diff --git a/app/controllers/groups/milestones_controller.rb b/app/controllers/groups/milestones_controller.rb index b42116b0f36..868deea3f01 100644 --- a/app/controllers/groups/milestones_controller.rb +++ b/app/controllers/groups/milestones_controller.rb @@ -43,14 +43,7 @@ class Groups::MilestonesController < Groups::ApplicationController def update # Keep this compatible with legacy group milestones where we have to update # all projects milestones states at once. - if @milestone.legacy_group_milestone? - update_params = milestone_params.select { |key| key == "state_event" } - milestones = @milestone.milestones - else - update_params = milestone_params - milestones = [@milestone] - end - + milestones, update_params = get_milestones_for_update milestones.each do |milestone| Milestones::UpdateService.new(milestone.parent, current_user, update_params).execute(milestone) end @@ -71,6 +64,14 @@ class Groups::MilestonesController < Groups::ApplicationController private + def get_milestones_for_update + if @milestone.legacy_group_milestone? + [@milestone.milestones, legacy_milestone_params] + else + [[@milestone], milestone_params] + end + end + def authorize_admin_milestones! return render_404 unless can?(current_user, :admin_milestone, group) end @@ -79,6 +80,10 @@ class Groups::MilestonesController < Groups::ApplicationController params.require(:milestone).permit(:title, :description, :start_date, :due_date, :state_event) end + def legacy_milestone_params + params.require(:milestone).permit(:state_event) + end + def milestone_path if @milestone.legacy_group_milestone? group_milestone_path(group, @milestone.safe_title, title: @milestone.title) diff --git a/app/helpers/milestones_helper.rb b/app/helpers/milestones_helper.rb index 327b69e5110..50aec83b867 100644 --- a/app/helpers/milestones_helper.rb +++ b/app/helpers/milestones_helper.rb @@ -231,12 +231,15 @@ module MilestonesHelper end end - def group_or_dashboard_milestone_path(milestone) - if milestone.group_milestone? - group_milestone_path(milestone.group, milestone.iid, milestone: { title: milestone.title }) - else - dashboard_milestone_path(milestone.safe_title, title: milestone.title) - end + def group_or_project_milestone_path(milestone) + params = + if milestone.group_milestone? + { milestone: { title: milestone.title } } + else + { title: milestone.title } + end + + milestone_path(milestone.milestone, params) end def can_admin_project_milestones? diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index b128a254b96..aeb35538d67 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -221,6 +221,10 @@ module Ci next unless build.project build.deployment&.drop + end + + after_transition any => [:failed] do |build| + next unless build.project if build.retry_failure? begin diff --git a/app/models/clusters/applications/ingress.rb b/app/models/clusters/applications/ingress.rb index 8f8790585a3..7799f069742 100644 --- a/app/models/clusters/applications/ingress.rb +++ b/app/models/clusters/applications/ingress.rb @@ -23,7 +23,7 @@ module Clusters FETCH_IP_ADDRESS_DELAY = 30.seconds state_machine :status do - before_transition any => [:installed] do |application| + after_transition any => [:installed] do |application| application.run_after_commit do ClusterWaitForIngressIpAddressWorker.perform_in( FETCH_IP_ADDRESS_DELAY, application.name, application.id) diff --git a/app/models/clusters/applications/knative.rb b/app/models/clusters/applications/knative.rb index 0c72d7d8340..5ac152278da 100644 --- a/app/models/clusters/applications/knative.rb +++ b/app/models/clusters/applications/knative.rb @@ -20,7 +20,7 @@ module Clusters self.reactive_cache_key = ->(knative) { [knative.class.model_name.singular, knative.id] } state_machine :status do - before_transition any => [:installed] do |application| + after_transition any => [:installed] do |application| application.run_after_commit do ClusterWaitForIngressIpAddressWorker.perform_in( FETCH_IP_ADDRESS_DELAY, application.name, application.id) diff --git a/app/models/concerns/milestoneish.rb b/app/models/concerns/milestoneish.rb index e44a069b730..055ffe04646 100644 --- a/app/models/concerns/milestoneish.rb +++ b/app/models/concerns/milestoneish.rb @@ -42,7 +42,7 @@ module Milestoneish def issues_visible_to_user(user) memoize_per_user(user, :issues_visible_to_user) do IssuesFinder.new(user, issues_finder_params) - .execute.preload(:assignees).where(milestone_id: milestoneish_ids) + .execute.preload(:assignees).where(milestone_id: milestoneish_id) end end diff --git a/app/models/dashboard_group_milestone.rb b/app/models/dashboard_group_milestone.rb index ad0bb55f0a7..9bcc95e35a5 100644 --- a/app/models/dashboard_group_milestone.rb +++ b/app/models/dashboard_group_milestone.rb @@ -6,7 +6,7 @@ class DashboardGroupMilestone < GlobalMilestone attr_reader :group_name def initialize(milestone) - super(milestone.title, Array(milestone)) + super @group_name = milestone.group.full_name end @@ -18,22 +18,4 @@ class DashboardGroupMilestone < GlobalMilestone .active .map { |m| new(m) } end - - override :group_milestone? - def group_milestone? - @first_milestone.group_milestone? - end - - override :milestoneish_ids - def milestoneish_ids - milestones.map(&:id) - end - - def group - @first_milestone.group - end - - def iid - @first_milestone.iid - end end diff --git a/app/models/dashboard_milestone.rb b/app/models/dashboard_milestone.rb index 96bc8090b81..9b377b70e5b 100644 --- a/app/models/dashboard_milestone.rb +++ b/app/models/dashboard_milestone.rb @@ -1,11 +1,15 @@ # frozen_string_literal: true class DashboardMilestone < GlobalMilestone - def issues_finder_params - { authorized_only: true } + attr_reader :project_name + + def initialize(milestone) + super + + @project_name = milestone.project.full_name end - def dashboard_milestone? + def project_milestone? true end end diff --git a/app/models/global_milestone.rb b/app/models/global_milestone.rb index 085ffd16c6a..4e82f3fed27 100644 --- a/app/models/global_milestone.rb +++ b/app/models/global_milestone.rb @@ -3,69 +3,78 @@ class GlobalMilestone include Milestoneish - EPOCH = DateTime.parse('1970-01-01') STATE_COUNT_HASH = { opened: 0, closed: 0, all: 0 }.freeze - attr_accessor :title, :milestones + attr_reader :milestone alias_attribute :name, :title + delegate :title, :state, :due_date, :start_date, :participants, :project, :group, :expires_at, :closed?, :iid, :group_milestone?, :safe_title, :milestoneish_id, to: :milestone + + def to_hash + { + name: title, + title: title, + group_name: group&.full_name, + project_name: project&.full_name + } + end + def for_display - @first_milestone + @milestone end def self.build_collection(projects, params) - params = - { project_ids: projects.map(&:id), state: params[:state] } - - child_milestones = MilestonesFinder.new(params).execute # rubocop: disable CodeReuse/Finder - - milestones = child_milestones.select(:id, :title).group_by(&:title).map do |title, grouped| - milestones_relation = Milestone.where(id: grouped.map(&:id)) - new(title, milestones_relation) - end + items = Milestone.of_projects(projects) + .reorder_by_due_date_asc + .order_by_name_asc - milestones.sort_by { |milestone| milestone.due_date || EPOCH } + Milestone.filter_by_state(items, params[:state]).map { |m| new(m) } end + # necessary for legacy milestones def self.build(projects, title) - child_milestones = Milestone.of_projects(projects).where(title: title) - return if child_milestones.blank? + milestones = Milestone.of_projects(projects).where(title: title) + return if milestones.blank? - new(title, child_milestones) + new(milestones.first) end - def self.count_by_state(milestones_by_state_and_title, state) - milestones_by_state_and_title.count do |(milestone_state, _), _| - milestone_state == state + def self.states_count(projects, group = nil) + legacy_group_milestones_count = legacy_group_milestone_states_count(projects) + group_milestones_count = group_milestones_states_count(group) + + legacy_group_milestones_count.merge(group_milestones_count) do |k, legacy_group_milestones_count, group_milestones_count| + legacy_group_milestones_count + group_milestones_count end end - private_class_method :count_by_state - def initialize(title, milestones) - @title = title - @name = title - @milestones = milestones - @first_milestone = milestones.find {|m| m.description.present? } || milestones.first - end + def self.group_milestones_states_count(group) + return STATE_COUNT_HASH unless group - def milestoneish_ids - milestones.select(:id) - end + counts_by_state = Milestone.of_groups(group).count_by_state - def safe_title - @title.to_slug.normalize.to_s + { + opened: counts_by_state['active'] || 0, + closed: counts_by_state['closed'] || 0, + all: counts_by_state.values.sum + } end - def projects - @projects ||= Project.for_milestones(milestoneish_ids) - end + def self.legacy_group_milestone_states_count(projects) + return STATE_COUNT_HASH unless projects - def state - milestones.each do |milestone| - return 'active' if milestone.state != 'closed' - end + # We need to reorder(nil) on the projects, because the controller passes them in sorted. + relation = Milestone.of_projects(projects.reorder(nil)).count_by_state - 'closed' + { + opened: relation['active'] || 0, + closed: relation['closed'] || 0, + all: relation.values.sum + } + end + + def initialize(milestone) + @milestone = milestone end def active? @@ -77,37 +86,14 @@ class GlobalMilestone end def issues - @issues ||= Issue.of_milestones(milestoneish_ids).includes(:project, :assignees, :labels) + @issues ||= Issue.of_milestones(milestone).includes(:project, :assignees, :labels) end def merge_requests - @merge_requests ||= MergeRequest.of_milestones(milestoneish_ids).includes(:target_project, :assignee, :labels) - end - - def participants - @participants ||= milestones.map(&:participants).flatten.uniq + @merge_requests ||= MergeRequest.of_milestones(milestone).includes(:target_project, :assignee, :labels) end def labels - @labels ||= GlobalLabel.build_collection(milestones.includes(:labels).map(&:labels).flatten) - .sort_by!(&:title) - end - - def due_date - return @due_date if defined?(@due_date) - - @due_date = - if @milestones.all? { |x| x.due_date == @milestones.first.due_date } - @milestones.first.due_date - end - end - - def start_date - return @start_date if defined?(@start_date) - - @start_date = - if @milestones.all? { |x| x.start_date == @milestones.first.start_date } - @milestones.first.start_date - end + @labels ||= GlobalLabel.build_collection(milestone.labels).sort_by!(&:title) end end diff --git a/app/models/group_milestone.rb b/app/models/group_milestone.rb index 9dfaebacc83..a58537de319 100644 --- a/app/models/group_milestone.rb +++ b/app/models/group_milestone.rb @@ -1,18 +1,35 @@ # frozen_string_literal: true # Group Milestones are milestones that can be shared among many projects within the same group class GroupMilestone < GlobalMilestone - attr_accessor :group + attr_reader :group, :milestones def self.build_collection(group, projects, params) - super(projects, params).each do |milestone| - milestone.group = group + params = + { state: params[:state] } + + project_milestones = Milestone.of_projects(projects) + child_milestones = Milestone.filter_by_state(project_milestones, params[:state]) + grouped_milestones = child_milestones.group_by(&:title) + + grouped_milestones.map do |title, grouped| + new(title, grouped, group) end end def self.build(group, projects, title) - super(projects, title).tap do |milestone| - milestone&.group = group - end + child_milestones = Milestone.of_projects(projects).where(title: title) + return if child_milestones.blank? + + new(title, child_milestones, group) + end + + def initialize(title, milestones, group) + @milestones = milestones + @group = group + end + + def milestone + @milestone ||= milestones.find { |m| m.description.present? } || milestones.first end def issues_finder_params diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index b937bef100b..6092c56b925 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -105,7 +105,9 @@ class MergeRequest < ActiveRecord::Base before_transition any => :opened do |merge_request| merge_request.merge_jid = nil + end + after_transition any => :opened do |merge_request| merge_request.run_after_commit do UpdateHeadPipelineForMergeRequestWorker.perform_async(merge_request.id) end diff --git a/app/models/milestone.rb b/app/models/milestone.rb index 6dc0fca68e6..f55c39d9912 100644 --- a/app/models/milestone.rb +++ b/app/models/milestone.rb @@ -94,6 +94,10 @@ class Milestone < ActiveRecord::Base end end + def count_by_state + reorder(nil).group(:state).count + end + def predefined?(milestone) milestone == Any || milestone == None || @@ -215,7 +219,7 @@ class Milestone < ActiveRecord::Base self.class.reference_prefix + self.title end - def milestoneish_ids + def milestoneish_id id end diff --git a/app/models/project_services/kubernetes_service.rb b/app/models/project_services/kubernetes_service.rb index b801fd84a07..f69edd60003 100644 --- a/app/models/project_services/kubernetes_service.rb +++ b/app/models/project_services/kubernetes_service.rb @@ -53,7 +53,7 @@ class KubernetesService < DeploymentService end def description - 'Kubernetes / Openshift integration' + 'Kubernetes / OpenShift integration' end def help diff --git a/app/views/dashboard/milestones/_milestone.html.haml b/app/views/dashboard/milestones/_milestone.html.haml index b876d6fd1f3..89212eb6bf9 100644 --- a/app/views/dashboard/milestones/_milestone.html.haml +++ b/app/views/dashboard/milestones/_milestone.html.haml @@ -1,5 +1,5 @@ = render 'shared/milestones/milestone', - milestone_path: group_or_dashboard_milestone_path(milestone), + milestone_path: group_or_project_milestone_path(milestone), issues_path: issues_dashboard_path(milestone_title: milestone.title), merge_requests_path: merge_requests_dashboard_path(milestone_title: milestone.title), milestone: milestone, diff --git a/app/views/projects/services/prometheus/_metrics.html.haml b/app/views/projects/services/prometheus/_metrics.html.haml index 597c029f755..a1d74b91002 100644 --- a/app/views/projects/services/prometheus/_metrics.html.haml +++ b/app/views/projects/services/prometheus/_metrics.html.haml @@ -1,6 +1,6 @@ - project = local_assigns.fetch(:project) -.card.js-panel-monitored-metrics{ data: { active_metrics: active_common_project_prometheus_metrics_path(project, :json), metrics_help_path: help_page_path('user/project/integrations/prometheus_library/metrics') } } +.card.js-panel-monitored-metrics{ data: { active_metrics: active_common_project_prometheus_metrics_path(project, :json), metrics_help_path: help_page_path('user/project/integrations/prometheus_library/index') } } .card-header = s_('PrometheusService|Common metrics') %span.badge.badge-pill.js-monitored-count 0 diff --git a/app/views/projects/services/prometheus/_show.html.haml b/app/views/projects/services/prometheus/_show.html.haml index 1d0b0265bb7..9d4574c4590 100644 --- a/app/views/projects/services/prometheus/_show.html.haml +++ b/app/views/projects/services/prometheus/_show.html.haml @@ -4,7 +4,7 @@ = s_('PrometheusService|Metrics') %p = s_('PrometheusService|Common metrics are automatically monitored based on a library of metrics from popular exporters.') - = link_to s_('PrometheusService|More information'), help_page_path('user/project/integrations/prometheus_library/metrics'), target: '_blank', rel: "noopener noreferrer" + = link_to s_('PrometheusService|More information'), help_page_path('user/project/integrations/prometheus_library/index'), target: '_blank', rel: "noopener noreferrer" .col-lg-9 = render 'projects/services/prometheus/metrics', project: @project diff --git a/app/views/shared/milestones/_milestone.html.haml b/app/views/shared/milestones/_milestone.html.haml index ed7fefba56d..40b8374848e 100644 --- a/app/views/shared/milestones/_milestone.html.haml +++ b/app/views/shared/milestones/_milestone.html.haml @@ -1,5 +1,5 @@ - dashboard = local_assigns[:dashboard] -- custom_dom_id = dom_id(milestone.try(:milestones) ? milestone.milestones.first : milestone) +- custom_dom_id = dom_id(milestone.try(:milestone) ? milestone.milestone : milestone) - milestone_type = milestone.group_milestone? ? 'Group Milestone' : 'Project Milestone' %li{ class: "milestone milestone-#{milestone.closed? ? 'closed' : 'open'}", id: custom_dom_id } @@ -21,10 +21,12 @@ = milestone.group.full_name - if milestone.legacy_group_milestone? .projects - - milestone.milestones.each do |milestone| - = link_to milestone_path(milestone) do - %span.label-badge.label-badge-blue.d-inline-block.append-bottom-5 - = dashboard ? milestone.project.full_name : milestone.project.name + - link_to milestone_path(milestone.milestone) do + %span.label-badge.label-badge-blue.d-inline-block.append-bottom-5 + = dashboard ? milestone.project.full_name : milestone.project.name + - if milestone.project + .label-badge.label-badge-gray.d-inline-block + = milestone.project.full_name .col-sm-4.milestone-progress = milestone_progress_bar(milestone) @@ -58,5 +60,5 @@ - else = link_to 'Close Milestone', group_milestone_route(milestone, {state_event: :close }), method: :put, class: "btn btn-sm btn-grouped btn-close" - if dashboard - .status-box.status-box-milestone + .label-badge.label-badge-gray = milestone_type diff --git a/app/views/shared/milestones/_top.html.haml b/app/views/shared/milestones/_top.html.haml index 0499b04a482..55b1c14022f 100644 --- a/app/views/shared/milestones/_top.html.haml +++ b/app/views/shared/milestones/_top.html.haml @@ -62,20 +62,19 @@ %th Open issues %th State %th Due date - - milestone.milestones.each do |ms| %tr %td - - project_name = group ? ms.project.name : ms.project.full_name - = link_to project_name, project_milestone_path(ms.project, ms) + - project_name = group ? milestone.project.name : milestone.project.full_name + = link_to project_name, milestone_path(milestone.milestone) %td - = ms.issues_visible_to_user(current_user).opened.count + = milestone.milestone.issues_visible_to_user(current_user).opened.count %td - - if ms.closed? + - if milestone.closed? Closed - else Open %td - = ms.expires_at + = milestone.expires_at - elsif milestone.group_milestone? %br View diff --git a/changelogs/unreleased/51668-fix-line-numbers.yml b/changelogs/unreleased/51668-fix-line-numbers.yml deleted file mode 100644 index ac6e45e3cc7..00000000000 --- a/changelogs/unreleased/51668-fix-line-numbers.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Adjust line-height of blame view line numbers -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/54844-report-syntax-dep-scan-ado.yml b/changelogs/unreleased/54844-report-syntax-dep-scan-ado.yml new file mode 100644 index 00000000000..95fc5cb804d --- /dev/null +++ b/changelogs/unreleased/54844-report-syntax-dep-scan-ado.yml @@ -0,0 +1,5 @@ +--- +title: Use reports syntax for Dependency scanning in Auto DevOps +merge_request: 24081 +author: +type: added diff --git a/changelogs/unreleased/54953-fix-commit_email_hostname-accessor-in-fake_application_settings.yml b/changelogs/unreleased/54953-fix-commit_email_hostname-accessor-in-fake_application_settings.yml deleted file mode 100644 index 623b3a7319c..00000000000 --- a/changelogs/unreleased/54953-fix-commit_email_hostname-accessor-in-fake_application_settings.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix a 500 error that could occur until all migrations are done -merge_request: 23939 -author: -type: fixed diff --git a/changelogs/unreleased/55103-hide-group-cluster-features.yml b/changelogs/unreleased/55103-hide-group-cluster-features.yml deleted file mode 100644 index fbe780d6f01..00000000000 --- a/changelogs/unreleased/55103-hide-group-cluster-features.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Hide cluster features that don't work yet with Group Clusters -merge_request: 23935 -author: -type: fixed diff --git a/changelogs/unreleased/55836-docs-fix-navigation-style-in-docs.yml b/changelogs/unreleased/55836-docs-fix-navigation-style-in-docs.yml new file mode 100644 index 00000000000..2ac3599175b --- /dev/null +++ b/changelogs/unreleased/55836-docs-fix-navigation-style-in-docs.yml @@ -0,0 +1,5 @@ +--- +title: Fix navigation style in docs +merge_request: 24090 +author: Takuya Noguchi +type: other diff --git a/changelogs/unreleased/allow-basic-auth-on-go-get-middleware.yml b/changelogs/unreleased/allow-basic-auth-on-go-get-middleware.yml new file mode 100644 index 00000000000..fda3fdc28cf --- /dev/null +++ b/changelogs/unreleased/allow-basic-auth-on-go-get-middleware.yml @@ -0,0 +1,5 @@ +--- +title: Allow basic authentication on go get middleware +merge_request: 23497 +author: Morty Choi @mortyccp +type: changed diff --git a/changelogs/unreleased/ccr-49289_milestone_link.yml b/changelogs/unreleased/ccr-49289_milestone_link.yml new file mode 100644 index 00000000000..14c09752a24 --- /dev/null +++ b/changelogs/unreleased/ccr-49289_milestone_link.yml @@ -0,0 +1,5 @@ +--- +title: Add project milestone link +merge_request: 22552 +author: +type: added diff --git a/changelogs/unreleased/deprecated-insert-sql.yml b/changelogs/unreleased/deprecated-insert-sql.yml new file mode 100644 index 00000000000..ad21fbd9dde --- /dev/null +++ b/changelogs/unreleased/deprecated-insert-sql.yml @@ -0,0 +1,5 @@ +--- +title: 'Fix deprecation: insert_sql is deprecated and will be removed' +merge_request: 23944 +author: Jasper Maes +type: other diff --git a/changelogs/unreleased/deprecated-positional-spec-arguments.yml b/changelogs/unreleased/deprecated-positional-spec-arguments.yml new file mode 100644 index 00000000000..8e541df1ad4 --- /dev/null +++ b/changelogs/unreleased/deprecated-positional-spec-arguments.yml @@ -0,0 +1,5 @@ +--- +title: 'Fix deprecation: Using positional arguments in integration tests' +merge_request: 24110 +author: Jasper Maes +type: other diff --git a/changelogs/unreleased/fj-55781-fix-api-blob-content-disposition.yml b/changelogs/unreleased/fj-55781-fix-api-blob-content-disposition.yml deleted file mode 100644 index 2e1d9889b22..00000000000 --- a/changelogs/unreleased/fj-55781-fix-api-blob-content-disposition.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix content-disposition in blobs and files API endpoint -merge_request: 24078 -author: -type: fixed diff --git a/changelogs/unreleased/markdown-toolbar-btn-fix.yml b/changelogs/unreleased/markdown-toolbar-btn-fix.yml deleted file mode 100644 index eefb4d19f86..00000000000 --- a/changelogs/unreleased/markdown-toolbar-btn-fix.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fixed markdown toolbar buttons -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/sh-fix-clone-geo-dropdown.yml b/changelogs/unreleased/sh-fix-clone-geo-dropdown.yml deleted file mode 100644 index 1c0cbdc3a2c..00000000000 --- a/changelogs/unreleased/sh-fix-clone-geo-dropdown.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix clone dropdown parent inheritance issues in HAML -merge_request: 24029 -author: -type: fixed diff --git a/changelogs/unreleased/sh-fix-http-clone-panel.yml b/changelogs/unreleased/sh-fix-http-clone-panel.yml deleted file mode 100644 index ab220bd5076..00000000000 --- a/changelogs/unreleased/sh-fix-http-clone-panel.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix missing Git clone button when protocol restriction setting enabled -merge_request: 24015 -author: -type: fixed diff --git a/db/migrate/20180413022611_create_missing_namespace_for_internal_users.rb b/db/migrate/20180413022611_create_missing_namespace_for_internal_users.rb index 90d869a8c10..8de8b3bcc2e 100644 --- a/db/migrate/20180413022611_create_missing_namespace_for_internal_users.rb +++ b/db/migrate/20180413022611_create_missing_namespace_for_internal_users.rb @@ -46,7 +46,7 @@ class CreateMissingNamespaceForInternalUsers < ActiveRecord::Migration[4.2] end insert_query = "INSERT INTO namespaces(owner_id, path, name, created_at, updated_at) VALUES(#{user_id}, '#{path}', '#{path}', NOW(), NOW())" - namespace_id = connection.insert_sql(insert_query) + namespace_id = connection.insert(insert_query) create_route(namespace_id) end diff --git a/doc/administration/job_artifacts.md b/doc/administration/job_artifacts.md index a1ea78b64bd..25ae535d1ec 100644 --- a/doc/administration/job_artifacts.md +++ b/doc/administration/job_artifacts.md @@ -165,12 +165,6 @@ _The artifacts are stored by default in gitlab-rake gitlab:artifacts:migrate ``` - Currently this has to be executed manually and it will allow you to - migrate the existing artifacts to the object storage, but all new - artifacts will still be stored on the local disk. In the future - you will be given an option to define a default storage artifacts for all - new files. - --- **In installations from source:** @@ -201,12 +195,6 @@ _The artifacts are stored by default in sudo -u git -H bundle exec rake gitlab:artifacts:migrate RAILS_ENV=production ``` - Currently this has to be executed manually and it will allow you to - migrate the existing artifacts to the object storage, but all new - artifacts will still be stored on the local disk. In the future - you will be given an option to define a default storage artifacts for all - new files. - ## Expiring artifacts If an expiry date is used for the artifacts, they are marked for deletion diff --git a/doc/administration/monitoring/performance/request_profiling.md b/doc/administration/monitoring/performance/request_profiling.md index dfd9be3d04c..726882fbb87 100644 --- a/doc/administration/monitoring/performance/request_profiling.md +++ b/doc/administration/monitoring/performance/request_profiling.md @@ -2,14 +2,14 @@ ## Procedure -1. Grab the profiling token from `Monitoring > Requests Profiles` admin page +1. Grab the profiling token from **Monitoring > Requests Profiles** admin page (highlighted in a blue in the image below). ![Profile token](img/request_profiling_token.png) 1. Pass the header `X-Profile-Token: <token>` to the request you want to profile. You can use: - Browser extensions. For example, [ModHeader](https://chrome.google.com/webstore/detail/modheader/idgpnmonknjnojddfkpgkljpfnnfcklj) Chrome extension. - `curl`. For example, `curl --header 'X-Profile-Token: <token>' https://gitlab.example.com/group/project`. 1. Once request is finished (which will take a little longer than usual), you can - view the profiling output from `Monitoring > Requests Profiles` admin page. + view the profiling output from **Monitoring > Requests Profiles** admin page. ![Profiling output](img/request_profile_result.png) ## Cleaning up diff --git a/doc/api/services.md b/doc/api/services.md index f122bac6f1f..c4edaa17815 100644 --- a/doc/api/services.md +++ b/doc/api/services.md @@ -573,7 +573,7 @@ DELETE /projects/:id/services/jira ## Kubernetes -Kubernetes / Openshift integration +Kubernetes / OpenShift integration CAUTION: **Warning:** Kubernetes service integration has been deprecated in GitLab 10.3. API service endpoints will continue to work as long as the Kubernetes service is active, however if the service is inactive API endpoints will automatically return a `400 Bad Request`. Read [GitLab 10.3 release post](https://about.gitlab.com/2017/12/22/gitlab-10-3-released/#kubernetes-integration-service) for more information. diff --git a/doc/ci/examples/test-and-deploy-python-application-to-heroku.md b/doc/ci/examples/test-and-deploy-python-application-to-heroku.md index ec0b5aaed09..b59271e400f 100644 --- a/doc/ci/examples/test-and-deploy-python-application-to-heroku.md +++ b/doc/ci/examples/test-and-deploy-python-application-to-heroku.md @@ -47,7 +47,7 @@ This project has three jobs: ## Store API keys -You'll need to create two variables in `Settings > CI/CD > Variables` on your GitLab project settings: +You'll need to create two variables in **Settings > CI/CD > Variables** in your GitLab project: - `HEROKU_STAGING_API_KEY` - Heroku API key used to deploy staging app. - `HEROKU_PRODUCTION_API_KEY` - Heroku API key used to deploy production app. diff --git a/doc/install/openshift_and_gitlab/index.md b/doc/install/openshift_and_gitlab/index.md index 4c88b6f97fc..25a87dbd9de 100644 --- a/doc/install/openshift_and_gitlab/index.md +++ b/doc/install/openshift_and_gitlab/index.md @@ -18,7 +18,7 @@ In this tutorial, we will see how to deploy GitLab in OpenShift using GitLab's official Docker image while getting familiar with the web interface and CLI tools that will help us achieve our goal. -For a video demonstration on installing GitLab on Openshift, check the article [In 13 minutes from Kubernetes to a complete application development tool](https://about.gitlab.com/2016/11/14/idea-to-production/). +For a video demonstration on installing GitLab on OpenShift, check the article [In 13 minutes from Kubernetes to a complete application development tool](https://about.gitlab.com/2016/11/14/idea-to-production/). --- @@ -518,7 +518,7 @@ PaaS and managing your applications with the ease of containers. [templates]: https://docs.openshift.org/latest/architecture/core_concepts/templates.html "Documentation - OpenShift templates" [old-post]: https://blog.openshift.com/deploy-gitlab-openshift/ "Old post - Deploy GitLab on OpenShift" [line]: https://gitlab.com/gitlab-org/omnibus-gitlab/blob/658c065c8d022ce858dd63eaeeadb0b2ddc8deea/docker/openshift-template.json#L239 "GitLab - OpenShift template" -[oc-gh]: https://github.com/openshift/origin/releases/tag/v1.3.0 "Openshift 1.3.0 release on GitHub" +[oc-gh]: https://github.com/openshift/origin/releases/tag/v1.3.0 "OpenShift Origin 1.3.0 release on GitHub" [ha]: ../../administration/high_availability/gitlab.html "Documentation - GitLab High Availability" [replicas]: https://docs.openshift.org/latest/architecture/core_concepts/deployments.html#replication-controllers "Documentation - Replication controller" [autoscaling]: https://docs.openshift.org/latest/dev_guide/pod_autoscaling.html "Documentation - Autoscale" diff --git a/doc/topics/authentication/index.md b/doc/topics/authentication/index.md index 766a23c419d..a354d3e7884 100644 --- a/doc/topics/authentication/index.md +++ b/doc/topics/authentication/index.md @@ -45,4 +45,4 @@ This page gathers all the resources for the topic **Authentication** within GitL - [Kanboard Plugin GitLab Authentication](https://github.com/kanboard/plugin-gitlab-auth) - [Jenkins GitLab OAuth Plugin](https://wiki.jenkins.io/display/JENKINS/GitLab+OAuth+Plugin) - [How to customize GitLab to support OpenID authentication](http://eric.van-der-vlist.com/blog/2013/11/23/how-to-customize-gitlab-to-support-openid-authentication/) -- [Openshift - Configuring Authentication and User Agent](https://docs.openshift.org/latest/install_config/configuring_authentication.html#GitLab) +- [OKD - Configuring Authentication and User Agent](https://docs.okd.io/latest/install_config/configuring_authentication.html#GitLab) diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md index b41f401e14c..e937d372384 100644 --- a/doc/topics/autodevops/index.md +++ b/doc/topics/autodevops/index.md @@ -340,6 +340,9 @@ Any security warnings are also NOTE: **Note:** The Auto SAST stage will be skipped on licenses other than Ultimate. +NOTE: **Note:** +The Auto SAST job requires GitLab Runner 11.5 or above. + ### Auto Dependency Scanning **[ULTIMATE]** > Introduced in [GitLab Ultimate][ee] 10.7. @@ -356,6 +359,9 @@ Any security warnings are also NOTE: **Note:** The Auto Dependency Scanning stage will be skipped on licenses other than Ultimate. +NOTE: **Note:** +The Auto Dependency Scanning job requires GitLab Runner 11.5 or above. + ### Auto License Management **[ULTIMATE]** > Introduced in [GitLab Ultimate][ee] 11.0. diff --git a/doc/user/project/integrations/prometheus.md b/doc/user/project/integrations/prometheus.md index d9a2eeb32ae..5de8e66e7eb 100644 --- a/doc/user/project/integrations/prometheus.md +++ b/doc/user/project/integrations/prometheus.md @@ -30,7 +30,7 @@ GitLab can seamlessly deploy and manage Prometheus on a [connected Kubernetes cl Once you have a connected Kubernetes cluster with Helm installed, deploying a managed Prometheus is as easy as a single click. -1. Go to the `Operations > Kubernetes` page, to view your connected clusters +1. Go to the **Operations > Kubernetes** page to view your connected clusters 1. Select the cluster you would like to deploy Prometheus to 1. Click the **Install** button to deploy Prometheus to the cluster @@ -88,7 +88,7 @@ to integrate with. Once configured, GitLab will attempt to retrieve performance metrics for any environment which has had a successful deployment. -GitLab will automatically scan the Prometheus server for metrics from known serves like Kubernetes and NGINX, and attempt to identify individual environment. The supported metrics and scan process is detailed in our [Prometheus Metric Library documentation](prometheus_library/metrics.html). +GitLab will automatically scan the Prometheus server for metrics from known serves like Kubernetes and NGINX, and attempt to identify individual environment. The supported metrics and scan process is detailed in our [Prometheus Metric Library documentation](prometheus_library/index.md). You can view the performance dashboard for an environment by [clicking on the monitoring button](../../../ci/environments.md#monitoring-environments). diff --git a/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml b/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml index a9e361b0b32..8f6cf8d2d03 100644 --- a/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml @@ -185,7 +185,8 @@ dependency_scanning: - setup_docker - dependency_scanning artifacts: - paths: [gl-dependency-scanning-report.json] + reports: + dependency_scanning: gl-dependency-scanning-report.json only: refs: - branches diff --git a/lib/gitlab/middleware/go.rb b/lib/gitlab/middleware/go.rb index d1a87c3b3bb..72a788022ef 100644 --- a/lib/gitlab/middleware/go.rb +++ b/lib/gitlab/middleware/go.rb @@ -6,6 +6,7 @@ module Gitlab module Middleware class Go include ActionView::Helpers::TagHelper + include ActionController::HttpAuthentication::Basic PROJECT_PATH_REGEX = %r{\A(#{Gitlab::PathRegex.full_namespace_route_regex}/#{Gitlab::PathRegex.project_route_regex})/}.freeze @@ -14,7 +15,7 @@ module Gitlab end def call(env) - request = Rack::Request.new(env) + request = ActionDispatch::Request.new(env) render_go_doc(request) || @app.call(env) end @@ -110,21 +111,23 @@ module Gitlab def project_for_paths(paths, request) project = Project.where_full_path_in(paths).first - return unless Ability.allowed?(current_user(request), :read_project, project) + return unless Ability.allowed?(current_user(request, project), :read_project, project) project end - def current_user(request) - authenticator = Gitlab::Auth::RequestAuthenticator.new(request) - user = authenticator.find_user_from_access_token || authenticator.find_user_from_warden + def current_user(request, project) + return unless has_basic_credentials?(request) - return unless user&.can?(:access_api) + login, password = user_name_and_password(request) + auth_result = Gitlab::Auth.find_for_git_client(login, password, project: project, ip: request.ip) + return unless auth_result.success? - # Right now, the `api` scope is the only one that should be able to determine private project existence. - return unless authenticator.valid_access_token?(scopes: [:api]) + return unless auth_result.actor&.can?(:access_git) - user + return unless auth_result.authentication_abilities.include?(:read_project) + + auth_result.actor end end end diff --git a/qa/Gemfile b/qa/Gemfile index d69c71003ae..75ad7bd07af 100644 --- a/qa/Gemfile +++ b/qa/Gemfile @@ -5,5 +5,6 @@ gem 'capybara', '~> 2.16.1' gem 'capybara-screenshot', '~> 1.0.18' gem 'rake', '~> 12.3.0' gem 'rspec', '~> 3.7' -gem 'selenium-webdriver', '~> 3.8.0' +gem 'selenium-webdriver', '~> 3.12' gem 'airborne', '~> 0.2.13' +gem 'nokogiri', '~> 1.8.5' diff --git a/qa/Gemfile.lock b/qa/Gemfile.lock index d61ecf8fbb5..55f3211482b 100644 --- a/qa/Gemfile.lock +++ b/qa/Gemfile.lock @@ -25,7 +25,7 @@ GEM capybara-screenshot (1.0.18) capybara (>= 1.0, < 3) launchy - childprocess (0.8.0) + childprocess (0.9.0) ffi (~> 1.0, >= 1.0.11) coderay (1.1.2) concurrent-ruby (1.0.5) @@ -47,7 +47,7 @@ GEM mini_portile2 (2.3.0) minitest (5.11.1) netrc (0.11.0) - nokogiri (1.8.2) + nokogiri (1.8.5) mini_portile2 (~> 2.3.0) pry (0.11.3) coderay (~> 1.1.0) @@ -78,9 +78,9 @@ GEM rspec-support (~> 3.7.0) rspec-support (3.7.0) rubyzip (1.2.2) - selenium-webdriver (3.8.0) + selenium-webdriver (3.141.0) childprocess (~> 0.5) - rubyzip (~> 1.0) + rubyzip (~> 1.2, >= 1.2.2) thread_safe (0.3.6) tzinfo (1.2.4) thread_safe (~> 0.1) @@ -97,10 +97,11 @@ DEPENDENCIES airborne (~> 0.2.13) capybara (~> 2.16.1) capybara-screenshot (~> 1.0.18) + nokogiri (~> 1.8.5) pry-byebug (~> 3.5.1) rake (~> 12.3.0) rspec (~> 3.7) - selenium-webdriver (~> 3.8.0) + selenium-webdriver (~> 3.12) BUNDLED WITH - 1.17.1 + 1.17.3 @@ -283,6 +283,7 @@ module QA autoload :Select2, 'qa/page/component/select2' autoload :DropdownFilter, 'qa/page/component/dropdown_filter' autoload :UsersSelect, 'qa/page/component/users_select' + autoload :Note, 'qa/page/component/note' module Issuable autoload :Common, 'qa/page/component/issuable/common' diff --git a/qa/qa/page/component/note.rb b/qa/qa/page/component/note.rb new file mode 100644 index 00000000000..67d7f114786 --- /dev/null +++ b/qa/qa/page/component/note.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +module QA + module Page + module Component + module Note + def self.included(base) + base.view 'app/assets/javascripts/notes/components/comment_form.vue' do + element :note_dropdown + element :discussion_option + end + + base.view 'app/assets/javascripts/notes/components/note_form.vue' do + element :reply_input + element :reply_comment_button + end + + base.view 'app/assets/javascripts/notes/components/noteable_discussion.vue' do + element :discussion_reply + end + + base.view 'app/assets/javascripts/notes/components/toggle_replies_widget.vue' do + element :expand_replies + element :collapse_replies + end + end + + def start_discussion(text) + fill_element :comment_input, text + click_element :note_dropdown + click_element :discussion_option + click_element :comment_button + end + + def reply_to_discussion(reply_text) + all_elements(:discussion_reply).last.click + fill_element :reply_input, reply_text + click_element :reply_comment_button + end + + def collapse_replies + click_element :collapse_replies + end + + def expand_replies + click_element :expand_replies + end + end + end + end +end diff --git a/qa/qa/page/merge_request/show.rb b/qa/qa/page/merge_request/show.rb index 869dc0b9d21..4f21ed602d9 100644 --- a/qa/qa/page/merge_request/show.rb +++ b/qa/qa/page/merge_request/show.rb @@ -4,6 +4,8 @@ module QA module Page module MergeRequest class Show < Page::Base + include Page::Component::Note + view 'app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue' do element :merge_button element :fast_forward_message, 'Fast-forward merge without a merge commit' # rubocop:disable QA/ElementWithPattern @@ -34,19 +36,6 @@ module QA element :diff_comment end - view 'app/assets/javascripts/notes/components/comment_form.vue' do - element :note_dropdown - element :discussion_option - end - - view 'app/assets/javascripts/notes/components/note_form.vue' do - element :reply_input - end - - view 'app/assets/javascripts/notes/components/noteable_discussion.vue' do - element :discussion_reply - end - view 'app/assets/javascripts/diffs/components/inline_diff_table_row.vue' do element :new_diff_line end @@ -163,18 +152,6 @@ module QA fill_element :reply_input, text end - def start_discussion(text) - fill_element :comment_input, text - click_element :note_dropdown - click_element :discussion_option - click_element :comment_button - end - - def reply_to_discussion(reply_text) - all_elements(:discussion_reply).last.click - fill_element :reply_input, reply_text - end - def edit! click_element :edit_button end diff --git a/qa/qa/page/project/issue/show.rb b/qa/qa/page/project/issue/show.rb index 9ec6d90719e..1028cc045a0 100644 --- a/qa/qa/page/project/issue/show.rb +++ b/qa/qa/page/project/issue/show.rb @@ -6,6 +6,7 @@ module QA module Issue class Show < Page::Base include Page::Component::Issuable::Common + include Page::Component::Note view 'app/views/shared/notes/_form.html.haml' do element :new_note_form, 'new-note' # rubocop:disable QA/ElementWithPattern diff --git a/qa/qa/specs/features/browser_ui/2_plan/issue/collapse_comments_in_discussions_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/issue/collapse_comments_in_discussions_spec.rb new file mode 100644 index 00000000000..fa779bd1f4e --- /dev/null +++ b/qa/qa/specs/features/browser_ui/2_plan/issue/collapse_comments_in_discussions_spec.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +module QA + context 'Plan' do + describe 'collapse comments in issue discussions' do + let(:issue_title) { 'issue title' } + + it 'user collapses reply for comments in an issue' do + Runtime::Browser.visit(:gitlab, Page::Main::Login) + Page::Main::Login.perform(&:sign_in_using_credentials) + + Resource::Issue.fabricate! do |issue| + issue.title = issue_title + end + + expect(page).to have_content(issue_title) + + Page::Project::Issue::Show.perform do |show_page| + show_page.select_all_activities_filter + show_page.start_discussion("My first discussion") + expect(show_page).to have_content("My first discussion") + + show_page.reply_to_discussion("My First Reply") + expect(show_page).to have_content("My First Reply") + + show_page.collapse_replies + expect(show_page).to have_content("1 reply") + expect(show_page).not_to have_content("My First Reply") + + show_page.expand_replies + expect(show_page).to have_content("My First Reply") + expect(show_page).not_to have_content("1 reply") + end + end + end + end +end diff --git a/spec/controllers/dashboard/milestones_controller_spec.rb b/spec/controllers/dashboard/milestones_controller_spec.rb index 8a8cc14fd4c..c9ccd5f7c55 100644 --- a/spec/controllers/dashboard/milestones_controller_spec.rb +++ b/spec/controllers/dashboard/milestones_controller_spec.rb @@ -52,7 +52,7 @@ describe Dashboard::MilestonesController do expect(response).to have_gitlab_http_status(200) expect(json_response.size).to eq(2) - expect(json_response.map { |i| i["first_milestone"]["id"] }).to match_array([group_milestone.id, project_milestone.id]) + expect(json_response.map { |i| i["name"] }).to match_array([group_milestone.name, project_milestone.name]) expect(json_response.map { |i| i["group_name"] }.compact).to match_array(group.name) end diff --git a/spec/controllers/groups/milestones_controller_spec.rb b/spec/controllers/groups/milestones_controller_spec.rb index b8e1e08cff7..40d991a669c 100644 --- a/spec/controllers/groups/milestones_controller_spec.rb +++ b/spec/controllers/groups/milestones_controller_spec.rb @@ -64,7 +64,7 @@ describe Groups::MilestonesController do context 'when there is a title parameter' do it 'searches for a legacy group milestone' do - expect(GlobalMilestone).to receive(:build) + expect(GroupMilestone).to receive(:build) expect(Milestone).not_to receive(:find_by_iid) get :show, params: { group_id: group.to_param, id: title, title: milestone1.safe_title } diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb index d46b9ffb3ce..4f4d3ca226f 100644 --- a/spec/controllers/projects/merge_requests_controller_spec.rb +++ b/spec/controllers/projects/merge_requests_controller_spec.rb @@ -950,7 +950,7 @@ describe Projects::MergeRequestsController do end it 'returns 200' do - get :discussions, namespace_id: project.namespace, project_id: project, id: merge_request.iid + get :discussions, params: { namespace_id: project.namespace, project_id: project, id: merge_request.iid } expect(response.status).to eq(200) end @@ -969,7 +969,7 @@ describe Projects::MergeRequestsController do expect(collection).to receive(:find_by_id).with(note_diff_file.id).and_call_original end - get :discussions, namespace_id: project.namespace, project_id: project, id: merge_request.iid + get :discussions, params: { namespace_id: project.namespace, project_id: project, id: merge_request.iid } end end @@ -986,7 +986,7 @@ describe Projects::MergeRequestsController do expect(collection).to receive(:find_by_id).with(note_diff_file.id).and_call_original end - get :discussions, namespace_id: project.namespace, project_id: project, id: merge_request.iid + get :discussions, params: { namespace_id: project.namespace, project_id: project, id: merge_request.iid } end it 'does not preload highlights when diff note is resolved' do @@ -999,7 +999,7 @@ describe Projects::MergeRequestsController do expect(collection).to receive(:find_by_id).with(note_diff_file.id).and_call_original end - get :discussions, namespace_id: project.namespace, project_id: project, id: merge_request.iid + get :discussions, params: { namespace_id: project.namespace, project_id: project, id: merge_request.iid } end end end diff --git a/spec/controllers/projects/snippets_controller_spec.rb b/spec/controllers/projects/snippets_controller_spec.rb index e4b78aff25d..75c9839dd9b 100644 --- a/spec/controllers/projects/snippets_controller_spec.rb +++ b/spec/controllers/projects/snippets_controller_spec.rb @@ -385,7 +385,7 @@ describe Projects::SnippetsController do before do sign_in(user) - get :show, namespace_id: project.namespace, project_id: project, id: project_snippet.to_param, format: :js + get :show, params: { namespace_id: project.namespace, project_id: project, id: project_snippet.to_param }, format: :js end context 'when snippet is private' do diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb index 4747d837273..f84f069f4db 100644 --- a/spec/controllers/projects_controller_spec.rb +++ b/spec/controllers/projects_controller_spec.rb @@ -667,7 +667,7 @@ describe ProjectsController do project.add_guest(user) sign_in(user) - get :refs, namespace_id: project.namespace, id: project + get :refs, params: { namespace_id: project.namespace, id: project } expect(response).to have_gitlab_http_status(404) end diff --git a/spec/controllers/snippets_controller_spec.rb b/spec/controllers/snippets_controller_spec.rb index d762531da7e..5c6858dc7b2 100644 --- a/spec/controllers/snippets_controller_spec.rb +++ b/spec/controllers/snippets_controller_spec.rb @@ -82,7 +82,7 @@ describe SnippetsController do end it 'responds with status 404 when embeddable content is requested' do - get :show, id: personal_snippet.to_param, format: :js + get :show, params: { id: personal_snippet.to_param }, format: :js expect(response).to have_gitlab_http_status(404) end @@ -114,7 +114,7 @@ describe SnippetsController do end it 'responds with status 404 when embeddable content is requested' do - get :show, id: personal_snippet.to_param, format: :js + get :show, params: { id: personal_snippet.to_param }, format: :js expect(response).to have_gitlab_http_status(404) end @@ -145,7 +145,7 @@ describe SnippetsController do end it 'responds with status 200 when embeddable content is requested' do - get :show, id: personal_snippet.to_param, format: :js + get :show, params: { id: personal_snippet.to_param }, format: :js expect(assigns(:snippet)).to eq(personal_snippet) expect(response).to have_gitlab_http_status(200) diff --git a/spec/features/groups/milestone_spec.rb b/spec/features/groups/milestone_spec.rb index 174840794ed..d57eb87ca77 100644 --- a/spec/features/groups/milestone_spec.rb +++ b/spec/features/groups/milestone_spec.rb @@ -81,7 +81,7 @@ describe 'Group milestones' do description: 'Lorem Ipsum is simply dummy text' ) end - let!(:active_project_milestone2) { create(:milestone, project: other_project, state: 'active', title: 'v1.0') } + let!(:active_project_milestone2) { create(:milestone, project: other_project, state: 'active', title: 'v1.1') } let!(:closed_project_milestone1) { create(:milestone, project: project, state: 'closed', title: 'v2.0') } let!(:closed_project_milestone2) { create(:milestone, project: other_project, state: 'closed', title: 'v2.0') } let!(:active_group_milestone) { create(:milestone, group: group, state: 'active', title: 'GL-113') } @@ -104,7 +104,7 @@ describe 'Group milestones' do legacy_milestone = GroupMilestone.build_collection(group, group.projects, { state: 'active' }).first expect(page).to have_selector("#milestone_#{active_group_milestone.id}", count: 1) - expect(page).to have_selector("#milestone_#{legacy_milestone.milestones.first.id}", count: 1) + expect(page).to have_selector("#milestone_#{legacy_milestone.milestone.id}", count: 1) end it 'shows milestone detail and supports its edit' do @@ -121,6 +121,7 @@ describe 'Group milestones' do it 'renders milestones' do expect(page).to have_content('v1.0') + expect(page).to have_content('v1.1') expect(page).to have_content('GL-113') expect(page).to have_link( '1 Issue', diff --git a/spec/features/groups/milestones_sorting_spec.rb b/spec/features/groups/milestones_sorting_spec.rb index bc226ff41c1..7bc015ea28f 100644 --- a/spec/features/groups/milestones_sorting_spec.rb +++ b/spec/features/groups/milestones_sorting_spec.rb @@ -42,6 +42,7 @@ describe 'Milestones sorting', :js do expect(page).to have_button('Due later') + # assert descending sorting within '.milestones' do expect(page.all('ul.content-list > li').first.text).to include('v1.0') expect(page.all('ul.content-list > li')[1].text).to include('v3.0') diff --git a/spec/lib/gitlab/middleware/go_spec.rb b/spec/lib/gitlab/middleware/go_spec.rb index 7a3a9ab875b..f52095bf633 100644 --- a/spec/lib/gitlab/middleware/go_spec.rb +++ b/spec/lib/gitlab/middleware/go_spec.rb @@ -96,43 +96,36 @@ describe Gitlab::Middleware::Go do it_behaves_like 'unauthorized' end - end - - context 'using warden' do - before do - env['warden'] = double(authenticate: current_user) - end - context 'when active' do - it_behaves_like 'authenticated' - end - - context 'when blocked' do + context 'with user is blocked' do before do - current_user.block! + current_user.block end it_behaves_like 'unauthorized' end end - context 'using a personal access token' do - let(:personal_access_token) { create(:personal_access_token, user: current_user) } - - before do - env['HTTP_PRIVATE_TOKEN'] = personal_access_token.token - end - - context 'with api scope' do - it_behaves_like 'authenticated' - end + context 'using basic auth' do + context 'using a personal access token' do + let(:personal_access_token) { create(:personal_access_token, user: current_user) } - context 'with read_user scope' do before do - personal_access_token.update_attribute(:scopes, [:read_user]) + env['REMOTE_ADDR'] = "192.168.0.1" + env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials(current_user.username, personal_access_token.token) end - it_behaves_like 'unauthorized' + context 'with api scope' do + it_behaves_like 'authenticated' + end + + context 'with read_user scope' do + before do + personal_access_token.update_attribute(:scopes, [:read_user]) + end + + it_behaves_like 'unauthorized' + end end end end diff --git a/spec/models/global_milestone_spec.rb b/spec/models/global_milestone_spec.rb index b6355455c1d..62699df5611 100644 --- a/spec/models/global_milestone_spec.rb +++ b/spec/models/global_milestone_spec.rb @@ -65,56 +65,103 @@ describe GlobalMilestone do ) end - before do - projects = [ + let!(:projects) do + [ project1, project2, project3 ] - - @global_milestones = described_class.build_collection(projects, {}) end - it 'has all project milestones' do - expect(@global_milestones.count).to eq(2) + let!(:global_milestones) { described_class.build_collection(projects, {}) } + + context 'when building a collection of milestones' do + it 'has all project milestones' do + expect(global_milestones.count).to eq(6) + end + + it 'has all project milestones titles' do + expect(global_milestones.map(&:title)).to match_array(['Milestone v1.2', 'Milestone v1.2', 'Milestone v1.2', 'VD-123', 'VD-123', 'VD-123']) + end + + it 'has all project milestones' do + expect(global_milestones.size).to eq(6) + end + + it 'sorts collection by due date' do + expect(global_milestones.map(&:due_date)).to eq [milestone1_due_date, milestone1_due_date, milestone1_due_date, nil, nil, nil] + end end - it 'has all project milestones titles' do - expect(@global_milestones.map(&:title)).to match_array(['Milestone v1.2', 'VD-123']) + context 'when adding new milestones' do + it 'does not add more queries' do + control_count = ActiveRecord::QueryRecorder.new do + described_class.build_collection(projects, {}) + end.count + + create_list(:milestone, 3, project: project3) + + expect do + described_class.build_collection(projects, {}) + end.not_to exceed_all_query_limit(control_count) + end end + end + + describe '.states_count' do + context 'when the projects have milestones' do + before do + create(:closed_milestone, title: 'Active Group Milestone', project: project3) + create(:active_milestone, title: 'Active Group Milestone', project: project1) + create(:active_milestone, title: 'Active Group Milestone', project: project2) + create(:closed_milestone, title: 'Closed Group Milestone', project: project1) + create(:closed_milestone, title: 'Closed Group Milestone', project: project2) + create(:closed_milestone, title: 'Closed Group Milestone', project: project3) + create(:closed_milestone, title: 'Closed Group Milestone 4', group: group) + end + + it 'returns the quantity of global milestones and group milestones in each possible state' do + expected_count = { opened: 2, closed: 5, all: 7 } - it 'has all project milestones' do - expect(@global_milestones.map { |group_milestone| group_milestone.milestones.count }.sum).to eq(6) + count = described_class.states_count(Project.all, group) + + expect(count).to eq(expected_count) + end + + it 'returns the quantity of global milestones in each possible state' do + expected_count = { opened: 2, closed: 4, all: 6 } + + count = described_class.states_count(Project.all) + + expect(count).to eq(expected_count) + end end - it 'sorts collection by due date' do - expect(@global_milestones.map(&:due_date)).to eq [nil, milestone1_due_date] + context 'when the projects do not have milestones' do + before do + project1 + end + + it 'returns 0 as the quantity of global milestones in each state' do + expected_count = { opened: 0, closed: 0, all: 0 } + + count = described_class.states_count(Project.all) + + expect(count).to eq(expected_count) + end end end describe '#initialize' do let(:milestone1_project1) { create(:milestone, title: "Milestone v1.2", project: project1) } - let(:milestone1_project2) { create(:milestone, title: "Milestone v1.2", project: project2) } - let(:milestone1_project3) { create(:milestone, title: "Milestone v1.2", project: project3) } - - before do - milestones = - [ - milestone1_project1, - milestone1_project2, - milestone1_project3 - ] - milestones_relation = Milestone.where(id: milestones.map(&:id)) - - @global_milestone = described_class.new(milestone1_project1.title, milestones_relation) - end + subject(:global_milestone) { described_class.new(milestone1_project1) } it 'has exactly one group milestone' do - expect(@global_milestone.title).to eq('Milestone v1.2') + expect(global_milestone.title).to eq('Milestone v1.2') end it 'has all project milestones with the same title' do - expect(@global_milestone.milestones.count).to eq(3) + expect(global_milestone.milestone).to eq(milestone1_project1) end end @@ -122,7 +169,7 @@ describe GlobalMilestone do let(:milestone) { create(:milestone, title: "git / test", project: project1) } it 'strips out slashes and spaces' do - global_milestone = described_class.new(milestone.title, Milestone.where(id: milestone.id)) + global_milestone = described_class.new(milestone) expect(global_milestone.safe_title).to eq('git-test') end @@ -132,11 +179,8 @@ describe GlobalMilestone do context 'when at least one milestone is active' do it 'returns active' do title = 'Active Group Milestone' - milestones = [ - create(:active_milestone, title: title), - create(:closed_milestone, title: title) - ] - global_milestone = described_class.new(title, milestones) + + global_milestone = described_class.new(create(:active_milestone, title: title)) expect(global_milestone.state).to eq('active') end @@ -145,11 +189,8 @@ describe GlobalMilestone do context 'when all milestones are closed' do it 'returns closed' do title = 'Closed Group Milestone' - milestones = [ - create(:closed_milestone, title: title), - create(:closed_milestone, title: title) - ] - global_milestone = described_class.new(title, milestones) + + global_milestone = described_class.new(create(:closed_milestone, title: title)) expect(global_milestone.state).to eq('closed') end diff --git a/spec/models/group_milestone_spec.rb b/spec/models/group_milestone_spec.rb index b60676afc91..fcc33cd95fe 100644 --- a/spec/models/group_milestone_spec.rb +++ b/spec/models/group_milestone_spec.rb @@ -20,13 +20,36 @@ describe GroupMilestone do end describe '.build_collection' do - before do - project_milestone + let(:group) { create(:group) } + let(:project1) { create(:project, group: group) } + let(:project2) { create(:project, path: 'gitlab-ci', group: group) } + let(:project3) { create(:project, path: 'cookbook-gitlab', group: group) } + + let!(:projects) do + [ + project1, + project2, + project3 + ] end it 'returns array of milestones, each with group assigned' do milestones = described_class.build_collection(group, [project], {}) expect(milestones).to all(have_attributes(group: group)) end + + context 'when adding new milestones' do + it 'does not add more queries' do + control_count = ActiveRecord::QueryRecorder.new do + described_class.build_collection(group, projects, {}) + end.count + + create(:milestone, title: 'This title', project: project1) + + expect do + described_class.build_collection(group, projects, {}) + end.not_to exceed_all_query_limit(control_count) + end + end end end |