Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
e83144f0ee
commit
ba2e4183d9
|
@ -15,9 +15,14 @@ export default class GlFieldErrors {
|
||||||
|
|
||||||
initValidators() {
|
initValidators() {
|
||||||
// register selectors here as needed
|
// register selectors here as needed
|
||||||
const validateSelectors = [':text', ':password', '[type=email]', '[type=url]', '[type=number]']
|
const validateSelectors = [
|
||||||
.map((selector) => `input${selector}`)
|
'input:text',
|
||||||
.join(',');
|
'input:password',
|
||||||
|
'input[type=email]',
|
||||||
|
'input[type=url]',
|
||||||
|
'input[type=number]',
|
||||||
|
'textarea',
|
||||||
|
].join(',');
|
||||||
|
|
||||||
this.state.inputs = this.form
|
this.state.inputs = this.form
|
||||||
.find(validateSelectors)
|
.find(validateSelectors)
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
import { s__, __ } from '~/locale';
|
||||||
|
|
||||||
|
export const STOP_JOBS_MODAL_ID = 'stop-jobs-modal';
|
||||||
|
export const STOP_JOBS_MODAL_TITLE = s__('AdminArea|Stop all jobs?');
|
||||||
|
export const STOP_JOBS_BUTTON_TEXT = s__('AdminArea|Stop all jobs');
|
||||||
|
export const CANCEL_TEXT = __('Cancel');
|
||||||
|
export const STOP_JOBS_FAILED_TEXT = s__('AdminArea|Stopping jobs failed');
|
||||||
|
export const PRIMARY_ACTION_TEXT = s__('AdminArea|Stop jobs');
|
||||||
|
export const STOP_JOBS_WARNING = s__(
|
||||||
|
'AdminArea|You’re about to stop all jobs. This will halt all current jobs that are running.',
|
||||||
|
);
|
|
@ -3,7 +3,14 @@ import { GlModal } from '@gitlab/ui';
|
||||||
import { createAlert } from '~/flash';
|
import { createAlert } from '~/flash';
|
||||||
import axios from '~/lib/utils/axios_utils';
|
import axios from '~/lib/utils/axios_utils';
|
||||||
import { redirectTo } from '~/lib/utils/url_utility';
|
import { redirectTo } from '~/lib/utils/url_utility';
|
||||||
import { __, s__ } from '~/locale';
|
import {
|
||||||
|
CANCEL_TEXT,
|
||||||
|
STOP_JOBS_MODAL_ID,
|
||||||
|
STOP_JOBS_FAILED_TEXT,
|
||||||
|
STOP_JOBS_MODAL_TITLE,
|
||||||
|
STOP_JOBS_WARNING,
|
||||||
|
PRIMARY_ACTION_TEXT,
|
||||||
|
} from './constants';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
@ -15,13 +22,6 @@ export default {
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
|
||||||
text() {
|
|
||||||
return s__(
|
|
||||||
'AdminArea|You’re about to stop all jobs. This will halt all current jobs that are running.',
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
methods: {
|
methods: {
|
||||||
onSubmit() {
|
onSubmit() {
|
||||||
return axios
|
return axios
|
||||||
|
@ -32,30 +32,33 @@ export default {
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
createAlert({
|
createAlert({
|
||||||
message: s__('AdminArea|Stopping jobs failed'),
|
message: STOP_JOBS_FAILED_TEXT,
|
||||||
});
|
});
|
||||||
throw error;
|
throw error;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
primaryAction: {
|
primaryAction: {
|
||||||
text: s__('AdminArea|Stop jobs'),
|
text: PRIMARY_ACTION_TEXT,
|
||||||
attributes: [{ variant: 'danger' }],
|
attributes: [{ variant: 'danger' }],
|
||||||
},
|
},
|
||||||
cancelAction: {
|
cancelAction: {
|
||||||
text: __('Cancel'),
|
text: CANCEL_TEXT,
|
||||||
},
|
},
|
||||||
|
STOP_JOBS_WARNING,
|
||||||
|
STOP_JOBS_MODAL_ID,
|
||||||
|
STOP_JOBS_MODAL_TITLE,
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<gl-modal
|
<gl-modal
|
||||||
modal-id="stop-jobs-modal"
|
:modal-id="$options.STOP_JOBS_MODAL_ID"
|
||||||
:action-primary="$options.primaryAction"
|
:action-primary="$options.primaryAction"
|
||||||
:action-cancel="$options.cancelAction"
|
:action-cancel="$options.cancelAction"
|
||||||
@primary="onSubmit"
|
@primary="onSubmit"
|
||||||
>
|
>
|
||||||
<template #modal-title>{{ s__('AdminArea|Stop all jobs?') }}</template>
|
<template #modal-title>{{ $options.STOP_JOBS_MODAL_TITLE }}</template>
|
||||||
{{ text }}
|
{{ $options.STOP_JOBS_WARNING }}
|
||||||
</gl-modal>
|
</gl-modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,29 +1,29 @@
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import { BV_SHOW_MODAL } from '~/lib/utils/constants';
|
import { BV_SHOW_MODAL } from '~/lib/utils/constants';
|
||||||
import Translate from '~/vue_shared/translate';
|
import Translate from '~/vue_shared/translate';
|
||||||
|
import { STOP_JOBS_MODAL_ID } from './components/constants';
|
||||||
import StopJobsModal from './components/stop_jobs_modal.vue';
|
import StopJobsModal from './components/stop_jobs_modal.vue';
|
||||||
|
|
||||||
Vue.use(Translate);
|
Vue.use(Translate);
|
||||||
|
|
||||||
function initJobs() {
|
function initJobs() {
|
||||||
const buttonId = 'js-stop-jobs-button';
|
const buttonId = 'js-stop-jobs-button';
|
||||||
const modalId = 'stop-jobs-modal';
|
|
||||||
const stopJobsButton = document.getElementById(buttonId);
|
const stopJobsButton = document.getElementById(buttonId);
|
||||||
if (stopJobsButton) {
|
if (stopJobsButton) {
|
||||||
// eslint-disable-next-line no-new
|
// eslint-disable-next-line no-new
|
||||||
new Vue({
|
new Vue({
|
||||||
el: `#js-${modalId}`,
|
el: `#js-${STOP_JOBS_MODAL_ID}`,
|
||||||
components: {
|
components: {
|
||||||
StopJobsModal,
|
StopJobsModal,
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
stopJobsButton.classList.remove('disabled');
|
stopJobsButton.classList.remove('disabled');
|
||||||
stopJobsButton.addEventListener('click', () => {
|
stopJobsButton.addEventListener('click', () => {
|
||||||
this.$root.$emit(BV_SHOW_MODAL, modalId, `#${buttonId}`);
|
this.$root.$emit(BV_SHOW_MODAL, STOP_JOBS_MODAL_ID, `#${buttonId}`);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
render(createElement) {
|
render(createElement) {
|
||||||
return createElement(modalId, {
|
return createElement(STOP_JOBS_MODAL_ID, {
|
||||||
props: {
|
props: {
|
||||||
url: stopJobsButton.dataset.url,
|
url: stopJobsButton.dataset.url,
|
||||||
},
|
},
|
||||||
|
|
|
@ -5,7 +5,7 @@ module Types
|
||||||
graphql_name 'DeploymentDetails'
|
graphql_name 'DeploymentDetails'
|
||||||
description 'The details of the deployment'
|
description 'The details of the deployment'
|
||||||
authorize :read_deployment
|
authorize :read_deployment
|
||||||
present_using Deployments::DeploymentPresenter
|
present_using ::Deployments::DeploymentPresenter
|
||||||
|
|
||||||
field :tags,
|
field :tags,
|
||||||
[Types::DeploymentTagType],
|
[Types::DeploymentTagType],
|
||||||
|
@ -13,3 +13,5 @@ module Types
|
||||||
calls_gitaly: true
|
calls_gitaly: true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Types::DeploymentDetailsType.prepend_mod_with('Types::DeploymentDetailsType')
|
||||||
|
|
|
@ -11,7 +11,7 @@ module Types
|
||||||
graphql_name 'Deployment'
|
graphql_name 'Deployment'
|
||||||
description 'The deployment of an environment'
|
description 'The deployment of an environment'
|
||||||
|
|
||||||
present_using Deployments::DeploymentPresenter
|
present_using ::Deployments::DeploymentPresenter
|
||||||
|
|
||||||
authorize :read_deployment
|
authorize :read_deployment
|
||||||
|
|
||||||
|
|
|
@ -281,76 +281,28 @@ module Nav
|
||||||
end
|
end
|
||||||
|
|
||||||
def projects_submenu_items(builder:)
|
def projects_submenu_items(builder:)
|
||||||
if Feature.enabled?(:remove_extra_primary_submenu_options)
|
title = _('View all projects')
|
||||||
title = _('View all projects')
|
|
||||||
|
|
||||||
builder.add_primary_menu_item(
|
builder.add_primary_menu_item(
|
||||||
id: 'your',
|
id: 'your',
|
||||||
title: title,
|
title: title,
|
||||||
href: dashboard_projects_path,
|
href: dashboard_projects_path,
|
||||||
data: { qa_selector: 'menu_item_link', qa_title: title, **menu_data_tracking_attrs(title) }
|
data: { qa_selector: 'menu_item_link', qa_title: title, **menu_data_tracking_attrs(title) }
|
||||||
)
|
)
|
||||||
else
|
|
||||||
# These project links come from `app/views/layouts/nav/projects_dropdown/_show.html.haml`
|
|
||||||
[
|
|
||||||
{ id: 'your', title: _('Your projects'), href: dashboard_projects_path },
|
|
||||||
{ id: 'starred', title: _('Starred projects'), href: starred_dashboard_projects_path },
|
|
||||||
{ id: 'explore', title: _('Explore projects'), href: explore_root_path },
|
|
||||||
{ id: 'topics', title: _('Explore topics'), href: topics_explore_projects_path }
|
|
||||||
].each do |item|
|
|
||||||
builder.add_primary_menu_item(
|
|
||||||
**item,
|
|
||||||
data: { qa_selector: 'menu_item_link', qa_title: item[:title], **menu_data_tracking_attrs(item[:title]) }
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
title = _('Create new project')
|
|
||||||
|
|
||||||
builder.add_secondary_menu_item(
|
|
||||||
id: 'create',
|
|
||||||
title: title,
|
|
||||||
href: new_project_path,
|
|
||||||
data: { qa_selector: 'menu_item_link', qa_title: title, **menu_data_tracking_attrs(title) }
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def groups_submenu
|
def groups_submenu
|
||||||
# These group links come from `app/views/layouts/nav/groups_dropdown/_show.html.haml`
|
# These group links come from `app/views/layouts/nav/groups_dropdown/_show.html.haml`
|
||||||
builder = ::Gitlab::Nav::TopNavMenuBuilder.new
|
builder = ::Gitlab::Nav::TopNavMenuBuilder.new
|
||||||
|
|
||||||
if Feature.enabled?(:remove_extra_primary_submenu_options)
|
title = _('View all groups')
|
||||||
title = _('View all groups')
|
|
||||||
|
|
||||||
builder.add_primary_menu_item(
|
|
||||||
id: 'your',
|
|
||||||
title: title,
|
|
||||||
href: dashboard_groups_path,
|
|
||||||
data: { qa_selector: 'menu_item_link', qa_title: title, **menu_data_tracking_attrs(title) }
|
|
||||||
)
|
|
||||||
else
|
|
||||||
[
|
|
||||||
{ id: 'your', title: _('Your groups'), href: dashboard_groups_path },
|
|
||||||
{ id: 'explore', title: _('Explore groups'), href: explore_groups_path }
|
|
||||||
].each do |item|
|
|
||||||
builder.add_primary_menu_item(
|
|
||||||
**item,
|
|
||||||
data: { qa_selector: 'menu_item_link', qa_title: item[:title], **menu_data_tracking_attrs(item[:title]) }
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
if current_user.can_create_group?
|
|
||||||
title = _('Create group')
|
|
||||||
|
|
||||||
builder.add_secondary_menu_item(
|
|
||||||
id: 'create',
|
|
||||||
title: title,
|
|
||||||
href: new_group_path,
|
|
||||||
data: { qa_selector: 'menu_item_link', qa_title: title, **menu_data_tracking_attrs(title) }
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
builder.add_primary_menu_item(
|
||||||
|
id: 'your',
|
||||||
|
title: title,
|
||||||
|
href: dashboard_groups_path,
|
||||||
|
data: { qa_selector: 'menu_item_link', qa_title: title, **menu_data_tracking_attrs(title) }
|
||||||
|
)
|
||||||
builder.build
|
builder.build
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -136,7 +136,7 @@ class MergeRequest < ApplicationRecord
|
||||||
|
|
||||||
before_validation :set_draft_status
|
before_validation :set_draft_status
|
||||||
|
|
||||||
after_create :ensure_merge_request_diff
|
after_create :ensure_merge_request_diff, unless: :skip_ensure_merge_request_diff
|
||||||
after_update :clear_memoized_shas
|
after_update :clear_memoized_shas
|
||||||
after_update :reload_diff_if_branch_changed
|
after_update :reload_diff_if_branch_changed
|
||||||
after_commit :ensure_metrics, on: [:create, :update], unless: :importing?
|
after_commit :ensure_metrics, on: [:create, :update], unless: :importing?
|
||||||
|
@ -146,6 +146,10 @@ class MergeRequest < ApplicationRecord
|
||||||
# It allows us to close or modify broken merge requests
|
# It allows us to close or modify broken merge requests
|
||||||
attr_accessor :allow_broken
|
attr_accessor :allow_broken
|
||||||
|
|
||||||
|
# Temporary flag to skip merge_request_diff creation on create.
|
||||||
|
# See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/100390
|
||||||
|
attr_accessor :skip_ensure_merge_request_diff
|
||||||
|
|
||||||
# Temporary fields to store compare vars
|
# Temporary fields to store compare vars
|
||||||
# when creating new merge request
|
# when creating new merge request
|
||||||
attr_accessor :can_be_created, :compare_commits, :diff_options, :compare
|
attr_accessor :can_be_created, :compare_commits, :diff_options, :compare
|
||||||
|
|
|
@ -5,6 +5,8 @@ module MergeRequests
|
||||||
include Gitlab::Utils::StrongMemoize
|
include Gitlab::Utils::StrongMemoize
|
||||||
|
|
||||||
def execute(merge_request)
|
def execute(merge_request)
|
||||||
|
merge_request.ensure_merge_request_diff
|
||||||
|
|
||||||
prepare_for_mergeability(merge_request)
|
prepare_for_mergeability(merge_request)
|
||||||
prepare_merge_request(merge_request)
|
prepare_merge_request(merge_request)
|
||||||
end
|
end
|
||||||
|
|
|
@ -34,7 +34,12 @@ module MergeRequests
|
||||||
# callback (e.g. after_create), a database transaction will be
|
# callback (e.g. after_create), a database transaction will be
|
||||||
# open while the Gitaly RPC waits. To avoid an idle in transaction
|
# open while the Gitaly RPC waits. To avoid an idle in transaction
|
||||||
# timeout, we do this before we attempt to save the merge request.
|
# timeout, we do this before we attempt to save the merge request.
|
||||||
merge_request.eager_fetch_ref!
|
|
||||||
|
if Feature.enabled?(:async_merge_request_diff_creation, current_user)
|
||||||
|
merge_request.skip_ensure_merge_request_diff = true
|
||||||
|
else
|
||||||
|
merge_request.eager_fetch_ref!
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_projects!
|
def set_projects!
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
.gl-display-none.gl-sm-display-block
|
.gl-display-none.gl-sm-display-block
|
||||||
= render "layouts/nav/top_nav"
|
= render "layouts/nav/top_nav"
|
||||||
|
|
||||||
- if top_nav_show_search && Feature.enabled?(:new_navbar_layout)
|
- if top_nav_show_search
|
||||||
.navbar-collapse.gl-transition-medium.collapse.gl-mr-auto.global-search-container.hide-when-top-nav-responsive-open
|
.navbar-collapse.gl-transition-medium.collapse.gl-mr-auto.global-search-container.hide-when-top-nav-responsive-open
|
||||||
- search_menu_item = top_nav_search_menu_item_attrs
|
- search_menu_item = top_nav_search_menu_item_attrs
|
||||||
%ul.nav.navbar-nav.gl-w-full.gl-align-items-center
|
%ul.nav.navbar-nav.gl-w-full.gl-align-items-center
|
||||||
|
@ -42,21 +42,10 @@
|
||||||
= link_to search_menu_item.fetch(:href), title: search_menu_item.fetch(:title), aria: { label: search_menu_item.fetch(:title) }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
|
= link_to search_menu_item.fetch(:href), title: search_menu_item.fetch(:title), aria: { label: search_menu_item.fetch(:title) }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
|
||||||
= sprite_icon(search_menu_item.fetch(:icon))
|
= sprite_icon(search_menu_item.fetch(:icon))
|
||||||
|
|
||||||
.navbar-collapse.gl-transition-medium.collapse{ class: ('global-search-container' unless Feature.enabled?(:new_navbar_layout)) }
|
.navbar-collapse.gl-transition-medium.collapse
|
||||||
%ul.nav.navbar-nav.gl-w-full.gl-align-items-center.gl-justify-content-end
|
%ul.nav.navbar-nav.gl-w-full.gl-align-items-center.gl-justify-content-end
|
||||||
- if current_user
|
- if current_user
|
||||||
= render 'layouts/header/new_dropdown', class: 'gl-display-none gl-sm-display-block gl-white-space-nowrap gl-text-right'
|
= render 'layouts/header/new_dropdown', class: 'gl-display-none gl-sm-display-block gl-white-space-nowrap gl-text-right'
|
||||||
- if top_nav_show_search && Feature.disabled?(:new_navbar_layout)
|
|
||||||
- search_menu_item = top_nav_search_menu_item_attrs
|
|
||||||
%li.nav-item.header-search-new.gl-display-none.gl-lg-display-block.gl-w-full
|
|
||||||
- unless current_controller?(:search)
|
|
||||||
- if Feature.enabled?(:new_header_search)
|
|
||||||
= render 'layouts/header_search'
|
|
||||||
- else
|
|
||||||
= render 'layouts/search'
|
|
||||||
%li.nav-item{ class: 'd-none d-sm-inline-block d-lg-none' }
|
|
||||||
= link_to search_menu_item.fetch(:href), title: search_menu_item.fetch(:title), aria: { label: search_menu_item.fetch(:title) }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
|
|
||||||
= sprite_icon(search_menu_item.fetch(:icon))
|
|
||||||
- if header_link?(:issues)
|
- if header_link?(:issues)
|
||||||
= nav_link(path: 'dashboard#issues', html_options: { class: "user-counter" }) do
|
= nav_link(path: 'dashboard#issues', html_options: { class: "user-counter" }) do
|
||||||
= link_to assigned_issues_dashboard_path, title: _('Issues'), class: 'dashboard-shortcuts-issues js-prefetch-document', aria: { label: _('Issues') },
|
= link_to assigned_issues_dashboard_path, title: _('Issues'), class: 'dashboard-shortcuts-issues js-prefetch-document', aria: { label: _('Issues') },
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
---
|
||||||
|
name: async_merge_request_diff_creation
|
||||||
|
introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/100390"
|
||||||
|
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/376759
|
||||||
|
milestone: '15.6'
|
||||||
|
type: development
|
||||||
|
group: group::code review
|
||||||
|
default_enabled: false
|
|
@ -1,8 +0,0 @@
|
||||||
---
|
|
||||||
name: new_navbar_layout
|
|
||||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96853
|
|
||||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/373078
|
|
||||||
milestone: '15.4'
|
|
||||||
type: development
|
|
||||||
group: group::foundations
|
|
||||||
default_enabled: true
|
|
|
@ -1,8 +0,0 @@
|
||||||
---
|
|
||||||
name: remove_extra_primary_submenu_options
|
|
||||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96931
|
|
||||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/373078
|
|
||||||
milestone: '15.4'
|
|
||||||
type: development
|
|
||||||
group: group::foundations
|
|
||||||
default_enabled: true
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class AddIndexAuthorIdTargetProjectIdOnMergeRequests < Gitlab::Database::Migration[2.0]
|
||||||
|
INDEX_NAME = 'index_merge_requests_on_author_id_and_target_project_id'
|
||||||
|
|
||||||
|
disable_ddl_transaction!
|
||||||
|
|
||||||
|
def up
|
||||||
|
add_concurrent_index :merge_requests, %i[author_id target_project_id], name: INDEX_NAME
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
remove_concurrent_index_by_name :merge_requests, INDEX_NAME
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1 @@
|
||||||
|
250ec3ff701dacd333d669f128762e9f035a626f2f7720c6e7e1dc61499d431d
|
|
@ -29481,6 +29481,8 @@ CREATE INDEX index_merge_requests_on_assignee_id ON merge_requests USING btree (
|
||||||
|
|
||||||
CREATE INDEX index_merge_requests_on_author_id ON merge_requests USING btree (author_id);
|
CREATE INDEX index_merge_requests_on_author_id ON merge_requests USING btree (author_id);
|
||||||
|
|
||||||
|
CREATE INDEX index_merge_requests_on_author_id_and_target_project_id ON merge_requests USING btree (author_id, target_project_id);
|
||||||
|
|
||||||
CREATE INDEX index_merge_requests_on_created_at ON merge_requests USING btree (created_at);
|
CREATE INDEX index_merge_requests_on_created_at ON merge_requests USING btree (created_at);
|
||||||
|
|
||||||
CREATE INDEX index_merge_requests_on_description_trigram ON merge_requests USING gin (description gin_trgm_ops);
|
CREATE INDEX index_merge_requests_on_description_trigram ON merge_requests USING gin (description gin_trgm_ops);
|
||||||
|
|
|
@ -11442,6 +11442,33 @@ The deployment of an environment.
|
||||||
| <a id="deploymenttriggerer"></a>`triggerer` | [`UserCore`](#usercore) | User who executed the deployment. |
|
| <a id="deploymenttriggerer"></a>`triggerer` | [`UserCore`](#usercore) | User who executed the deployment. |
|
||||||
| <a id="deploymentupdatedat"></a>`updatedAt` | [`Time`](#time) | When the deployment record was updated. |
|
| <a id="deploymentupdatedat"></a>`updatedAt` | [`Time`](#time) | When the deployment record was updated. |
|
||||||
|
|
||||||
|
### `DeploymentApproval`
|
||||||
|
|
||||||
|
Approval of the deployment.
|
||||||
|
|
||||||
|
#### Fields
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
| ---- | ---- | ----------- |
|
||||||
|
| <a id="deploymentapprovalcomment"></a>`comment` | [`String`](#string) | Additional comment. |
|
||||||
|
| <a id="deploymentapprovalcreatedat"></a>`createdAt` | [`Time`](#time) | When the user approved/rejected first time. |
|
||||||
|
| <a id="deploymentapprovalstatus"></a>`status` | [`DeploymentsApprovalStatus`](#deploymentsapprovalstatus) | Whether the deployment was approved/rejected. |
|
||||||
|
| <a id="deploymentapprovalupdatedat"></a>`updatedAt` | [`Time`](#time) | When the user updated the approval. |
|
||||||
|
| <a id="deploymentapprovaluser"></a>`user` | [`UserCore`](#usercore) | User who approved or rejected the deployment. |
|
||||||
|
|
||||||
|
### `DeploymentApprovalSummary`
|
||||||
|
|
||||||
|
Approval summary of the deployment.
|
||||||
|
|
||||||
|
#### Fields
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
| ---- | ---- | ----------- |
|
||||||
|
| <a id="deploymentapprovalsummaryrules"></a>`rules` | [`[ProtectedEnvironmentApprovalRuleForSummary!]`](#protectedenvironmentapprovalruleforsummary) | Approval Rules for the deployment. |
|
||||||
|
| <a id="deploymentapprovalsummarystatus"></a>`status` | [`DeploymentApprovalSummaryStatus`](#deploymentapprovalsummarystatus) | Status of the approvals. |
|
||||||
|
| <a id="deploymentapprovalsummarytotalpendingapprovalcount"></a>`totalPendingApprovalCount` | [`Int`](#int) | Total pending approval count. |
|
||||||
|
| <a id="deploymentapprovalsummarytotalrequiredapprovals"></a>`totalRequiredApprovals` | [`Int`](#int) | Total number of required approvals. |
|
||||||
|
|
||||||
### `DeploymentDetails`
|
### `DeploymentDetails`
|
||||||
|
|
||||||
The details of the deployment.
|
The details of the deployment.
|
||||||
|
@ -11450,6 +11477,7 @@ The details of the deployment.
|
||||||
|
|
||||||
| Name | Type | Description |
|
| Name | Type | Description |
|
||||||
| ---- | ---- | ----------- |
|
| ---- | ---- | ----------- |
|
||||||
|
| <a id="deploymentdetailsapprovalsummary"></a>`approvalSummary` | [`DeploymentApprovalSummary`](#deploymentapprovalsummary) | Approval summary of the deployment. |
|
||||||
| <a id="deploymentdetailscommit"></a>`commit` | [`Commit`](#commit) | Commit details of the deployment. |
|
| <a id="deploymentdetailscommit"></a>`commit` | [`Commit`](#commit) | Commit details of the deployment. |
|
||||||
| <a id="deploymentdetailscreatedat"></a>`createdAt` | [`Time`](#time) | When the deployment record was created. |
|
| <a id="deploymentdetailscreatedat"></a>`createdAt` | [`Time`](#time) | When the deployment record was created. |
|
||||||
| <a id="deploymentdetailsfinishedat"></a>`finishedAt` | [`Time`](#time) | When the deployment finished. |
|
| <a id="deploymentdetailsfinishedat"></a>`finishedAt` | [`Time`](#time) | When the deployment finished. |
|
||||||
|
@ -17605,6 +17633,23 @@ Which group, user or role is allowed to approve deployments to the environment.
|
||||||
| <a id="protectedenvironmentapprovalrulerequiredapprovals"></a>`requiredApprovals` | [`Int`](#int) | Number of required approvals. |
|
| <a id="protectedenvironmentapprovalrulerequiredapprovals"></a>`requiredApprovals` | [`Int`](#int) | Number of required approvals. |
|
||||||
| <a id="protectedenvironmentapprovalruleuser"></a>`user` | [`UserCore`](#usercore) | User details. Present if it's user specific access control. |
|
| <a id="protectedenvironmentapprovalruleuser"></a>`user` | [`UserCore`](#usercore) | User details. Present if it's user specific access control. |
|
||||||
|
|
||||||
|
### `ProtectedEnvironmentApprovalRuleForSummary`
|
||||||
|
|
||||||
|
Which group, user or role is allowed to approve deployments to the environment.
|
||||||
|
|
||||||
|
#### Fields
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
| ---- | ---- | ----------- |
|
||||||
|
| <a id="protectedenvironmentapprovalruleforsummaryaccesslevel"></a>`accessLevel` | [`AccessLevel`](#accesslevel) | Role details. Present if it's role specific access control. |
|
||||||
|
| <a id="protectedenvironmentapprovalruleforsummaryapprovals"></a>`approvals` | [`[DeploymentApproval!]`](#deploymentapproval) | Current approvals of the deployment. |
|
||||||
|
| <a id="protectedenvironmentapprovalruleforsummaryapprovedcount"></a>`approvedCount` | [`Int`](#int) | Approved count. |
|
||||||
|
| <a id="protectedenvironmentapprovalruleforsummarygroup"></a>`group` | [`Group`](#group) | Group details. Present if it's group specific access control. |
|
||||||
|
| <a id="protectedenvironmentapprovalruleforsummarypendingapprovalcount"></a>`pendingApprovalCount` | [`Int`](#int) | Pending approval count. |
|
||||||
|
| <a id="protectedenvironmentapprovalruleforsummaryrequiredapprovals"></a>`requiredApprovals` | [`Int`](#int) | Number of required approvals. |
|
||||||
|
| <a id="protectedenvironmentapprovalruleforsummarystatus"></a>`status` | [`DeploymentApprovalSummaryStatus`](#deploymentapprovalsummarystatus) | Status of the approval summary. |
|
||||||
|
| <a id="protectedenvironmentapprovalruleforsummaryuser"></a>`user` | [`UserCore`](#usercore) | User details. Present if it's user specific access control. |
|
||||||
|
|
||||||
### `ProtectedEnvironmentDeployAccessLevel`
|
### `ProtectedEnvironmentDeployAccessLevel`
|
||||||
|
|
||||||
Which group, user or role is allowed to execute deployments to the environment.
|
Which group, user or role is allowed to execute deployments to the environment.
|
||||||
|
@ -20479,6 +20524,16 @@ Weight of the data visualization palette.
|
||||||
| <a id="dependencyproxymanifeststatuspending_destruction"></a>`PENDING_DESTRUCTION` | Dependency proxy manifest has a status of pending_destruction. |
|
| <a id="dependencyproxymanifeststatuspending_destruction"></a>`PENDING_DESTRUCTION` | Dependency proxy manifest has a status of pending_destruction. |
|
||||||
| <a id="dependencyproxymanifeststatusprocessing"></a>`PROCESSING` | Dependency proxy manifest has a status of processing. |
|
| <a id="dependencyproxymanifeststatusprocessing"></a>`PROCESSING` | Dependency proxy manifest has a status of processing. |
|
||||||
|
|
||||||
|
### `DeploymentApprovalSummaryStatus`
|
||||||
|
|
||||||
|
Status of the deployment approval summary.
|
||||||
|
|
||||||
|
| Value | Description |
|
||||||
|
| ----- | ----------- |
|
||||||
|
| <a id="deploymentapprovalsummarystatusapproved"></a>`APPROVED` | Summarized deployment approval status that is approved. |
|
||||||
|
| <a id="deploymentapprovalsummarystatuspending_approval"></a>`PENDING_APPROVAL` | Summarized deployment approval status that is pending approval. |
|
||||||
|
| <a id="deploymentapprovalsummarystatusrejected"></a>`REJECTED` | Summarized deployment approval status that is rejected. |
|
||||||
|
|
||||||
### `DeploymentStatus`
|
### `DeploymentStatus`
|
||||||
|
|
||||||
All deployment statuses.
|
All deployment statuses.
|
||||||
|
@ -20505,6 +20560,15 @@ All environment deployment tiers.
|
||||||
| <a id="deploymenttierstaging"></a>`STAGING` | Staging. |
|
| <a id="deploymenttierstaging"></a>`STAGING` | Staging. |
|
||||||
| <a id="deploymenttiertesting"></a>`TESTING` | Testing. |
|
| <a id="deploymenttiertesting"></a>`TESTING` | Testing. |
|
||||||
|
|
||||||
|
### `DeploymentsApprovalStatus`
|
||||||
|
|
||||||
|
Status of the deployment approval.
|
||||||
|
|
||||||
|
| Value | Description |
|
||||||
|
| ----- | ----------- |
|
||||||
|
| <a id="deploymentsapprovalstatusapproved"></a>`APPROVED` | A deployment approval that is approved. |
|
||||||
|
| <a id="deploymentsapprovalstatusrejected"></a>`REJECTED` | A deployment approval that is rejected. |
|
||||||
|
|
||||||
### `DesignCollectionCopyState`
|
### `DesignCollectionCopyState`
|
||||||
|
|
||||||
Copy state of a DesignCollection.
|
Copy state of a DesignCollection.
|
||||||
|
|
|
@ -2830,6 +2830,42 @@ Read more in the [Project members](members.md) documentation.
|
||||||
|
|
||||||
Read more in the [Project vulnerabilities](project_vulnerabilities.md) documentation.
|
Read more in the [Project vulnerabilities](project_vulnerabilities.md) documentation.
|
||||||
|
|
||||||
|
## Get a project's pull mirror details **(PREMIUM)**
|
||||||
|
|
||||||
|
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/354506) in GitLab 15.5.
|
||||||
|
|
||||||
|
Returns the details of the project's pull mirror.
|
||||||
|
|
||||||
|
```plaintext
|
||||||
|
GET /projects/:id/mirror/pull
|
||||||
|
```
|
||||||
|
|
||||||
|
Supported attributes:
|
||||||
|
|
||||||
|
| Attribute | Type | Required | Description |
|
||||||
|
|:----------|:------|:------------|:------------|
|
||||||
|
| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding). |
|
||||||
|
|
||||||
|
Example request:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
curl --request GET --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/:id/mirror/pull"
|
||||||
|
```
|
||||||
|
|
||||||
|
Example response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": 101486,
|
||||||
|
"last_error": null,
|
||||||
|
"last_successful_update_at": "2020-01-06T17:32:02.823Z",
|
||||||
|
"last_update_at": "2020-01-06T17:32:02.823Z",
|
||||||
|
"last_update_started_at": "2020-01-06T17:31:55.864Z",
|
||||||
|
"update_status": "finished",
|
||||||
|
"url": "https://*****:*****@gitlab.com/gitlab-org/security/gitlab.git"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Configure pull mirroring for a project **(PREMIUM)**
|
## Configure pull mirroring for a project **(PREMIUM)**
|
||||||
|
|
||||||
> Moved to GitLab Premium in 13.9.
|
> Moved to GitLab Premium in 13.9.
|
||||||
|
|
|
@ -1580,7 +1580,7 @@ Returns:
|
||||||
- `404 User Not Found` if user cannot be found.
|
- `404 User Not Found` if user cannot be found.
|
||||||
- `403 Forbidden` when trying to deactivate a user:
|
- `403 Forbidden` when trying to deactivate a user:
|
||||||
- Blocked by administrator or by LDAP synchronization.
|
- Blocked by administrator or by LDAP synchronization.
|
||||||
- That has any activity in past 90 days. These users cannot be deactivated.
|
- That is not [dormant](../user/admin_area/moderate_users.md#automatically-deactivate-dormant-users).
|
||||||
- That is internal.
|
- That is internal.
|
||||||
|
|
||||||
## Activate user **(FREE SELF)**
|
## Activate user **(FREE SELF)**
|
||||||
|
|
|
@ -162,7 +162,7 @@ A user can be deactivated from the Admin Area. To do this:
|
||||||
For the deactivation option to be visible to an administrator, the user:
|
For the deactivation option to be visible to an administrator, the user:
|
||||||
|
|
||||||
- Must be currently active.
|
- Must be currently active.
|
||||||
- Must not have signed in, or have any activity, in the last 90 days.
|
- Must not be [dormant](#automatically-deactivate-dormant-users).
|
||||||
|
|
||||||
NOTE:
|
NOTE:
|
||||||
Users can also be deactivated using the [GitLab API](../../api/users.md#deactivate-user).
|
Users can also be deactivated using the [GitLab API](../../api/users.md#deactivate-user).
|
||||||
|
|
|
@ -201,6 +201,13 @@ To remove a member from a group:
|
||||||
- To unassign the user from linked issues and merge requests, select the **Also unassign this user from linked issues and merge requests** checkbox.
|
- To unassign the user from linked issues and merge requests, select the **Also unassign this user from linked issues and merge requests** checkbox.
|
||||||
1. Select **Remove member**.
|
1. Select **Remove member**.
|
||||||
|
|
||||||
|
## Ensure removed users cannot invite themselves back
|
||||||
|
|
||||||
|
Malicious users with the Maintainer or Owner role could exploit a race condition that allows
|
||||||
|
them to invite themselves back to a group or project that a GitLab administrator has removed them from.
|
||||||
|
|
||||||
|
To avoid this problem, GitLab administrators can [ensure removed users cannot invite themselves back](../project/members/index.md#ensure-removed-users-cannot-invite-themselves-back).
|
||||||
|
|
||||||
## Add projects to a group
|
## Add projects to a group
|
||||||
|
|
||||||
There are two different ways to add a new project to a group:
|
There are two different ways to add a new project to a group:
|
||||||
|
|
|
@ -89,8 +89,10 @@ At any time, you can revoke a personal access token.
|
||||||
|
|
||||||
## View the last time a token was used
|
## View the last time a token was used
|
||||||
|
|
||||||
Token usage is updated once every 24 hours. It is updated each time the token is used to request
|
Token usage information is updated every 24 hours. GitLab considers a token used when the token is used to:
|
||||||
[API resources](../../api/api_resources.md) and the [GraphQL API](../../api/graphql/index.md).
|
|
||||||
|
- Authenticate with the [REST](../../api/index.md) or [GraphQL](../../api/graphql/index.md) APIs.
|
||||||
|
- Perform a Git operation.
|
||||||
|
|
||||||
To view the last time a token was used:
|
To view the last time a token was used:
|
||||||
|
|
||||||
|
|
|
@ -187,6 +187,21 @@ To remove a member from a project:
|
||||||
[from being forked outside their group](../../group/access_and_permissions.md#prevent-project-forking-outside-group).
|
[from being forked outside their group](../../group/access_and_permissions.md#prevent-project-forking-outside-group).
|
||||||
1. Select **Remove member**.
|
1. Select **Remove member**.
|
||||||
|
|
||||||
|
## Ensure removed users cannot invite themselves back
|
||||||
|
|
||||||
|
Malicious users with the Maintainer or Owner role could exploit a race condition that allows
|
||||||
|
them to invite themselves back to a group or project that a GitLab administrator has removed them from.
|
||||||
|
|
||||||
|
To avoid this problem, GitLab administrators can:
|
||||||
|
|
||||||
|
- Remove the malicious user session from the [GitLab Rails console](../../../administration/operations/rails_console.md).
|
||||||
|
- Impersonate the malicious user to:
|
||||||
|
- Remove the user from the project.
|
||||||
|
- Log the user out of GitLab.
|
||||||
|
- Block the malicious user account.
|
||||||
|
- Remove the malicious user account.
|
||||||
|
- Change the password for the malicious user account.
|
||||||
|
|
||||||
## Filter and sort members
|
## Filter and sort members
|
||||||
|
|
||||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/21727) in GitLab 12.6.
|
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/21727) in GitLab 12.6.
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module API
|
||||||
|
module Entities
|
||||||
|
class PullMirror < Grape::Entity
|
||||||
|
expose :id
|
||||||
|
expose :status, as: :update_status
|
||||||
|
expose :url do |import_state|
|
||||||
|
import_state.project.safe_import_url
|
||||||
|
end
|
||||||
|
expose :last_error
|
||||||
|
expose :last_update_at
|
||||||
|
expose :last_update_started_at
|
||||||
|
expose :last_successful_update_at
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -42,13 +42,10 @@ module Gitlab
|
||||||
def build
|
def build
|
||||||
menu = @menu_builder.build
|
menu = @menu_builder.build
|
||||||
|
|
||||||
hide_menu_text = Feature.enabled?(:new_navbar_layout)
|
|
||||||
|
|
||||||
menu.merge({
|
menu.merge({
|
||||||
views: @views,
|
views: @views,
|
||||||
shortcuts: @shortcuts,
|
shortcuts: @shortcuts,
|
||||||
menuTitle: (_('Menu') unless hide_menu_text),
|
menuTooltip: _('Main menu')
|
||||||
menuTooltip: (_('Main menu') if hide_menu_text)
|
|
||||||
}.compact)
|
}.compact)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2689,6 +2689,24 @@ msgstr ""
|
||||||
msgid "AdminDashboard|Error loading the statistics. Please try again"
|
msgid "AdminDashboard|Error loading the statistics. Please try again"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "AdminEmail|Body"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "AdminEmail|Body is required."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "AdminEmail|Recipient group or project"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "AdminEmail|Recipient group or project is required."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "AdminEmail|Subject"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "AdminEmail|Subject is required."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "AdminLabels|Define your default set of project labels"
|
msgid "AdminLabels|Define your default set of project labels"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -11226,9 +11244,6 @@ msgstr ""
|
||||||
msgid "Create new label"
|
msgid "Create new label"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Create new project"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Create new..."
|
msgid "Create new..."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
|
@ -64,12 +64,7 @@ module QA
|
||||||
|
|
||||||
def go_to_groups
|
def go_to_groups
|
||||||
within_groups_menu do
|
within_groups_menu do
|
||||||
# Remove if statement once :remove_extra_primary_submenu_options ff is enabled by default
|
click_element(:menu_item_link, title: 'View all groups')
|
||||||
if has_element?(:menu_item_link, title: 'Your groups')
|
|
||||||
click_element(:menu_item_link, title: 'Your groups')
|
|
||||||
else
|
|
||||||
click_element(:menu_item_link, title: 'View all groups')
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -80,12 +75,7 @@ module QA
|
||||||
|
|
||||||
def go_to_projects
|
def go_to_projects
|
||||||
within_projects_menu do
|
within_projects_menu do
|
||||||
# Remove if statement once :remove_extra_primary_submenu_options ff is enabled by default
|
click_element(:menu_item_link, title: 'View all projects')
|
||||||
if has_element?(:menu_item_link, title: 'Your projects')
|
|
||||||
click_element(:menu_item_link, title: 'Your projects')
|
|
||||||
else
|
|
||||||
click_element(:menu_item_link, title: 'View all projects')
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,7 @@ module QA
|
||||||
add_failure_issues_link(example)
|
add_failure_issues_link(example)
|
||||||
add_ci_job_link(example)
|
add_ci_job_link(example)
|
||||||
set_flaky_status(example)
|
set_flaky_status(example)
|
||||||
|
set_behaviour_categories(example)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -97,6 +98,19 @@ module QA
|
||||||
log(:error, "Failed to add spec pass rate data for example '#{example.description}', error: #{e}")
|
log(:error, "Failed to add spec pass rate data for example '#{example.description}', error: #{e}")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Add behaviour categories to report
|
||||||
|
#
|
||||||
|
# @param [RSpec::Core::Example] example
|
||||||
|
# @return [void]
|
||||||
|
def set_behaviour_categories(example)
|
||||||
|
file_path = example.file_path.gsub('./qa/specs/features', '')
|
||||||
|
devops_stage = file_path.match(%r{\d{1,2}_(\w+)/})&.captures&.first
|
||||||
|
product_group = example.metadata[:product_group]
|
||||||
|
|
||||||
|
example.epic(devops_stage) if devops_stage
|
||||||
|
example.feature(product_group) if product_group
|
||||||
|
end
|
||||||
|
|
||||||
# Flaky specs with pass rate below 98%
|
# Flaky specs with pass rate below 98%
|
||||||
#
|
#
|
||||||
# @return [Array]
|
# @return [Array]
|
||||||
|
@ -107,7 +121,7 @@ module QA
|
||||||
|
|
||||||
runs = records.count
|
runs = records.count
|
||||||
failed = records.count { |r| r.values["status"] == "failed" }
|
failed = records.count { |r| r.values["status"] == "failed" }
|
||||||
pass_rate = 100 - ((failed.to_f / runs.to_f) * 100)
|
pass_rate = 100 - ((failed.to_f / runs) * 100)
|
||||||
|
|
||||||
# Consider spec with a pass rate less than 98% as flaky
|
# Consider spec with a pass rate less than 98% as flaky
|
||||||
result[records.last.values["testcase"]] = pass_rate if pass_rate < 98
|
result[records.last.values["testcase"]] = pass_rate if pass_rate < 98
|
||||||
|
|
|
@ -72,6 +72,7 @@ module QA
|
||||||
merge_request: merge_request,
|
merge_request: merge_request,
|
||||||
run_type: run_type,
|
run_type: run_type,
|
||||||
stage: devops_stage(file_path),
|
stage: devops_stage(file_path),
|
||||||
|
product_group: example.metadata[:product_group],
|
||||||
testcase: example.metadata[:testcase]
|
testcase: example.metadata[:testcase]
|
||||||
},
|
},
|
||||||
fields: {
|
fields: {
|
||||||
|
|
|
@ -28,6 +28,7 @@ describe QA::Support::Formatters::TestStatsFormatter do
|
||||||
let(:api_fabrication) { 0 }
|
let(:api_fabrication) { 0 }
|
||||||
let(:fabrication_resources) { {} }
|
let(:fabrication_resources) { {} }
|
||||||
let(:testcase) { 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/1234' }
|
let(:testcase) { 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/1234' }
|
||||||
|
let(:product_group) { nil }
|
||||||
|
|
||||||
let(:influx_client_args) do
|
let(:influx_client_args) do
|
||||||
{
|
{
|
||||||
|
@ -53,6 +54,7 @@ describe QA::Support::Formatters::TestStatsFormatter do
|
||||||
merge_request: 'false',
|
merge_request: 'false',
|
||||||
run_type: run_type,
|
run_type: run_type,
|
||||||
stage: stage.match(%r{\d{1,2}_(\w+)}).captures.first,
|
stage: stage.match(%r{\d{1,2}_(\w+)}).captures.first,
|
||||||
|
product_group: product_group,
|
||||||
testcase: testcase
|
testcase: testcase
|
||||||
},
|
},
|
||||||
fields: {
|
fields: {
|
||||||
|
@ -146,6 +148,19 @@ describe QA::Support::Formatters::TestStatsFormatter do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with product group tag' do
|
||||||
|
let(:product_group) { :import }
|
||||||
|
|
||||||
|
it 'exports data to influxdb with correct reliable tag' do
|
||||||
|
run_spec do
|
||||||
|
it('spec', product_group: :import, testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/1234') {}
|
||||||
|
end
|
||||||
|
|
||||||
|
expect(influx_write_api).to have_received(:write).once
|
||||||
|
expect(influx_write_api).to have_received(:write).with(data: [data])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context 'with smoke spec' do
|
context 'with smoke spec' do
|
||||||
let(:smoke) { 'true' }
|
let(:smoke) { 'true' }
|
||||||
|
|
||||||
|
|
|
@ -56,7 +56,7 @@ RSpec.describe 'Value Stream Analytics', :js do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when there's value stream analytics data" do
|
context "when there's value stream analytics data", :sidekiq_inline do
|
||||||
# NOTE: in https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68595 travel back
|
# NOTE: in https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68595 travel back
|
||||||
# 5 days in time before we create data for these specs, to mitigate some flakiness
|
# 5 days in time before we create data for these specs, to mitigate some flakiness
|
||||||
# So setting the date range to be the last 2 days should skip past the existing data
|
# So setting the date range to be the last 2 days should skip past the existing data
|
||||||
|
@ -103,7 +103,7 @@ RSpec.describe 'Value Stream Analytics', :js do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'shows data on each stage', :sidekiq_might_not_need_inline, quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/338332' do
|
it 'shows data on each stage', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/338332' do
|
||||||
expect_issue_to_be_present
|
expect_issue_to_be_present
|
||||||
|
|
||||||
click_stage('Plan')
|
click_stage('Plan')
|
||||||
|
@ -207,11 +207,11 @@ RSpec.describe 'Value Stream Analytics', :js do
|
||||||
wait_for_requests
|
wait_for_requests
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'does not show the commit stats' do
|
it 'does not show the commit stats', :sidekiq_inline do
|
||||||
expect(page.find(metrics_selector)).not_to have_selector("#commits")
|
expect(page.find(metrics_selector)).not_to have_selector("#commits")
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'does not show restricted stages', :aggregate_failures do
|
it 'does not show restricted stages', :aggregate_failures, :sidekiq_inline do
|
||||||
expect(find(stage_table_selector)).to have_content(issue.title)
|
expect(find(stage_table_selector)).to have_content(issue.title)
|
||||||
|
|
||||||
expect(page).to have_selector('.gl-path-nav-list-item', text: 'Issue')
|
expect(page).to have_selector('.gl-path-nav-list-item', text: 'Issue')
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"id",
|
||||||
|
"url",
|
||||||
|
"update_status",
|
||||||
|
"last_update_at",
|
||||||
|
"last_update_started_at",
|
||||||
|
"last_successful_update_at",
|
||||||
|
"last_error"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"update_status": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"last_update_at": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"last_update_started_at": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"last_successful_update_at": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"last_error": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
|
@ -17,6 +17,9 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<input class="custom gl-field-error-ignore" type="text">Custom, do not validate</input>
|
<input class="custom gl-field-error-ignore" type="text">Custom, do not validate</input>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<textarea required title="Textarea is required">Textarea</textarea>
|
||||||
|
</div>
|
||||||
<div class="form-group"></div>
|
<div class="form-group"></div>
|
||||||
<input class="submit" type="submit">Submit</input>
|
<input class="submit" type="submit">Submit</input>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -27,7 +27,7 @@ describe('GL Style Field Errors', () => {
|
||||||
expect(testContext.fieldErrors).toBeDefined();
|
expect(testContext.fieldErrors).toBeDefined();
|
||||||
const { inputs } = testContext.fieldErrors.state;
|
const { inputs } = testContext.fieldErrors.state;
|
||||||
|
|
||||||
expect(inputs.length).toBe(4);
|
expect(inputs.length).toBe(5);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should ignore elements with custom error handling', () => {
|
it('should ignore elements with custom error handling', () => {
|
||||||
|
|
|
@ -10,7 +10,7 @@ RSpec.describe GitlabSchema.types['DeploymentDetails'] do
|
||||||
id iid ref tag tags sha created_at updated_at finished_at status commit job triggerer
|
id iid ref tag tags sha created_at updated_at finished_at status commit job triggerer
|
||||||
]
|
]
|
||||||
|
|
||||||
expect(described_class).to have_graphql_fields(*expected_fields)
|
expect(described_class).to include_graphql_fields(*expected_fields)
|
||||||
end
|
end
|
||||||
|
|
||||||
specify { expect(described_class).to require_graphql_authorizations(:read_deployment) }
|
specify { expect(described_class).to require_graphql_authorizations(:read_deployment) }
|
||||||
|
|
|
@ -27,11 +27,9 @@ RSpec.describe Nav::TopNavHelper do
|
||||||
|
|
||||||
let(:subject) { helper.top_nav_view_model(project: current_project, group: current_group) }
|
let(:subject) { helper.top_nav_view_model(project: current_project, group: current_group) }
|
||||||
|
|
||||||
let(:menu_title) { 'Menu' }
|
let(:menu_tooltip) { 'Main menu' }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
stub_feature_flags(new_navbar_layout: false)
|
|
||||||
|
|
||||||
allow(Gitlab::CurrentSettings).to receive(:admin_mode) { with_current_settings_admin_mode }
|
allow(Gitlab::CurrentSettings).to receive(:admin_mode) { with_current_settings_admin_mode }
|
||||||
allow(helper).to receive(:header_link?).with(:admin_mode) { with_header_link_admin_mode }
|
allow(helper).to receive(:header_link?).with(:admin_mode) { with_header_link_admin_mode }
|
||||||
|
|
||||||
|
@ -46,8 +44,8 @@ RSpec.describe Nav::TopNavHelper do
|
||||||
allow(helper).to receive(:dashboard_nav_link?).with(:activity) { with_activity }
|
allow(helper).to receive(:dashboard_nav_link?).with(:activity) { with_activity }
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'has :menuTitle' do
|
it 'has :menuTooltip' do
|
||||||
expect(subject[:menuTitle]).to eq(menu_title)
|
expect(subject[:menuTooltip]).to eq(menu_tooltip)
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when current_user is nil (anonymous)' do
|
context 'when current_user is nil (anonymous)' do
|
||||||
|
@ -108,7 +106,7 @@ RSpec.describe Nav::TopNavHelper do
|
||||||
let(:current_user) { user }
|
let(:current_user) { user }
|
||||||
|
|
||||||
it 'has no menu items or views by default' do
|
it 'has no menu items or views by default' do
|
||||||
expect(subject).to eq({ menuTitle: menu_title,
|
expect(subject).to eq({ menuTooltip: menu_tooltip,
|
||||||
primary: [],
|
primary: [],
|
||||||
secondary: [],
|
secondary: [],
|
||||||
shortcuts: [],
|
shortcuts: [],
|
||||||
|
@ -176,74 +174,6 @@ RSpec.describe Nav::TopNavHelper do
|
||||||
expect(projects_view[:linksSecondary]).to eq([])
|
expect(projects_view[:linksSecondary]).to eq([])
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when extra submenu options are not hidden' do
|
|
||||||
before do
|
|
||||||
stub_feature_flags(remove_extra_primary_submenu_options: false)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'has expected :linksPrimary' do
|
|
||||||
expected_links_primary = [
|
|
||||||
::Gitlab::Nav::TopNavMenuItem.build(
|
|
||||||
data: {
|
|
||||||
qa_selector: 'menu_item_link',
|
|
||||||
qa_title: 'Your projects',
|
|
||||||
**menu_data_tracking_attrs('your_projects')
|
|
||||||
},
|
|
||||||
href: '/dashboard/projects',
|
|
||||||
id: 'your',
|
|
||||||
title: 'Your projects'
|
|
||||||
),
|
|
||||||
::Gitlab::Nav::TopNavMenuItem.build(
|
|
||||||
data: {
|
|
||||||
qa_selector: 'menu_item_link',
|
|
||||||
qa_title: 'Starred projects',
|
|
||||||
**menu_data_tracking_attrs('starred_projects')
|
|
||||||
},
|
|
||||||
href: '/dashboard/projects/starred',
|
|
||||||
id: 'starred',
|
|
||||||
title: 'Starred projects'
|
|
||||||
),
|
|
||||||
::Gitlab::Nav::TopNavMenuItem.build(
|
|
||||||
data: {
|
|
||||||
qa_selector: 'menu_item_link',
|
|
||||||
qa_title: 'Explore projects',
|
|
||||||
**menu_data_tracking_attrs('explore_projects')
|
|
||||||
},
|
|
||||||
href: '/explore',
|
|
||||||
id: 'explore',
|
|
||||||
title: 'Explore projects'
|
|
||||||
),
|
|
||||||
::Gitlab::Nav::TopNavMenuItem.build(
|
|
||||||
data: {
|
|
||||||
qa_selector: 'menu_item_link',
|
|
||||||
qa_title: 'Explore topics',
|
|
||||||
**menu_data_tracking_attrs('explore_topics')
|
|
||||||
},
|
|
||||||
href: '/explore/projects/topics',
|
|
||||||
id: 'topics',
|
|
||||||
title: 'Explore topics'
|
|
||||||
)
|
|
||||||
]
|
|
||||||
expect(projects_view[:linksPrimary]).to eq(expected_links_primary)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'has expected :linksSecondary' do
|
|
||||||
expected_links_secondary = [
|
|
||||||
::Gitlab::Nav::TopNavMenuItem.build(
|
|
||||||
data: {
|
|
||||||
qa_selector: 'menu_item_link',
|
|
||||||
qa_title: 'Create new project',
|
|
||||||
**menu_data_tracking_attrs('create_new_project')
|
|
||||||
},
|
|
||||||
href: '/projects/new',
|
|
||||||
id: 'create',
|
|
||||||
title: 'Create new project'
|
|
||||||
)
|
|
||||||
]
|
|
||||||
expect(projects_view[:linksSecondary]).to eq(expected_links_secondary)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'with current nav as project' do
|
context 'with current nav as project' do
|
||||||
before do
|
before do
|
||||||
helper.nav('project')
|
helper.nav('project')
|
||||||
|
@ -341,54 +271,6 @@ RSpec.describe Nav::TopNavHelper do
|
||||||
expect(groups_view[:linksSecondary]).to eq([])
|
expect(groups_view[:linksSecondary]).to eq([])
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when extra submenu options are not hidden' do
|
|
||||||
before do
|
|
||||||
stub_feature_flags(remove_extra_primary_submenu_options: false)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'has expected :linksPrimary' do
|
|
||||||
expected_links_primary = [
|
|
||||||
::Gitlab::Nav::TopNavMenuItem.build(
|
|
||||||
data: {
|
|
||||||
qa_selector: 'menu_item_link',
|
|
||||||
qa_title: 'Your groups',
|
|
||||||
**menu_data_tracking_attrs('your_groups')
|
|
||||||
},
|
|
||||||
href: '/dashboard/groups',
|
|
||||||
id: 'your',
|
|
||||||
title: 'Your groups'
|
|
||||||
),
|
|
||||||
::Gitlab::Nav::TopNavMenuItem.build(
|
|
||||||
data: {
|
|
||||||
qa_selector: 'menu_item_link',
|
|
||||||
qa_title: 'Explore groups',
|
|
||||||
**menu_data_tracking_attrs('explore_groups')
|
|
||||||
},
|
|
||||||
href: '/explore/groups',
|
|
||||||
id: 'explore',
|
|
||||||
title: 'Explore groups'
|
|
||||||
)
|
|
||||||
]
|
|
||||||
expect(groups_view[:linksPrimary]).to eq(expected_links_primary)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'has expected :linksSecondary' do
|
|
||||||
expected_links_secondary = [
|
|
||||||
::Gitlab::Nav::TopNavMenuItem.build(
|
|
||||||
data: {
|
|
||||||
qa_selector: 'menu_item_link',
|
|
||||||
qa_title: 'Create group',
|
|
||||||
**menu_data_tracking_attrs('create_group')
|
|
||||||
},
|
|
||||||
href: '/groups/new',
|
|
||||||
id: 'create',
|
|
||||||
title: 'Create group'
|
|
||||||
)
|
|
||||||
]
|
|
||||||
expect(groups_view[:linksSecondary]).to eq(expected_links_secondary)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'with external user' do
|
context 'with external user' do
|
||||||
let(:current_user) { external_user }
|
let(:current_user) { external_user }
|
||||||
|
|
||||||
|
|
|
@ -278,6 +278,34 @@ RSpec.describe MergeRequest, factory_default: :keep do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'callbacks' do
|
describe 'callbacks' do
|
||||||
|
describe '#ensure_merge_request_diff' do
|
||||||
|
let(:merge_request) { build(:merge_request) }
|
||||||
|
|
||||||
|
context 'when async_merge_request_diff_creation is true' do
|
||||||
|
before do
|
||||||
|
merge_request.skip_ensure_merge_request_diff = true
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not create a merge_request_diff after create' do
|
||||||
|
merge_request.save!
|
||||||
|
|
||||||
|
expect(merge_request.merge_request_diff).to be_empty
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when async_merge_request_diff_creation is false' do
|
||||||
|
before do
|
||||||
|
merge_request.skip_ensure_merge_request_diff = false
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates merge_request_diff after create' do
|
||||||
|
merge_request.save!
|
||||||
|
|
||||||
|
expect(merge_request.merge_request_diff).not_to be_empty
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe '#ensure_merge_request_metrics' do
|
describe '#ensure_merge_request_metrics' do
|
||||||
let(:merge_request) { create(:merge_request) }
|
let(:merge_request) { create(:merge_request) }
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ RSpec.describe 'value stream analytics events' do
|
||||||
let(:project) { create(:project, :repository, public_builds: false) }
|
let(:project) { create(:project, :repository, public_builds: false) }
|
||||||
let(:issue) { create(:issue, project: project, created_at: 2.days.ago) }
|
let(:issue) { create(:issue, project: project, created_at: 2.days.ago) }
|
||||||
|
|
||||||
describe 'GET /:namespace/:project/value_stream_analytics/events/issues' do
|
describe 'GET /:namespace/:project/value_stream_analytics/events/issues', :sidekiq_inline do
|
||||||
let(:first_issue_iid) { project.issues.sort_by_attribute(:created_desc).pick(:iid).to_s }
|
let(:first_issue_iid) { project.issues.sort_by_attribute(:created_desc).pick(:iid).to_s }
|
||||||
let(:first_mr_iid) { project.merge_requests.sort_by_attribute(:created_desc).pick(:iid).to_s }
|
let(:first_mr_iid) { project.merge_requests.sort_by_attribute(:created_desc).pick(:iid).to_s }
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ RSpec.describe 'value stream analytics events' do
|
||||||
expect(json_response['events'].first['iid']).to eq(first_mr_iid)
|
expect(json_response['events'].first['iid']).to eq(first_mr_iid)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'lists the staging events', :sidekiq_inline do
|
it 'lists the staging events' do
|
||||||
get project_cycle_analytics_staging_path(project, format: :json)
|
get project_cycle_analytics_staging_path(project, format: :json)
|
||||||
|
|
||||||
expect(json_response['events']).not_to be_empty
|
expect(json_response['events']).not_to be_empty
|
||||||
|
|
|
@ -495,15 +495,40 @@ RSpec.describe MergeRequests::CreateService, :clean_gitlab_redis_shared_state do
|
||||||
project.add_developer(user)
|
project.add_developer(user)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'creates the merge request', :sidekiq_might_not_need_inline do
|
context 'when async_merge_request_diff_creation is enabled' do
|
||||||
expect_next_instance_of(MergeRequest) do |instance|
|
before do
|
||||||
expect(instance).to receive(:eager_fetch_ref!).and_call_original
|
stub_feature_flags(async_merge_request_diff_creation: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
merge_request = described_class.new(project: project, current_user: user, params: opts).execute
|
it 'creates the merge request', :sidekiq_inline do
|
||||||
|
expect_next_instance_of(MergeRequest) do |instance|
|
||||||
|
expect(instance).not_to receive(:eager_fetch_ref!)
|
||||||
|
end
|
||||||
|
|
||||||
expect(merge_request).to be_persisted
|
merge_request = described_class.new(project: project, current_user: user, params: opts).execute
|
||||||
expect(merge_request.iid).to be > 0
|
|
||||||
|
expect(merge_request).to be_persisted
|
||||||
|
expect(merge_request.iid).to be > 0
|
||||||
|
expect(merge_request.merge_request_diff).not_to be_empty
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when async_merge_request_diff_creation is disabled' do
|
||||||
|
before do
|
||||||
|
stub_feature_flags(async_merge_request_diff_creation: false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates the merge request' do
|
||||||
|
expect_next_instance_of(MergeRequest) do |instance|
|
||||||
|
expect(instance).to receive(:eager_fetch_ref!).and_call_original
|
||||||
|
end
|
||||||
|
|
||||||
|
merge_request = described_class.new(project: project, current_user: user, params: opts).execute
|
||||||
|
|
||||||
|
expect(merge_request).to be_persisted
|
||||||
|
expect(merge_request.iid).to be > 0
|
||||||
|
expect(merge_request.merge_request_diff).not_to be_empty
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'does not create the merge request when the target project is archived' do
|
it 'does not create the merge request when the target project is archived' do
|
||||||
|
|
Loading…
Reference in New Issue