Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
beab869416
commit
248057a54a
|
@ -1,3 +1,4 @@
|
|||
import TagSortDropdown from '~/tags';
|
||||
import { initRemoveTag } from '../remove_tag';
|
||||
|
||||
initRemoveTag({
|
||||
|
@ -5,3 +6,4 @@ initRemoveTag({
|
|||
document.querySelector(`[data-path="${path}"]`).closest('.js-tag-list').remove();
|
||||
},
|
||||
});
|
||||
TagSortDropdown();
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
<script>
|
||||
import { GlDropdown, GlDropdownItem, GlSearchBoxByClick } from '@gitlab/ui';
|
||||
import { mergeUrlParams, visitUrl, getParameterValues } from '~/lib/utils/url_utility';
|
||||
import { s__ } from '~/locale';
|
||||
|
||||
export default {
|
||||
i18n: {
|
||||
searchPlaceholder: s__('TagsPage|Filter by tag name'),
|
||||
},
|
||||
components: {
|
||||
GlDropdown,
|
||||
GlDropdownItem,
|
||||
GlSearchBoxByClick,
|
||||
},
|
||||
inject: ['sortOptions', 'filterTagsPath'],
|
||||
data() {
|
||||
return {
|
||||
selectedKey: 'updated_desc',
|
||||
searchTerm: '',
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
selectedSortMethod() {
|
||||
return this.sortOptions[this.selectedKey];
|
||||
},
|
||||
},
|
||||
created() {
|
||||
const sortValue = getParameterValues('sort');
|
||||
const searchValue = getParameterValues('search');
|
||||
|
||||
if (sortValue.length > 0) {
|
||||
[this.selectedKey] = sortValue;
|
||||
}
|
||||
|
||||
if (searchValue.length > 0) {
|
||||
[this.searchTerm] = searchValue;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
isSortMethodSelected(sortKey) {
|
||||
return sortKey === this.selectedKey;
|
||||
},
|
||||
visitUrlFromOption(sortKey) {
|
||||
this.selectedKey = sortKey;
|
||||
const urlParams = {};
|
||||
|
||||
urlParams.search = this.searchTerm.length > 0 ? this.searchTerm : null;
|
||||
urlParams.sort = sortKey;
|
||||
|
||||
const newUrl = mergeUrlParams(urlParams, this.filterTagsPath);
|
||||
visitUrl(newUrl);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<div class="gl-display-flex gl-pr-3">
|
||||
<gl-search-box-by-click
|
||||
v-model="searchTerm"
|
||||
:placeholder="$options.i18n.searchPlaceholder"
|
||||
class="gl-pr-3"
|
||||
data-testid="tag-search"
|
||||
@submit="visitUrlFromOption(selectedKey)"
|
||||
/>
|
||||
<gl-dropdown :text="selectedSortMethod" right data-testid="tags-dropdown">
|
||||
<gl-dropdown-item
|
||||
v-for="(value, key) in sortOptions"
|
||||
:key="key"
|
||||
:is-checked="isSortMethodSelected(key)"
|
||||
is-check-item
|
||||
@click="visitUrlFromOption(key)"
|
||||
>
|
||||
{{ value }}
|
||||
</gl-dropdown-item>
|
||||
</gl-dropdown>
|
||||
</div>
|
||||
</template>
|
|
@ -0,0 +1,24 @@
|
|||
import Vue from 'vue';
|
||||
import SortDropdown from './components/sort_dropdown.vue';
|
||||
|
||||
const mountDropdownApp = (el) => {
|
||||
const { sortOptions, filterTagsPath } = el.dataset;
|
||||
|
||||
return new Vue({
|
||||
el,
|
||||
name: 'SortTagsDropdownApp',
|
||||
components: {
|
||||
SortDropdown,
|
||||
},
|
||||
provide: {
|
||||
sortOptions: JSON.parse(sortOptions),
|
||||
filterTagsPath,
|
||||
},
|
||||
render: (createElement) => createElement(SortDropdown),
|
||||
});
|
||||
};
|
||||
|
||||
export default () => {
|
||||
const el = document.getElementById('js-tags-sort-dropdown');
|
||||
return el ? mountDropdownApp(el) : null;
|
||||
};
|
|
@ -9,6 +9,9 @@ class Projects::TagsController < Projects::ApplicationController
|
|||
before_action :require_non_empty_project
|
||||
before_action :authorize_download_code!
|
||||
before_action :authorize_admin_tag!, only: [:new, :create, :destroy]
|
||||
before_action do
|
||||
push_frontend_feature_flag(:gldropdown_tags, default_enabled: :yaml)
|
||||
end
|
||||
|
||||
feature_category :source_code_management, [:index, :show, :new, :destroy]
|
||||
feature_category :release_evidence, [:create]
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
module Clusters
|
||||
module Applications
|
||||
class Prometheus < ApplicationRecord
|
||||
include PrometheusAdapter
|
||||
include ::Clusters::Concerns::PrometheusClient
|
||||
|
||||
VERSION = '10.4.1'
|
||||
|
||||
|
@ -58,14 +58,6 @@ module Clusters
|
|||
'https://gitlab-org.gitlab.io/cluster-integration/helm-stable-archive'
|
||||
end
|
||||
|
||||
def service_name
|
||||
'prometheus-prometheus-server'
|
||||
end
|
||||
|
||||
def service_port
|
||||
80
|
||||
end
|
||||
|
||||
def install_command
|
||||
helm_command_module::InstallCommand.new(
|
||||
name: name,
|
||||
|
@ -106,29 +98,6 @@ module Clusters
|
|||
files.merge('values.yaml': replaced_values)
|
||||
end
|
||||
|
||||
def prometheus_client
|
||||
return unless kube_client
|
||||
|
||||
proxy_url = kube_client.proxy_url('service', service_name, service_port, Gitlab::Kubernetes::Helm::NAMESPACE)
|
||||
|
||||
# ensures headers containing auth data are appended to original k8s client options
|
||||
options = kube_client.rest_client.options
|
||||
.merge(prometheus_client_default_options)
|
||||
.merge(headers: kube_client.headers)
|
||||
Gitlab::PrometheusClient.new(proxy_url, options)
|
||||
rescue Kubeclient::HttpError, Errno::ECONNRESET, Errno::ECONNREFUSED, Errno::ENETUNREACH
|
||||
# If users have mistakenly set parameters or removed the depended clusters,
|
||||
# `proxy_url` could raise an exception because gitlab can not communicate with the cluster.
|
||||
# Since `PrometheusAdapter#can_query?` is eargely loaded on environement pages in gitlab,
|
||||
# we need to silence the exceptions
|
||||
end
|
||||
|
||||
def configured?
|
||||
kube_client.present? && available?
|
||||
rescue Gitlab::UrlBlocker::BlockedUrlError
|
||||
false
|
||||
end
|
||||
|
||||
def generate_alert_manager_token!
|
||||
unless alert_manager_token.present?
|
||||
update!(alert_manager_token: generate_token)
|
||||
|
@ -146,10 +115,6 @@ module Clusters
|
|||
.perform_async(cluster_id, ::PrometheusService.to_param) # rubocop:disable CodeReuse/ServiceClass
|
||||
end
|
||||
|
||||
def kube_client
|
||||
cluster&.kubeclient&.core_client
|
||||
end
|
||||
|
||||
def install_knative_metrics
|
||||
return [] unless cluster.application_knative_available?
|
||||
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Clusters
|
||||
module Concerns
|
||||
module PrometheusClient
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
include PrometheusAdapter
|
||||
|
||||
def service_name
|
||||
'prometheus-prometheus-server'
|
||||
end
|
||||
|
||||
def service_port
|
||||
80
|
||||
end
|
||||
|
||||
def prometheus_client
|
||||
return unless kube_client
|
||||
|
||||
proxy_url = kube_client.proxy_url('service', service_name, service_port, Gitlab::Kubernetes::Helm::NAMESPACE)
|
||||
|
||||
# ensures headers containing auth data are appended to original k8s client options
|
||||
options = kube_client.rest_client.options
|
||||
.merge(prometheus_client_default_options)
|
||||
.merge(headers: kube_client.headers)
|
||||
Gitlab::PrometheusClient.new(proxy_url, options)
|
||||
rescue Kubeclient::HttpError, Errno::ECONNRESET, Errno::ECONNREFUSED, Errno::ENETUNREACH
|
||||
# If users have mistakenly set parameters or removed the depended clusters,
|
||||
# `proxy_url` could raise an exception because gitlab can not communicate with the cluster.
|
||||
# Since `PrometheusAdapter#can_query?` is eargely loaded on environement pages in gitlab,
|
||||
# we need to silence the exceptions
|
||||
end
|
||||
|
||||
def configured?
|
||||
kube_client.present? && available?
|
||||
rescue Gitlab::UrlBlocker::BlockedUrlError
|
||||
false
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def kube_client
|
||||
cluster&.kubeclient&.core_client
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -127,7 +127,7 @@ class User < ApplicationRecord
|
|||
|
||||
# Groups
|
||||
has_many :members
|
||||
has_many :group_members, -> { where(requested_at: nil).where("access_level >= ?", Gitlab::Access::GUEST) }, source: 'GroupMember'
|
||||
has_many :group_members, -> { where(requested_at: nil).where("access_level >= ?", Gitlab::Access::GUEST) }, class_name: 'GroupMember'
|
||||
has_many :groups, through: :group_members
|
||||
has_many :owned_groups, -> { where(members: { access_level: Gitlab::Access::OWNER }) }, through: :group_members, source: :group
|
||||
has_many :maintainers_groups, -> { where(members: { access_level: Gitlab::Access::MAINTAINER }) }, through: :group_members, source: :group
|
||||
|
@ -141,7 +141,7 @@ class User < ApplicationRecord
|
|||
-> { where(members: { access_level: [Gitlab::Access::REPORTER, Gitlab::Access::DEVELOPER, Gitlab::Access::MAINTAINER, Gitlab::Access::OWNER] }) },
|
||||
through: :group_members,
|
||||
source: :group
|
||||
has_many :minimal_access_group_members, -> { where(access_level: [Gitlab::Access::MINIMAL_ACCESS]) }, source: 'GroupMember', class_name: 'GroupMember'
|
||||
has_many :minimal_access_group_members, -> { where(access_level: [Gitlab::Access::MINIMAL_ACCESS]) }, class_name: 'GroupMember'
|
||||
has_many :minimal_access_groups, through: :minimal_access_group_members, source: :group
|
||||
|
||||
# Projects
|
||||
|
|
|
@ -38,7 +38,7 @@ module Issues
|
|||
user_agent_detail_service.create
|
||||
resolve_discussions_with_issue(issue)
|
||||
|
||||
if Feature.disabled?(:issue_perform_after_creation_tasks_async, issue.project)
|
||||
if Feature.disabled?(:issue_perform_after_creation_tasks_async, issue.project, default_enabled: :yaml)
|
||||
Issues::AfterCreateService
|
||||
.new(issue.project, current_user)
|
||||
.execute(issue)
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
.row.gl-mt-3.gl-mb-3
|
||||
.col-lg-3
|
||||
%h4.gl-mt-0
|
||||
Recent Deliveries
|
||||
%p When an event in GitLab triggers a webhook, you can use the request details to figure out if something went wrong.
|
||||
= _('Recent Deliveries')
|
||||
%p= _('When an event in GitLab triggers a webhook, you can use the request details to figure out if something went wrong.')
|
||||
.col-lg-9
|
||||
- if hook_logs.any?
|
||||
%table.table
|
||||
%thead
|
||||
%tr
|
||||
%th Status
|
||||
%th Trigger
|
||||
%th URL
|
||||
%th Elapsed time
|
||||
%th Request time
|
||||
%th= _('Status')
|
||||
%th= _('Trigger')
|
||||
%th= _('URL')
|
||||
%th= _('Elapsed time')
|
||||
%th= _('Request time')
|
||||
%th
|
||||
- hook_logs.each do |hook_log|
|
||||
%tr
|
||||
|
@ -28,10 +28,10 @@
|
|||
%td.light
|
||||
= time_ago_with_tooltip(hook_log.created_at)
|
||||
%td
|
||||
= link_to 'View details', admin_hook_hook_log_path(hook, hook_log)
|
||||
= link_to _('View details'), admin_hook_hook_log_path(hook, hook_log)
|
||||
|
||||
= paginate hook_logs, theme: 'gitlab'
|
||||
|
||||
- else
|
||||
.settings-message.text-center
|
||||
You don't have any webhooks deliveries
|
||||
= _("You don't have any webhooks deliveries")
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
- @projects.each_with_index do |project|
|
||||
%li.project-row{ class: ('no-description' if project.description.blank?) }
|
||||
.controls
|
||||
= link_to 'Edit', edit_project_path(project), id: "edit_#{dom_id(project)}", class: "btn gl-button btn-default"
|
||||
= link_to _('Edit'), edit_project_path(project), id: "edit_#{dom_id(project)}", class: "btn gl-button btn-default"
|
||||
%button.delete-project-button.gl-button.btn.btn-danger{ data: { delete_project_url: admin_project_path(project), project_name: project.name } }
|
||||
= s_('AdminProjects|Delete')
|
||||
|
||||
|
@ -31,6 +31,6 @@
|
|||
|
||||
= paginate @projects, theme: 'gitlab'
|
||||
- else
|
||||
.nothing-here-block No projects found
|
||||
.nothing-here-block= _('No projects found')
|
||||
|
||||
#delete-project-modal
|
||||
|
|
|
@ -3,15 +3,15 @@
|
|||
.table-mobile-header{ role: 'rowheader' }= _('Type')
|
||||
.table-mobile-content
|
||||
- if runner.instance_type?
|
||||
%span.badge.badge-pill.gl-badge.sm.badge-success shared
|
||||
%span.badge.badge-pill.gl-badge.sm.badge-success= _("shared")
|
||||
- elsif runner.group_type?
|
||||
%span.badge.badge-pill.gl-badge.sm.badge-success group
|
||||
%span.badge.badge-pill.gl-badge.sm.badge-success= _("group")
|
||||
- else
|
||||
%span.badge.badge-pill.gl-badge.sm.badge-info specific
|
||||
%span.badge.badge-pill.gl-badge.sm.badge-info= _("specific")
|
||||
- if runner.locked?
|
||||
%span.badge.badge-pill.gl-badge.sm.badge-warning locked
|
||||
%span.badge.badge-pill.gl-badge.sm.badge-warning= _("locked")
|
||||
- unless runner.active?
|
||||
%span.badge.badge-pill.gl-badge.sm.badge-danger paused
|
||||
%span.badge.badge-pill.gl-badge.sm.badge-danger= _("paused")
|
||||
|
||||
.table-section.section-10
|
||||
.table-mobile-header{ role: 'rowheader' }= _('Runner token')
|
||||
|
|
|
@ -9,20 +9,23 @@
|
|||
= s_('TagsPage|Tags give the ability to mark specific points in history as being important')
|
||||
|
||||
.nav-controls
|
||||
= form_tag(filter_tags_path, method: :get) do
|
||||
= search_field_tag :search, params[:search], { placeholder: s_('TagsPage|Filter by tag name'), id: 'tag-search', class: 'form-control search-text-input input-short', spellcheck: false }
|
||||
- unless Gitlab::Ci::Features.gldropdown_tags_enabled?
|
||||
= form_tag(filter_tags_path, method: :get) do
|
||||
= search_field_tag :search, params[:search], { placeholder: s_('TagsPage|Filter by tag name'), id: 'tag-search', class: 'form-control search-text-input input-short', spellcheck: false }
|
||||
|
||||
.dropdown
|
||||
%button.dropdown-menu-toggle{ type: 'button', data: { toggle: 'dropdown'} }
|
||||
%span.light
|
||||
= tags_sort_options_hash[@sort]
|
||||
= sprite_icon('chevron-down', css_class: 'dropdown-menu-toggle-icon gl-top-3')
|
||||
%ul.dropdown-menu.dropdown-menu-right.dropdown-menu-selectable
|
||||
%li.dropdown-header
|
||||
= s_('TagsPage|Sort by')
|
||||
- tags_sort_options_hash.each do |value, title|
|
||||
%li
|
||||
= link_to title, filter_tags_path(sort: value), class: ("is-active" if @sort == value)
|
||||
.dropdown
|
||||
%button.dropdown-menu-toggle{ type: 'button', data: { toggle: 'dropdown'} }
|
||||
%span.light
|
||||
= tags_sort_options_hash[@sort]
|
||||
= sprite_icon('chevron-down', css_class: 'dropdown-menu-toggle-icon gl-top-3')
|
||||
%ul.dropdown-menu.dropdown-menu-right.dropdown-menu-selectable
|
||||
%li.dropdown-header
|
||||
= s_('TagsPage|Sort by')
|
||||
- tags_sort_options_hash.each do |value, title|
|
||||
%li
|
||||
= link_to title, filter_tags_path(sort: value), class: ("is-active" if @sort == value)
|
||||
- else
|
||||
#js-tags-sort-dropdown{ data: { filter_tags_path: filter_tags_path, sort_options: tags_sort_options_hash.to_json } }
|
||||
- if can?(current_user, :admin_tag, @project)
|
||||
= link_to new_project_tag_path(@project), class: 'btn gl-button btn-confirm', data: { qa_selector: "new_tag_button" } do
|
||||
= s_('TagsPage|New tag')
|
||||
|
|
|
@ -17,7 +17,7 @@ class NewIssueWorker # rubocop:disable Scalability/IdempotentWorker
|
|||
|
||||
issuable.create_cross_references!(user)
|
||||
|
||||
if Feature.enabled?(:issue_perform_after_creation_tasks_async, issuable.project)
|
||||
if Feature.enabled?(:issue_perform_after_creation_tasks_async, issuable.project, default_enabled: :yaml)
|
||||
Issues::AfterCreateService
|
||||
.new(issuable.project, user)
|
||||
.execute(issuable)
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Externalize strings in hook_logs/_index.html.haml
|
||||
merge_request: 58155
|
||||
author: nuwe1
|
||||
type: other
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Externalize strings in projects/_projects.html.haml
|
||||
merge_request: 58158
|
||||
author: nuwe1
|
||||
type: other
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Externalise strings in runners/_runner.html.haml
|
||||
merge_request: 58168
|
||||
author: nuwe1
|
||||
type: other
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: gldropdown_tags
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/58589
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/327055
|
||||
milestone: '13.11'
|
||||
type: development
|
||||
group: group::continuous integration
|
||||
default_enabled: false
|
|
@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/21140
|
|||
milestone: '13.11'
|
||||
type: development
|
||||
group: group::geo
|
||||
default_enabled: false
|
||||
default_enabled: true
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
---
|
||||
key_path: counts.confidential_epics
|
||||
description:
|
||||
product_section: dev
|
||||
product_stage: plan
|
||||
product_group: group::portfolio management
|
||||
product_category:
|
||||
value_type: number
|
||||
status: data_available
|
||||
time_frame: all
|
||||
data_source: database
|
||||
distribution:
|
||||
- ce
|
||||
tier:
|
||||
- free
|
||||
skip_validation: true
|
|
@ -1,16 +0,0 @@
|
|||
---
|
||||
key_path: counts.epics
|
||||
description:
|
||||
product_section: dev
|
||||
product_stage: plan
|
||||
product_group: group::portfolio management
|
||||
product_category:
|
||||
value_type: number
|
||||
status: data_available
|
||||
time_frame: all
|
||||
data_source: database
|
||||
distribution:
|
||||
- ce
|
||||
tier:
|
||||
- free
|
||||
skip_validation: true
|
|
@ -1,16 +0,0 @@
|
|||
---
|
||||
key_path: counts.issues_with_health_status
|
||||
description:
|
||||
product_section: dev
|
||||
product_stage: plan
|
||||
product_group: group::portfolio management
|
||||
product_category:
|
||||
value_type: number
|
||||
status: data_available
|
||||
time_frame: all
|
||||
data_source: database
|
||||
distribution:
|
||||
- ce
|
||||
tier:
|
||||
- free
|
||||
skip_validation: true
|
|
@ -921,6 +921,7 @@ cluster.
|
|||
> - [Made generally available and enabled by default](https://gitlab.com/gitlab-org/gitaly/-/issues/2951) in GitLab 13.3.
|
||||
> - [Disabled by default](https://gitlab.com/gitlab-org/gitaly/-/issues/3178) in GitLab 13.5.
|
||||
> - [Enabled by default](https://gitlab.com/gitlab-org/gitaly/-/issues/3334) in GitLab 13.8.
|
||||
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitaly/-/issues/3383) in GitLab 13.11.
|
||||
|
||||
Praefect supports distribution of read operations across Gitaly nodes that are
|
||||
configured for the virtual node.
|
||||
|
|
|
@ -790,15 +790,15 @@ Tiers: `free`
|
|||
|
||||
### `counts.confidential_epics`
|
||||
|
||||
Missing description
|
||||
Count of confidential epics
|
||||
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_all/20210216181205_confidential_epics.yml)
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/counts_all/20210216181205_confidential_epics.yml)
|
||||
|
||||
Group: `group::portfolio management`
|
||||
Group: `group::product planning`
|
||||
|
||||
Status: `data_available`
|
||||
|
||||
Tiers: `free`
|
||||
Tiers: `premium`, `ultimate`
|
||||
|
||||
### `counts.container_scanning_jobs`
|
||||
|
||||
|
@ -962,7 +962,7 @@ Count of issues that are assigned to an epic
|
|||
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/counts_all/20210216181208_epic_issues.yml)
|
||||
|
||||
Group: `group::portfolio management`
|
||||
Group: `group::product planning`
|
||||
|
||||
Status: `data_available`
|
||||
|
||||
|
@ -970,27 +970,27 @@ Tiers: `premium`, `ultimate`
|
|||
|
||||
### `counts.epics`
|
||||
|
||||
Missing description
|
||||
Count of all epics
|
||||
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_all/20210216181206_epics.yml)
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/counts_all/20210216181206_epics.yml)
|
||||
|
||||
Group: `group::portfolio management`
|
||||
Group: `group::product planning`
|
||||
|
||||
Status: `data_available`
|
||||
|
||||
Tiers: `free`
|
||||
Tiers: `premium`, `ultimate`
|
||||
|
||||
### `counts.epics_deepest_relationship_level`
|
||||
|
||||
Missing description
|
||||
Count of the deepest relationship level for epics
|
||||
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/counts_all/20210216181212_epics_deepest_relationship_level.yml)
|
||||
|
||||
Group: `group::portfolio management`
|
||||
Group: `group::product planning`
|
||||
|
||||
Status: `data_available`
|
||||
|
||||
Tiers:
|
||||
Tiers: `ultimate`
|
||||
|
||||
### `counts.failed_deployments`
|
||||
|
||||
|
@ -2698,15 +2698,15 @@ Tiers: `free`, `premium`, `ultimate`
|
|||
|
||||
### `counts.issues_with_health_status`
|
||||
|
||||
Missing description
|
||||
Count of issues with health status
|
||||
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_all/20210216181210_issues_with_health_status.yml)
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/counts_all/20210216181210_issues_with_health_status.yml)
|
||||
|
||||
Group: `group::portfolio management`
|
||||
Group: `group::product planning`
|
||||
|
||||
Status: `data_available`
|
||||
|
||||
Tiers: `free`
|
||||
Tiers: `ultimate`
|
||||
|
||||
### `counts.jira_imports_projects_count`
|
||||
|
||||
|
|
|
@ -571,9 +571,8 @@ Instructions are available in the [legacy template project](https://gitlab.com/g
|
|||
|
||||
This is the current default behavior, because the job's status indicates success or failure of the analyzer itself.
|
||||
Analyzer results are displayed in the [job logs](../../ci/jobs/index.md#expand-and-collapse-job-log-sections),
|
||||
[Merge Request widget](sast/index.md)
|
||||
[Merge Request widget](#view-security-scan-information-in-merge-requests)
|
||||
or [Security Dashboard](security_dashboard/index.md).
|
||||
There is [an open issue](https://gitlab.com/gitlab-org/gitlab/-/issues/235772) in which changes to this behavior are being discussed.
|
||||
|
||||
### Error: job `is used for configuration only, and its script should not be executed`
|
||||
|
||||
|
|
|
@ -262,6 +262,9 @@ To view the activity feed in Atom format, select the
|
|||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/18328) in GitLab 12.7.
|
||||
|
||||
NOTE:
|
||||
In GitLab 13.11, you can [replace this form with a modal window](#share-a-group-modal-window).
|
||||
|
||||
Similar to how you [share a project with a group](../project/members/share_project_with_groups.md),
|
||||
you can share a group with another group. Members get direct access
|
||||
to the shared group. This is not valid for inherited members.
|
||||
|
@ -278,6 +281,27 @@ To share a given group, for example, `Frontend` with another group, for example,
|
|||
|
||||
All the members of the `Engineering` group are added to the `Frontend` group.
|
||||
|
||||
### Share a group modal window
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/247208) in GitLab 13.11.
|
||||
> - [Deployed behind a feature flag](../feature_flags.md), disabled by default.
|
||||
> - Enabled on GitLab.com.
|
||||
> - Recommended for production use.
|
||||
> - Replaces the existing form with buttons to open a modal window.
|
||||
> - To use in GitLab self-managed instances, ask a GitLab administrator to [enable it](../project/members/index.md#enable-or-disable-modal-window). **(FREE SELF)**
|
||||
|
||||
WARNING:
|
||||
This feature might not be available to you. Check the **version history** note above for details.
|
||||
|
||||
In GitLab 13.11, you can optionally replace the sharing form with a modal window.
|
||||
To share a group after enabling this feature:
|
||||
|
||||
1. Go to your group's page.
|
||||
1. In the left sidebar, go to **Members**, and then select **Invite a group**.
|
||||
1. Select a group, and select a **Max access level**.
|
||||
1. (Optional) Select an **Access expiration date**.
|
||||
1. Select **Invite**.
|
||||
|
||||
## Manage group memberships via LDAP **(PREMIUM SELF)**
|
||||
|
||||
Group syncing allows LDAP groups to be mapped to GitLab groups. This provides more control over per-group user management. To configure group syncing, edit the `group_base` **DN** (`'OU=Global Groups,OU=GitLab INT,DC=GitLab,DC=org'`). This **OU** contains all groups that will be associated with GitLab groups.
|
||||
|
|
|
@ -109,6 +109,9 @@ had on the project you imported from are retained.
|
|||
|
||||
## Invite people using their e-mail address
|
||||
|
||||
NOTE:
|
||||
In GitLab 13.11, you can [replace this form with a modal window](#add-a-member-modal-window).
|
||||
|
||||
If a user you want to give access to doesn't have an account on your GitLab
|
||||
instance, you can invite them just by typing their e-mail address in the
|
||||
user search field.
|
||||
|
@ -135,6 +138,46 @@ GitLab account using the same e-mail address the invitation was sent to.
|
|||
NOTE:
|
||||
Unaccepted invites are automatically deleted after 90 days.
|
||||
|
||||
### Add a member modal window
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/247208) in GitLab 13.11.
|
||||
> - [Deployed behind a feature flag](../../feature_flags.md), disabled by default.
|
||||
> - Enabled on GitLab.com.
|
||||
> - Recommended for production use.
|
||||
> - Replaces the existing form with buttons to open a modal window.
|
||||
> - To use in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-modal-window). **(FREE SELF)**
|
||||
|
||||
WARNING:
|
||||
This feature might not be available to you. Check the **version history** note above for details.
|
||||
|
||||
In GitLab 13.11, you can optionally replace the form to add a member with a modal window.
|
||||
To add a member after enabling this feature:
|
||||
|
||||
1. Go to your project's page.
|
||||
1. In the left sidebar, go to **Members**, and then select **Invite members**.
|
||||
1. Enter an email address, and select a role permission for this user.
|
||||
1. (Optional) Select an **Access expiration date**.
|
||||
1. Select **Invite**.
|
||||
|
||||
### Enable or disable modal window **(FREE SELF)**
|
||||
|
||||
The modal window for adding a member is under development and is ready for production use. It is
|
||||
deployed behind a feature flag that is **disabled by default**.
|
||||
[GitLab administrators with access to the GitLab Rails console](../../../administration/feature_flags.md)
|
||||
can enable it.
|
||||
|
||||
To enable it:
|
||||
|
||||
```ruby
|
||||
Feature.enable(:invite_members_group_modal)
|
||||
```
|
||||
|
||||
To disable it:
|
||||
|
||||
```ruby
|
||||
Feature.disable(:invite_members_group_modal)
|
||||
```
|
||||
|
||||
## Project membership and requesting access
|
||||
|
||||
Project owners can :
|
||||
|
|
|
@ -17,6 +17,9 @@ members.
|
|||
|
||||
## Sharing a project with a group of users
|
||||
|
||||
NOTE:
|
||||
In GitLab 13.11, you can [replace this form with a modal window](#share-a-project-modal-window).
|
||||
|
||||
The primary mechanism to give a group of users, say 'Engineering', access to a project,
|
||||
say 'Project Acme', in GitLab is to make the 'Engineering' group the owner of 'Project
|
||||
Acme'. But what if 'Project Acme' already belongs to another group, say 'Open Source'?
|
||||
|
@ -48,6 +51,46 @@ Note that you can only share a project with:
|
|||
|
||||
Administrators are able to share projects with any group in the system.
|
||||
|
||||
### Share a project modal window
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/247208) in GitLab 13.11.
|
||||
> - [Deployed behind a feature flag](../../feature_flags.md), disabled by default.
|
||||
> - Enabled on GitLab.com.
|
||||
> - Recommended for production use.
|
||||
> - Replaces the existing form with buttons to open a modal window.
|
||||
> - To use in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-modal-window). **(FREE SELF)**
|
||||
|
||||
WARNING:
|
||||
This feature might not be available to you. Check the **version history** note above for details.
|
||||
|
||||
In GitLab 13.11, you can optionally replace the sharing form with a modal window.
|
||||
To share a project after enabling this feature:
|
||||
|
||||
1. Go to your project's page.
|
||||
1. In the left sidebar, go to **Members**, and then select **Invite a group**.
|
||||
1. Select a group, and select a **Max access level**.
|
||||
1. (Optional) Select an **Access expiration date**.
|
||||
1. Select **Invite**.
|
||||
|
||||
### Enable or disable modal window **(FREE SELF)**
|
||||
|
||||
The modal window for sharing a project is under development and is ready for production use. It is
|
||||
deployed behind a feature flag that is **disabled by default**.
|
||||
[GitLab administrators with access to the GitLab Rails console](../../../administration/feature_flags.md)
|
||||
can enable it.
|
||||
|
||||
To enable it:
|
||||
|
||||
```ruby
|
||||
Feature.enable(:invite_members_group_modal)
|
||||
```
|
||||
|
||||
To disable it:
|
||||
|
||||
```ruby
|
||||
Feature.disable(:invite_members_group_modal)
|
||||
```
|
||||
|
||||
## Maximum access level
|
||||
|
||||
In the example above, the maximum access level of 'Developer' for members from 'Engineering' means that users with higher access levels in 'Engineering' ('Maintainer' or 'Owner') only have 'Developer' access to 'Project Acme'.
|
||||
|
|
|
@ -63,6 +63,10 @@ module Gitlab
|
|||
def self.multiple_cache_per_job?
|
||||
::Feature.enabled?(:multiple_cache_per_job, default_enabled: :yaml)
|
||||
end
|
||||
|
||||
def self.gldropdown_tags_enabled?
|
||||
::Feature.enabled?(:gldropdown_tags, default_enabled: :yaml)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -11547,6 +11547,9 @@ msgstr ""
|
|||
msgid "Editor Lite instance is required to set up an extension."
|
||||
msgstr ""
|
||||
|
||||
msgid "Elapsed time"
|
||||
msgstr ""
|
||||
|
||||
msgid "Elasticsearch AWS IAM credentials"
|
||||
msgstr ""
|
||||
|
||||
|
@ -21356,6 +21359,9 @@ msgstr ""
|
|||
msgid "No prioritized labels with such name or description"
|
||||
msgstr ""
|
||||
|
||||
msgid "No projects found"
|
||||
msgstr ""
|
||||
|
||||
msgid "No public groups"
|
||||
msgstr ""
|
||||
|
||||
|
@ -25767,6 +25773,9 @@ msgstr ""
|
|||
msgid "Recent Activity"
|
||||
msgstr ""
|
||||
|
||||
msgid "Recent Deliveries"
|
||||
msgstr ""
|
||||
|
||||
msgid "Recent Project Activity"
|
||||
msgstr ""
|
||||
|
||||
|
@ -26580,6 +26589,9 @@ msgstr ""
|
|||
msgid "Request review from"
|
||||
msgstr ""
|
||||
|
||||
msgid "Request time"
|
||||
msgstr ""
|
||||
|
||||
msgid "Request to link SAML account must be authorized"
|
||||
msgstr ""
|
||||
|
||||
|
@ -35018,6 +35030,9 @@ msgstr ""
|
|||
msgid "When a runner is locked, it cannot be assigned to other projects"
|
||||
msgstr ""
|
||||
|
||||
msgid "When an event in GitLab triggers a webhook, you can use the request details to figure out if something went wrong."
|
||||
msgstr ""
|
||||
|
||||
msgid "When enabled, any user visiting %{host} and creating an account will have to be explicitly approved by an admin before they can sign in. This setting is effective only if sign-ups are enabled."
|
||||
msgstr ""
|
||||
|
||||
|
@ -35647,6 +35662,9 @@ msgstr ""
|
|||
msgid "You don't have any recent searches"
|
||||
msgstr ""
|
||||
|
||||
msgid "You don't have any webhooks deliveries"
|
||||
msgstr ""
|
||||
|
||||
msgid "You don't have sufficient permission to perform this action."
|
||||
msgstr ""
|
||||
|
||||
|
@ -37556,6 +37574,9 @@ msgstr ""
|
|||
msgid "severity|Unknown"
|
||||
msgstr ""
|
||||
|
||||
msgid "shared"
|
||||
msgstr ""
|
||||
|
||||
msgid "should be an array of %{object_name} objects"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
import { GlDropdownItem, GlSearchBoxByClick } from '@gitlab/ui';
|
||||
import { mount } from '@vue/test-utils';
|
||||
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
|
||||
import * as urlUtils from '~/lib/utils/url_utility';
|
||||
import SortDropdown from '~/tags/components/sort_dropdown.vue';
|
||||
|
||||
describe('Tags sort dropdown', () => {
|
||||
let wrapper;
|
||||
|
||||
const createWrapper = (props = {}) => {
|
||||
return extendedWrapper(
|
||||
mount(SortDropdown, {
|
||||
provide: {
|
||||
filterTagsPath: '/root/ci-cd-project-demo/-/tags',
|
||||
sortOptions: {
|
||||
name_asc: 'Name',
|
||||
updated_asc: 'Oldest updated',
|
||||
updated_desc: 'Last updated',
|
||||
},
|
||||
...props,
|
||||
},
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
const findSearchBox = () => wrapper.findComponent(GlSearchBoxByClick);
|
||||
const findTagsDropdown = () => wrapper.findByTestId('tags-dropdown');
|
||||
|
||||
afterEach(() => {
|
||||
if (wrapper) {
|
||||
wrapper.destroy();
|
||||
}
|
||||
});
|
||||
|
||||
describe('default state', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = createWrapper();
|
||||
});
|
||||
|
||||
it('should have a search box with a placeholder', () => {
|
||||
const searchBox = findSearchBox();
|
||||
|
||||
expect(searchBox.exists()).toBe(true);
|
||||
expect(searchBox.find('input').attributes('placeholder')).toBe('Filter by tag name');
|
||||
});
|
||||
|
||||
it('should have a sort order dropdown', () => {
|
||||
const branchesDropdown = findTagsDropdown();
|
||||
|
||||
expect(branchesDropdown.exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when submitting a search term', () => {
|
||||
beforeEach(() => {
|
||||
urlUtils.visitUrl = jest.fn();
|
||||
|
||||
wrapper = createWrapper();
|
||||
});
|
||||
|
||||
it('should call visitUrl', () => {
|
||||
const searchBox = findSearchBox();
|
||||
|
||||
searchBox.vm.$emit('submit');
|
||||
|
||||
expect(urlUtils.visitUrl).toHaveBeenCalledWith(
|
||||
'/root/ci-cd-project-demo/-/tags?sort=updated_desc',
|
||||
);
|
||||
});
|
||||
|
||||
it('should send a sort parameter', () => {
|
||||
const sortDropdownItems = findTagsDropdown().findAllComponents(GlDropdownItem).at(0);
|
||||
|
||||
sortDropdownItems.vm.$emit('click');
|
||||
|
||||
expect(urlUtils.visitUrl).toHaveBeenCalledWith(
|
||||
'/root/ci-cd-project-demo/-/tags?sort=name_asc',
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -74,85 +74,8 @@ RSpec.describe Clusters::Applications::Prometheus do
|
|||
end
|
||||
|
||||
describe '#prometheus_client' do
|
||||
shared_examples 'exception caught for prometheus client' do
|
||||
before do
|
||||
allow(kube_client).to receive(:proxy_url).and_raise(exception)
|
||||
end
|
||||
|
||||
it 'returns nil' do
|
||||
expect(subject.prometheus_client).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'cluster is nil' do
|
||||
it 'returns nil' do
|
||||
expect(subject.cluster).to be_nil
|
||||
expect(subject.prometheus_client).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context "cluster doesn't have kubeclient" do
|
||||
let(:cluster) { create(:cluster) }
|
||||
|
||||
subject { create(:clusters_applications_prometheus, cluster: cluster) }
|
||||
|
||||
it 'returns nil' do
|
||||
expect(subject.prometheus_client).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'cluster has kubeclient' do
|
||||
let(:cluster) { create(:cluster, :project, :provided_by_gcp) }
|
||||
let(:kubernetes_url) { subject.cluster.platform_kubernetes.api_url }
|
||||
let(:kube_client) { subject.cluster.kubeclient.core_client }
|
||||
|
||||
subject { create(:clusters_applications_prometheus, cluster: cluster) }
|
||||
|
||||
before do
|
||||
subject.cluster.platform_kubernetes.namespace = 'a-namespace'
|
||||
stub_kubeclient_discover(cluster.platform_kubernetes.api_url)
|
||||
|
||||
create(:cluster_kubernetes_namespace,
|
||||
cluster: cluster,
|
||||
cluster_project: cluster.cluster_project,
|
||||
project: cluster.cluster_project.project)
|
||||
end
|
||||
|
||||
it 'creates proxy prometheus_client' do
|
||||
expect(subject.prometheus_client).to be_instance_of(Gitlab::PrometheusClient)
|
||||
end
|
||||
|
||||
it 'merges proxy_url, options and headers from kube client with prometheus_client options' do
|
||||
expect(Gitlab::PrometheusClient)
|
||||
.to(receive(:new))
|
||||
.with(a_valid_url, kube_client.rest_client.options.merge({
|
||||
headers: kube_client.headers,
|
||||
timeout: PrometheusAdapter::DEFAULT_PROMETHEUS_REQUEST_TIMEOUT_SEC
|
||||
}))
|
||||
subject.prometheus_client
|
||||
end
|
||||
|
||||
context 'when cluster is not reachable' do
|
||||
it_behaves_like 'exception caught for prometheus client' do
|
||||
let(:exception) { Kubeclient::HttpError.new(401, 'Unauthorized', nil) }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there is a socket error while contacting cluster' do
|
||||
it_behaves_like 'exception caught for prometheus client' do
|
||||
let(:exception) { Errno::ECONNREFUSED }
|
||||
end
|
||||
|
||||
it_behaves_like 'exception caught for prometheus client' do
|
||||
let(:exception) { Errno::ECONNRESET }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the network is unreachable' do
|
||||
it_behaves_like 'exception caught for prometheus client' do
|
||||
let(:exception) { Errno::ENETUNREACH }
|
||||
end
|
||||
end
|
||||
include_examples '#prometheus_client shared' do
|
||||
let(:factory) { :clusters_applications_prometheus }
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Input
|
||||
# - factory: [:clusters_applications_prometheus, :clusters_integrations_prometheus]
|
||||
RSpec.shared_examples '#prometheus_client shared' do
|
||||
shared_examples 'exception caught for prometheus client' do
|
||||
before do
|
||||
allow(kube_client).to receive(:proxy_url).and_raise(exception)
|
||||
end
|
||||
|
||||
it 'returns nil' do
|
||||
expect(subject.prometheus_client).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'cluster is nil' do
|
||||
it 'returns nil' do
|
||||
expect(subject.cluster).to be_nil
|
||||
expect(subject.prometheus_client).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context "cluster doesn't have kubeclient" do
|
||||
let(:cluster) { create(:cluster) }
|
||||
|
||||
subject { create(factory, cluster: cluster) }
|
||||
|
||||
it 'returns nil' do
|
||||
expect(subject.prometheus_client).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'cluster has kubeclient' do
|
||||
let(:cluster) { create(:cluster, :project, :provided_by_gcp) }
|
||||
let(:kubernetes_url) { subject.cluster.platform_kubernetes.api_url }
|
||||
let(:kube_client) { subject.cluster.kubeclient.core_client }
|
||||
|
||||
subject { create(factory, cluster: cluster) }
|
||||
|
||||
before do
|
||||
subject.cluster.platform_kubernetes.namespace = 'a-namespace'
|
||||
stub_kubeclient_discover(cluster.platform_kubernetes.api_url)
|
||||
|
||||
create(:cluster_kubernetes_namespace,
|
||||
cluster: cluster,
|
||||
cluster_project: cluster.cluster_project,
|
||||
project: cluster.cluster_project.project)
|
||||
end
|
||||
|
||||
it 'creates proxy prometheus_client' do
|
||||
expect(subject.prometheus_client).to be_instance_of(Gitlab::PrometheusClient)
|
||||
end
|
||||
|
||||
it 'merges proxy_url, options and headers from kube client with prometheus_client options' do
|
||||
expect(Gitlab::PrometheusClient)
|
||||
.to(receive(:new))
|
||||
.with(a_valid_url, kube_client.rest_client.options.merge({
|
||||
headers: kube_client.headers,
|
||||
timeout: PrometheusAdapter::DEFAULT_PROMETHEUS_REQUEST_TIMEOUT_SEC
|
||||
}))
|
||||
subject.prometheus_client
|
||||
end
|
||||
|
||||
context 'when cluster is not reachable' do
|
||||
it_behaves_like 'exception caught for prometheus client' do
|
||||
let(:exception) { Kubeclient::HttpError.new(401, 'Unauthorized', nil) }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there is a socket error while contacting cluster' do
|
||||
it_behaves_like 'exception caught for prometheus client' do
|
||||
let(:exception) { Errno::ECONNREFUSED }
|
||||
end
|
||||
|
||||
it_behaves_like 'exception caught for prometheus client' do
|
||||
let(:exception) { Errno::ECONNRESET }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the network is unreachable' do
|
||||
it_behaves_like 'exception caught for prometheus client' do
|
||||
let(:exception) { Errno::ENETUNREACH }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -21,6 +21,7 @@ RSpec.describe 'projects/tags/index.html.haml' do
|
|||
end
|
||||
|
||||
it 'defaults sort dropdown toggle to last updated' do
|
||||
stub_feature_flags(gldropdown_tags: false)
|
||||
render
|
||||
expect(rendered).to have_button('Last updated')
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue