Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
4c788f43cb
commit
a83a97f604
|
@ -84,15 +84,10 @@ export default {
|
|||
|
||||
<div v-show="hasError" class="btn-group">
|
||||
<div class="btn btn-default btn-sm disabled">
|
||||
<icon
|
||||
:size="16"
|
||||
class="prepend-left-8 append-right-8"
|
||||
name="doc-image"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<icon :size="16" class="gl-ml-3 append-right-8" name="doc-image" aria-hidden="true" />
|
||||
</div>
|
||||
<div class="btn btn-default btn-sm disabled">
|
||||
<span class="prepend-left-8 append-right-8">{{ s__('Badges|No badge image') }}</span>
|
||||
<span class="gl-ml-3 append-right-8">{{ s__('Badges|No badge image') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -89,7 +89,7 @@ export default {
|
|||
:type="file.type"
|
||||
:path="file.path"
|
||||
:is-open="dropdownOpen"
|
||||
class="prepend-left-8"
|
||||
class="gl-ml-3"
|
||||
v-on="$listeners"
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -24,7 +24,7 @@ export default {
|
|||
<template>
|
||||
<div class="d-flex align-items-center">
|
||||
<ci-icon :status="job.status" :borderless="true" :size="24" class="d-flex" />
|
||||
<span class="prepend-left-8">
|
||||
<span class="gl-ml-3">
|
||||
{{ job.name }}
|
||||
<a :href="job.path" target="_blank" class="ide-external-link position-relative">
|
||||
{{ jobId }} <icon :size="12" name="external-link" />
|
||||
|
|
|
@ -71,7 +71,7 @@ export default {
|
|||
v-tooltip="showTooltip"
|
||||
:title="showTooltip ? stage.name : null"
|
||||
data-container="body"
|
||||
class="prepend-left-8 text-truncate"
|
||||
class="gl-ml-3 text-truncate"
|
||||
>
|
||||
{{ stage.name }}
|
||||
</strong>
|
||||
|
|
|
@ -63,7 +63,7 @@ export default {
|
|||
<template v-else-if="hasLoadedPipeline">
|
||||
<header v-if="latestPipeline" class="ide-tree-header ide-pipeline-header">
|
||||
<ci-icon :status="latestPipeline.details.status" :size="24" class="d-flex" />
|
||||
<span class="prepend-left-8">
|
||||
<span class="gl-ml-3">
|
||||
<strong> {{ __('Pipeline') }} </strong>
|
||||
<a
|
||||
:href="latestPipeline.path"
|
||||
|
|
|
@ -128,7 +128,7 @@ export default {
|
|||
:key="id"
|
||||
v-gl-tooltip.bottom
|
||||
:title="tooltipTitle"
|
||||
class="suggestion-help-hover prepend-left-8 text-tertiary"
|
||||
class="suggestion-help-hover gl-ml-3 text-tertiary"
|
||||
>
|
||||
<icon :name="icon" /> {{ count }}
|
||||
</span>
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
import initCreateCluster from '~/create_cluster/init_create_cluster';
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
initCreateCluster(document, gon);
|
||||
});
|
|
@ -1,10 +1,7 @@
|
|||
import Project from './project';
|
||||
import ShortcutsNavigation from '../../behaviors/shortcuts/shortcuts_navigation';
|
||||
import initCreateCluster from '~/create_cluster/init_create_cluster';
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
initCreateCluster(document, gon);
|
||||
|
||||
new Project(); // eslint-disable-line no-new
|
||||
new ShortcutsNavigation(); // eslint-disable-line no-new
|
||||
});
|
||||
|
|
|
@ -405,7 +405,6 @@ img.emoji {
|
|||
.prepend-top-20 { margin-top: 20px; }
|
||||
.prepend-top-32 { margin-top: 32px; }
|
||||
.prepend-left-5 { margin-left: 5px; }
|
||||
.prepend-left-8 { margin-left: 8px; }
|
||||
.prepend-left-10 { margin-left: 10px; }
|
||||
.prepend-left-15 { margin-left: 15px; }
|
||||
.prepend-left-default { margin-left: $gl-padding; }
|
||||
|
|
|
@ -221,7 +221,7 @@
|
|||
}
|
||||
|
||||
@function calc-btn-hover-padding($original-padding, $original-border: 1px) {
|
||||
@return calc(#{$original-padding + $original-border} - var(--ide-btn-hover-border-width, $original-border));
|
||||
@return calc(#{$original-padding + $original-border} - var(--ide-btn-hover-border-width, #{$original-border}));
|
||||
}
|
||||
|
||||
.btn:not(.btn-link):not([disabled]):hover {
|
||||
|
|
|
@ -51,13 +51,7 @@ module MilestoneActions
|
|||
}
|
||||
end
|
||||
|
||||
# rubocop:disable Gitlab/ModuleWithInstanceVariables
|
||||
def milestone_redirect_path
|
||||
if @milestone.global_milestone?
|
||||
url_for(action: :show, title: @milestone.title)
|
||||
else
|
||||
url_for(action: :show)
|
||||
end
|
||||
url_for(action: :show)
|
||||
end
|
||||
# rubocop:enable Gitlab/ModuleWithInstanceVariables
|
||||
end
|
||||
|
|
|
@ -1,48 +1,32 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Dashboard::MilestonesController < Dashboard::ApplicationController
|
||||
include MilestoneActions
|
||||
|
||||
before_action :projects
|
||||
before_action :groups, only: :index
|
||||
before_action :milestone, only: [:show, :merge_requests, :participants, :labels]
|
||||
|
||||
def index
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
@milestone_states = Milestone.states_count(@projects.select(:id), @groups.select(:id))
|
||||
@milestones = Kaminari.paginate_array(milestones).page(params[:page])
|
||||
@milestone_states = Milestone.states_count(@projects.select(:id), groups.select(:id))
|
||||
@milestones = milestones.page(params[:page])
|
||||
end
|
||||
format.json do
|
||||
render json: milestones
|
||||
render json: milestones.to_json(only: [:id, :title], methods: :name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def show
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def group_milestones
|
||||
DashboardGroupMilestone.build_collection(groups, params)
|
||||
end
|
||||
|
||||
# See [#39545](https://gitlab.com/gitlab-org/gitlab-foss/issues/39545) for info about the deprecation of dynamic milestones
|
||||
def dynamic_milestones
|
||||
DashboardMilestone.build_collection(@projects, params)
|
||||
end
|
||||
|
||||
def milestones
|
||||
@milestones = group_milestones + dynamic_milestones
|
||||
end
|
||||
|
||||
def milestone
|
||||
@milestone = DashboardMilestone.build(@projects, params[:title])
|
||||
render_404 unless @milestone
|
||||
MilestonesFinder.new(search_params).execute
|
||||
end
|
||||
|
||||
def groups
|
||||
@groups ||= GroupsFinder.new(current_user, all_available: false).execute
|
||||
end
|
||||
|
||||
def search_params
|
||||
params.permit(:state, :search_title).merge(group_ids: groups, project_ids: projects)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,17 +6,17 @@ class Groups::MilestonesController < Groups::ApplicationController
|
|||
before_action :milestone, only: [:edit, :show, :update, :merge_requests, :participants, :labels, :destroy]
|
||||
before_action :authorize_admin_milestones!, only: [:edit, :new, :create, :update, :destroy]
|
||||
before_action do
|
||||
push_frontend_feature_flag(:burnup_charts)
|
||||
push_frontend_feature_flag(:burnup_charts, @group)
|
||||
end
|
||||
|
||||
def index
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
@milestone_states = Milestone.states_count(group_projects_with_access, [group])
|
||||
@milestones = Kaminari.paginate_array(milestones).page(params[:page])
|
||||
@milestones = milestones.page(params[:page])
|
||||
end
|
||||
format.json do
|
||||
render json: milestones.map { |m| m.for_display.slice(:id, :title, :name) }
|
||||
render json: milestones.to_json(only: [:id, :title], methods: :name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -29,7 +29,7 @@ class Groups::MilestonesController < Groups::ApplicationController
|
|||
@milestone = Milestones::CreateService.new(group, current_user, milestone_params).execute
|
||||
|
||||
if @milestone.persisted?
|
||||
redirect_to milestone_path
|
||||
redirect_to milestone_path(@milestone)
|
||||
else
|
||||
render "new"
|
||||
end
|
||||
|
@ -39,23 +39,15 @@ class Groups::MilestonesController < Groups::ApplicationController
|
|||
end
|
||||
|
||||
def edit
|
||||
render_404 if @milestone.legacy_group_milestone?
|
||||
end
|
||||
|
||||
def update
|
||||
# Keep this compatible with legacy group milestones where we have to update
|
||||
# all projects milestones states at once.
|
||||
milestones, update_params = get_milestones_for_update
|
||||
milestones.each do |milestone|
|
||||
Milestones::UpdateService.new(milestone.resource_parent, current_user, update_params).execute(milestone)
|
||||
end
|
||||
Milestones::UpdateService.new(@milestone.parent, current_user, milestone_params).execute(@milestone)
|
||||
|
||||
redirect_to milestone_path
|
||||
redirect_to milestone_path(@milestone)
|
||||
end
|
||||
|
||||
def destroy
|
||||
return render_404 if @milestone.legacy_group_milestone?
|
||||
|
||||
Milestones::DestroyService.new(group, current_user).execute(@milestone)
|
||||
|
||||
respond_to do |format|
|
||||
|
@ -66,14 +58,6 @@ 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
|
||||
|
@ -82,27 +66,21 @@ 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)
|
||||
else
|
||||
group_milestone_path(group, @milestone.iid)
|
||||
end
|
||||
end
|
||||
|
||||
def milestones
|
||||
milestones = MilestonesFinder.new(search_params).execute
|
||||
MilestonesFinder.new(search_params).execute
|
||||
end
|
||||
|
||||
def milestone
|
||||
@milestone = group.milestones.find_by_iid(params[:id])
|
||||
|
||||
render_404 unless @milestone
|
||||
end
|
||||
|
||||
def search_params
|
||||
groups = request.format.json? ? group_ids(include_ancestors: true) : group_ids
|
||||
|
||||
@sort = params[:sort] || 'due_date_asc'
|
||||
MilestoneArray.sort(milestones + legacy_milestones, @sort)
|
||||
end
|
||||
|
||||
def legacy_milestones
|
||||
GroupMilestone.build_collection(group, group_projects_with_access, params)
|
||||
params.permit(:state, :search_title).merge(sort: @sort, group_ids: groups, project_ids: group_projects_with_access)
|
||||
end
|
||||
|
||||
def group_projects_with_access
|
||||
|
@ -116,23 +94,6 @@ class Groups::MilestonesController < Groups::ApplicationController
|
|||
group.self_and_descendants.public_or_visible_to_user(current_user).select(:id)
|
||||
end
|
||||
end
|
||||
|
||||
def milestone
|
||||
@milestone =
|
||||
if params[:title]
|
||||
GroupMilestone.build(group, group_projects_with_access, params[:title])
|
||||
else
|
||||
group.milestones.find_by_iid(params[:id])
|
||||
end
|
||||
|
||||
render_404 unless @milestone
|
||||
end
|
||||
|
||||
def search_params
|
||||
groups = request.format.json? ? group_ids(include_ancestors: true) : group_ids
|
||||
|
||||
params.permit(:state, :search_title).merge(group_ids: groups)
|
||||
end
|
||||
end
|
||||
|
||||
Groups::MilestonesController.prepend_if_ee('EE::Groups::MilestonesController')
|
||||
|
|
|
@ -7,7 +7,7 @@ class Projects::MilestonesController < Projects::ApplicationController
|
|||
before_action :check_issuables_available!
|
||||
before_action :milestone, only: [:edit, :update, :destroy, :show, :merge_requests, :participants, :labels, :promote]
|
||||
before_action do
|
||||
push_frontend_feature_flag(:burnup_charts)
|
||||
push_frontend_feature_flag(:burnup_charts, @project)
|
||||
end
|
||||
|
||||
# Allow read any milestone
|
||||
|
@ -34,7 +34,7 @@ class Projects::MilestonesController < Projects::ApplicationController
|
|||
@milestones = @milestones.page(params[:page])
|
||||
end
|
||||
format.json do
|
||||
render json: @milestones.to_json(methods: :name)
|
||||
render json: @milestones.to_json(only: [:id, :title], methods: :name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -58,10 +58,8 @@ class MilestonesFinder
|
|||
Milestone.filter_by_state(items, params[:state])
|
||||
end
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def order(items)
|
||||
order_statement = Gitlab::Database.nulls_last_order('due_date', 'ASC')
|
||||
items.reorder(order_statement).order('title ASC')
|
||||
sort_by = params[:sort].presence || 'due_date_asc'
|
||||
items.sort_by_attribute(sort_by)
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
end
|
||||
|
|
|
@ -207,7 +207,7 @@ module IssuablesHelper
|
|||
|
||||
output << content_tag(:span, (issuable_first_contribution_icon if issuable.first_contribution?), class: 'has-tooltip gl-ml-2', title: _('1st contribution!'))
|
||||
|
||||
output << content_tag(:span, (issuable.task_status if issuable.tasks?), id: "task_status", class: "d-none d-sm-none d-md-inline-block prepend-left-8")
|
||||
output << content_tag(:span, (issuable.task_status if issuable.tasks?), id: "task_status", class: "d-none d-sm-none d-md-inline-block gl-ml-3")
|
||||
output << content_tag(:span, (issuable.task_status_short if issuable.tasks?), id: "task_status_short", class: "d-md-none")
|
||||
|
||||
output.join.html_safe
|
||||
|
|
|
@ -229,11 +229,7 @@ module TimeboxesHelper
|
|||
alias_method :milestone_date_range, :timebox_date_range
|
||||
|
||||
def milestone_tab_path(milestone, tab)
|
||||
if milestone.global_milestone?
|
||||
url_for(action: tab, title: milestone.title, format: :json)
|
||||
else
|
||||
url_for(action: tab, format: :json)
|
||||
end
|
||||
url_for(action: tab, format: :json)
|
||||
end
|
||||
|
||||
def update_milestone_path(milestone, params = {})
|
||||
|
@ -247,11 +243,7 @@ module TimeboxesHelper
|
|||
def group_milestone_route(milestone, params = {})
|
||||
params = nil if params.empty?
|
||||
|
||||
if milestone.legacy_group_milestone?
|
||||
group_milestone_path(@group, milestone.safe_title, title: milestone.title, milestone: params)
|
||||
else
|
||||
group_milestone_path(milestone.group, milestone.iid, milestone: params)
|
||||
end
|
||||
group_milestone_path(milestone.group, milestone.iid, milestone: params)
|
||||
end
|
||||
|
||||
def group_or_project_milestone_path(milestone)
|
||||
|
|
|
@ -97,26 +97,6 @@ module Milestoneish
|
|||
due_date && due_date.past?
|
||||
end
|
||||
|
||||
def group_milestone?
|
||||
false
|
||||
end
|
||||
|
||||
def project_milestone?
|
||||
false
|
||||
end
|
||||
|
||||
def legacy_group_milestone?
|
||||
false
|
||||
end
|
||||
|
||||
def dashboard_milestone?
|
||||
false
|
||||
end
|
||||
|
||||
def global_milestone?
|
||||
false
|
||||
end
|
||||
|
||||
def total_time_spent
|
||||
@total_time_spent ||= issues.joins(:timelogs).sum(:time_spent) + merge_requests.joins(:timelogs).sum(:time_spent)
|
||||
end
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
# Dashboard Group Milestones are milestones that allow us to pull more info out for the UI that the Milestone object doesn't allow for
|
||||
class DashboardGroupMilestone < GlobalMilestone
|
||||
extend ::Gitlab::Utils::Override
|
||||
|
||||
attr_reader :group_name
|
||||
|
||||
def initialize(milestone)
|
||||
super
|
||||
|
||||
@group_name = milestone.group.full_name
|
||||
end
|
||||
|
||||
def self.build_collection(groups, params)
|
||||
milestones = Milestone.of_groups(groups.select(:id))
|
||||
.reorder_by_due_date_asc
|
||||
.order_by_name_asc
|
||||
milestones = milestones.search_title(params[:search_title]) if params[:search_title].present?
|
||||
Milestone.filter_by_state(milestones, params[:state]).map { |m| new(m) }
|
||||
end
|
||||
|
||||
def dashboard_milestone?
|
||||
true
|
||||
end
|
||||
|
||||
def merge_requests_enabled?
|
||||
true
|
||||
end
|
||||
end
|
|
@ -1,19 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class DashboardMilestone < GlobalMilestone
|
||||
attr_reader :project_name
|
||||
|
||||
def initialize(milestone)
|
||||
super
|
||||
|
||||
@project_name = milestone.project.full_name
|
||||
end
|
||||
|
||||
def project_milestone?
|
||||
true
|
||||
end
|
||||
|
||||
def merge_requests_enabled?
|
||||
project.merge_requests_enabled?
|
||||
end
|
||||
end
|
|
@ -1,108 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
# Global Milestones are milestones that can be shared across multiple projects
|
||||
class GlobalMilestone
|
||||
include Milestoneish
|
||||
|
||||
STATE_COUNT_HASH = { opened: 0, closed: 0, all: 0 }.freeze
|
||||
|
||||
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,
|
||||
:timebox_id, :milestoneish_id, :resource_parent, :releases, to: :milestone
|
||||
|
||||
def to_hash
|
||||
{
|
||||
name: title,
|
||||
title: title,
|
||||
group_name: group&.full_name,
|
||||
project_name: project&.full_name
|
||||
}
|
||||
end
|
||||
|
||||
def for_display
|
||||
@milestone
|
||||
end
|
||||
|
||||
def self.build_collection(projects, params)
|
||||
items = Milestone.of_projects(projects)
|
||||
.reorder_by_due_date_asc
|
||||
.order_by_name_asc
|
||||
items = items.search_title(params[:search_title]) if params[:search_title].present?
|
||||
|
||||
Milestone.filter_by_state(items, params[:state]).map { |m| new(m) }
|
||||
end
|
||||
|
||||
# necessary for legacy milestones
|
||||
def self.build(projects, title)
|
||||
milestones = Milestone.of_projects(projects).where(title: title)
|
||||
return if milestones.blank?
|
||||
|
||||
new(milestones.first)
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
def self.group_milestones_states_count(group)
|
||||
return STATE_COUNT_HASH unless group
|
||||
|
||||
counts_by_state = Milestone.of_groups(group).count_by_state
|
||||
|
||||
{
|
||||
opened: counts_by_state['active'] || 0,
|
||||
closed: counts_by_state['closed'] || 0,
|
||||
all: counts_by_state.values.sum
|
||||
}
|
||||
end
|
||||
|
||||
def self.legacy_group_milestone_states_count(projects)
|
||||
return STATE_COUNT_HASH unless projects
|
||||
|
||||
# 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
|
||||
|
||||
{
|
||||
opened: relation['active'] || 0,
|
||||
closed: relation['closed'] || 0,
|
||||
all: relation.values.sum
|
||||
}
|
||||
end
|
||||
|
||||
def initialize(milestone)
|
||||
@milestone = milestone
|
||||
end
|
||||
|
||||
def active?
|
||||
state == 'active'
|
||||
end
|
||||
|
||||
def closed?
|
||||
state == 'closed'
|
||||
end
|
||||
|
||||
def issues
|
||||
@issues ||= Issue.of_milestones(milestone).includes(:project, :assignees, :labels)
|
||||
end
|
||||
|
||||
def merge_requests
|
||||
@merge_requests ||= MergeRequest.of_milestones(milestone).includes(:target_project, :assignees, :labels)
|
||||
end
|
||||
|
||||
def labels
|
||||
@labels ||= GlobalLabel.build_collection(milestone.labels).sort_by!(&:title)
|
||||
end
|
||||
|
||||
def global_milestone?
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
GlobalMilestone.include_if_ee('::EE::GlobalMilestone')
|
|
@ -1,49 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
# Group Milestones are milestones that can be shared among many projects within the same group
|
||||
class GroupMilestone < GlobalMilestone
|
||||
attr_reader :group, :milestones
|
||||
|
||||
def self.build_collection(group, projects, params)
|
||||
params =
|
||||
{ state: params[:state], search_title: params[:search_title] }
|
||||
|
||||
project_milestones = Milestone.of_projects(projects)
|
||||
project_milestones = project_milestones.search_title(params[:search_title]) if params[:search_title].present?
|
||||
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)
|
||||
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
|
||||
{ group_id: group.id }
|
||||
end
|
||||
|
||||
def legacy_group_milestone?
|
||||
true
|
||||
end
|
||||
|
||||
def merge_requests_enabled?
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
GroupMilestone.include_if_ee('::EE::GroupMilestone')
|
|
@ -1313,8 +1313,6 @@ class MergeRequest < ApplicationRecord
|
|||
end
|
||||
|
||||
def has_accessibility_reports?
|
||||
return false unless Feature.enabled?(:accessibility_report_view, project)
|
||||
|
||||
actual_head_pipeline.present? && actual_head_pipeline.has_reports?(Ci::JobArtifact.accessibility_reports)
|
||||
end
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
= render 'shared/milestones/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,
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
- header_title "Milestones", dashboard_milestones_path
|
||||
|
||||
= render 'shared/milestones/top', milestone: @milestone
|
||||
= render 'shared/milestones/tabs', milestone: @milestone, show_full_project_name: true
|
||||
= render 'shared/milestones/sidebar', milestone: @milestone, affix_offset: 51
|
|
@ -17,7 +17,7 @@
|
|||
%span
|
||||
= _("Group ID: %{group_id}") % { group_id: @group.id }
|
||||
- if current_user
|
||||
%span.access-request-links.prepend-left-8
|
||||
%span.access-request-links.gl-ml-3
|
||||
= render 'shared/members/access_request_links', source: @group
|
||||
|
||||
.home-panel-buttons.col-md-12.col-lg-6.d-inline-flex.flex-wrap.justify-content-lg-end
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
|
||||
= render 'shared/milestones/milestone',
|
||||
milestone_path: group_milestone_route(milestone),
|
||||
issues_path: issues_group_path(@group, milestone_title: milestone.title),
|
||||
merge_requests_path: merge_requests_group_path(@group, milestone_title: milestone.title),
|
||||
milestone: milestone
|
||||
|
|
|
@ -16,5 +16,8 @@
|
|||
.nothing-here-block No milestones to show
|
||||
- else
|
||||
- @milestones.each do |milestone|
|
||||
= render 'milestone', milestone: milestone
|
||||
- if milestone.project_milestone?
|
||||
= render 'projects/milestones/milestone', milestone: milestone
|
||||
- else
|
||||
= render 'milestone', milestone: milestone
|
||||
= paginate @milestones, theme: "gitlab"
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
- if ActionController::Base.asset_host
|
||||
%link{ rel: 'dns-prefetch', href: ActionController::Base.asset_host }
|
||||
%link{ rel: 'preconnnect', href: ActionController::Base.asset_host, crossorigin: '' }
|
||||
%link{ rel: 'preconnect', href: ActionController::Base.asset_host, crossorigin: '' }
|
||||
|
||||
%meta{ 'http-equiv' => 'X-UA-Compatible', content: 'IE=edge' }
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
= brand_header_logo
|
||||
- logo_text = brand_header_logo_type
|
||||
- if logo_text.present?
|
||||
%span.logo-text.d-none.d-lg-block.prepend-left-8
|
||||
%span.logo-text.d-none.d-lg-block.gl-ml-3
|
||||
= logo_text
|
||||
- if Gitlab.com_and_canary?
|
||||
= link_to 'https://next.gitlab.com', class: 'label-link canary-badge bg-transparent', target: :_blank do
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
%header.navbar.fixed-top.navbar-gitlab.justify-content-center
|
||||
= render 'shared/logo.svg'
|
||||
%span.logo-text.d-none.d-lg-block.prepend-left-8.pt-1
|
||||
%span.logo-text.d-none.d-lg-block.gl-ml-3.pt-1
|
||||
= render 'shared/logo_type.svg'
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
= brand_header_logo
|
||||
- logo_text = brand_header_logo_type
|
||||
- if logo_text.present?
|
||||
%span.logo-text.prepend-left-8
|
||||
%span.logo-text.gl-ml-3
|
||||
= logo_text
|
||||
- if header_link?(:user_dropdown)
|
||||
.navbar-collapse
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
%p
|
||||
Merge Request #{@merge_request.to_reference} was merged
|
||||
Merge Request #{link_to @merge_request.to_reference, project_merge_request_url(@merge_request.target_project, @merge_request)} was merged
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
Merge Request #{@merge_request.to_reference} was merged
|
||||
|
||||
Merge Request url: #{project_merge_request_url(@merge_request.target_project, @merge_request)}
|
||||
Merge Request URL: #{project_merge_request_url(@merge_request.target_project, @merge_request)}
|
||||
|
||||
= merge_path_description(@merge_request, 'to')
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
%span.text-secondary
|
||||
= s_('ProjectPage|Project ID: %{project_id}') % { project_id: @project.id }
|
||||
- if current_user
|
||||
%span.access-request-links.prepend-left-8
|
||||
%span.access-request-links.gl-ml-3
|
||||
= render 'shared/members/access_request_links', source: @project
|
||||
- if @project.tag_list.present?
|
||||
%span.home-panel-topic-list.mt-2.w-100.d-inline-flex
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
.branch-info
|
||||
.branch-title
|
||||
= sprite_icon('fork', size: 12)
|
||||
= link_to project_tree_path(@project, branch.name), class: 'item-title str-truncated-100 ref-name prepend-left-8 qa-branch-name' do
|
||||
= link_to project_tree_path(@project, branch.name), class: 'item-title str-truncated-100 ref-name gl-ml-3 qa-branch-name' do
|
||||
= branch.name
|
||||
- if branch.name == @repository.root_ref
|
||||
%span.badge.badge-primary.prepend-left-5 default
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
.d-flex
|
||||
%h3.page-title= @environment.name
|
||||
- if @environment.auto_stop_at?
|
||||
%p.align-self-end.prepend-left-8
|
||||
%p.align-self-end.gl-ml-3
|
||||
= s_('Environments|Auto stops %{auto_stop_time}').html_safe % {auto_stop_time: time_ago_with_tooltip(@environment.auto_stop_at)}
|
||||
.nav-controls.my-2
|
||||
= render 'projects/environments/pin_button', environment: @environment
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
= _('Create branch')
|
||||
%li.divider.droplab-item-ignore
|
||||
|
||||
%li.droplab-item-ignore.prepend-left-8.append-right-8.prepend-top-16
|
||||
%li.droplab-item-ignore.gl-ml-3.append-right-8.prepend-top-16
|
||||
- if can_create_confidential_merge_request?
|
||||
#js-forked-project{ data: { namespace_path: @project.namespace.full_path, project_path: @project.full_path, new_fork_path: new_project_fork_path(@project), help_page_path: help_page_path('user/project/merge_requests') } }
|
||||
.form-group
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
= render 'shared/milestones/milestone',
|
||||
milestone_path: project_milestone_path(milestone.project, milestone),
|
||||
issues_path: project_issues_path(milestone.project, milestone_title: milestone.title),
|
||||
merge_requests_path: project_merge_requests_path(milestone.project, milestone_title: milestone.title),
|
||||
milestone: milestone
|
||||
|
|
|
@ -11,8 +11,7 @@
|
|||
|
||||
.milestone-buttons
|
||||
- if can?(current_user, :admin_milestone, @group || @project)
|
||||
- unless milestone.legacy_group_milestone?
|
||||
= link_to _('Edit'), edit_milestone_path(milestone), class: 'btn btn-grouped'
|
||||
= link_to _('Edit'), edit_milestone_path(milestone), class: 'btn btn-grouped'
|
||||
|
||||
- if milestone.project_milestone? && milestone.project.group
|
||||
%button.js-promote-project-milestone-button.btn.btn-grouped{ data: { toggle: 'modal',
|
||||
|
@ -31,8 +30,7 @@
|
|||
- else
|
||||
= link_to _('Reopen milestone'), update_milestone_path(milestone, { state_event: :activate }), method: :put, class: 'btn btn-grouped btn-reopen'
|
||||
|
||||
- unless milestone.legacy_group_milestone?
|
||||
= render 'shared/milestones/delete_button'
|
||||
= render 'shared/milestones/delete_button'
|
||||
|
||||
%button.btn.btn-default.btn-grouped.float-right.d-block.d-sm-none.js-sidebar-toggle{ type: 'button' }
|
||||
= icon('angle-double-left')
|
||||
|
|
|
@ -6,39 +6,33 @@
|
|||
.row
|
||||
.col-sm-6
|
||||
.append-bottom-5
|
||||
%strong= link_to truncate(milestone.title, length: 100), milestone_path
|
||||
%strong= link_to truncate(milestone.title, length: 100), milestone_path(milestone)
|
||||
- if @group
|
||||
= " - #{milestone_type}"
|
||||
|
||||
- if @project || milestone.is_a?(GlobalMilestone) || milestone.group_milestone?
|
||||
- if milestone.due_date || milestone.start_date
|
||||
.text-tertiary.append-bottom-5
|
||||
= milestone_date_range(milestone)
|
||||
- recent_releases, total_count, more_count = recent_releases_with_counts(milestone)
|
||||
- unless total_count.zero?
|
||||
.text-tertiary.append-bottom-5.milestone-release-links
|
||||
= icon('rocket')
|
||||
= n_('Release', 'Releases', total_count)
|
||||
- recent_releases.each do |release|
|
||||
= link_to release.name, project_releases_path(release.project, anchor: release.tag)
|
||||
- unless release == recent_releases.last
|
||||
•
|
||||
- if total_count > recent_releases.count
|
||||
- if milestone.due_date || milestone.start_date
|
||||
.text-tertiary.append-bottom-5
|
||||
= milestone_date_range(milestone)
|
||||
- recent_releases, total_count, more_count = recent_releases_with_counts(milestone)
|
||||
- unless total_count.zero?
|
||||
.text-tertiary.append-bottom-5.milestone-release-links
|
||||
= icon('rocket')
|
||||
= n_('Release', 'Releases', total_count)
|
||||
- recent_releases.each do |release|
|
||||
= link_to release.name, project_releases_path(release.project, anchor: release.tag)
|
||||
- unless release == recent_releases.last
|
||||
•
|
||||
= link_to n_('%{count} more release', '%{count} more releases', more_count) % { count: more_count }, project_releases_path(milestone.project)
|
||||
%div
|
||||
= render('shared/milestone_expired', milestone: milestone)
|
||||
- if milestone.group_milestone?
|
||||
.label-badge.label-badge-blue.d-inline-block
|
||||
= milestone.group.full_name
|
||||
- if milestone.legacy_group_milestone?
|
||||
.projects
|
||||
- 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
|
||||
- if total_count > recent_releases.count
|
||||
•
|
||||
= link_to n_('%{count} more release', '%{count} more releases', more_count) % { count: more_count }, project_releases_path(milestone.project)
|
||||
%div
|
||||
= render('shared/milestone_expired', milestone: milestone)
|
||||
- if milestone.group_milestone?
|
||||
.label-badge.label-badge-blue.d-inline-block
|
||||
= milestone.group.full_name
|
||||
- if milestone.project_milestone?
|
||||
.label-badge.label-badge-gray.d-inline-block
|
||||
= milestone.project.full_name
|
||||
|
||||
.col-sm-4.milestone-progress
|
||||
= milestone_progress_bar(milestone)
|
||||
|
@ -49,29 +43,25 @@
|
|||
.float-lg-right.light #{milestone.percent_complete}% complete
|
||||
.col-sm-2
|
||||
.milestone-actions.d-flex.justify-content-sm-start.justify-content-md-end
|
||||
- if @project
|
||||
- if can_admin_project_milestones? and milestone.active?
|
||||
- if can_admin_group_milestones?
|
||||
%button.js-promote-project-milestone-button.btn.btn-blank.btn-sm.btn-grouped.has-tooltip{ title: s_('Milestones|Promote to Group Milestone'),
|
||||
disabled: true,
|
||||
type: 'button',
|
||||
data: { url: promote_project_milestone_path(milestone.project, milestone),
|
||||
milestone_title: milestone.title,
|
||||
group_name: @project.group.name,
|
||||
target: '#promote-milestone-modal',
|
||||
container: 'body',
|
||||
toggle: 'modal' } }
|
||||
= sprite_icon('level-up', size: 14)
|
||||
- if @project # if in milestones list on project level
|
||||
- if can_admin_group_milestones?
|
||||
%button.js-promote-project-milestone-button.btn.btn-blank.btn-sm.btn-grouped.has-tooltip{ title: s_('Milestones|Promote to Group Milestone'),
|
||||
disabled: true,
|
||||
type: 'button',
|
||||
data: { url: promote_project_milestone_path(milestone.project, milestone),
|
||||
milestone_title: milestone.title,
|
||||
group_name: @project.group.name,
|
||||
target: '#promote-milestone-modal',
|
||||
container: 'body',
|
||||
toggle: 'modal' } }
|
||||
= sprite_icon('level-up', size: 14)
|
||||
|
||||
- if can?(current_user, :admin_milestone, milestone)
|
||||
- if milestone.closed?
|
||||
= link_to s_('Milestones|Reopen Milestone'), milestone_path(milestone, milestone: { state_event: :activate }), method: :put, class: "btn btn-sm btn-grouped btn-reopen"
|
||||
- else
|
||||
= link_to s_('Milestones|Close Milestone'), milestone_path(milestone, milestone: { state_event: :close }), method: :put, class: "btn btn-sm btn-grouped btn-close"
|
||||
|
||||
= link_to s_('Milestones|Close Milestone'), project_milestone_path(@project, milestone, milestone: {state_event: :close }), method: :put, remote: true, class: "btn btn-sm btn-close btn-grouped"
|
||||
- unless milestone.active?
|
||||
= link_to s_('Milestones|Reopen Milestone'), project_milestone_path(@project, milestone, milestone: {state_event: :activate }), method: :put, class: "btn btn-grouped btn-reopen"
|
||||
- if @group
|
||||
- if can?(current_user, :admin_milestone, @group)
|
||||
- if milestone.closed?
|
||||
= link_to s_('Milestones|Reopen Milestone'), group_milestone_route(milestone, {state_event: :activate }), method: :put, class: "btn btn-sm btn-grouped btn-reopen"
|
||||
- else
|
||||
= link_to s_('Milestones|Close Milestone'), group_milestone_route(milestone, {state_event: :close }), method: :put, class: "btn btn-sm btn-grouped btn-close"
|
||||
- if dashboard
|
||||
.label-badge.label-badge-gray
|
||||
= milestone_type
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
- page_title milestone.title
|
||||
- @breadcrumb_link = dashboard_milestone_path(milestone.safe_title, title: milestone.title)
|
||||
- @breadcrumb_link = milestone_path(milestone)
|
||||
|
||||
- group = local_assigns[:group]
|
||||
- is_dynamic_milestone = milestone.legacy_group_milestone? || milestone.dashboard_milestone?
|
||||
|
||||
= render 'shared/milestones/header', milestone: milestone
|
||||
= render 'shared/milestones/deprecation_message' if is_dynamic_milestone
|
||||
= render 'shared/milestones/description', milestone: milestone
|
||||
|
||||
- if milestone.complete? && milestone.active?
|
||||
|
@ -15,26 +13,3 @@
|
|||
= group ? _('You may close the milestone now.') : _('Navigate to the project to close the milestone.')
|
||||
|
||||
= render_if_exists 'shared/milestones/burndown', milestone: milestone, project: @project
|
||||
|
||||
- if is_dynamic_milestone
|
||||
.table-holder
|
||||
%table.table
|
||||
%thead
|
||||
%tr
|
||||
%th= _('Project')
|
||||
%th= _('Open issues')
|
||||
%th= _('State')
|
||||
%th= _('Due date')
|
||||
%tr
|
||||
%td
|
||||
- project_name = group ? milestone.project.name : milestone.project.full_name
|
||||
= link_to project_name, milestone_path(milestone.milestone)
|
||||
%td
|
||||
= milestone.milestone.issues_visible_to_user(current_user).opened.count
|
||||
%td
|
||||
- if milestone.closed?
|
||||
= _('Closed')
|
||||
- else
|
||||
= _('Open')
|
||||
%td
|
||||
= milestone.expires_at
|
||||
|
|
|
@ -6,13 +6,13 @@
|
|||
.card-footer.footer-block.clearfix
|
||||
- if can?(current_user, :accept_terms, @term)
|
||||
.float-right
|
||||
= button_to accept_term_path(@term, redirect_params), class: 'btn btn-success prepend-left-8', data: { qa_selector: 'accept_terms_button' } do
|
||||
= button_to accept_term_path(@term, redirect_params), class: 'btn btn-success gl-ml-3', data: { qa_selector: 'accept_terms_button' } do
|
||||
= _('Accept terms')
|
||||
- else
|
||||
.pull-right
|
||||
= link_to root_path, class: 'btn btn-success prepend-left-8' do
|
||||
= link_to root_path, class: 'btn btn-success gl-ml-3' do
|
||||
= _('Continue')
|
||||
- if can?(current_user, :decline_terms, @term)
|
||||
.float-right
|
||||
= button_to decline_term_path(@term, redirect_params), class: 'btn btn-default prepend-left-8' do
|
||||
= button_to decline_term_path(@term, redirect_params), class: 'btn btn-default gl-ml-3' do
|
||||
= _('Decline and sign out')
|
||||
|
|
|
@ -70,7 +70,7 @@ class IrkerWorker # rubocop:disable Scalability/IdempotentWorker
|
|||
|
||||
def send_new_branch(project, repo_name, committer, branch)
|
||||
repo_path = project.full_path
|
||||
newbranch = "#{Gitlab.config.gitlab.url}/#{repo_path}/branches"
|
||||
newbranch = "#{Gitlab.config.gitlab.url}/#{repo_path}/-/branches"
|
||||
newbranch = "\x0302\x1f#{newbranch}\x0f" if @colors
|
||||
|
||||
privmsg = "[#{repo_name}] #{committer} has created a new branch " \
|
||||
|
@ -124,7 +124,7 @@ class IrkerWorker # rubocop:disable Scalability/IdempotentWorker
|
|||
def compare_url(data, repo_path)
|
||||
sha1 = Commit.truncate_sha(data['before'])
|
||||
sha2 = Commit.truncate_sha(data['after'])
|
||||
compare_url = "#{Gitlab.config.gitlab.url}/#{repo_path}/compare" \
|
||||
compare_url = "#{Gitlab.config.gitlab.url}/#{repo_path}/-/compare" \
|
||||
"/#{sha1}...#{sha2}"
|
||||
colorize_url compare_url
|
||||
end
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Refine SAST language detection by frameworks
|
||||
merge_request: 33226
|
||||
author:
|
||||
type: changed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Remove deprecated dashboard & group milestone pages
|
||||
merge_request: 13237
|
||||
author:
|
||||
type: removed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Render Merge request reference as link
|
||||
merge_request: 33248
|
||||
author:
|
||||
type: changed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix preconnect typo in rel link
|
||||
merge_request: 33255
|
||||
author:
|
||||
type: performance
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Update deprecated routes in irker integration
|
||||
merge_request: 32923
|
||||
author: Marc Jeanmougin
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add accessibility report MR widget
|
||||
merge_request: 32902
|
||||
author:
|
||||
type: added
|
|
@ -5,13 +5,7 @@ resource :dashboard, controller: 'dashboard', only: [] do
|
|||
get :activity
|
||||
|
||||
scope module: :dashboard do
|
||||
resources :milestones, only: [:index, :show] do
|
||||
member do
|
||||
get :merge_requests
|
||||
get :participants
|
||||
get :labels
|
||||
end
|
||||
end
|
||||
resources :milestones, only: [:index]
|
||||
resources :labels, only: [:index]
|
||||
|
||||
resources :groups, only: [:index]
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class DropVulnerabilityConfidenceIndex < ActiveRecord::Migration[6.0]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
DOWNTIME = false
|
||||
INDEX_NAME = 'index_vulnerability_on_id_and_confidence_eq_zero'
|
||||
|
||||
def up
|
||||
Gitlab::BackgroundMigration.steal('RemoveUndefinedVulnerabilityConfidenceLevel')
|
||||
|
||||
remove_concurrent_index_by_name :vulnerabilities, INDEX_NAME
|
||||
end
|
||||
|
||||
def down
|
||||
add_concurrent_index(:vulnerabilities, :id, where: 'confidence = 0', name: INDEX_NAME)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,20 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class DropVulnerabilityOccurrenceConfidenceIndex < ActiveRecord::Migration[6.0]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
DOWNTIME = false
|
||||
INDEX_NAME = 'index_vulnerability_occurrences_on_id_and_confidence_eq_zero'
|
||||
|
||||
def up
|
||||
Gitlab::BackgroundMigration.steal('RemoveUndefinedOccurrenceConfidenceLevel')
|
||||
|
||||
remove_concurrent_index_by_name :vulnerability_occurrences, INDEX_NAME
|
||||
end
|
||||
|
||||
def down
|
||||
add_concurrent_index(:vulnerability_occurrences, :id, where: 'confidence = 0', name: INDEX_NAME)
|
||||
end
|
||||
end
|
|
@ -10932,8 +10932,6 @@ CREATE UNIQUE INDEX index_vulnerability_occurrence_identifiers_on_unique_keys ON
|
|||
|
||||
CREATE INDEX index_vulnerability_occurrence_pipelines_on_pipeline_id ON public.vulnerability_occurrence_pipelines USING btree (pipeline_id);
|
||||
|
||||
CREATE INDEX index_vulnerability_occurrences_on_id_and_confidence_eq_zero ON public.vulnerability_occurrences USING btree (id) WHERE (confidence = 0);
|
||||
|
||||
CREATE INDEX index_vulnerability_occurrences_on_primary_identifier_id ON public.vulnerability_occurrences USING btree (primary_identifier_id);
|
||||
|
||||
CREATE INDEX index_vulnerability_occurrences_on_scanner_id ON public.vulnerability_occurrences USING btree (scanner_id);
|
||||
|
@ -10944,8 +10942,6 @@ CREATE UNIQUE INDEX index_vulnerability_occurrences_on_uuid ON public.vulnerabil
|
|||
|
||||
CREATE INDEX index_vulnerability_occurrences_on_vulnerability_id ON public.vulnerability_occurrences USING btree (vulnerability_id);
|
||||
|
||||
CREATE INDEX index_vulnerability_on_id_and_confidence_eq_zero ON public.vulnerabilities USING btree (id) WHERE (confidence = 0);
|
||||
|
||||
CREATE UNIQUE INDEX index_vulnerability_scanners_on_project_id_and_external_id ON public.vulnerability_scanners USING btree (project_id, external_id);
|
||||
|
||||
CREATE UNIQUE INDEX index_vulnerability_user_mentions_on_note_id ON public.vulnerability_user_mentions USING btree (note_id) WHERE (note_id IS NOT NULL);
|
||||
|
@ -13958,5 +13954,7 @@ COPY "schema_migrations" (version) FROM STDIN;
|
|||
20200526120714
|
||||
20200526164946
|
||||
20200526164947
|
||||
20200527094322
|
||||
20200527095401
|
||||
\.
|
||||
|
||||
|
|
|
@ -184,6 +184,27 @@ required:
|
|||
set the database to read-write:
|
||||
- Amazon RDS - [Promoting a Read Replica to Be a Standalone DB Instance](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_ReadRepl.html#USER_ReadRepl.Promote)
|
||||
- Azure Database for PostgreSQL - [Stop replication](https://docs.microsoft.com/en-us/azure/postgresql/howto-read-replicas-portal#stop-replication)
|
||||
- Other external PostgreSQL databases - save the script below in you secondary node, for example
|
||||
`/tmp/geo_promote.sh`, and modify the connection parameters to match your
|
||||
environment. Then, execute it to promote the replica:
|
||||
|
||||
```shell
|
||||
#!/bin/bash
|
||||
|
||||
PG_SUPERUSER=postgres
|
||||
|
||||
# The path to your pg_ctl binary. You may need to adjust this path to match
|
||||
# your PostgreSQL installation
|
||||
PG_CTL_BINARY=/usr/lib/postgresql/10/bin/pg_ctl
|
||||
|
||||
# The path to your PostgreSQL data directory. You may need to adjust this
|
||||
# path to match your PostgreSQL installation. You can also run
|
||||
# `SHOW data_directory;` from PostgreSQL to find your data directory
|
||||
PG_DATA_DIRECTORY=/etc/postgresql/10/main
|
||||
|
||||
# Promote the PostgreSQL database and allow read/write operations
|
||||
sudo -u $PG_SUPERUSER $PG_CTL_BINARY -D $PG_DATA_DIRECTORY promote
|
||||
```
|
||||
|
||||
1. Edit `/etc/gitlab/gitlab.rb` on every node in the **secondary** site to
|
||||
reflect its new status as **primary** by removing any lines that enabled the
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
type: reference
|
||||
---
|
||||
|
||||
# Load Balancer for GitLab HA
|
||||
# Load Balancer for multi-node GitLab
|
||||
|
||||
In an active/active GitLab configuration, you will need a load balancer to route
|
||||
In an multi-node GitLab configuration, you will need a load balancer to route
|
||||
traffic to the application servers. The specifics on which load balancer to use
|
||||
or the exact configuration is beyond the scope of GitLab documentation. We hope
|
||||
that if you're managing HA systems like GitLab you have a load balancer of
|
||||
|
@ -14,7 +14,7 @@ you need to use with GitLab.
|
|||
|
||||
## SSL
|
||||
|
||||
How will you handle SSL in your HA environment? There are several different
|
||||
How will you handle SSL in your multi-node environment? There are several different
|
||||
options:
|
||||
|
||||
- Each application node terminates SSL
|
||||
|
|
|
@ -204,11 +204,11 @@ Following you'll find some general common practices you will find as part of our
|
|||
|
||||
### How to query DOM elements
|
||||
|
||||
When it comes to querying DOM elements in your tests, it is best to uniquely target the element, without adding additional attributes specifically for testing purposes. Sometimes this cannot be done feasibly. In these cases, adding test attributes to simplify the selectors might be the best option.
|
||||
When it comes to querying DOM elements in your tests, it is best to uniquely and semantically target the element. Sometimes this cannot be done feasibly. In these cases, adding test attributes to simplify the selectors might be the best option.
|
||||
|
||||
Preferentially, in component testing with `@vue/test-utils`, you should query for child components using the component itself. This helps enforce that specific behavior can be covered by that component's individual unit tests. Otherwise, try to use:
|
||||
|
||||
- A behavioral attribute like `name` (also verifies that `name` was setup properly)
|
||||
- A semantic attribute like `name` (also verifies that `name` was setup properly)
|
||||
- A `data-testid` attribute ([recommended by maintainers of `@vue/test-utils`](https://github.com/vuejs/vue-test-utils/issues/1498#issuecomment-610133465))
|
||||
- a Vue `ref` (if using `@vue/test-utils`)
|
||||
|
||||
|
@ -216,11 +216,17 @@ Examples:
|
|||
|
||||
```javascript
|
||||
it('exists', () => {
|
||||
// Good
|
||||
wrapper.find(FooComponent);
|
||||
wrapper.find('input[name=foo]');
|
||||
wrapper.find('[data-testid="foo"]');
|
||||
wrapper.find({ ref: 'foo'});
|
||||
|
||||
// Bad
|
||||
wrapper.find('.js-foo');
|
||||
wrapper.find('.btn-primary');
|
||||
wrapper.find('.qa-foo-component');
|
||||
wrapper.find('[data-qa-selector="foo"]');
|
||||
});
|
||||
```
|
||||
|
||||
|
|
|
@ -288,10 +288,11 @@ You can provide feedback [on this issue](https://gitlab.com/gitlab-org/gitlab/-/
|
|||
|
||||
GitLab supports the following web browsers:
|
||||
|
||||
- Firefox
|
||||
- Chrome/Chromium
|
||||
- Safari
|
||||
- Microsoft Edge
|
||||
- [Mozilla Firefox](https://www.mozilla.org/firefox/new/)
|
||||
- [Google Chrome](https://www.google.com/chrome/)
|
||||
- [Chromium](https://www.chromium.org/getting-involved/dev-channel)
|
||||
- [Apple Safari](https://www.apple.com/safari/)
|
||||
- [Microsoft Edge](https://www.microsoft.com/edge)
|
||||
- Internet Explorer 11 (until May 2020)
|
||||
|
||||
For the listed web browsers, GitLab supports:
|
||||
|
|
|
@ -115,16 +115,16 @@ repository in GitLab 10.4 and later:
|
|||
- Ancestor renamed
|
||||
- Ancestor transferred to another namespace
|
||||
|
||||
Bare repositories are **not** importable by GitLab 10.4 and later when all the following are true about the repository:
|
||||
Bare repositories are **not** importable by GitLab 10.4 to GitLab 11.6, if all the following are true about the repository:
|
||||
|
||||
- It was created in GitLab 10.3 or earlier.
|
||||
- It was not renamed, transferred, or migrated to hashed storage in GitLab 10.4 and later.
|
||||
- Its ancestor namespaces were not renamed or transferred in GitLab 10.4 and later.
|
||||
- It was not renamed, transferred, or migrated to [hashed storage](../administration/repository_storage_types.md#hashed-storage) in GitLab 10.4 to GitLab 11.6.
|
||||
- Its ancestor namespaces were not renamed or transferred in GitLab 10.4 to GitLab 11.6.
|
||||
|
||||
There is an [open issue to add a migration to make all bare repositories
|
||||
importable](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/41776).
|
||||
[Since GitLab 11.6](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/41776), all
|
||||
bare repositories are importable.
|
||||
|
||||
Until then, you may wish to manually migrate repositories yourself. You can use
|
||||
To manually migrate repositories yourself (for GitLab 10.4 to GitLab 11.6), you can use the
|
||||
[Rails console](../administration/troubleshooting/debug.md#starting-a-rails-console-session)
|
||||
to do so. In a Rails console session, run the following to migrate a project:
|
||||
|
||||
|
|
|
@ -25,16 +25,6 @@ Accessibility Report in the merge request widget area:
|
|||
|
||||
![Accessibility Merge Request Widget](img/accessibility_mr_widget_v13_0.png)
|
||||
|
||||
This widget comes with the `:accessibility_report_view` feature flag disabled by default while we test feature stability.
|
||||
Once we have determined the widget is stable, this feature will be enabled by default.
|
||||
|
||||
To enable this feature, ask a GitLab administrator with [Rails console access](../../../administration/feature_flags.md#how-to-enable-and-disable-features-behind-flags) to run the
|
||||
following command:
|
||||
|
||||
```ruby
|
||||
Feature.enable(:accessibility_report_view)
|
||||
```
|
||||
|
||||
## Configure Accessibility Testing
|
||||
|
||||
This example shows how to run [pa11y](https://pa11y.org/)
|
||||
|
|
|
@ -83,7 +83,7 @@ brakeman-sast:
|
|||
$GITLAB_FEATURES =~ /\bsast\b/ &&
|
||||
$SAST_DEFAULT_ANALYZERS =~ /brakeman/
|
||||
exists:
|
||||
- '**/*.rb'
|
||||
- 'config/routes.rb'
|
||||
|
||||
eslint-sast:
|
||||
extends: .sast-analyzer
|
||||
|
@ -149,7 +149,7 @@ nodejs-scan-sast:
|
|||
$GITLAB_FEATURES =~ /\bsast\b/ &&
|
||||
$SAST_DEFAULT_ANALYZERS =~ /nodejs-scan/
|
||||
exists:
|
||||
- '**/*.js'
|
||||
- 'package.json'
|
||||
|
||||
phpcs-security-audit-sast:
|
||||
extends: .sast-analyzer
|
||||
|
@ -213,8 +213,7 @@ sobelow-sast:
|
|||
$GITLAB_FEATURES =~ /\bsast\b/ &&
|
||||
$SAST_DEFAULT_ANALYZERS =~ /sobelow/
|
||||
exists:
|
||||
- '**/*.ex'
|
||||
- '**/*.exs'
|
||||
- 'mix.exs'
|
||||
|
||||
spotbugs-sast:
|
||||
extends: .sast-analyzer
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module MilestoneArray
|
||||
class << self
|
||||
def sort(array, sort_method)
|
||||
case sort_method
|
||||
when 'due_date_asc'
|
||||
sort_asc_nulls_last(array, 'due_date')
|
||||
when 'due_date_desc'
|
||||
sort_desc_nulls_last(array, 'due_date')
|
||||
when 'start_date_asc'
|
||||
sort_asc_nulls_last(array, 'start_date')
|
||||
when 'start_date_desc'
|
||||
sort_desc_nulls_last(array, 'start_date')
|
||||
when 'name_asc'
|
||||
sort_asc(array, 'title')
|
||||
when 'name_desc'
|
||||
sort_asc(array, 'title').reverse
|
||||
else
|
||||
array
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def sort_asc_nulls_last(array, attribute)
|
||||
attribute = attribute.to_sym
|
||||
|
||||
array.select(&attribute).sort_by(&attribute) + array.reject(&attribute)
|
||||
end
|
||||
|
||||
def sort_desc_nulls_last(array, attribute)
|
||||
attribute = attribute.to_sym
|
||||
|
||||
array.select(&attribute).sort_by(&attribute).reverse + array.reject(&attribute)
|
||||
end
|
||||
|
||||
def sort_asc(array, attribute)
|
||||
array.sort_by(&attribute.to_sym)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -20735,9 +20735,6 @@ msgstr ""
|
|||
msgid "Starts at (UTC)"
|
||||
msgstr ""
|
||||
|
||||
msgid "State"
|
||||
msgstr ""
|
||||
|
||||
msgid "State your message to activate"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -8,12 +8,7 @@ describe Dashboard::MilestonesController do
|
|||
let(:user) { create(:user) }
|
||||
let(:project_milestone) { create(:milestone, project: project) }
|
||||
let(:group_milestone) { create(:milestone, group: group) }
|
||||
let(:milestone) do
|
||||
DashboardMilestone.build(
|
||||
[project],
|
||||
project_milestone.title
|
||||
)
|
||||
end
|
||||
let(:milestone) { create(:milestone, group: group) }
|
||||
let(:issue) { create(:issue, project: project, milestone: project_milestone) }
|
||||
let(:group_issue) { create(:issue, milestone: group_milestone, project: create(:project, group: group)) }
|
||||
|
||||
|
@ -28,22 +23,6 @@ describe Dashboard::MilestonesController do
|
|||
group.add_developer(user)
|
||||
end
|
||||
|
||||
it_behaves_like 'milestone tabs'
|
||||
|
||||
describe "#show" do
|
||||
render_views
|
||||
|
||||
def view_milestone
|
||||
get :show, params: { id: milestone.safe_title, title: milestone.title }
|
||||
end
|
||||
|
||||
it 'shows milestone page' do
|
||||
view_milestone
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
end
|
||||
end
|
||||
|
||||
describe "#index" do
|
||||
let(:public_group) { create(:group, :public) }
|
||||
let!(:public_milestone) { create(:milestone, group: public_group) }
|
||||
|
@ -58,7 +37,6 @@ describe Dashboard::MilestonesController do
|
|||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(json_response.size).to eq(2)
|
||||
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
|
||||
|
||||
it 'returns closed group and project milestones to which the user belongs' do
|
||||
|
@ -67,7 +45,6 @@ describe Dashboard::MilestonesController do
|
|||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(json_response.size).to eq(2)
|
||||
expect(json_response.map { |i| i["name"] }).to match_array([closed_group_milestone.name, closed_project_milestone.name])
|
||||
expect(json_response.map { |i| i["group_name"] }.compact).to match_array(group.name)
|
||||
end
|
||||
|
||||
it 'searches legacy project milestones by title when search_title is given' do
|
||||
|
|
|
@ -8,15 +8,7 @@ describe Groups::MilestonesController do
|
|||
let!(:project2) { create(:project, group: group) }
|
||||
let(:user) { create(:user) }
|
||||
let(:title) { '肯定不是中文的问题' }
|
||||
let(:milestone) do
|
||||
project_milestone = create(:milestone, project: project)
|
||||
|
||||
GroupMilestone.build(
|
||||
group,
|
||||
[project],
|
||||
project_milestone.title
|
||||
)
|
||||
end
|
||||
let(:milestone) { create(:milestone, project: project) }
|
||||
let(:milestone_path) { group_milestone_path(group, milestone.safe_title, title: milestone.title) }
|
||||
|
||||
let(:milestone_params) do
|
||||
|
@ -168,17 +160,16 @@ describe Groups::MilestonesController do
|
|||
|
||||
context 'as JSON' do
|
||||
let!(:milestone) { create(:milestone, group: group, title: 'group milestone') }
|
||||
let!(:legacy_milestone1) { create(:milestone, project: project, title: 'legacy') }
|
||||
let!(:legacy_milestone2) { create(:milestone, project: project2, title: 'legacy') }
|
||||
let!(:project_milestone1) { create(:milestone, project: project, title: 'same name') }
|
||||
let!(:project_milestone2) { create(:milestone, project: project2, title: 'same name') }
|
||||
|
||||
it 'lists legacy group milestones and group milestones' do
|
||||
it 'lists project and group milestones' do
|
||||
get :index, params: { group_id: group.to_param }, format: :json
|
||||
|
||||
milestones = json_response
|
||||
|
||||
expect(milestones.count).to eq(2)
|
||||
expect(milestones.first["title"]).to eq("group milestone")
|
||||
expect(milestones.second["title"]).to eq("legacy")
|
||||
expect(milestones.count).to eq(3)
|
||||
expect(milestones.collect { |m| m['title'] }).to match_array(['same name', 'same name', 'group milestone'])
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response.content_type).to eq 'application/json'
|
||||
end
|
||||
|
@ -191,8 +182,9 @@ describe Groups::MilestonesController do
|
|||
get :index, params: { group_id: group.to_param }, format: :json
|
||||
milestones = json_response
|
||||
|
||||
expect(milestones.count).to eq(3)
|
||||
expect(milestones.second["title"]).to eq("subgroup milestone")
|
||||
milestone_titles = milestones.map { |m| m['title'] }
|
||||
expect(milestones.count).to eq(4)
|
||||
expect(milestone_titles).to match_array(['same name', 'same name', 'group milestone', 'subgroup milestone'])
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -218,31 +210,18 @@ describe Groups::MilestonesController do
|
|||
end
|
||||
|
||||
describe '#show' do
|
||||
let(:milestone1) { create(:milestone, project: project, title: 'legacy') }
|
||||
let(:milestone2) { create(:milestone, project: project, title: 'legacy') }
|
||||
let(:group_milestone) { create(:milestone, group: group) }
|
||||
render_views
|
||||
|
||||
context 'when there is a title parameter' do
|
||||
it 'searches for a legacy group milestone' do
|
||||
expect(GroupMilestone).to receive(:build)
|
||||
expect(Milestone).not_to receive(:find_by_iid)
|
||||
let!(:group_milestone) { create(:milestone, group: group) }
|
||||
|
||||
get :show, params: { group_id: group.to_param, id: title, title: milestone1.safe_title }
|
||||
end
|
||||
end
|
||||
it 'renders for a group milestone' do
|
||||
get :show, params: { group_id: group.to_param, id: group_milestone.iid }
|
||||
|
||||
context 'when there is not a title parameter' do
|
||||
it 'searches for a group milestone' do
|
||||
expect(GlobalMilestone).not_to receive(:build)
|
||||
expect(Milestone).to receive(:find_by_iid)
|
||||
|
||||
get :show, params: { group_id: group.to_param, id: group_milestone.id }
|
||||
end
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response.body).to include(group_milestone.title)
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'milestone tabs'
|
||||
|
||||
describe "#create" do
|
||||
it "creates group milestone with Chinese title" do
|
||||
post :create,
|
||||
|
@ -277,34 +256,6 @@ describe Groups::MilestonesController do
|
|||
expect(response).to redirect_to(group_milestone_path(group, milestone.iid))
|
||||
expect(milestone.title).to eq("title changed")
|
||||
end
|
||||
|
||||
context "legacy group milestones" do
|
||||
let!(:milestone1) { create(:milestone, project: project, title: 'legacy milestone', description: "old description") }
|
||||
let!(:milestone2) { create(:milestone, project: project2, title: 'legacy milestone', description: "old description") }
|
||||
|
||||
it "updates only group milestones state" do
|
||||
milestone_params[:title] = "title changed"
|
||||
milestone_params[:description] = "description changed"
|
||||
milestone_params[:state_event] = "close"
|
||||
|
||||
put :update,
|
||||
params: {
|
||||
id: milestone1.title.to_slug.to_s,
|
||||
group_id: group.to_param,
|
||||
milestone: milestone_params,
|
||||
title: milestone1.title
|
||||
}
|
||||
|
||||
expect(response).to redirect_to(group_milestone_path(group, milestone1.safe_title, title: milestone1.title))
|
||||
|
||||
[milestone1, milestone2].each do |milestone|
|
||||
milestone.reload
|
||||
expect(milestone.title).to eq("legacy milestone")
|
||||
expect(milestone.description).to eq("old description")
|
||||
expect(milestone.state).to eq("closed")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#destroy" do
|
||||
|
|
|
@ -1409,20 +1409,6 @@ describe Projects::MergeRequestsController do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when feature flag is disabled' do
|
||||
let(:accessibility_comparison) { { status: :parsed, data: { summary: 1 } } }
|
||||
|
||||
before do
|
||||
stub_feature_flags(accessibility_report_view: false)
|
||||
end
|
||||
|
||||
it 'returns 204 HTTP status' do
|
||||
subject
|
||||
|
||||
expect(response).to have_gitlab_http_status(:no_content)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when pipeline has jobs with accessibility reports' do
|
||||
before do
|
||||
allow_any_instance_of(MergeRequest)
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe 'Dashboard milestone tabs', :js do
|
||||
let(:user) { create(:user) }
|
||||
let(:project) { create(:project) }
|
||||
let!(:label) { create(:label, project: project) }
|
||||
let(:project_milestone) { create(:milestone, project: project) }
|
||||
let(:milestone) do
|
||||
DashboardMilestone.build(
|
||||
[project],
|
||||
project_milestone.title
|
||||
)
|
||||
end
|
||||
let!(:merge_request) { create(:labeled_merge_request, source_project: project, target_project: project, milestone: project_milestone, labels: [label]) }
|
||||
|
||||
before do
|
||||
project.add_maintainer(user)
|
||||
sign_in(user)
|
||||
|
||||
visit dashboard_milestone_path(milestone.safe_title, title: milestone.title)
|
||||
end
|
||||
|
||||
it 'loads merge requests async' do
|
||||
click_link 'Merge Requests'
|
||||
|
||||
expect(page).to have_selector('.milestone-merge_requests-list')
|
||||
end
|
||||
|
||||
it 'loads participants async' do
|
||||
click_link 'Participants'
|
||||
|
||||
expect(page).to have_selector('#tab-participants .bordered-list')
|
||||
end
|
||||
|
||||
it 'loads labels async' do
|
||||
click_link 'Labels'
|
||||
|
||||
expect(page).to have_selector('#tab-labels .bordered-list')
|
||||
end
|
||||
end
|
|
@ -102,11 +102,9 @@ describe 'Group milestones' do
|
|||
expect(find('.top-area .all .badge').text).to eq("6")
|
||||
end
|
||||
|
||||
it 'lists legacy group milestones and group milestones' do
|
||||
legacy_milestone = GroupMilestone.build_collection(group, group.projects, { state: 'active' }).first
|
||||
|
||||
it 'lists group and project milestones' do
|
||||
expect(page).to have_selector("#milestone_#{active_group_milestone.id}", count: 1)
|
||||
expect(page).to have_selector("#milestone_#{legacy_milestone.milestone.id}", count: 1)
|
||||
expect(page).to have_selector("#milestone_#{active_project_milestone2.id}", count: 1)
|
||||
end
|
||||
|
||||
it 'shows milestone detail and supports its edit' do
|
||||
|
@ -125,75 +123,35 @@ describe 'Group 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(
|
||||
'v1.0',
|
||||
href: project_milestone_path(project, active_project_milestone1)
|
||||
)
|
||||
expect(page).to have_link(
|
||||
'1 Issue',
|
||||
href: issues_group_path(group, milestone_title: 'v1.0')
|
||||
href: project_issues_path(project, milestone_title: 'v1.0')
|
||||
)
|
||||
expect(page).to have_link(
|
||||
'0 Merge Requests',
|
||||
href: merge_requests_group_path(group, milestone_title: 'v1.0')
|
||||
href: project_merge_requests_path(project, milestone_title: 'v1.0')
|
||||
)
|
||||
expect(page).to have_link(
|
||||
'GL-113',
|
||||
href: group_milestone_path(group, active_group_milestone)
|
||||
)
|
||||
expect(page).to have_link(
|
||||
'0 Issues',
|
||||
href: issues_group_path(group, milestone_title: 'GL-113')
|
||||
)
|
||||
expect(page).to have_link(
|
||||
'0 Merge Requests',
|
||||
href: merge_requests_group_path(group, milestone_title: 'GL-113')
|
||||
)
|
||||
end
|
||||
|
||||
it 'renders group milestone details' do
|
||||
click_link 'v1.0'
|
||||
|
||||
expect(page).to have_content('expires on Aug 20, 2114')
|
||||
expect(page).to have_content('v1.0')
|
||||
expect(page).to have_content('Issues 1 Open: 1 Closed: 0')
|
||||
expect(page).to have_link(issue.title, href: project_issue_path(issue.project, issue))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'milestone tabs', :js do
|
||||
context 'for a legacy group milestone' do
|
||||
let_it_be(:milestone) { create(:milestone, project: project) }
|
||||
let_it_be(:label) { create(:label, project: project) }
|
||||
let_it_be(:issue) { create(:labeled_issue, project: project, milestone: milestone, labels: [label], assignees: [create(:user)]) }
|
||||
let_it_be(:mr) { create(:merge_request, source_project: project, milestone: milestone) }
|
||||
|
||||
before do
|
||||
visit group_milestone_path(group, milestone.title, title: milestone.title)
|
||||
end
|
||||
|
||||
it 'renders the issues tab' do
|
||||
within('#tab-issues') do
|
||||
expect(page).to have_content issue.title
|
||||
end
|
||||
end
|
||||
|
||||
it 'renders the merge requests tab' do
|
||||
within('.js-milestone-tabs') do
|
||||
click_link('Merge Requests')
|
||||
end
|
||||
|
||||
within('#tab-merge-requests') do
|
||||
expect(page).to have_content mr.title
|
||||
end
|
||||
end
|
||||
|
||||
it 'renders the participants tab' do
|
||||
within('.js-milestone-tabs') do
|
||||
click_link('Participants')
|
||||
end
|
||||
|
||||
within('#tab-participants') do
|
||||
expect(page).to have_content issue.assignees.first.name
|
||||
end
|
||||
end
|
||||
|
||||
it 'renders the labels tab' do
|
||||
within('.js-milestone-tabs') do
|
||||
click_link('Labels')
|
||||
end
|
||||
|
||||
within('#tab-labels') do
|
||||
expect(page).to have_content label.title
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'for a group milestone' do
|
||||
let_it_be(:other_project) { create(:project_empty_repo, group: group) }
|
||||
let_it_be(:milestone) { create(:milestone, group: group) }
|
||||
|
|
|
@ -24,21 +24,12 @@ describe 'Milestones sorting', :js do
|
|||
|
||||
# assert default sorting
|
||||
within '.milestones' do
|
||||
expect(page.all('ul.content-list > li').first.text).to include('v2.0')
|
||||
expect(page.all('ul.content-list > li')[1].text).to include('v3.0')
|
||||
expect(page.all('ul.content-list > li').last.text).to include('v1.0')
|
||||
expect(page.all('ul.content-list > li strong > a').map(&:text)).to eq(['v2.0', 'v2.0', 'v3.0', 'v1.0', 'v1.0'])
|
||||
end
|
||||
|
||||
click_button 'Due soon'
|
||||
|
||||
sort_options = find('ul.dropdown-menu-sort li').all('a').collect(&:text)
|
||||
|
||||
expect(sort_options[0]).to eq('Due soon')
|
||||
expect(sort_options[1]).to eq('Due later')
|
||||
expect(sort_options[2]).to eq('Start soon')
|
||||
expect(sort_options[3]).to eq('Start later')
|
||||
expect(sort_options[4]).to eq('Name, ascending')
|
||||
expect(sort_options[5]).to eq('Name, descending')
|
||||
expect(find('ul.dropdown-menu-sort li').all('a').map(&:text)).to eq(['Due soon', 'Due later', 'Start soon', 'Start later', 'Name, ascending', 'Name, descending'])
|
||||
|
||||
click_link 'Due later'
|
||||
|
||||
|
@ -46,9 +37,7 @@ describe 'Milestones sorting', :js do
|
|||
|
||||
# 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')
|
||||
expect(page.all('ul.content-list > li').last.text).to include('v2.0')
|
||||
expect(page.all('ul.content-list > li strong > a').map(&:text)).to eq(['v1.0', 'v1.0', 'v3.0', 'v2.0', 'v2.0'])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -111,20 +111,6 @@ describe 'Milestone' do
|
|||
end
|
||||
end
|
||||
|
||||
describe 'deprecation popover', :js do
|
||||
it 'opens deprecation popover' do
|
||||
milestone = create(:milestone, project: project)
|
||||
|
||||
visit group_milestone_path(group, milestone, title: milestone.title)
|
||||
|
||||
expect(page).to have_selector('.milestone-deprecation-message')
|
||||
|
||||
find('.milestone-deprecation-message .js-popover-link').click
|
||||
|
||||
expect(page).to have_selector('.popover')
|
||||
end
|
||||
end
|
||||
|
||||
describe 'reopen closed milestones' do
|
||||
before do
|
||||
create(:milestone, :closed, project: project)
|
||||
|
|
|
@ -14,7 +14,7 @@ exports[`IDE pipeline stage renders stage details & icon 1`] = `
|
|||
/>
|
||||
|
||||
<strong
|
||||
class="prepend-left-8 text-truncate"
|
||||
class="gl-ml-3 text-truncate"
|
||||
data-container="body"
|
||||
data-original-title=""
|
||||
title=""
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe MilestoneArray do
|
||||
let(:object1) { instance_double("BirdMilestone", due_date: Time.now, start_date: Time.now - 15.days, title: 'v2.0') }
|
||||
let(:object2) { instance_double("CatMilestone", due_date: Time.now - 1.day, start_date: nil, title: 'v1.0') }
|
||||
let(:object3) { instance_double("DogMilestone", due_date: nil, start_date: Time.now - 30.days, title: 'v3.0') }
|
||||
let(:array) { [object1, object3, object2] }
|
||||
|
||||
describe '#sort' do
|
||||
it 'reorders array with due date in ascending order with nulls last' do
|
||||
expect(described_class.sort(array, 'due_date_asc')).to eq([object2, object1, object3])
|
||||
end
|
||||
|
||||
it 'reorders array with due date in desc order with nulls last' do
|
||||
expect(described_class.sort(array, 'due_date_desc')).to eq([object1, object2, object3])
|
||||
end
|
||||
|
||||
it 'reorders array with start date in ascending order with nulls last' do
|
||||
expect(described_class.sort(array, 'start_date_asc')).to eq([object3, object1, object2])
|
||||
end
|
||||
|
||||
it 'reorders array with start date in descending order with nulls last' do
|
||||
expect(described_class.sort(array, 'start_date_desc')).to eq([object1, object3, object2])
|
||||
end
|
||||
|
||||
it 'reorders array with title in ascending order' do
|
||||
expect(described_class.sort(array, 'name_asc')).to eq([object2, object1, object3])
|
||||
end
|
||||
|
||||
it 'reorders array with title in descending order' do
|
||||
expect(described_class.sort(array, 'name_desc')).to eq([object3, object1, object2])
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,208 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe GlobalMilestone do
|
||||
let(:user) { create(:user) }
|
||||
let(:user2) { create(:user) }
|
||||
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) }
|
||||
|
||||
describe '.build_collection' do
|
||||
let(:milestone1_due_date) { 2.weeks.from_now.to_date }
|
||||
|
||||
let!(:milestone1_project1) do
|
||||
create(
|
||||
:milestone,
|
||||
title: "Milestone v1.2",
|
||||
project: project1,
|
||||
due_date: milestone1_due_date
|
||||
)
|
||||
end
|
||||
|
||||
let!(:milestone1_project2) do
|
||||
create(
|
||||
:milestone,
|
||||
title: "Milestone v1.2",
|
||||
project: project2,
|
||||
due_date: milestone1_due_date
|
||||
)
|
||||
end
|
||||
|
||||
let!(:milestone1_project3) do
|
||||
create(
|
||||
:milestone,
|
||||
title: "Milestone v1.2",
|
||||
project: project3,
|
||||
due_date: milestone1_due_date
|
||||
)
|
||||
end
|
||||
|
||||
let!(:milestone2_project1) do
|
||||
create(
|
||||
:milestone,
|
||||
title: "VD-123",
|
||||
project: project1,
|
||||
due_date: nil
|
||||
)
|
||||
end
|
||||
|
||||
let!(:milestone2_project2) do
|
||||
create(
|
||||
:milestone,
|
||||
title: "VD-123",
|
||||
project: project2,
|
||||
due_date: nil
|
||||
)
|
||||
end
|
||||
|
||||
let!(:milestone2_project3) do
|
||||
create(
|
||||
:milestone,
|
||||
title: "VD-123",
|
||||
project: project3,
|
||||
due_date: nil
|
||||
)
|
||||
end
|
||||
|
||||
let!(:projects) do
|
||||
[
|
||||
project1,
|
||||
project2,
|
||||
project3
|
||||
]
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
it 'filters milestones by search_title when params[:search_title] is present' do
|
||||
global_milestones = described_class.build_collection(projects, { search_title: 'v1.2' })
|
||||
|
||||
expect(global_milestones.map(&:title)).to match_array(['Milestone v1.2', 'Milestone v1.2', 'Milestone v1.2'])
|
||||
end
|
||||
end
|
||||
|
||||
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 }
|
||||
|
||||
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
|
||||
|
||||
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) }
|
||||
|
||||
subject(:global_milestone) { described_class.new(milestone1_project1) }
|
||||
|
||||
it 'has exactly one group milestone' do
|
||||
expect(global_milestone.title).to eq('Milestone v1.2')
|
||||
end
|
||||
|
||||
it 'has all project milestones with the same title' do
|
||||
expect(global_milestone.milestone).to eq(milestone1_project1)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#safe_title' do
|
||||
let(:milestone) { create(:milestone, title: "git / test", project: project1) }
|
||||
|
||||
it 'strips out slashes and spaces' do
|
||||
global_milestone = described_class.new(milestone)
|
||||
|
||||
expect(global_milestone.safe_title).to eq('git-test')
|
||||
end
|
||||
end
|
||||
|
||||
describe '#state' do
|
||||
context 'when at least one milestone is active' do
|
||||
it 'returns active' do
|
||||
title = 'Active Group Milestone'
|
||||
|
||||
global_milestone = described_class.new(create(:active_milestone, title: title))
|
||||
|
||||
expect(global_milestone.state).to eq('active')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when all milestones are closed' do
|
||||
it 'returns closed' do
|
||||
title = 'Closed Group Milestone'
|
||||
|
||||
global_milestone = described_class.new(create(:closed_milestone, title: title))
|
||||
|
||||
expect(global_milestone.state).to eq('closed')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,57 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe GroupMilestone do
|
||||
let(:group) { create(:group) }
|
||||
let(:project) { create(:project, group: group) }
|
||||
let(:project_milestone) do
|
||||
create(:milestone, title: "Milestone v1.2", project: project)
|
||||
end
|
||||
|
||||
describe '.build' do
|
||||
it 'returns milestone with group assigned' do
|
||||
milestone = described_class.build(
|
||||
group,
|
||||
[project],
|
||||
project_milestone.title
|
||||
)
|
||||
|
||||
expect(milestone.group).to eq group
|
||||
end
|
||||
end
|
||||
|
||||
describe '.build_collection' do
|
||||
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
|
|
@ -1651,14 +1651,6 @@ describe MergeRequest do
|
|||
let(:merge_request) { create(:merge_request, :with_accessibility_reports, source_project: project) }
|
||||
|
||||
it { is_expected.to be_truthy }
|
||||
|
||||
context 'when feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(accessibility_report_view: false)
|
||||
end
|
||||
|
||||
it { is_expected.to be_falsey }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when head pipeline does not have accessibility reports' do
|
||||
|
|
|
@ -2,15 +2,7 @@
|
|||
|
||||
RSpec.shared_examples 'milestone tabs' do
|
||||
def go(path, extra_params = {})
|
||||
params =
|
||||
case milestone
|
||||
when DashboardMilestone
|
||||
{ id: milestone.safe_title, title: milestone.title }
|
||||
when GroupMilestone
|
||||
{ group_id: group.to_param, id: milestone.safe_title, title: milestone.title }
|
||||
else
|
||||
{ namespace_id: project.namespace.to_param, project_id: project, id: milestone.iid }
|
||||
end
|
||||
params = { namespace_id: project.namespace.to_param, project_id: project, id: milestone.iid }
|
||||
|
||||
get path, params: params.merge(extra_params)
|
||||
end
|
||||
|
|
|
@ -50,7 +50,7 @@ describe 'layouts/_head' do
|
|||
it 'adds a link preconnect tag' do
|
||||
render
|
||||
|
||||
expect(rendered).to match(%Q(<link crossorigin="" href="#{asset_host}" rel="preconnnect">))
|
||||
expect(rendered).to match(%Q(<link crossorigin="" href="#{asset_host}" rel="preconnect">))
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -12,22 +12,6 @@ describe 'shared/milestones/_top.html.haml' do
|
|||
allow(milestone).to receive(:milestone) { milestone }
|
||||
end
|
||||
|
||||
it 'renders a deprecation message for a legacy milestone' do
|
||||
allow(milestone).to receive(:legacy_group_milestone?) { true }
|
||||
|
||||
render 'shared/milestones/top', milestone: milestone
|
||||
|
||||
expect(rendered).to have_css('.milestone-deprecation-message')
|
||||
end
|
||||
|
||||
it 'renders a deprecation message for a dashboard milestone' do
|
||||
allow(milestone).to receive(:dashboard_milestone?) { true }
|
||||
|
||||
render 'shared/milestones/top', milestone: milestone
|
||||
|
||||
expect(rendered).to have_css('.milestone-deprecation-message')
|
||||
end
|
||||
|
||||
it 'does not render a deprecation message for a non-legacy and non-dashboard milestone' do
|
||||
assign :group, group
|
||||
|
||||
|
|
Loading…
Reference in New Issue