Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
213f46f188
commit
b53b2fbb6b
|
|
@ -16,7 +16,7 @@ Please read the below documentations for a workflow of triaging and resolving br
|
|||
|
||||
<!-- If the pipeline failure is reproducible, provide steps to recreate the issue locally. Please use an ordered list. -->
|
||||
|
||||
Please refer to [Flaky tests documentation](https://docs.gitlab.com/ee/development/testing_guide/flaky_tests.html) to
|
||||
Please refer to [Flaky tests documentation](https://docs.gitlab.com/ee/development/testing_guide/flaky_tests.html) to
|
||||
learn more about how to reproduce them.
|
||||
|
||||
### Proposed Resolution
|
||||
|
|
@ -25,4 +25,4 @@ learn more about how to reproduce them.
|
|||
|
||||
Please refer to the [Resolution guidance](https://about.gitlab.com/handbook/engineering/workflow/#resolution-of-broken-master) to learn more about resolution of broken master.
|
||||
|
||||
/label ~"failure::flaky-test" ~"Engineering Productivity" ~"priority::2" ~"severity::3" ~"type::bug" ~"bug::transient"
|
||||
/label ~"type::maintenance" ~"failure::flaky-test" ~"priority::3" ~"severity::3"
|
||||
|
|
|
|||
|
|
@ -21,4 +21,4 @@ Please read the below documentations for a workflow of triaging and resolving br
|
|||
|
||||
Please refer to the [Resolution guidance](https://about.gitlab.com/handbook/engineering/workflow/#resolution-of-broken-master) to learn more about resolution of broken master.
|
||||
|
||||
/label ~"master:broken" ~"Engineering Productivity" ~"priority::1" ~"severity::1" ~"type::bug" ~"bug::transient"
|
||||
/label ~"master:broken" ~"Engineering Productivity" ~"priority::1" ~"severity::1" ~"type::maintenance" ~"maintenance::pipelines"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
<script>
|
||||
import { GlBanner } from '@gitlab/ui';
|
||||
import UserCalloutDismisser from '~/vue_shared/components/user_callout_dismisser.vue';
|
||||
import {
|
||||
I18N_FEEDBACK_BANNER_TITLE,
|
||||
I18N_FEEDBACK_BANNER_BODY,
|
||||
I18N_FEEDBACK_BANNER_BUTTON,
|
||||
FEEDBACK_URL,
|
||||
} from '../constants';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlBanner,
|
||||
UserCalloutDismisser,
|
||||
},
|
||||
inject: ['artifactsManagementFeedbackImagePath'],
|
||||
FEEDBACK_URL,
|
||||
i18n: {
|
||||
title: I18N_FEEDBACK_BANNER_TITLE,
|
||||
body: I18N_FEEDBACK_BANNER_BODY,
|
||||
button: I18N_FEEDBACK_BANNER_BUTTON,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<user-callout-dismisser feature-name="artifacts_management_page_feedback_banner">
|
||||
<template #default="{ dismiss, shouldShowCallout }">
|
||||
<gl-banner
|
||||
v-if="shouldShowCallout"
|
||||
class="gl-mb-6"
|
||||
:title="$options.i18n.title"
|
||||
:button-text="$options.i18n.button"
|
||||
:button-link="$options.FEEDBACK_URL"
|
||||
:svg-path="artifactsManagementFeedbackImagePath"
|
||||
@close="dismiss"
|
||||
>
|
||||
<p>{{ $options.i18n.body }}</p>
|
||||
</gl-banner>
|
||||
</template>
|
||||
</user-callout-dismisser>
|
||||
</template>
|
||||
|
|
@ -35,6 +35,7 @@ import {
|
|||
INITIAL_LAST_PAGE_SIZE,
|
||||
} from '../constants';
|
||||
import ArtifactsTableRowDetails from './artifacts_table_row_details.vue';
|
||||
import FeedbackBanner from './feedback_banner.vue';
|
||||
|
||||
const INITIAL_PAGINATION_STATE = {
|
||||
currentPage: INITIAL_CURRENT_PAGE,
|
||||
|
|
@ -58,6 +59,7 @@ export default {
|
|||
CiIcon,
|
||||
TimeAgo,
|
||||
ArtifactsTableRowDetails,
|
||||
FeedbackBanner,
|
||||
},
|
||||
inject: ['projectPath'],
|
||||
apollo: {
|
||||
|
|
@ -214,6 +216,7 @@ export default {
|
|||
</script>
|
||||
<template>
|
||||
<div>
|
||||
<feedback-banner />
|
||||
<gl-table
|
||||
:items="jobArtifacts"
|
||||
:fields="$options.fields"
|
||||
|
|
|
|||
|
|
@ -43,6 +43,13 @@ export const I18N_MODAL_BODY = s__(
|
|||
export const I18N_MODAL_PRIMARY = s__('Artifacts|Delete artifact');
|
||||
export const I18N_MODAL_CANCEL = __('Cancel');
|
||||
|
||||
export const I18N_FEEDBACK_BANNER_TITLE = s__('Artifacts|Help us improve this page');
|
||||
export const I18N_FEEDBACK_BANNER_BODY = s__(
|
||||
'Artifacts|We want you to be able to use this page to easily manage your CI/CD job artifacts. We are working to improve this experience and would appreciate any feedback you have about the improvements we are making.',
|
||||
);
|
||||
export const I18N_FEEDBACK_BANNER_BUTTON = s__('Artifacts|Take a quick survey');
|
||||
export const FEEDBACK_URL = 'https://gitlab.fra1.qualtrics.com/jfe/form/SV_cI9rAUI20Vo2St8';
|
||||
|
||||
export const INITIAL_CURRENT_PAGE = 1;
|
||||
export const INITIAL_PREVIOUS_PAGE_CURSOR = '';
|
||||
export const INITIAL_NEXT_PAGE_CURSOR = '';
|
||||
|
|
|
|||
|
|
@ -16,13 +16,14 @@ export const initArtifactsTable = () => {
|
|||
return false;
|
||||
}
|
||||
|
||||
const { projectPath } = el.dataset;
|
||||
const { projectPath, artifactsManagementFeedbackImagePath } = el.dataset;
|
||||
|
||||
return new Vue({
|
||||
el,
|
||||
apolloProvider,
|
||||
provide: {
|
||||
projectPath,
|
||||
artifactsManagementFeedbackImagePath,
|
||||
},
|
||||
render: (createElement) => createElement(JobArtifactsTable),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { GlBadge, GlToggle } from '@gitlab/ui';
|
|||
import axios from '~/lib/utils/axios_utils';
|
||||
import { createAlert } from '~/flash';
|
||||
import { s__ } from '~/locale';
|
||||
import Tracking from '~/tracking';
|
||||
|
||||
export default {
|
||||
i18n: {
|
||||
|
|
@ -37,6 +38,12 @@ export default {
|
|||
async toggleNav() {
|
||||
try {
|
||||
await axios.put(this.endpoint, { user: { use_new_navigation: !this.enabled } });
|
||||
|
||||
Tracking.event(undefined, 'click_toggle', {
|
||||
label: this.enabled ? 'disable_new_nav_beta' : 'enable_new_nav_beta',
|
||||
property: 'navigation',
|
||||
});
|
||||
|
||||
window.location.reload();
|
||||
} catch (error) {
|
||||
createAlert({
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
import leaveByUrl from '~/namespaces/leave_by_url';
|
||||
import { initGroupOverviewTabs } from '~/groups/init_overview_tabs';
|
||||
import initReadMore from '~/read_more';
|
||||
import initGroupDetails from '../shared/group_details';
|
||||
|
||||
leaveByUrl('group');
|
||||
initGroupDetails();
|
||||
initGroupOverviewTabs();
|
||||
initReadMore();
|
||||
|
|
|
|||
|
|
@ -31,9 +31,9 @@ export default function initReadMore(triggerSelector = '.js-read-more-trigger')
|
|||
|
||||
triggerEl.addEventListener(
|
||||
'click',
|
||||
(e) => {
|
||||
() => {
|
||||
targetEl.classList.add('is-expanded');
|
||||
e.target.remove();
|
||||
triggerEl.remove();
|
||||
},
|
||||
{ once: true },
|
||||
);
|
||||
|
|
|
|||
|
|
@ -10,11 +10,13 @@ import { initBlobRefSwitcher } from './under_topbar';
|
|||
|
||||
export const initSearchApp = () => {
|
||||
syntaxHighlight(document.querySelectorAll('.js-search-results'));
|
||||
const query = queryToObject(window.location.search, { gatherArrays: true });
|
||||
const { navigationJsonParsed: navigation } = sidebarInitState() || {};
|
||||
|
||||
const query = queryToObject(window.location.search);
|
||||
const navigation = sidebarInitState();
|
||||
|
||||
const store = createStore({ query, navigation });
|
||||
const store = createStore({
|
||||
query,
|
||||
navigation,
|
||||
});
|
||||
|
||||
initTopbar(store);
|
||||
initSidebar(store);
|
||||
|
|
|
|||
|
|
@ -21,6 +21,6 @@ export default {
|
|||
<template>
|
||||
<div>
|
||||
<radio-filter :class="ffBasedXPadding" :filter-data="$options.confidentialFilterData" />
|
||||
<hr class="gl-my-5 gl-border-gray-100" />
|
||||
<hr class="gl-my-5 gl-mx-5 gl-border-gray-100" />
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ export default {
|
|||
<form class="gl-pt-5 gl-md-pt-0" @submit.prevent="applyQuery">
|
||||
<hr
|
||||
v-if="searchPageVerticalNavFeatureFlag"
|
||||
class="gl-my-5 gl-border-gray-100 gl-display-none gl-md-display-block"
|
||||
class="gl-my-5 gl-mx-5 gl-border-gray-100 gl-display-none gl-md-display-block"
|
||||
/>
|
||||
<status-filter v-if="showStatusFilter" />
|
||||
<confidentiality-filter v-if="showConfidentialityFilter" />
|
||||
|
|
|
|||
|
|
@ -1,13 +1,10 @@
|
|||
<script>
|
||||
import { GlNav, GlNavItem, GlIcon } from '@gitlab/ui';
|
||||
import { mapActions, mapState } from 'vuex';
|
||||
import { formatNumber, s__ } from '~/locale';
|
||||
import { s__ } from '~/locale';
|
||||
import Tracking from '~/tracking';
|
||||
import {
|
||||
NAV_LINK_DEFAULT_CLASSES,
|
||||
NUMBER_FORMATING_OPTIONS,
|
||||
NAV_LINK_COUNT_DEFAULT_CLASSES,
|
||||
} from '../constants';
|
||||
import { NAV_LINK_DEFAULT_CLASSES, NAV_LINK_COUNT_DEFAULT_CLASSES } from '../constants';
|
||||
import { formatSearchResultCount } from '../../store/utils';
|
||||
|
||||
export default {
|
||||
name: 'ScopeNavigation',
|
||||
|
|
@ -29,11 +26,7 @@ export default {
|
|||
methods: {
|
||||
...mapActions(['fetchSidebarCount']),
|
||||
showFormatedCount(count) {
|
||||
if (!count) {
|
||||
return '0';
|
||||
}
|
||||
const countNumber = parseInt(count.replace(/,/g, ''), 10);
|
||||
return formatNumber(countNumber, NUMBER_FORMATING_OPTIONS);
|
||||
return formatSearchResultCount(count);
|
||||
},
|
||||
isCountOverLimit(count) {
|
||||
return count.includes('+');
|
||||
|
|
@ -82,6 +75,6 @@ export default {
|
|||
</span>
|
||||
</gl-nav-item>
|
||||
</gl-nav>
|
||||
<hr class="gl-mt-5 gl-mb-0 gl-border-gray-100 gl-md-display-none" />
|
||||
<hr class="gl-mt-5 gl-mx-5 gl-mb-0 gl-border-gray-100 gl-md-display-none" />
|
||||
</nav>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -21,6 +21,6 @@ export default {
|
|||
<template>
|
||||
<div>
|
||||
<radio-filter :class="ffBasedXPadding" :filter-data="$options.stateFilterData" />
|
||||
<hr class="gl-my-5 gl-border-gray-100" />
|
||||
<hr class="gl-my-5 gl-mx-5 gl-border-gray-100" />
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -1,13 +1,16 @@
|
|||
export const SCOPE_ISSUES = 'issues';
|
||||
export const SCOPE_MERGE_REQUESTS = 'merge_requests';
|
||||
|
||||
export const NUMBER_FORMATING_OPTIONS = { notation: 'compact', compactDisplay: 'short' };
|
||||
export const NAV_LINK_DEFAULT_CLASSES = [
|
||||
export const SCOPE_BLOB = 'blobs';
|
||||
export const LABEL_DEFAULT_CLASSES = [
|
||||
'gl-display-flex',
|
||||
'gl-flex-direction-row',
|
||||
'gl-flex-wrap-nowrap',
|
||||
'gl-justify-content-space-between',
|
||||
'gl-text-gray-900',
|
||||
];
|
||||
|
||||
export const NAV_LINK_DEFAULT_CLASSES = [
|
||||
...LABEL_DEFAULT_CLASSES,
|
||||
'gl-justify-content-space-between',
|
||||
];
|
||||
export const NAV_LINK_COUNT_DEFAULT_CLASSES = ['gl-font-sm', 'gl-font-weight-normal'];
|
||||
export const HR_DEFAULT_CLASSES = ['gl-my-5', 'gl-mx-5', 'gl-border-gray-100'];
|
||||
export const ONLY_SHOW_MD = ['gl-display-none', 'gl-md-display-block'];
|
||||
|
|
|
|||
|
|
@ -6,11 +6,11 @@ Vue.use(Translate);
|
|||
|
||||
export const sidebarInitState = () => {
|
||||
const el = document.getElementById('js-search-sidebar');
|
||||
|
||||
if (!el) return {};
|
||||
|
||||
const { navigation } = el.dataset;
|
||||
return JSON.parse(navigation);
|
||||
const { navigationJson } = el.dataset;
|
||||
const navigationJsonParsed = JSON.parse(navigationJson);
|
||||
return { navigationJsonParsed };
|
||||
};
|
||||
|
||||
export const initSidebar = (store) => {
|
||||
|
|
|
|||
|
|
@ -10,3 +10,5 @@ export const GROUPS_LOCAL_STORAGE_KEY = 'global-search-frequent-groups';
|
|||
export const PROJECTS_LOCAL_STORAGE_KEY = 'global-search-frequent-projects';
|
||||
|
||||
export const SIDEBAR_PARAMS = [stateFilterData.filterParam, confidentialFilterData.filterParam];
|
||||
|
||||
export const NUMBER_FORMATING_OPTIONS = { notation: 'compact', compactDisplay: 'short' };
|
||||
|
|
|
|||
|
|
@ -1,5 +1,12 @@
|
|||
import AccessorUtilities from '~/lib/utils/accessor';
|
||||
import { MAX_FREQUENT_ITEMS, MAX_FREQUENCY, SIDEBAR_PARAMS } from './constants';
|
||||
import { formatNumber } from '~/locale';
|
||||
import { joinPaths } from '~/lib/utils/url_utility';
|
||||
import {
|
||||
MAX_FREQUENT_ITEMS,
|
||||
MAX_FREQUENCY,
|
||||
SIDEBAR_PARAMS,
|
||||
NUMBER_FORMATING_OPTIONS,
|
||||
} from './constants';
|
||||
|
||||
function extractKeys(object, keyList) {
|
||||
return Object.fromEntries(keyList.map((key) => [key, object[key]]));
|
||||
|
|
@ -90,3 +97,18 @@ export const isSidebarDirty = (currentQuery, urlQuery) => {
|
|||
return userAddedParam || userChangedExistingParam;
|
||||
});
|
||||
};
|
||||
|
||||
export const formatSearchResultCount = (count) => {
|
||||
if (!count) {
|
||||
return '0';
|
||||
}
|
||||
|
||||
const countNumber = typeof count === 'string' ? parseInt(count.replace(/,/g, ''), 10) : count;
|
||||
return formatNumber(countNumber, NUMBER_FORMATING_OPTIONS);
|
||||
};
|
||||
|
||||
export const getAggregationsUrl = () => {
|
||||
const currentUrl = new URL(window.location.href);
|
||||
currentUrl.pathname = joinPaths('/search', 'aggregations');
|
||||
return currentUrl.toString();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,12 +1,10 @@
|
|||
<script>
|
||||
import { GlButton, GlLoadingIcon, GlIcon } from '@gitlab/ui';
|
||||
import { GlButton } from '@gitlab/ui';
|
||||
import { __ } from '~/locale';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlButton,
|
||||
GlLoadingIcon,
|
||||
GlIcon,
|
||||
},
|
||||
props: {
|
||||
title: {
|
||||
|
|
@ -32,7 +30,7 @@ export default {
|
|||
|
||||
computed: {
|
||||
arrowIconName() {
|
||||
return this.isCollapsed ? 'chevron-lg-right' : 'chevron-lg-down';
|
||||
return this.isCollapsed ? 'chevron-right' : 'chevron-down';
|
||||
},
|
||||
ariaLabel() {
|
||||
return this.isCollapsed ? __('Expand') : __('Collapse');
|
||||
|
|
@ -47,7 +45,7 @@ export default {
|
|||
</script>
|
||||
<template>
|
||||
<div class="mr-widget-extension">
|
||||
<div class="d-flex align-items-center pl-3">
|
||||
<div class="d-flex align-items-center pl-3 gl-py-3">
|
||||
<div v-if="hasError" class="ci-widget media">
|
||||
<div class="media-body">
|
||||
<span class="gl-font-sm mr-widget-margin-left gl-line-height-24 js-error-state">
|
||||
|
|
@ -57,16 +55,15 @@ export default {
|
|||
</div>
|
||||
|
||||
<template v-else>
|
||||
<button
|
||||
class="btn-blank btn s32 square"
|
||||
type="button"
|
||||
<gl-button
|
||||
class="gl-mr-3"
|
||||
size="small"
|
||||
:aria-label="ariaLabel"
|
||||
:disabled="isLoading"
|
||||
:loading="isLoading"
|
||||
:icon="arrowIconName"
|
||||
category="tertiary"
|
||||
@click="toggleCollapsed"
|
||||
>
|
||||
<gl-loading-icon v-if="isLoading" size="sm" />
|
||||
<gl-icon v-else :name="arrowIconName" class="js-icon" />
|
||||
</button>
|
||||
/>
|
||||
<template v-if="isCollapsed">
|
||||
<slot name="header"></slot>
|
||||
<gl-button
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ module Ci
|
|||
'artifacts', path
|
||||
].join('/')
|
||||
|
||||
"#{project.pages_group_url}/#{artifact_path}"
|
||||
"#{project.pages_namespace_url}/#{artifact_path}"
|
||||
end
|
||||
|
||||
def external_link?(job)
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ class DeployKey < Key
|
|||
scope :with_projects, -> { includes(deploy_keys_projects: { project: [:route, namespace: :route] }) }
|
||||
scope :including_projects_with_write_access, -> { includes(:projects_with_write_access) }
|
||||
|
||||
accepts_nested_attributes_for :deploy_keys_projects
|
||||
accepts_nested_attributes_for :deploy_keys_projects, reject_if: :reject_deploy_keys_projects?
|
||||
|
||||
def private?
|
||||
!public?
|
||||
|
|
@ -72,4 +72,10 @@ class DeployKey < Key
|
|||
def impersonated?
|
||||
false
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def reject_deploy_keys_projects?
|
||||
!self.valid?
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ module Pages
|
|||
strong_memoize_attr :source
|
||||
|
||||
def prefix
|
||||
if project.pages_group_root?
|
||||
if project.pages_namespace_url == project.pages_url
|
||||
'/'
|
||||
else
|
||||
project.full_path.delete_prefix(trim_prefix) + '/'
|
||||
|
|
|
|||
|
|
@ -2101,7 +2101,7 @@ class Project < ApplicationRecord
|
|||
pages_metadatum&.deployed?
|
||||
end
|
||||
|
||||
def pages_group_url
|
||||
def pages_namespace_url
|
||||
# The host in URL always needs to be downcased
|
||||
Gitlab.config.pages.url.sub(%r{^https?://}) do |prefix|
|
||||
"#{prefix}#{pages_subdomain}."
|
||||
|
|
@ -2109,19 +2109,23 @@ class Project < ApplicationRecord
|
|||
end
|
||||
|
||||
def pages_url
|
||||
url = pages_group_url
|
||||
url = pages_namespace_url
|
||||
url_path = full_path.partition('/').last
|
||||
namespace_url = "#{Settings.pages.protocol}://#{url_path}".downcase
|
||||
|
||||
if Rails.env.development?
|
||||
url_without_port = URI.parse(url)
|
||||
url_without_port.port = nil
|
||||
|
||||
return url if url_without_port.to_s == namespace_url
|
||||
end
|
||||
|
||||
# If the project path is the same as host, we serve it as group page
|
||||
return url if url == "#{Settings.pages.protocol}://#{url_path}".downcase
|
||||
return url if url == namespace_url
|
||||
|
||||
"#{url}/#{url_path}"
|
||||
end
|
||||
|
||||
def pages_group_root?
|
||||
pages_group_url == pages_url
|
||||
end
|
||||
|
||||
def pages_subdomain
|
||||
full_path.partition('/').first
|
||||
end
|
||||
|
|
|
|||
|
|
@ -24,25 +24,23 @@
|
|||
- if current_user
|
||||
.home-panel-buttons.gl-display-flex.gl-justify-content-md-end.gl-align-items-center.gl-flex-wrap.gl-gap-3{ data: { testid: 'group-buttons' } }
|
||||
- if current_user.admin?
|
||||
= link_to [:admin, @group], class: 'btn btn-default gl-button btn-icon', title: _('View group in admin area'),
|
||||
data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
|
||||
= sprite_icon('admin')
|
||||
= render Pajamas::ButtonComponent.new(href: [:admin, @group], icon: 'admin', button_options: { title: _('View group in admin area'), data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } })
|
||||
- if @notification_setting
|
||||
.js-vue-notification-dropdown{ data: { disabled: emails_disabled.to_s, dropdown_items: notification_dropdown_items(@notification_setting).to_json, notification_level: @notification_setting.level, help_page_path: help_page_path('user/profile/notifications'), group_id: @group.id, container_class: 'gl-vertical-align-top', no_flip: 'true' } }
|
||||
- if can_create_subgroups
|
||||
.gl-sm-w-auto.gl-w-full
|
||||
= link_to _("New subgroup"),
|
||||
new_group_path(parent_id: @group.id, anchor: 'create-group-pane'),
|
||||
class: "btn btn-default gl-button gl-sm-w-auto gl-w-full",
|
||||
data: { qa_selector: 'new_subgroup_button' }
|
||||
= render Pajamas::ButtonComponent.new(href: new_group_path(parent_id: @group.id, anchor: 'create-group-pane'), button_options: { data: { qa_selector: 'new_subgroup_button' }, class: 'gl-sm-w-auto gl-w-full'}) do
|
||||
= _("New subgroup")
|
||||
|
||||
- if can_create_projects
|
||||
.gl-sm-w-auto.gl-w-full
|
||||
= link_to _("New project"), new_project_path(namespace_id: @group.id), class: "btn btn-confirm gl-button gl-sm-w-auto gl-w-full", data: { qa_selector: 'new_project_button' }
|
||||
= render Pajamas::ButtonComponent.new(href: new_project_path(namespace_id: @group.id), variant: :confirm, button_options: { data: { qa_selector: 'new_project_button' }, class: 'gl-sm-w-auto gl-w-full' }) do
|
||||
= _('New project')
|
||||
|
||||
- if @group.description.present?
|
||||
.group-home-desc.mt-1
|
||||
.home-panel-description
|
||||
.home-panel-description-markdown.read-more-container{ itemprop: 'description' }
|
||||
= markdown_field(@group, :description)
|
||||
%button.gl-button.btn.btn-link.js-read-more-trigger.d-lg-none{ type: "button" }
|
||||
= render Pajamas::ButtonComponent.new(variant: :link, button_options: { class: 'js-read-more-trigger gl-lg-display-none' }) do
|
||||
= _("Read more")
|
||||
|
|
|
|||
|
|
@ -6,4 +6,5 @@
|
|||
.gl-mb-6
|
||||
%strong= s_('Artifacts|Total artifacts size')
|
||||
= number_to_human_size(@total_size, precicion: 2)
|
||||
#js-artifact-management{ data: { "project-path" => @project.full_path } }
|
||||
#js-artifact-management{ data: { "project-path": @project.full_path,
|
||||
"artifacts-management-feedback-image-path": image_path('illustrations/chat-bubble-sm.svg') } }
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
= render_if_exists 'shared/promotions/promote_advanced_search'
|
||||
- if Feature.enabled?(:search_page_vertical_nav, current_user)
|
||||
.results.gl-md-display-flex.gl-mt-0
|
||||
#js-search-sidebar{ class: search_bar_classes, data: { navigation: search_navigation_json } }
|
||||
#js-search-sidebar{ class: search_bar_classes, data: { navigation_json: search_navigation_json } }
|
||||
.gl-w-full.gl-flex-grow-1.gl-overflow-x-hidden
|
||||
= render partial: 'search/results_status' unless @search_objects.to_a.empty?
|
||||
= render partial: 'search/results_list'
|
||||
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
.results.gl-md-display-flex.gl-mt-3
|
||||
- if %w[issues merge_requests].include?(@scope)
|
||||
#js-search-sidebar{ class: search_bar_classes, data: { navigation: search_navigation_json } }
|
||||
#js-search-sidebar{ class: search_bar_classes, data: { navigation_json: search_navigation_json } }
|
||||
|
||||
.gl-w-full.gl-flex-grow-1.gl-overflow-x-hidden
|
||||
= render partial: 'search/results_list'
|
||||
|
|
|
|||
|
|
@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/365350
|
|||
milestone: '15.1'
|
||||
type: development
|
||||
group: group::release
|
||||
default_enabled: false
|
||||
default_enabled: true
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
- title: 'Approvers and Approver Group fields in Merge Request Approval API'
|
||||
announcement_milestone: '15.8'
|
||||
announcement_date: '2023-01-22'
|
||||
removal_milestone: '16.0'
|
||||
removal_date: '2023-05-22'
|
||||
breaking_change: true
|
||||
body: |
|
||||
The endpoint to get the configuration of approvals for a project returns empty arrays for `approvers` and `approval_groups`. These fields were deprecated in favor of the endpoint to [get project-level rules](https://docs.gitlab.com/ee/api/merge_request_approvals.html#get-project-level-rules) for a merge request. API users are encouraged to switch to this endpoint instead. These fields will be removed from the `get configuration` endpoint in v5 of the GitLab REST API.
|
||||
stage: create
|
||||
tiers:
|
||||
issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/353097
|
||||
documentation_url: https://docs.gitlab.com/ee/api/merge_request_approvals.html#get-project-level-rules
|
||||
image_url:
|
||||
video_url:
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class PartitionPmPackageMetadataTables < Gitlab::Database::Migration[2.1]
|
||||
def up
|
||||
# no-op
|
||||
# This migration was reverted as part of https://gitlab.com/gitlab-org/gitlab/-/merge_requests/108644
|
||||
# The migration file is re-added to ensure that all environments have the same list of migrations.
|
||||
end
|
||||
|
||||
def down
|
||||
# no-op
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1 @@
|
|||
37df82f093bb81ff1bc36ea9ba29f4e70bcb96274e2dcc70438ce0710dd7e9d9
|
||||
|
|
@ -16,7 +16,8 @@ in the project. Must be authenticated for all endpoints.
|
|||
|
||||
### Get Configuration
|
||||
|
||||
> Moved to GitLab Premium in 13.9.
|
||||
> - Moved to GitLab Premium in 13.9.
|
||||
> - The `approvers` and `approver_groups` fields were deprecated in GitLab 12.3 and always return empty. Use the [project level approval rules](#get-project-level-rules) to access this information.
|
||||
|
||||
You can request information about a project's approval configuration using the
|
||||
following endpoint:
|
||||
|
|
@ -27,12 +28,14 @@ GET /projects/:id/approvals
|
|||
|
||||
Supported attributes:
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
| --------- | ------- | -------- | ------------------- |
|
||||
| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding). |
|
||||
| Attribute | Type | Required | Description |
|
||||
|-----------|-------------------|------------------------|-------------------------------------------------------------------------------|
|
||||
| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding). |
|
||||
|
||||
```json
|
||||
{
|
||||
"approvers": [], // Deprecated in GitLab 12.3, always returns empty
|
||||
"approver_groups": [], // Deprecated in GitLab 12.3, always returns empty
|
||||
"approvals_before_merge": 2,
|
||||
"reset_approvals_on_push": true,
|
||||
"selective_code_owner_removals": false,
|
||||
|
|
@ -56,16 +59,16 @@ POST /projects/:id/approvals
|
|||
|
||||
Supported attributes:
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
| ------------------------------------------------ | ------- | -------- | -- |
|
||||
| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding). |
|
||||
| `approvals_before_merge` (deprecated) | integer | **{dotted-circle}** No | How many approvals are required before a merge request can be merged. [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/11132) in GitLab 12.3. |
|
||||
| `disable_overriding_approvers_per_merge_request` | boolean | **{dotted-circle}** No | Allow or prevent overriding approvers per merge request. |
|
||||
| `merge_requests_author_approval` | boolean | **{dotted-circle}** No | Allow or prevent authors from self approving merge requests; `true` means authors can self approve. |
|
||||
| `merge_requests_disable_committers_approval` | boolean | **{dotted-circle}** No | Allow or prevent committers from self approving merge requests. |
|
||||
| `require_password_to_approve` | boolean | **{dotted-circle}** No | Require approver to enter a password to authenticate before adding the approval. |
|
||||
| `reset_approvals_on_push` | boolean | **{dotted-circle}** No | Reset approvals on a new push. |
|
||||
| `selective_code_owner_removals` | boolean | **{dotted-circle}** No | Reset approvals from Code Owners if their files changed. Can be enabled only if `reset_approvals_on_push` is disabled. |
|
||||
| Attribute | Type | Required | Description |
|
||||
|--------------------------------------------------|-------------------|------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding). |
|
||||
| `approvals_before_merge` (deprecated) | integer | **{dotted-circle}** No | How many approvals are required before a merge request can be merged. [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/11132) in GitLab 12.3. |
|
||||
| `disable_overriding_approvers_per_merge_request` | boolean | **{dotted-circle}** No | Allow or prevent overriding approvers per merge request. |
|
||||
| `merge_requests_author_approval` | boolean | **{dotted-circle}** No | Allow or prevent authors from self approving merge requests; `true` means authors can self approve. |
|
||||
| `merge_requests_disable_committers_approval` | boolean | **{dotted-circle}** No | Allow or prevent committers from self approving merge requests. |
|
||||
| `require_password_to_approve` | boolean | **{dotted-circle}** No | Require approver to enter a password to authenticate before adding the approval. |
|
||||
| `reset_approvals_on_push` | boolean | **{dotted-circle}** No | Reset approvals on a new push. |
|
||||
| `selective_code_owner_removals` | boolean | **{dotted-circle}** No | Reset approvals from Code Owners if their files changed. Can be enabled only if `reset_approvals_on_push` is disabled. |
|
||||
|
||||
```json
|
||||
{
|
||||
|
|
@ -97,9 +100,9 @@ Use the `page` and `per_page` [pagination](index.md#offset-based-pagination) par
|
|||
|
||||
Supported attributes:
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
|----------------------|---------|----------|-----------------------------------------------------------|
|
||||
| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding). |
|
||||
| Attribute | Type | Required | Description |
|
||||
|-----------|-------------------|------------------------|-------------------------------------------------------------------------------|
|
||||
| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding). |
|
||||
|
||||
```json
|
||||
[
|
||||
|
|
@ -199,10 +202,10 @@ GET /projects/:id/approval_rules/:approval_rule_id
|
|||
|
||||
Supported attributes:
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
|----------------------|---------|----------|-----------------------------------------------------------|
|
||||
| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding). |
|
||||
| `approval_rule_id` | integer | **{check-circle}** Yes | The ID of a approval rule. |
|
||||
| Attribute | Type | Required | Description |
|
||||
|--------------------|-------------------|------------------------|-------------------------------------------------------------------------------|
|
||||
| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding). |
|
||||
| `approval_rule_id` | integer | **{check-circle}** Yes | The ID of a approval rule. |
|
||||
|
||||
```json
|
||||
{
|
||||
|
|
@ -301,18 +304,18 @@ POST /projects/:id/approval_rules
|
|||
|
||||
Supported attributes:
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
|-------------------------------------|-------------------|----------|------------ |
|
||||
| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding). |
|
||||
| `approvals_required` | integer | **{check-circle}** Yes | The number of required approvals for this rule. |
|
||||
| `name` | string | **{check-circle}** Yes | The name of the approval rule. |
|
||||
| `applies_to_all_protected_branches` | boolean | **{dotted-circle}** No | Whether the rule is applied to all protected branches. If set to `true`, the value of `protected_branch_ids` is ignored. Default is `false`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/335316) in GitLab 15.3. |
|
||||
| `group_ids` | Array | **{dotted-circle}** No | The IDs of groups as approvers. |
|
||||
| `protected_branch_ids` | Array | **{dotted-circle}** No | The IDs of protected branches to scope the rule by. To identify the ID, [use the API](protected_branches.md#list-protected-branches). |
|
||||
| `report_type` | string | **{dotted-circle}** No | The report type required when the rule type is `report_approver`. The supported report types are `license_scanning` and `code_coverage`. |
|
||||
| `rule_type` | string | **{dotted-circle}** No | The type of rule. `any_approver` is a pre-configured default rule with `approvals_required` at `0`. Other rules are `regular`. |
|
||||
| `user_ids` | Array | **{dotted-circle}** No | The IDs of users as approvers. |
|
||||
| `usernames` | string array | **{dotted-circle}** No | The usernames for this rule. |
|
||||
| Attribute | Type | Required | Description |
|
||||
|-------------------------------------|-------------------|------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding). |
|
||||
| `approvals_required` | integer | **{check-circle}** Yes | The number of required approvals for this rule. |
|
||||
| `name` | string | **{check-circle}** Yes | The name of the approval rule. |
|
||||
| `applies_to_all_protected_branches` | boolean | **{dotted-circle}** No | Whether the rule is applied to all protected branches. If set to `true`, the value of `protected_branch_ids` is ignored. Default is `false`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/335316) in GitLab 15.3. |
|
||||
| `group_ids` | Array | **{dotted-circle}** No | The IDs of groups as approvers. |
|
||||
| `protected_branch_ids` | Array | **{dotted-circle}** No | The IDs of protected branches to scope the rule by. To identify the ID, [use the API](protected_branches.md#list-protected-branches). |
|
||||
| `report_type` | string | **{dotted-circle}** No | The report type required when the rule type is `report_approver`. The supported report types are `license_scanning` and `code_coverage`. |
|
||||
| `rule_type` | string | **{dotted-circle}** No | The type of rule. `any_approver` is a pre-configured default rule with `approvals_required` at `0`. Other rules are `regular`. |
|
||||
| `user_ids` | Array | **{dotted-circle}** No | The IDs of users as approvers. |
|
||||
| `usernames` | string array | **{dotted-circle}** No | The usernames for this rule. |
|
||||
|
||||
```json
|
||||
{
|
||||
|
|
@ -430,18 +433,18 @@ PUT /projects/:id/approval_rules/:approval_rule_id
|
|||
|
||||
Supported attributes:
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
|-------------------------------------|-------------------|----------|-------------|
|
||||
| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding). |
|
||||
| `approvals_required` | integer | **{check-circle}** Yes | The number of required approvals for this rule. |
|
||||
| `approval_rule_id` | integer | **{check-circle}** Yes | The ID of a approval rule. |
|
||||
| `name` | string | **{check-circle}** Yes | The name of the approval rule. |
|
||||
| `applies_to_all_protected_branches` | boolean | **{dotted-circle}** No | Whether the rule is applied to all protected branches. If set to `true`, the value of `protected_branch_ids` is ignored. Default is `false`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/335316) in GitLab 15.3. |
|
||||
| `group_ids` | Array | **{dotted-circle}** No | The IDs of groups as approvers. |
|
||||
| `protected_branch_ids` | Array | **{dotted-circle}** No | The IDs of protected branches to scope the rule by. To identify the ID, [use the API](protected_branches.md#list-protected-branches). |
|
||||
| `remove_hidden_groups` | boolean | **{dotted-circle}** No | Whether hidden groups should be removed. |
|
||||
| `user_ids` | Array | **{dotted-circle}** No | The IDs of users as approvers. |
|
||||
| `usernames` | string array | **{dotted-circle}** No | The usernames for this rule. |
|
||||
| Attribute | Type | Required | Description |
|
||||
|-------------------------------------|-------------------|------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding). |
|
||||
| `approvals_required` | integer | **{check-circle}** Yes | The number of required approvals for this rule. |
|
||||
| `approval_rule_id` | integer | **{check-circle}** Yes | The ID of a approval rule. |
|
||||
| `name` | string | **{check-circle}** Yes | The name of the approval rule. |
|
||||
| `applies_to_all_protected_branches` | boolean | **{dotted-circle}** No | Whether the rule is applied to all protected branches. If set to `true`, the value of `protected_branch_ids` is ignored. Default is `false`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/335316) in GitLab 15.3. |
|
||||
| `group_ids` | Array | **{dotted-circle}** No | The IDs of groups as approvers. |
|
||||
| `protected_branch_ids` | Array | **{dotted-circle}** No | The IDs of protected branches to scope the rule by. To identify the ID, [use the API](protected_branches.md#list-protected-branches). |
|
||||
| `remove_hidden_groups` | boolean | **{dotted-circle}** No | Whether hidden groups should be removed. |
|
||||
| `user_ids` | Array | **{dotted-circle}** No | The IDs of users as approvers. |
|
||||
| `usernames` | string array | **{dotted-circle}** No | The usernames for this rule. |
|
||||
|
||||
```json
|
||||
{
|
||||
|
|
@ -537,10 +540,10 @@ DELETE /projects/:id/approval_rules/:approval_rule_id
|
|||
|
||||
Supported attributes:
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
|--------------------|-------------------|----------|------------------------------------------------------------------------------|
|
||||
| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding). |
|
||||
| `approval_rule_id` | integer | **{check-circle}** Yes | The ID of a approval rule. |
|
||||
| Attribute | Type | Required | Description |
|
||||
|--------------------|-------------------|------------------------|-------------------------------------------------------------------------------|
|
||||
| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding). |
|
||||
| `approval_rule_id` | integer | **{check-circle}** Yes | The ID of a approval rule. |
|
||||
|
||||
## Merge request-level MR approvals
|
||||
|
||||
|
|
@ -559,10 +562,10 @@ GET /projects/:id/merge_requests/:merge_request_iid/approvals
|
|||
|
||||
Supported attributes:
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
|---------------------|-------------------|----------|------------------------------------------------------------------------------|
|
||||
| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding). |
|
||||
| `merge_request_iid` | integer | **{check-circle}** Yes | The IID of the merge request. |
|
||||
| Attribute | Type | Required | Description |
|
||||
|---------------------|-------------------|------------------------|-------------------------------------------------------------------------------|
|
||||
| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding). |
|
||||
| `merge_request_iid` | integer | **{check-circle}** Yes | The IID of the merge request. |
|
||||
|
||||
```json
|
||||
{
|
||||
|
|
@ -612,11 +615,11 @@ POST /projects/:id/merge_requests/:merge_request_iid/approvals
|
|||
|
||||
Supported attributes:
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
|----------------------|-------------------|----------|-------------|
|
||||
| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding). |
|
||||
| `approvals_required` | integer | **{check-circle}** Yes | Approvals required before MR can be merged. |
|
||||
| `merge_request_iid` | integer | **{check-circle}** Yes | The IID of the merge request. |
|
||||
| Attribute | Type | Required | Description |
|
||||
|----------------------|-------------------|------------------------|-------------------------------------------------------------------------------|
|
||||
| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding). |
|
||||
| `approvals_required` | integer | **{check-circle}** Yes | Approvals required before MR can be merged. |
|
||||
| `merge_request_iid` | integer | **{check-circle}** Yes | The IID of the merge request. |
|
||||
|
||||
```json
|
||||
{
|
||||
|
|
@ -653,10 +656,10 @@ This includes additional information about the users who have already approved
|
|||
|
||||
Supported attributes:
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
|---------------------|-------------------|----------|------------------------------------------------------------------------------|
|
||||
| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding). |
|
||||
| `merge_request_iid` | integer | **{check-circle}** Yes | The IID of the merge request. |
|
||||
| Attribute | Type | Required | Description |
|
||||
|---------------------|-------------------|------------------------|-------------------------------------------------------------------------------|
|
||||
| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding). |
|
||||
| `merge_request_iid` | integer | **{check-circle}** Yes | The IID of the merge request. |
|
||||
|
||||
```json
|
||||
{
|
||||
|
|
@ -723,10 +726,10 @@ Use the `page` and `per_page` [pagination](index.md#offset-based-pagination) par
|
|||
|
||||
Supported attributes:
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
|---------------------|---------|----------|---------------------|
|
||||
| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding). |
|
||||
| `merge_request_iid` | integer | **{check-circle}** Yes | The IID of the merge request. |
|
||||
| Attribute | Type | Required | Description |
|
||||
|---------------------|-------------------|------------------------|-------------------------------------------------------------------------------|
|
||||
| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding). |
|
||||
| `merge_request_iid` | integer | **{check-circle}** Yes | The IID of the merge request. |
|
||||
|
||||
```json
|
||||
[
|
||||
|
|
@ -800,11 +803,11 @@ GET /projects/:id/merge_requests/:merge_request_iid/approval_rules/:approval_rul
|
|||
|
||||
Supported attributes:
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
|---------------------|---------|----------|------------------------------------------------------------------------------|
|
||||
| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding). |
|
||||
| `approval_rule_id` | integer | **{check-circle}** Yes | The ID of an approval rule. |
|
||||
| `merge_request_iid` | integer | **{check-circle}** Yes | The IID of a merge request. |
|
||||
| Attribute | Type | Required | Description |
|
||||
|---------------------|-------------------|------------------------|-------------------------------------------------------------------------------|
|
||||
| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding). |
|
||||
| `approval_rule_id` | integer | **{check-circle}** Yes | The ID of an approval rule. |
|
||||
| `merge_request_iid` | integer | **{check-circle}** Yes | The IID of a merge request. |
|
||||
|
||||
```json
|
||||
{
|
||||
|
|
@ -876,16 +879,16 @@ POST /projects/:id/merge_requests/:merge_request_iid/approval_rules
|
|||
|
||||
Supported attributes:
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
|----------------------------|---------|----------|------------------------------------------------|
|
||||
| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding) |
|
||||
| `approvals_required` | integer | **{check-circle}** Yes | The number of required approvals for this rule. |
|
||||
| `merge_request_iid` | integer | **{check-circle}** Yes | The IID of the merge request. |
|
||||
| `name` | string | **{check-circle}** Yes | The name of the approval rule. |
|
||||
| `approval_project_rule_id` | integer | **{dotted-circle}** No | The ID of a project-level approval rule. |
|
||||
| `group_ids` | Array | **{dotted-circle}** No | The IDs of groups as approvers. |
|
||||
| `user_ids` | Array | **{dotted-circle}** No | The IDs of users as approvers. |
|
||||
| `usernames` | string array | **{dotted-circle}** No | The usernames for this rule. |
|
||||
| Attribute | Type | Required | Description |
|
||||
|----------------------------|-------------------|------------------------|------------------------------------------------------------------------------|
|
||||
| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding) |
|
||||
| `approvals_required` | integer | **{check-circle}** Yes | The number of required approvals for this rule. |
|
||||
| `merge_request_iid` | integer | **{check-circle}** Yes | The IID of the merge request. |
|
||||
| `name` | string | **{check-circle}** Yes | The name of the approval rule. |
|
||||
| `approval_project_rule_id` | integer | **{dotted-circle}** No | The ID of a project-level approval rule. |
|
||||
| `group_ids` | Array | **{dotted-circle}** No | The IDs of groups as approvers. |
|
||||
| `user_ids` | Array | **{dotted-circle}** No | The IDs of users as approvers. |
|
||||
| `usernames` | string array | **{dotted-circle}** No | The usernames for this rule. |
|
||||
|
||||
**Important:** When `approval_project_rule_id` is set, the `name`, `users` and
|
||||
`groups` of project-level rule are copied. The `approvals_required` specified
|
||||
|
|
@ -966,17 +969,17 @@ These are system generated rules.
|
|||
|
||||
Supported attributes:
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
|----------------------|---------|----------|------------------------------------------------|
|
||||
| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding). |
|
||||
| `approvals_required` | integer | **{check-circle}** Yes | The number of required approvals for this rule. |
|
||||
| `approval_rule_id` | integer | **{check-circle}** Yes | The ID of an approval rule. |
|
||||
| `merge_request_iid` | integer | **{check-circle}** Yes | The IID of a merge request. |
|
||||
| `name` | string | **{check-circle}** Yes | The name of the approval rule. |
|
||||
| `group_ids` | Array | **{dotted-circle}** No | The IDs of groups as approvers. |
|
||||
| `remove_hidden_groups` | boolean | **{dotted-circle}** No | Whether hidden groups should be removed. |
|
||||
| `user_ids` | Array | **{dotted-circle}** No | The IDs of users as approvers. |
|
||||
| `usernames` | string array | **{dotted-circle}** No | The usernames for this rule. |
|
||||
| Attribute | Type | Required | Description |
|
||||
|------------------------|-------------------|------------------------|-------------------------------------------------------------------------------|
|
||||
| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding). |
|
||||
| `approvals_required` | integer | **{check-circle}** Yes | The number of required approvals for this rule. |
|
||||
| `approval_rule_id` | integer | **{check-circle}** Yes | The ID of an approval rule. |
|
||||
| `merge_request_iid` | integer | **{check-circle}** Yes | The IID of a merge request. |
|
||||
| `name` | string | **{check-circle}** Yes | The name of the approval rule. |
|
||||
| `group_ids` | Array | **{dotted-circle}** No | The IDs of groups as approvers. |
|
||||
| `remove_hidden_groups` | boolean | **{dotted-circle}** No | Whether hidden groups should be removed. |
|
||||
| `user_ids` | Array | **{dotted-circle}** No | The IDs of users as approvers. |
|
||||
| `usernames` | string array | **{dotted-circle}** No | The usernames for this rule. |
|
||||
|
||||
```json
|
||||
{
|
||||
|
|
@ -1051,11 +1054,11 @@ These are system generated rules.
|
|||
|
||||
Supported attributes:
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
|---------------------|---------|----------|---------------------------|
|
||||
| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding). |
|
||||
| `approval_rule_id` | integer | **{check-circle}** Yes | The ID of an approval rule. |
|
||||
| `merge_request_iid` | integer | **{check-circle}** Yes | The IID of the merge request. |
|
||||
| Attribute | Type | Required | Description |
|
||||
|---------------------|-------------------|------------------------|-------------------------------------------------------------------------------|
|
||||
| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding). |
|
||||
| `approval_rule_id` | integer | **{check-circle}** Yes | The ID of an approval rule. |
|
||||
| `merge_request_iid` | integer | **{check-circle}** Yes | The IID of the merge request. |
|
||||
|
||||
## Approve merge request
|
||||
|
||||
|
|
@ -1070,12 +1073,12 @@ POST /projects/:id/merge_requests/:merge_request_iid/approve
|
|||
|
||||
Supported attributes:
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
|---------------------|---------|----------|-------------------------|
|
||||
| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding). |
|
||||
| `approval_password` | string | **{dotted-circle}** No | Current user's password. Required if [**Require user password to approve**](../user/project/merge_requests/approvals/settings.md#require-user-password-to-approve) is enabled in the project settings. |
|
||||
| `merge_request_iid` | integer | **{check-circle}** Yes | The IID of the merge request. |
|
||||
| `sha` | string | **{dotted-circle}** No | The `HEAD` of the merge request. |
|
||||
| Attribute | Type | Required | Description |
|
||||
|---------------------|-------------------|------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding). |
|
||||
| `approval_password` | string | **{dotted-circle}** No | Current user's password. Required if [**Require user password to approve**](../user/project/merge_requests/approvals/settings.md#require-user-password-to-approve) is enabled in the project settings. |
|
||||
| `merge_request_iid` | integer | **{check-circle}** Yes | The IID of the merge request. |
|
||||
| `sha` | string | **{dotted-circle}** No | The `HEAD` of the merge request. |
|
||||
|
||||
The `sha` parameter works in the same way as
|
||||
when [accepting a merge request](merge_requests.md#merge-a-merge-request): if it is passed, then it must
|
||||
|
|
@ -1133,7 +1136,7 @@ POST /projects/:id/merge_requests/:merge_request_iid/unapprove
|
|||
|
||||
Supported attributes:
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
|---------------------|---------|----------|---------------------|
|
||||
| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding). |
|
||||
| `merge_request_iid` | integer | **{check-circle}** Yes | The IID of a merge request. |
|
||||
| Attribute | Type | Required | Description |
|
||||
|---------------------|-------------------|------------------------|-------------------------------------------------------------------------------|
|
||||
| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding). |
|
||||
| `merge_request_iid` | integer | **{check-circle}** Yes | The IID of a merge request. |
|
||||
|
|
|
|||
|
|
@ -411,15 +411,13 @@ To configure markdownlint in your editor, install one of the following as approp
|
|||
|
||||
- Sublime Text [`SublimeLinter-contrib-markdownlint` package](https://packagecontrol.io/packages/SublimeLinter-contrib-markdownlint).
|
||||
- Visual Studio Code [`DavidAnson.vscode-markdownlint` extension](https://marketplace.visualstudio.com/items?itemName=DavidAnson.vscode-markdownlint).
|
||||
- Atom [`linter-node-markdownlint` package](https://atom.io/packages/linter-node-markdownlint).
|
||||
- Vim [ALE plugin](https://github.com/dense-analysis/ale).
|
||||
|
||||
To configure Vale in your editor, install one of the following as appropriate:
|
||||
|
||||
- Sublime Text [`SublimeLinter-contrib-vale` package](https://packagecontrol.io/packages/SublimeLinter-contrib-vale).
|
||||
- Sublime Text [`SublimeLinter-vale` package](https://packagecontrol.io/packages/SublimeLinter-vale).
|
||||
- Visual Studio Code [`errata-ai.vale-server` extension](https://marketplace.visualstudio.com/items?itemName=errata-ai.vale-server).
|
||||
You can configure the plugin to [display only a subset of alerts](#show-subset-of-vale-alerts).
|
||||
- Atom [`atomic-vale` package](https://atom.io/packages/atomic-vale).
|
||||
- Vim [ALE plugin](https://github.com/dense-analysis/ale).
|
||||
- JetBrains IDEs - No plugin exists, but
|
||||
[this issue comment](https://github.com/errata-ai/vale-server/issues/39#issuecomment-751714451)
|
||||
|
|
|
|||
|
|
@ -57,11 +57,14 @@ values extracted from the `alerts` field in the
|
|||
- Alert severity:
|
||||
Extracted from the alert payload field `labels/severity`. Maps case-insensitive
|
||||
value to [Alert's severity](../incident_management/alerts.md#alert-severity):
|
||||
- **Critical**: `critical`, `s1`, `p1`, `emergency`, `fatal`, or any value not in this list
|
||||
- **High**: `high`, `s2`, `p2`, `major`, `page`
|
||||
- **Medium**: `medium`, `s3`, `p3`, `error`, `alert`
|
||||
- **Low**: `low`, `s4`, `p4`, `warn`, `warning`
|
||||
- **Info**: `info`, `s5`, `p5`, `debug`, `information`, `notice`
|
||||
|
||||
| Alert payload | Mapped to alert severity |
|
||||
| ------------- | --------------------------------------------------------------------------- |
|
||||
| Critical | `critical`, `s1`, `p1`, `emergency`, `fatal`, or any value not in this list |
|
||||
| High | `high`, `s2`, `p2`, `major`, `page` |
|
||||
| Medium | `medium`, `s3`, `p3`, `error`, `alert` |
|
||||
| Low | `low`, `s4`, `p4`, `warn`, `warning` |
|
||||
| Info | `info`, `s5`, `p5`, `debug`, `information`, `notice` |
|
||||
|
||||
To further customize the incident, you can add labels, mentions, or any other supported
|
||||
[quick action](../../user/project/quick_actions.md) in the selected issue template,
|
||||
|
|
|
|||
|
|
@ -52,6 +52,20 @@ sole discretion of GitLab Inc.
|
|||
|
||||
<div class="deprecation removal-160 breaking-change">
|
||||
|
||||
### Approvers and Approver Group fields in Merge Request Approval API
|
||||
|
||||
Planned removal: GitLab <span class="removal-milestone">16.0</span> <span class="removal-date"></span>
|
||||
|
||||
WARNING:
|
||||
This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/).
|
||||
Review the details carefully before upgrading.
|
||||
|
||||
The endpoint to get the configuration of approvals for a project returns empty arrays for `approvers` and `approval_groups`. These fields were deprecated in favor of the endpoint to [get project-level rules](https://docs.gitlab.com/ee/api/merge_request_approvals.html#get-project-level-rules) for a merge request. API users are encouraged to switch to this endpoint instead. These fields will be removed from the `get configuration` endpoint in v5 of the GitLab REST API.
|
||||
|
||||
</div>
|
||||
|
||||
<div class="deprecation removal-160 breaking-change">
|
||||
|
||||
### Azure Storage Driver defaults to the correct root prefix
|
||||
|
||||
Planned removal: GitLab <span class="removal-milestone">16.0</span> <span class="removal-date"></span>
|
||||
|
|
|
|||
|
|
@ -281,6 +281,9 @@ module Gitlab
|
|||
# target_column - The name of the referenced column, defaults to "id".
|
||||
# on_delete - The action to perform when associated data is removed,
|
||||
# defaults to "CASCADE".
|
||||
# on_update - The action to perform when associated data is updated,
|
||||
# defaults to nil. This is useful for multi column FKs if
|
||||
# it's desirable to update one of the columns.
|
||||
# name - The name of the foreign key.
|
||||
# validate - Flag that controls whether the new foreign key will be validated after creation.
|
||||
# If the flag is not set, the constraint will only be enforced for new data.
|
||||
|
|
@ -288,7 +291,8 @@ module Gitlab
|
|||
# order of the ALTER TABLE. This can be useful in situations where the foreign
|
||||
# key creation could deadlock with another process.
|
||||
#
|
||||
def add_concurrent_foreign_key(source, target, column:, on_delete: :cascade, target_column: :id, name: nil, validate: true, reverse_lock_order: false)
|
||||
# rubocop: disable Metrics/ParameterLists
|
||||
def add_concurrent_foreign_key(source, target, column:, on_delete: :cascade, on_update: nil, target_column: :id, name: nil, validate: true, reverse_lock_order: false)
|
||||
# Transactions would result in ALTER TABLE locks being held for the
|
||||
# duration of the transaction, defeating the purpose of this method.
|
||||
if transaction_open?
|
||||
|
|
@ -298,6 +302,7 @@ module Gitlab
|
|||
options = {
|
||||
column: column,
|
||||
on_delete: on_delete,
|
||||
on_update: on_update,
|
||||
name: name.presence || concurrent_foreign_key_name(source, column),
|
||||
primary_key: target_column
|
||||
}
|
||||
|
|
@ -306,7 +311,8 @@ module Gitlab
|
|||
warning_message = "Foreign key not created because it exists already " \
|
||||
"(this may be due to an aborted migration or similar): " \
|
||||
"source: #{source}, target: #{target}, column: #{options[:column]}, "\
|
||||
"name: #{options[:name]}, on_delete: #{options[:on_delete]}"
|
||||
"name: #{options[:name]}, on_update: #{options[:on_update]}, "\
|
||||
"on_delete: #{options[:on_delete]}"
|
||||
|
||||
Gitlab::AppLogger.warn warning_message
|
||||
else
|
||||
|
|
@ -322,6 +328,7 @@ module Gitlab
|
|||
ADD CONSTRAINT #{options[:name]}
|
||||
FOREIGN KEY (#{multiple_columns(options[:column])})
|
||||
REFERENCES #{target} (#{multiple_columns(target_column)})
|
||||
#{on_update_statement(options[:on_update])}
|
||||
#{on_delete_statement(options[:on_delete])}
|
||||
NOT VALID;
|
||||
EOF
|
||||
|
|
@ -343,6 +350,7 @@ module Gitlab
|
|||
end
|
||||
end
|
||||
end
|
||||
# rubocop: enable Metrics/ParameterLists
|
||||
|
||||
def validate_foreign_key(source, column, name: nil)
|
||||
fk_name = name || concurrent_foreign_key_name(source, column)
|
||||
|
|
@ -1296,6 +1304,13 @@ into similar problems in the future (e.g. when new tables are created).
|
|||
"ON DELETE #{on_delete.upcase}"
|
||||
end
|
||||
|
||||
def on_update_statement(on_update)
|
||||
return '' if on_update.blank?
|
||||
return 'ON UPDATE SET NULL' if on_update == :nullify
|
||||
|
||||
"ON UPDATE #{on_update.upcase}"
|
||||
end
|
||||
|
||||
def create_column_from(table, old, new, type: nil, batch_column_name: :id, type_cast_function: nil, limit: nil)
|
||||
old_col = column_for(table, old)
|
||||
new_type = type || old_col.type
|
||||
|
|
|
|||
|
|
@ -7,11 +7,11 @@ module Gitlab
|
|||
class RunnerFleetPipelineSeeder
|
||||
DEFAULT_JOB_COUNT = 400
|
||||
|
||||
MAX_QUEUE_TIME_IN_SECONDS = 5 * 60
|
||||
PIPELINE_CREATION_RANGE_MIN_IN_MINUTES = 120
|
||||
PIPELINE_CREATION_RANGE_MAX_IN_MINUTES = 30 * 24 * 60
|
||||
PIPELINE_START_RANGE_MAX_IN_MINUTES = 60 * 60
|
||||
PIPELINE_FINISH_RANGE_MAX_IN_MINUTES = 60
|
||||
MAX_QUEUE_TIME_IN_SECONDS = 5.minutes.to_i
|
||||
PIPELINE_CREATION_RANGE_MIN_IN_SECONDS = 2.hours.to_i
|
||||
PIPELINE_CREATION_RANGE_MAX_IN_SECONDS = 30.days.to_i
|
||||
PIPELINE_START_RANGE_MAX_IN_SECONDS = 5.minutes.to_i
|
||||
PIPELINE_FINISH_RANGE_MAX_IN_SECONDS = 1.hour.to_i
|
||||
|
||||
PROJECT_JOB_DISTRIBUTION = [
|
||||
{ allocation: 70, job_count_default: 10 },
|
||||
|
|
@ -99,14 +99,14 @@ module Gitlab
|
|||
|
||||
sha = '00000000'
|
||||
if ::Ci::HasStatus::ALIVE_STATUSES.include?(status) || ::Ci::HasStatus::COMPLETED_STATUSES.include?(status)
|
||||
created_at = Random.rand(PIPELINE_CREATION_RANGE_MIN_IN_MINUTES..PIPELINE_CREATION_RANGE_MAX_IN_MINUTES)
|
||||
.minutes.ago
|
||||
created_at = Random.rand(PIPELINE_CREATION_RANGE_MIN_IN_SECONDS..PIPELINE_CREATION_RANGE_MAX_IN_SECONDS)
|
||||
.seconds.ago
|
||||
|
||||
if ::Ci::HasStatus::STARTED_STATUSES.include?(status) ||
|
||||
::Ci::HasStatus::COMPLETED_STATUSES.include?(status)
|
||||
started_at = created_at + Random.rand(1..PIPELINE_START_RANGE_MAX_IN_MINUTES).minutes
|
||||
started_at = created_at + Random.rand(1..PIPELINE_START_RANGE_MAX_IN_SECONDS)
|
||||
if ::Ci::HasStatus::COMPLETED_STATUSES.include?(status)
|
||||
finished_at = started_at + Random.rand(1..PIPELINE_FINISH_RANGE_MAX_IN_MINUTES).minutes
|
||||
finished_at = started_at + Random.rand(1..PIPELINE_FINISH_RANGE_MAX_IN_SECONDS)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -138,19 +138,15 @@ module Gitlab
|
|||
started_at = pipeline.started_at
|
||||
finished_at = pipeline.finished_at
|
||||
|
||||
max_job_duration =
|
||||
if finished_at
|
||||
[MAX_QUEUE_TIME_IN_SECONDS, finished_at - started_at].min
|
||||
else
|
||||
Random.rand(1..5).seconds
|
||||
end
|
||||
max_job_duration = [MAX_QUEUE_TIME_IN_SECONDS, 5, 2].sample
|
||||
max_job_duration = (finished_at - started_at) if finished_at && max_job_duration > finished_at - started_at
|
||||
|
||||
job_created_at = pipeline.created_at
|
||||
job_started_at = started_at + Random.rand(1..max_job_duration) if started_at
|
||||
job_started_at = job_created_at + Random.rand(1..max_job_duration) if started_at
|
||||
if finished_at
|
||||
job_finished_at = Random.rand(job_started_at..finished_at)
|
||||
elsif job_status == 'running'
|
||||
job_finished_at = job_started_at + Random.rand(1..PIPELINE_FINISH_RANGE_MAX_IN_MINUTES).minutes
|
||||
job_finished_at = job_started_at + Random.rand(1 * 60..PIPELINE_FINISH_RANGE_MAX_IN_SECONDS)
|
||||
end
|
||||
|
||||
# Do not use the first 2 runner tags ('runner-fleet', "#{registration_prefix}runner").
|
||||
|
|
|
|||
|
|
@ -2726,12 +2726,21 @@ msgstr ""
|
|||
msgid "AdminDashboard|Error loading the statistics. Please try again"
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminEmail|An error occurred fetching the groups and projects. Please refresh the page to try again."
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminEmail|Body"
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminEmail|Body is required."
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminEmail|Loading groups and projects."
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminEmail|No groups or projects found."
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminEmail|Recipient group or project"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -5396,12 +5405,21 @@ msgstr ""
|
|||
msgid "Artifacts|Delete artifact"
|
||||
msgstr ""
|
||||
|
||||
msgid "Artifacts|Help us improve this page"
|
||||
msgstr ""
|
||||
|
||||
msgid "Artifacts|Take a quick survey"
|
||||
msgstr ""
|
||||
|
||||
msgid "Artifacts|This artifact will be permanently deleted. Any reports generated from this artifact will be empty."
|
||||
msgstr ""
|
||||
|
||||
msgid "Artifacts|Total artifacts size"
|
||||
msgstr ""
|
||||
|
||||
msgid "Artifacts|We want you to be able to use this page to easily manage your CI/CD job artifacts. We are working to improve this experience and would appreciate any feedback you have about the improvements we are making."
|
||||
msgstr ""
|
||||
|
||||
msgid "As we continue to build more features for SAST, we'd love your feedback on the SAST configuration feature in %{linkStart}this issue%{linkEnd}."
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
module QA
|
||||
RSpec.describe 'Manage' do
|
||||
describe 'User', :requires_admin, product_group: :workspace do
|
||||
describe 'User', :requires_admin, product_group: :organization do
|
||||
let(:admin_api_client) { Runtime::API::Client.as_admin }
|
||||
|
||||
let!(:sub_group) do
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ require 'airborne'
|
|||
|
||||
module QA
|
||||
RSpec.describe 'Manage' do
|
||||
describe 'Users API', :reliable, product_group: :workspace do
|
||||
describe 'Users API', :reliable, product_group: :organization do
|
||||
let(:api_client) { Runtime::API::Client.new(:gitlab) }
|
||||
let(:request) { Runtime::API::Request.new(api_client, '/users') }
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
module QA
|
||||
RSpec.describe 'Manage' do
|
||||
describe 'Subgroup transfer', product_group: :workspace do
|
||||
describe 'Subgroup transfer', product_group: :organization do
|
||||
let(:source_group) do
|
||||
Resource::Group.fabricate_via_api! do |group|
|
||||
group.path = "source-group-for-transfer_#{SecureRandom.hex(8)}"
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
module QA
|
||||
RSpec.describe 'Manage' do
|
||||
describe 'Project transfer between groups', :reliable, product_group: :workspace do
|
||||
describe 'Project transfer between groups', :reliable, product_group: :organization do
|
||||
let(:source_group) do
|
||||
Resource::Group.fabricate_via_api! do |group|
|
||||
group.path = "source-group-#{SecureRandom.hex(8)}"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
RSpec.describe 'Manage', :reliable, product_group: :workspace do
|
||||
RSpec.describe 'Manage', :reliable, product_group: :organization do
|
||||
describe 'Add project member' do
|
||||
it 'user adds project member', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347887' do
|
||||
Flow::Login.sign_in
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
module QA
|
||||
RSpec.describe 'Manage' do
|
||||
describe 'Create project badge', :reliable, product_group: :workspace do
|
||||
describe 'Create project badge', :reliable, product_group: :organization do
|
||||
let(:badge_name) { "project-badge-#{SecureRandom.hex(8)}" }
|
||||
let(:expected_badge_link_url) { "#{Runtime::Scenario.gitlab_address}/#{project.path_with_namespace}" }
|
||||
let(:expected_badge_image_url) { "#{Runtime::Scenario.gitlab_address}/#{project.path_with_namespace}/badges/main/pipeline.svg" }
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
RSpec.describe 'Manage', :smoke, product_group: :workspace do
|
||||
RSpec.describe 'Manage', :smoke, product_group: :organization do
|
||||
describe 'Project' do
|
||||
shared_examples 'successful project creation' do
|
||||
it 'creates a new project' do
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
RSpec.describe 'Manage', product_group: :workspace do
|
||||
RSpec.describe 'Manage', product_group: :organization do
|
||||
shared_examples 'loads all images' do |admin|
|
||||
let(:api_client) { Runtime::API::Client.as_admin }
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
module QA
|
||||
RSpec.describe 'Manage' do
|
||||
describe 'Invite group', :reliable, product_group: :workspace do
|
||||
describe 'Invite group', :reliable, product_group: :organization do
|
||||
shared_examples 'invites group to project' do
|
||||
it 'verifies group is added and members can access project with correct access level' do
|
||||
Page::Project::Menu.perform(&:click_members)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
module QA
|
||||
RSpec.describe 'Manage' do
|
||||
describe 'Project owner permissions', :reliable, product_group: :workspace do
|
||||
describe 'Project owner permissions', :reliable, product_group: :organization do
|
||||
let!(:owner) do
|
||||
Resource::User.fabricate_or_use(Runtime::Env.gitlab_qa_username_1, Runtime::Env.gitlab_qa_password_1)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
module QA
|
||||
RSpec.describe 'Manage' do
|
||||
describe 'Project activity', :reliable, product_group: :workspace do
|
||||
describe 'Project activity', :reliable, product_group: :organization do
|
||||
it 'user creates an event in the activity page upon Git push',
|
||||
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347879' do
|
||||
Flow::Login.sign_in
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
module QA
|
||||
RSpec.describe 'Manage' do
|
||||
describe 'User', :requires_admin, product_group: :workspace do
|
||||
describe 'User', :requires_admin, product_group: :organization do
|
||||
let(:admin_api_client) { Runtime::API::Client.as_admin }
|
||||
|
||||
let(:followed_user_api_client) { Runtime::API::Client.new(:gitlab, user: followed_user) }
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
module QA
|
||||
RSpec.describe 'Manage' do
|
||||
describe 'User', :requires_admin, :reliable, product_group: :workspace do
|
||||
describe 'User', :requires_admin, :reliable, product_group: :organization do
|
||||
let(:admin_api_client) { Runtime::API::Client.as_admin }
|
||||
|
||||
let!(:user) do
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
module QA
|
||||
RSpec.describe 'Manage' do
|
||||
describe 'User', :requires_admin, product_group: :workspace do
|
||||
describe 'User', :requires_admin, product_group: :organization do
|
||||
let(:admin_api_client) { Runtime::API::Client.as_admin }
|
||||
|
||||
let!(:sub_group) do
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ RSpec.describe Projects::DeployKeysController do
|
|||
it 'shows an alert with the validations errors' do
|
||||
post :create, params: create_params(nil)
|
||||
|
||||
expect(flash[:alert]).to eq("Title can't be blank, Deploy keys projects deploy key title can't be blank")
|
||||
expect(flash[:alert]).to eq("Title can't be blank")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -126,8 +126,7 @@ RSpec.describe Projects::DeployKeysController do
|
|||
it 'shows an alert with the validations errors' do
|
||||
post :create, params: create_params
|
||||
|
||||
expect(flash[:alert]).to eq("Fingerprint sha256 has already been taken, " \
|
||||
"Deploy keys projects deploy key fingerprint sha256 has already been taken")
|
||||
expect(flash[:alert]).to eq("Fingerprint sha256 has already been taken")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,63 @@
|
|||
import { GlBanner } from '@gitlab/ui';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import FeedbackBanner from '~/artifacts/components/feedback_banner.vue';
|
||||
import { makeMockUserCalloutDismisser } from 'helpers/mock_user_callout_dismisser';
|
||||
import {
|
||||
I18N_FEEDBACK_BANNER_TITLE,
|
||||
I18N_FEEDBACK_BANNER_BUTTON,
|
||||
FEEDBACK_URL,
|
||||
} from '~/artifacts/constants';
|
||||
|
||||
const mockBannerImagePath = 'banner/image/path';
|
||||
|
||||
describe('Artifacts management feedback banner', () => {
|
||||
let wrapper;
|
||||
let userCalloutDismissSpy;
|
||||
|
||||
const findBanner = () => wrapper.findComponent(GlBanner);
|
||||
|
||||
const createComponent = ({ shouldShowCallout = true } = {}) => {
|
||||
userCalloutDismissSpy = jest.fn();
|
||||
|
||||
wrapper = shallowMount(FeedbackBanner, {
|
||||
provide: {
|
||||
artifactsManagementFeedbackImagePath: mockBannerImagePath,
|
||||
},
|
||||
stubs: {
|
||||
UserCalloutDismisser: makeMockUserCalloutDismisser({
|
||||
dismiss: userCalloutDismissSpy,
|
||||
shouldShowCallout,
|
||||
}),
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
it('is displayed with the correct props', () => {
|
||||
createComponent();
|
||||
|
||||
expect(findBanner().props()).toMatchObject({
|
||||
title: I18N_FEEDBACK_BANNER_TITLE,
|
||||
buttonText: I18N_FEEDBACK_BANNER_BUTTON,
|
||||
buttonLink: FEEDBACK_URL,
|
||||
svgPath: mockBannerImagePath,
|
||||
});
|
||||
});
|
||||
|
||||
it('dismisses the callout when closed', () => {
|
||||
createComponent();
|
||||
|
||||
findBanner().vm.$emit('close');
|
||||
|
||||
expect(userCalloutDismissSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('is not displayed once it has been dismissed', () => {
|
||||
createComponent({ shouldShowCallout: false });
|
||||
|
||||
expect(findBanner().exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
|
@ -5,6 +5,7 @@ import getJobArtifactsResponse from 'test_fixtures/graphql/artifacts/graphql/que
|
|||
import CiIcon from '~/vue_shared/components/ci_icon.vue';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import JobArtifactsTable from '~/artifacts/components/job_artifacts_table.vue';
|
||||
import FeedbackBanner from '~/artifacts/components/feedback_banner.vue';
|
||||
import ArtifactsTableRowDetails from '~/artifacts/components/artifacts_table_row_details.vue';
|
||||
import ArtifactDeleteModal from '~/artifacts/components/artifact_delete_modal.vue';
|
||||
import createMockApollo from 'helpers/mock_apollo_helper';
|
||||
|
|
@ -23,6 +24,8 @@ describe('JobArtifactsTable component', () => {
|
|||
let wrapper;
|
||||
let requestHandlers;
|
||||
|
||||
const findBanner = () => wrapper.findComponent(FeedbackBanner);
|
||||
|
||||
const findLoadingState = () => wrapper.findComponent(GlLoadingIcon);
|
||||
const findTable = () => wrapper.findComponent(GlTable);
|
||||
const findDetailsRows = () => wrapper.findAllComponents(ArtifactsTableRowDetails);
|
||||
|
|
@ -85,7 +88,10 @@ describe('JobArtifactsTable component', () => {
|
|||
apolloProvider: createMockApollo([
|
||||
[getJobArtifactsQuery, requestHandlers.getJobArtifactsQuery],
|
||||
]),
|
||||
provide: { projectPath: 'project/path' },
|
||||
provide: {
|
||||
projectPath: 'project/path',
|
||||
artifactsManagementFeedbackImagePath: 'banner/image/path',
|
||||
},
|
||||
data() {
|
||||
return data;
|
||||
},
|
||||
|
|
@ -96,6 +102,12 @@ describe('JobArtifactsTable component', () => {
|
|||
wrapper.destroy();
|
||||
});
|
||||
|
||||
it('renders feedback banner', () => {
|
||||
createComponent();
|
||||
|
||||
expect(findBanner().exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('when loading, shows a loading state', () => {
|
||||
createComponent();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { mount } from '@vue/test-utils';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import mockData from 'test_fixtures/issues/related_merge_requests.json';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
|
|
@ -20,7 +20,7 @@ describe('RelatedMergeRequests', () => {
|
|||
mock = new MockAdapter(axios);
|
||||
mock.onGet(`${API_ENDPOINT}?per_page=100`).reply(200, mockData, { 'x-total': 2 });
|
||||
|
||||
wrapper = mount(RelatedMergeRequests, {
|
||||
wrapper = shallowMount(RelatedMergeRequests, {
|
||||
store: createStore(),
|
||||
propsData: {
|
||||
endpoint: API_ENDPOINT,
|
||||
|
|
@ -42,18 +42,14 @@ describe('RelatedMergeRequests', () => {
|
|||
const assignees = [{ name: 'foo' }, { name: 'bar' }];
|
||||
|
||||
describe('when there is assignees array', () => {
|
||||
// https://gitlab.com/gitlab-org/gitlab/-/issues/387756
|
||||
// eslint-disable-next-line jest/no-disabled-tests
|
||||
it.skip('should return assignees array', () => {
|
||||
it('should return assignees array', () => {
|
||||
const mr = { assignees };
|
||||
|
||||
expect(wrapper.vm.getAssignees(mr)).toEqual(assignees);
|
||||
});
|
||||
});
|
||||
|
||||
// https://gitlab.com/gitlab-org/gitlab/-/issues/387789
|
||||
// eslint-disable-next-line jest/no-disabled-tests
|
||||
it.skip('should return an array with single assingee', () => {
|
||||
it('should return an array with single assignee', () => {
|
||||
const mr = { assignee: assignees[0] };
|
||||
|
||||
expect(wrapper.vm.getAssignees(mr)).toEqual([assignees[0]]);
|
||||
|
|
|
|||
|
|
@ -1,21 +1,23 @@
|
|||
import { loadHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
|
||||
import { loadHTMLFixture, resetHTMLFixture, setHTMLFixture } from 'helpers/fixtures';
|
||||
import initReadMore from '~/read_more';
|
||||
|
||||
describe('Read more click-to-expand functionality', () => {
|
||||
const fixtureName = 'projects/overview.html';
|
||||
|
||||
beforeEach(() => {
|
||||
loadHTMLFixture(fixtureName);
|
||||
});
|
||||
const findTrigger = () => document.querySelector('.js-read-more-trigger');
|
||||
|
||||
afterEach(() => {
|
||||
resetHTMLFixture();
|
||||
});
|
||||
|
||||
describe('expands target element', () => {
|
||||
beforeEach(() => {
|
||||
loadHTMLFixture(fixtureName);
|
||||
});
|
||||
|
||||
it('adds "is-expanded" class to target element', () => {
|
||||
const target = document.querySelector('.read-more-container');
|
||||
const trigger = document.querySelector('.js-read-more-trigger');
|
||||
const trigger = findTrigger();
|
||||
initReadMore();
|
||||
|
||||
trigger.click();
|
||||
|
|
@ -23,4 +25,25 @@ describe('Read more click-to-expand functionality', () => {
|
|||
expect(target.classList.contains('is-expanded')).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('given click on nested element', () => {
|
||||
beforeEach(() => {
|
||||
setHTMLFixture(`
|
||||
<p>Target</p>
|
||||
<button type="button" class="js-read-more-trigger">
|
||||
<span>Button text</span>
|
||||
</button>
|
||||
`);
|
||||
|
||||
const trigger = findTrigger();
|
||||
const nestedElement = trigger.firstElementChild;
|
||||
initReadMore();
|
||||
|
||||
nestedElement.click();
|
||||
});
|
||||
|
||||
it('removes the trigger element', async () => {
|
||||
expect(findTrigger()).toBe(null);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -5,7 +5,10 @@ import {
|
|||
setFrequentItemToLS,
|
||||
mergeById,
|
||||
isSidebarDirty,
|
||||
formatSearchResultCount,
|
||||
getAggregationsUrl,
|
||||
} from '~/search/store/utils';
|
||||
import { useMockLocationHelper } from 'helpers/mock_window_location_helper';
|
||||
import {
|
||||
MOCK_LS_KEY,
|
||||
MOCK_GROUPS,
|
||||
|
|
@ -241,4 +244,23 @@ describe('Global Search Store Utils', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
describe('formatSearchResultCount', () => {
|
||||
it('returns zero as string if no count is provided', () => {
|
||||
expect(formatSearchResultCount()).toStrictEqual('0');
|
||||
});
|
||||
it('returns 10K string for 10000 integer', () => {
|
||||
expect(formatSearchResultCount(10000)).toStrictEqual('10K');
|
||||
});
|
||||
it('returns 23K string for "23,000+" string', () => {
|
||||
expect(formatSearchResultCount('23,000+')).toStrictEqual('23K');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getAggregationsUrl', () => {
|
||||
useMockLocationHelper();
|
||||
it('returns zero as string if no count is provided', () => {
|
||||
const testURL = window.location.href;
|
||||
expect(getAggregationsUrl()).toStrictEqual(`${testURL}search/aggregations`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -42,8 +42,8 @@ describe('Merge Request Collapsible Extension', () => {
|
|||
expect(wrapper.find('[data-testid="collapsed-header"]').text()).toBe('hello there');
|
||||
});
|
||||
|
||||
it('renders chevron-lg-right icon', () => {
|
||||
expect(findIcon().props('name')).toBe('chevron-lg-right');
|
||||
it('renders chevron-right icon', () => {
|
||||
expect(findIcon().props('name')).toBe('chevron-right');
|
||||
});
|
||||
|
||||
describe('onClick', () => {
|
||||
|
|
@ -60,8 +60,8 @@ describe('Merge Request Collapsible Extension', () => {
|
|||
expect(findTitle().text()).toBe('Collapse');
|
||||
});
|
||||
|
||||
it('renders chevron-lg-down icon', () => {
|
||||
expect(findIcon().props('name')).toBe('chevron-lg-down');
|
||||
it('renders chevron-down icon', () => {
|
||||
expect(findIcon().props('name')).toBe('chevron-down');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -743,6 +743,75 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
|
|||
end
|
||||
end
|
||||
|
||||
context 'ON UPDATE statements' do
|
||||
context 'on_update: :nullify' do
|
||||
it 'appends ON UPDATE SET NULL statement' do
|
||||
expect(model).to receive(:with_lock_retries).and_call_original
|
||||
expect(model).to receive(:disable_statement_timeout).and_call_original
|
||||
expect(model).to receive(:statement_timeout_disabled?).and_return(false)
|
||||
expect(model).to receive(:execute).with(/SET statement_timeout TO/)
|
||||
expect(model).to receive(:execute).ordered.with(/VALIDATE CONSTRAINT/)
|
||||
expect(model).to receive(:execute).ordered.with(/RESET statement_timeout/)
|
||||
|
||||
expect(model).to receive(:execute).with(/ON UPDATE SET NULL/)
|
||||
|
||||
model.add_concurrent_foreign_key(:projects, :users,
|
||||
column: :user_id,
|
||||
on_update: :nullify)
|
||||
end
|
||||
end
|
||||
|
||||
context 'on_update: :cascade' do
|
||||
it 'appends ON UPDATE CASCADE statement' do
|
||||
expect(model).to receive(:with_lock_retries).and_call_original
|
||||
expect(model).to receive(:disable_statement_timeout).and_call_original
|
||||
expect(model).to receive(:statement_timeout_disabled?).and_return(false)
|
||||
expect(model).to receive(:execute).with(/SET statement_timeout TO/)
|
||||
expect(model).to receive(:execute).ordered.with(/VALIDATE CONSTRAINT/)
|
||||
expect(model).to receive(:execute).ordered.with(/RESET statement_timeout/)
|
||||
|
||||
expect(model).to receive(:execute).with(/ON UPDATE CASCADE/)
|
||||
|
||||
model.add_concurrent_foreign_key(:projects, :users,
|
||||
column: :user_id,
|
||||
on_update: :cascade)
|
||||
end
|
||||
end
|
||||
|
||||
context 'on_update: nil' do
|
||||
it 'appends no ON UPDATE statement' do
|
||||
expect(model).to receive(:with_lock_retries).and_call_original
|
||||
expect(model).to receive(:disable_statement_timeout).and_call_original
|
||||
expect(model).to receive(:statement_timeout_disabled?).and_return(false)
|
||||
expect(model).to receive(:execute).with(/SET statement_timeout TO/)
|
||||
expect(model).to receive(:execute).ordered.with(/VALIDATE CONSTRAINT/)
|
||||
expect(model).to receive(:execute).ordered.with(/RESET statement_timeout/)
|
||||
|
||||
expect(model).not_to receive(:execute).with(/ON UPDATE/)
|
||||
|
||||
model.add_concurrent_foreign_key(:projects, :users,
|
||||
column: :user_id,
|
||||
on_update: nil)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when on_update is not provided' do
|
||||
it 'appends no ON UPDATE statement' do
|
||||
expect(model).to receive(:with_lock_retries).and_call_original
|
||||
expect(model).to receive(:disable_statement_timeout).and_call_original
|
||||
expect(model).to receive(:statement_timeout_disabled?).and_return(false)
|
||||
expect(model).to receive(:execute).with(/SET statement_timeout TO/)
|
||||
expect(model).to receive(:execute).ordered.with(/VALIDATE CONSTRAINT/)
|
||||
expect(model).to receive(:execute).ordered.with(/RESET statement_timeout/)
|
||||
|
||||
expect(model).not_to receive(:execute).with(/ON UPDATE/)
|
||||
|
||||
model.add_concurrent_foreign_key(:projects, :users,
|
||||
column: :user_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when no custom key name is supplied' do
|
||||
it 'creates a concurrent foreign key and validates it' do
|
||||
expect(model).to receive(:with_lock_retries).and_call_original
|
||||
|
|
@ -760,6 +829,7 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
|
|||
name = model.concurrent_foreign_key_name(:projects, :user_id)
|
||||
expect(model).to receive(:foreign_key_exists?).with(:projects, :users,
|
||||
column: :user_id,
|
||||
on_update: nil,
|
||||
on_delete: :cascade,
|
||||
name: name,
|
||||
primary_key: :id).and_return(true)
|
||||
|
|
@ -792,6 +862,7 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
|
|||
expect(model).to receive(:foreign_key_exists?).with(:projects, :users,
|
||||
name: :foo,
|
||||
primary_key: :id,
|
||||
on_update: nil,
|
||||
on_delete: :cascade,
|
||||
column: :user_id).and_return(true)
|
||||
|
||||
|
|
@ -861,6 +932,7 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
|
|||
"ADD CONSTRAINT fk_multiple_columns\n" \
|
||||
"FOREIGN KEY \(partition_number, user_id\)\n" \
|
||||
"REFERENCES users \(partition_number, id\)\n" \
|
||||
"ON UPDATE CASCADE\n" \
|
||||
"ON DELETE CASCADE\n" \
|
||||
"NOT VALID;\n"
|
||||
)
|
||||
|
|
@ -871,7 +943,8 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
|
|||
column: [:partition_number, :user_id],
|
||||
target_column: [:partition_number, :id],
|
||||
validate: false,
|
||||
name: :fk_multiple_columns
|
||||
name: :fk_multiple_columns,
|
||||
on_update: :cascade
|
||||
)
|
||||
end
|
||||
|
||||
|
|
@ -883,6 +956,7 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
|
|||
{
|
||||
column: [:partition_number, :user_id],
|
||||
name: :fk_multiple_columns,
|
||||
on_update: :cascade,
|
||||
on_delete: :cascade,
|
||||
primary_key: [:partition_number, :id]
|
||||
}
|
||||
|
|
@ -898,6 +972,7 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
|
|||
:users,
|
||||
column: [:partition_number, :user_id],
|
||||
target_column: [:partition_number, :id],
|
||||
on_update: :cascade,
|
||||
validate: false,
|
||||
name: :fk_multiple_columns
|
||||
)
|
||||
|
|
|
|||
|
|
@ -32,6 +32,9 @@ RSpec.describe ::Gitlab::Seeders::Ci::Runner::RunnerFleetPipelineSeeder, feature
|
|||
expect { seeder.seed }.to change { Ci::Build.count }.by(job_count)
|
||||
.and change { Ci::Pipeline.count }.by(4)
|
||||
|
||||
expect(Ci::Pipeline.where.not(started_at: nil).map(&:queued_duration)).to all(be < 5.minutes)
|
||||
expect(Ci::Build.where.not(started_at: nil).map(&:queued_duration)).to all(be < 5.minutes)
|
||||
|
||||
projects_to_runners.first(3).each do |project|
|
||||
expect(Ci::Build.where(runner_id: project[:runner_ids])).not_to be_empty
|
||||
end
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Pages::LookupPath do
|
||||
RSpec.describe Pages::LookupPath, feature_category: :pages do
|
||||
let(:project) { create(:project, :pages_private, pages_https_only: true) }
|
||||
|
||||
subject(:lookup_path) { described_class.new(project) }
|
||||
|
|
@ -126,14 +126,18 @@ RSpec.describe Pages::LookupPath do
|
|||
|
||||
describe '#prefix' do
|
||||
it 'returns "/" for pages group root projects' do
|
||||
project = instance_double(Project, pages_group_root?: true)
|
||||
project = instance_double(Project, pages_namespace_url: "namespace.test", pages_url: "namespace.test")
|
||||
lookup_path = described_class.new(project, trim_prefix: 'mygroup')
|
||||
|
||||
expect(lookup_path.prefix).to eq('/')
|
||||
end
|
||||
|
||||
it 'returns the project full path with the provided prefix removed' do
|
||||
project = instance_double(Project, pages_group_root?: false, full_path: 'mygroup/myproject')
|
||||
project = instance_double(
|
||||
Project,
|
||||
pages_namespace_url: "namespace.test",
|
||||
pages_url: "namespace.other",
|
||||
full_path: 'mygroup/myproject')
|
||||
lookup_path = described_class.new(project, trim_prefix: 'mygroup')
|
||||
|
||||
expect(lookup_path.prefix).to eq('/myproject/')
|
||||
|
|
|
|||
|
|
@ -2596,16 +2596,28 @@ RSpec.describe Project, factory_default: :keep do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#pages_url' do
|
||||
describe '#pages_url', feature_category: :pages do
|
||||
let(:group) { create(:group, name: group_name) }
|
||||
let(:project) { create(:project, namespace: group, name: project_name) }
|
||||
|
||||
let(:project_path) { project_name.downcase }
|
||||
let(:project) do
|
||||
create(
|
||||
:project,
|
||||
namespace: group,
|
||||
name: project_name,
|
||||
path: project_path)
|
||||
end
|
||||
|
||||
let(:domain) { 'Example.com' }
|
||||
let(:port) { nil }
|
||||
|
||||
subject { project.pages_url }
|
||||
|
||||
before do
|
||||
allow(Settings.pages).to receive(:host).and_return(domain)
|
||||
allow(Gitlab.config.pages).to receive(:url).and_return('http://example.com')
|
||||
allow(Gitlab.config.pages)
|
||||
.to receive(:url)
|
||||
.and_return(['http://example.com', port].compact.join(':'))
|
||||
end
|
||||
|
||||
context 'group page' do
|
||||
|
|
@ -2615,9 +2627,7 @@ RSpec.describe Project, factory_default: :keep do
|
|||
it { is_expected.to eq("http://group.example.com") }
|
||||
|
||||
context 'mixed case path' do
|
||||
before do
|
||||
project.update!(path: 'Group.example.com')
|
||||
end
|
||||
let(:project_path) { 'Group.example.com' }
|
||||
|
||||
it { is_expected.to eq("http://group.example.com") }
|
||||
end
|
||||
|
|
@ -2630,22 +2640,88 @@ RSpec.describe Project, factory_default: :keep do
|
|||
it { is_expected.to eq("http://group.example.com/project") }
|
||||
|
||||
context 'mixed case path' do
|
||||
before do
|
||||
project.update!(path: 'Project')
|
||||
end
|
||||
let(:project_path) { 'Project' }
|
||||
|
||||
it { is_expected.to eq("http://group.example.com/Project") }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there is an explicit port' do
|
||||
let(:port) { 3000 }
|
||||
|
||||
context 'when not in dev mode' do
|
||||
before do
|
||||
stub_rails_env('production')
|
||||
end
|
||||
|
||||
context 'group page' do
|
||||
let(:group_name) { 'Group' }
|
||||
let(:project_name) { 'group.example.com' }
|
||||
|
||||
it { is_expected.to eq('http://group.example.com:3000/group.example.com') }
|
||||
|
||||
context 'mixed case path' do
|
||||
let(:project_path) { 'Group.example.com' }
|
||||
|
||||
it { is_expected.to eq('http://group.example.com:3000/Group.example.com') }
|
||||
end
|
||||
end
|
||||
|
||||
context 'project page' do
|
||||
let(:group_name) { 'Group' }
|
||||
let(:project_name) { 'Project' }
|
||||
|
||||
it { is_expected.to eq("http://group.example.com:3000/project") }
|
||||
|
||||
context 'mixed case path' do
|
||||
let(:project_path) { 'Project' }
|
||||
|
||||
it { is_expected.to eq("http://group.example.com:3000/Project") }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when in dev mode' do
|
||||
before do
|
||||
stub_rails_env('development')
|
||||
end
|
||||
|
||||
context 'group page' do
|
||||
let(:group_name) { 'Group' }
|
||||
let(:project_name) { 'group.example.com' }
|
||||
|
||||
it { is_expected.to eq('http://group.example.com:3000') }
|
||||
|
||||
context 'mixed case path' do
|
||||
let(:project_path) { 'Group.example.com' }
|
||||
|
||||
it { is_expected.to eq('http://group.example.com:3000') }
|
||||
end
|
||||
end
|
||||
|
||||
context 'project page' do
|
||||
let(:group_name) { 'Group' }
|
||||
let(:project_name) { 'Project' }
|
||||
|
||||
it { is_expected.to eq("http://group.example.com:3000/project") }
|
||||
|
||||
context 'mixed case path' do
|
||||
let(:project_path) { 'Project' }
|
||||
|
||||
it { is_expected.to eq("http://group.example.com:3000/Project") }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#pages_group_url' do
|
||||
describe '#pages_namespace_url', feature_category: :pages do
|
||||
let(:group) { create(:group, name: group_name) }
|
||||
let(:project) { create(:project, namespace: group, name: project_name) }
|
||||
let(:domain) { 'Example.com' }
|
||||
let(:port) { 1234 }
|
||||
|
||||
subject { project.pages_group_url }
|
||||
subject { project.pages_namespace_url }
|
||||
|
||||
before do
|
||||
allow(Settings.pages).to receive(:host).and_return(domain)
|
||||
|
|
@ -6992,21 +7068,6 @@ RSpec.describe Project, factory_default: :keep do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#pages_group_root?' do
|
||||
it 'returns returns true if pages_url is same as pages_group_url' do
|
||||
project = build(:project)
|
||||
expect(project).to receive(:pages_url).and_return(project.pages_group_url)
|
||||
|
||||
expect(project.pages_group_root?).to eq(true)
|
||||
end
|
||||
|
||||
it 'returns returns false if pages_url is different than pages_group_url' do
|
||||
project = build(:project)
|
||||
|
||||
expect(project.pages_group_root?).to eq(false)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#closest_setting' do
|
||||
shared_examples_for 'fetching closest setting' do
|
||||
let!(:namespace) { create(:namespace) }
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe BulkImports::CreatePipelineTrackersService do
|
||||
RSpec.describe BulkImports::CreatePipelineTrackersService, feature_category: :importers do
|
||||
describe '#execute!' do
|
||||
context 'when entity is group' do
|
||||
it 'creates trackers for group entity' do
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Packages::Conan::SearchService do
|
||||
RSpec.describe Packages::Conan::SearchService, feature_category: :package_registry do
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:project) { create(:project, :public) }
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Projects::CreateService, '#execute' do
|
||||
RSpec.describe Projects::CreateService, '#execute', feature_category: :projects do
|
||||
include ExternalAuthorizationServiceHelpers
|
||||
|
||||
let(:user) { create :user }
|
||||
|
|
|
|||
Loading…
Reference in New Issue