Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-03-29 03:08:22 +00:00
parent 2c99b3e0f3
commit f26311e234
29 changed files with 352 additions and 190 deletions

View file

@ -2,11 +2,13 @@
import { mapActions, mapGetters } from 'vuex';
import BoardContent from '~/boards/components/board_content.vue';
import BoardSettingsSidebar from '~/boards/components/board_settings_sidebar.vue';
import BoardTopBar from '~/boards/components/board_top_bar.vue';
export default {
components: {
BoardContent,
BoardSettingsSidebar,
BoardTopBar,
},
inject: ['disabled'],
computed: {
@ -23,6 +25,7 @@ export default {
<template>
<div class="boards-app gl-relative" :class="{ 'is-compact': isSidebarOpen }">
<board-top-bar />
<board-content :disabled="disabled" />
<board-settings-sidebar />
</div>

View file

@ -48,7 +48,7 @@ export default {
fullPath: {
default: '',
},
rootPath: {
boardBaseUrl: {
default: '',
},
},
@ -209,7 +209,7 @@ export default {
if (this.isDeleteForm) {
try {
await this.deleteBoard();
visitUrl(this.rootPath);
visitUrl(this.boardBaseUrl);
} catch {
this.setError({ message: this.$options.i18n.deleteErrorMessage });
} finally {

View file

@ -0,0 +1,54 @@
<script>
import { mapGetters } from 'vuex';
import BoardAddNewColumnTrigger from '~/boards/components/board_add_new_column_trigger.vue';
import BoardsSelector from 'ee_else_ce/boards/components/boards_selector.vue';
import IssueBoardFilteredSearch from 'ee_else_ce/boards/components/issue_board_filtered_search.vue';
import ConfigToggle from './config_toggle.vue';
import NewBoardButton from './new_board_button.vue';
import ToggleFocus from './toggle_focus.vue';
export default {
components: {
BoardAddNewColumnTrigger,
BoardsSelector,
IssueBoardFilteredSearch,
ConfigToggle,
NewBoardButton,
ToggleFocus,
ToggleLabels: () => import('ee_component/boards/components/toggle_labels.vue'),
ToggleEpicsSwimlanes: () => import('ee_component/boards/components/toggle_epics_swimlanes.vue'),
EpicBoardFilteredSearch: () =>
import('ee_component/boards/components/epic_filtered_search.vue'),
},
inject: ['swimlanesFeatureAvailable', 'canAdminList', 'isSignedIn'],
computed: {
...mapGetters(['isEpicBoard']),
},
};
</script>
<template>
<div class="issues-filters">
<div
class="issues-details-filters filtered-search-block gl-display-flex gl-flex-direction-column gl-lg-flex-direction-row row-content-block second-block"
>
<div
class="gl-display-flex gl-flex-direction-column gl-md-flex-direction-row gl-flex-grow-1 gl-lg-mb-0! mb-md-2 mb-sm-0 gl-w-full"
>
<boards-selector />
<new-board-button />
<epic-board-filtered-search v-if="isEpicBoard" />
<issue-board-filtered-search v-else />
</div>
<div
class="filter-dropdown-container gl-display-flex gl-flex-direction-column gl-md-flex-direction-row gl-align-items-flex-start"
>
<toggle-labels />
<toggle-epics-swimlanes v-if="swimlanesFeatureAvailable && isSignedIn" />
<config-toggle />
<board-add-new-column-trigger v-if="canAdminList" />
<toggle-focus />
</div>
</div>
</div>
</template>

View file

@ -40,37 +40,21 @@ export default {
directives: {
GlModalDirective,
},
inject: ['fullPath'],
inject: [
'boardBaseUrl',
'fullPath',
'canAdminBoard',
'multipleIssueBoardsAvailable',
'hasMissingBoards',
'scopedIssueBoardFeatureEnabled',
'weights',
],
props: {
throttleDuration: {
type: Number,
default: 200,
required: false,
},
boardBaseUrl: {
type: String,
required: true,
},
hasMissingBoards: {
type: Boolean,
required: true,
},
canAdminBoard: {
type: Boolean,
required: true,
},
multipleIssueBoardsAvailable: {
type: Boolean,
required: true,
},
scopedIssueBoardFeatureEnabled: {
type: Boolean,
required: true,
},
weights: {
type: Array,
required: true,
},
},
data() {
return {

View file

@ -14,16 +14,7 @@ export default {
GlModalDirective,
},
mixins: [Tracking.mixin()],
props: {
canAdminList: {
type: Boolean,
required: true,
},
hasScope: {
type: Boolean,
required: true,
},
},
inject: ['canAdminList', 'hasScope'],
computed: {
buttonText() {
return this.canAdminList ? s__('Boards|Edit board') : s__('Boards|View scope');

View file

@ -41,17 +41,7 @@ export default {
confidential: __('Confidential'),
},
components: { BoardFilteredSearch },
inject: ['isSignedIn', 'releasesFetchPath'],
props: {
fullPath: {
type: String,
required: true,
},
boardType: {
type: String,
required: true,
},
},
inject: ['isSignedIn', 'releasesFetchPath', 'fullPath', 'boardType'],
computed: {
isGroupBoard() {
return this.boardType === BoardType.group;

View file

@ -10,12 +10,6 @@ export default {
directives: {
GlTooltip,
},
props: {
issueBoardsContentSelector: {
type: String,
required: true,
},
},
data() {
return {
isFullscreen: false,
@ -25,7 +19,7 @@ export default {
toggleFocusMode() {
hide(this.$refs.toggleFocusModeButton);
const issueBoardsContent = document.querySelector(this.issueBoardsContentSelector);
const issueBoardsContent = document.querySelector('.content-wrapper > .js-focus-mode-board');
issueBoardsContent.classList.toggle('is-focused');
this.isFullscreen = !this.isFullscreen;

View file

@ -1,22 +1,20 @@
import PortalVue from 'portal-vue';
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import toggleEpicsSwimlanes from 'ee_else_ce/boards/toggle_epics_swimlanes';
import toggleLabels from 'ee_else_ce/boards/toggle_labels';
import BoardAddNewColumnTrigger from '~/boards/components/board_add_new_column_trigger.vue';
import BoardApp from '~/boards/components/board_app.vue';
import '~/boards/filters/due_date_filters';
import { issuableTypes } from '~/boards/constants';
import initBoardsFilteredSearch from '~/boards/mount_filtered_search_issue_boards';
import store from '~/boards/stores';
import toggleFocusMode from '~/boards/toggle_focus';
import { NavigationType, isLoggedIn, parseBoolean } from '~/lib/utils/common_utils';
import {
NavigationType,
isLoggedIn,
parseBoolean,
convertObjectPropsToCamelCase,
} from '~/lib/utils/common_utils';
import { queryToObject } from '~/lib/utils/url_utility';
import { fullBoardId } from './boards_util';
import boardConfigToggle from './config_toggle';
import initNewBoard from './new_board';
import { gqlClient } from './graphql';
import mountMultipleBoardsSwitcher from './mount_multiple_boards_switcher';
Vue.use(VueApollo);
Vue.use(PortalVue);
@ -28,6 +26,12 @@ const apolloProvider = new VueApollo({
function mountBoardApp(el) {
const { boardId, groupId, fullPath, rootPath } = el.dataset;
const rawFilterParams = queryToObject(window.location.search, { gatherArrays: true });
const initialFilterParams = {
...convertObjectPropsToCamelCase(rawFilterParams),
};
store.dispatch('fetchBoard', {
fullPath,
fullBoardId: fullBoardId(boardId),
@ -54,26 +58,41 @@ function mountBoardApp(el) {
boardId,
groupId: Number(groupId),
rootPath,
fullPath,
initialFilterParams,
boardBaseUrl: el.dataset.boardBaseUrl,
boardType: el.dataset.parent,
currentUserId: gon.current_user_id || null,
canUpdate: parseBoolean(el.dataset.canUpdate),
canAdminList: parseBoolean(el.dataset.canAdminList),
boardWeight: el.dataset.boardWeight ? parseInt(el.dataset.boardWeight, 10) : null,
labelsManagePath: el.dataset.labelsManagePath,
labelsFilterBasePath: el.dataset.labelsFilterBasePath,
releasesFetchPath: el.dataset.releasesFetchPath,
timeTrackingLimitToHours: parseBoolean(el.dataset.timeTrackingLimitToHours),
issuableType: issuableTypes.issue,
emailsDisabled: parseBoolean(el.dataset.emailsDisabled),
hasScope: parseBoolean(el.dataset.hasScope),
hasMissingBoards: parseBoolean(el.dataset.hasMissingBoards),
weights: el.dataset.weights ? JSON.parse(el.dataset.weights) : [],
// Permissions
canUpdate: parseBoolean(el.dataset.canUpdate),
canAdminList: parseBoolean(el.dataset.canAdminList),
canAdminBoard: parseBoolean(el.dataset.canAdminBoard),
allowLabelCreate: parseBoolean(el.dataset.canUpdate),
allowLabelEdit: parseBoolean(el.dataset.canUpdate),
isSignedIn: isLoggedIn(),
// Features
multipleAssigneesFeatureAvailable: parseBoolean(el.dataset.multipleAssigneesFeatureAvailable),
epicFeatureAvailable: parseBoolean(el.dataset.epicFeatureAvailable),
iterationFeatureAvailable: parseBoolean(el.dataset.iterationFeatureAvailable),
weightFeatureAvailable: parseBoolean(el.dataset.weightFeatureAvailable),
boardWeight: el.dataset.boardWeight ? parseInt(el.dataset.boardWeight, 10) : null,
scopedLabelsAvailable: parseBoolean(el.dataset.scopedLabels),
milestoneListsAvailable: parseBoolean(el.dataset.milestoneListsAvailable),
assigneeListsAvailable: parseBoolean(el.dataset.assigneeListsAvailable),
iterationListsAvailable: parseBoolean(el.dataset.iterationListsAvailable),
issuableType: issuableTypes.issue,
emailsDisabled: parseBoolean(el.dataset.emailsDisabled),
allowLabelCreate: parseBoolean(el.dataset.canUpdate),
allowLabelEdit: parseBoolean(el.dataset.canUpdate),
allowScopedLabels: parseBoolean(el.dataset.scopedLabels),
swimlanesFeatureAvailable: gon.licensed_features?.swimlanes,
multipleIssueBoardsAvailable: parseBoolean(el.dataset.multipleBoardsAvailable),
scopedIssueBoardFeatureEnabled: parseBoolean(el.dataset.scopedIssueBoardFeatureEnabled),
},
render: (createComponent) => createComponent(BoardApp),
});
@ -92,47 +111,7 @@ export default () => {
}
});
const { releasesFetchPath, epicFeatureAvailable, iterationFeatureAvailable } = $boardApp.dataset;
initBoardsFilteredSearch(
apolloProvider,
isLoggedIn(),
releasesFetchPath,
parseBoolean(epicFeatureAvailable),
parseBoolean(iterationFeatureAvailable),
);
mountBoardApp($boardApp);
const createColumnTriggerEl = document.querySelector('.js-create-column-trigger');
if (createColumnTriggerEl) {
// eslint-disable-next-line no-new
new Vue({
el: createColumnTriggerEl,
name: 'BoardAddNewColumnTriggerRoot',
components: {
BoardAddNewColumnTrigger,
},
store,
render(createElement) {
return createElement('board-add-new-column-trigger');
},
});
}
boardConfigToggle();
initNewBoard();
toggleFocusMode();
toggleLabels();
if (gon.licensed_features?.swimlanes) {
toggleEpicsSwimlanes();
}
mountMultipleBoardsSwitcher({
fullPath: $boardApp.dataset.fullPath,
rootPath: $boardApp.dataset.boardsEndpoint,
allowScopedLabels: $boardApp.dataset.scopedLabels,
labelsManagePath: $boardApp.dataset.labelsManagePath,
});
};

View file

@ -16,6 +16,7 @@ module BoardsHelper
bulk_update_path: @bulk_issues_path,
can_update: can_update?.to_s,
can_admin_list: can_admin_list?.to_s,
can_admin_board: can_admin_board?.to_s,
time_tracking_limit_to_hours: Gitlab::CurrentSettings.time_tracking_limit_to_hours.to_s,
parent: current_board_parent.model_name.param_key,
group_id: group_id,
@ -23,7 +24,11 @@ module BoardsHelper
labels_fetch_path: labels_fetch_path,
labels_manage_path: labels_manage_path,
releases_fetch_path: releases_fetch_path,
board_type: board.to_type
board_type: board.to_type,
has_scope: board.scoped?.to_s,
has_missing_boards: has_missing_boards?.to_s,
multiple_boards_available: multiple_boards_available?.to_s,
board_base_url: board_base_url
}
end
@ -85,6 +90,11 @@ module BoardsHelper
current_board_parent.multiple_issue_boards_available?
end
# Boards are hidden when extra boards were created but the license does not allow multiple boards
def has_missing_boards?
!multiple_boards_available? && current_board_parent.boards.size > 1
end
def current_board_path(board)
@current_board_path ||= if board.group_board?
group_board_path(current_board_parent, board)
@ -109,6 +119,10 @@ module BoardsHelper
can?(current_user, :admin_issue_board_list, current_board_parent)
end
def can_admin_board?
can?(current_user, :admin_issue_board, current_board_parent)
end
def can_admin_issue?
can?(current_user, :admin_issue, current_board_parent)
end

View file

@ -52,7 +52,7 @@ module LooseForeignKeys
end
def tracked_tables
@tracked_tables ||= Gitlab::Database::LooseForeignKeys.definitions_by_table.keys
@tracked_tables ||= Gitlab::Database::LooseForeignKeys.definitions_by_table.keys.shuffle
end
end
end

View file

@ -269,11 +269,10 @@
= link_to metrics_and_profiling_admin_application_settings_path, title: _('Metrics and profiling'), class: 'qa-admin-settings-metrics-and-profiling-item' do
%span
= _('Metrics and profiling')
- if Feature.enabled?(:admin_application_settings_service_usage_data_center, default_enabled: :yaml)
= nav_link(path: ['application_settings#service_usage_data']) do
= link_to service_usage_data_admin_application_settings_path, title: _('Service usage data') do
%span
= _('Service usage data')
= nav_link(path: ['application_settings#service_usage_data']) do
= link_to service_usage_data_admin_application_settings_path, title: _('Service usage data') do
%span
= _('Service usage data')
= nav_link(path: 'application_settings#network') do
= link_to network_admin_application_settings_path, title: _('Network'), data: { qa_selector: 'admin_settings_network_item' } do
%span

View file

@ -16,6 +16,4 @@
- page_title("#{board.name}", _("Boards"))
- add_page_specific_style 'page_bundles/boards'
= render 'shared/issuable/search_bar', type: :boards, board: board
#js-issuable-board-app{ data: board_data }

View file

@ -1,8 +0,0 @@
---
name: admin_application_settings_service_usage_data_center
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/78747
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/351268
milestone: '14.8'
type: development
group: group::product intelligence
default_enabled: true

View file

@ -809,24 +809,24 @@ Parameters:
| ------------------------------------------------------- | ------- | -------- | ----------- |
| `name` | string | yes | The name of the group. |
| `path` | string | yes | The path of the group. |
| `description` | string | no | The group's description. |
| `membership_lock` **(PREMIUM)** | boolean | no | Prevent adding new members to projects within this group. |
| `visibility` | string | no | The group's visibility. Can be `private`, `internal`, or `public`. |
| `share_with_group_lock` | boolean | no | Prevent sharing a project with another group within this group. |
| `require_two_factor_authentication` | boolean | no | Require all users in this group to setup Two-factor authentication. |
| `two_factor_grace_period` | integer | no | Time before Two-factor authentication is enforced (in hours). |
| `project_creation_level` | string | no | Determine if developers can create projects in the group. Can be `noone` (No one), `maintainer` (users with the Maintainer role), or `developer` (users with the Developer or Maintainer role). |
| `auto_devops_enabled` | boolean | no | Default to Auto DevOps pipeline for all projects within this group. |
| `subgroup_creation_level` | string | no | Allowed to [create subgroups](../user/group/subgroups/index.md#create-a-subgroup). Can be `owner` (Owners), or `maintainer` (users with the Maintainer role). |
| `emails_disabled` | boolean | no | Disable email notifications |
| `avatar` | mixed | no | Image file for avatar of the group. [Introduced in GitLab 12.9](https://gitlab.com/gitlab-org/gitlab/-/issues/36681) |
| `mentions_disabled` | boolean | no | Disable the capability of a group from getting mentioned |
| `lfs_enabled` | boolean | no | Enable/disable Large File Storage (LFS) for the projects in this group. |
| `request_access_enabled` | boolean | no | Allow users to request member access. |
| `parent_id` | integer | no | The parent group ID for creating nested group. |
| `default_branch_protection` | integer | no | See [Options for `default_branch_protection`](#options-for-default_branch_protection). Default to the global level default branch protection setting. |
| `shared_runners_minutes_limit` **(PREMIUM)** | integer | no | Can be set by administrators only. Maximum number of monthly CI/CD minutes for this group. Can be `nil` (default; inherit system default), `0` (unlimited), or `> 0`. |
| `description` | string | no | The group's description. |
| `emails_disabled` | boolean | no | Disable email notifications. |
| `lfs_enabled` | boolean | no | Enable/disable Large File Storage (LFS) for the projects in this group. |
| `mentions_disabled` | boolean | no | Disable the capability of a group from getting mentioned. |
| `parent_id` | integer | no | The parent group ID for creating nested group. |
| `project_creation_level` | string | no | Determine if developers can create projects in the group. Can be `noone` (No one), `maintainer` (users with the Maintainer role), or `developer` (users with the Developer or Maintainer role). |
| `request_access_enabled` | boolean | no | Allow users to request member access. |
| `require_two_factor_authentication` | boolean | no | Require all users in this group to setup Two-factor authentication. |
| `share_with_group_lock` | boolean | no | Prevent sharing a project with another group within this group. |
| `subgroup_creation_level` | string | no | Allowed to [create subgroups](../user/group/subgroups/index.md#create-a-subgroup). Can be `owner` (Owners), or `maintainer` (users with the Maintainer role). |
| `two_factor_grace_period` | integer | no | Time before Two-factor authentication is enforced (in hours). |
| `visibility` | string | no | The group's visibility. Can be `private`, `internal`, or `public`. |
| `membership_lock` **(PREMIUM)** | boolean | no | Prevent adding new members to projects within this group. |
| `extra_shared_runners_minutes_limit` **(PREMIUM)** | integer | no | Can be set by administrators only. Additional CI/CD minutes for this group. |
| `shared_runners_minutes_limit` **(PREMIUM)** | integer | no | Can be set by administrators only. Maximum number of monthly CI/CD minutes for this group. Can be `nil` (default; inherit system default), `0` (unlimited), or `> 0`. |
### Options for `default_branch_protection`
@ -912,27 +912,27 @@ PUT /groups/:id
| `id` | integer | yes | The ID of the group. |
| `name` | string | no | The name of the group. |
| `path` | string | no | The path of the group. |
| `description` | string | no | The description of the group. |
| `membership_lock` **(PREMIUM)** | boolean | no | Prevent adding new members to projects within this group. |
| `share_with_group_lock` | boolean | no | Prevent sharing a project with another group within this group. |
| `visibility` | string | no | The visibility level of the group. Can be `private`, `internal`, or `public`. |
| `require_two_factor_authentication` | boolean | no | Require all users in this group to setup Two-factor authentication. |
| `two_factor_grace_period` | integer | no | Time before Two-factor authentication is enforced (in hours). |
| `project_creation_level` | string | no | Determine if developers can create projects in the group. Can be `noone` (No one), `maintainer` (users with the Maintainer role), or `developer` (users with the Developer or Maintainer role). |
| `auto_devops_enabled` | boolean | no | Default to Auto DevOps pipeline for all projects within this group. |
| `subgroup_creation_level` | string | no | Allowed to [create subgroups](../user/group/subgroups/index.md#create-a-subgroup). Can be `owner` (Owners), or `maintainer` (users with the Maintainer role). |
| `emails_disabled` | boolean | no | Disable email notifications |
| `avatar` | mixed | no | Image file for avatar of the group. [Introduced in GitLab 12.9](https://gitlab.com/gitlab-org/gitlab/-/issues/36681) |
| `mentions_disabled` | boolean | no | Disable the capability of a group from getting mentioned |
| `lfs_enabled` (optional) | boolean | no | Enable/disable Large File Storage (LFS) for the projects in this group. |
| `request_access_enabled` | boolean | no | Allow users to request member access. |
| `default_branch_protection` | integer | no | See [Options for `default_branch_protection`](#options-for-default_branch_protection). |
| `file_template_project_id` **(PREMIUM)** | integer | no | The ID of a project to load custom file templates from. |
| `shared_runners_minutes_limit` **(PREMIUM)** | integer | no | Can be set by administrators only. Maximum number of monthly CI/CD minutes for this group. Can be `nil` (default; inherit system default), `0` (unlimited), or `> 0`. |
| `extra_shared_runners_minutes_limit` **(PREMIUM)** | integer | no | Can be set by administrators only. Additional CI/CD minutes for this group. |
| `prevent_forking_outside_group` **(PREMIUM)** | boolean | no | When enabled, users can **not** fork projects from this group to external namespaces
| `shared_runners_setting` | string | no | See [Options for `shared_runners_setting`](#options-for-shared_runners_setting). Enable or disable shared runners for a group's subgroups and projects. |
| `description` | string | no | The description of the group. |
| `emails_disabled` | boolean | no | Disable email notifications. |
| `lfs_enabled` | boolean | no | Enable/disable Large File Storage (LFS) for the projects in this group. |
| `mentions_disabled` | boolean | no | Disable the capability of a group from getting mentioned. |
| `prevent_sharing_groups_outside_hierarchy` | boolean | no | See [Prevent group sharing outside the group hierarchy](../user/group/index.md#prevent-group-sharing-outside-the-group-hierarchy). This attribute is only available on top-level groups. [Introduced in GitLab 14.1](https://gitlab.com/gitlab-org/gitlab/-/issues/333721) |
| `project_creation_level` | string | no | Determine if developers can create projects in the group. Can be `noone` (No one), `maintainer` (users with the Maintainer role), or `developer` (users with the Developer or Maintainer role). |
| `request_access_enabled` | boolean | no | Allow users to request member access. |
| `require_two_factor_authentication` | boolean | no | Require all users in this group to setup Two-factor authentication. |
| `shared_runners_setting` | string | no | See [Options for `shared_runners_setting`](#options-for-shared_runners_setting). Enable or disable shared runners for a group's subgroups and projects. |
| `share_with_group_lock` | boolean | no | Prevent sharing a project with another group within this group. |
| `subgroup_creation_level` | string | no | Allowed to [create subgroups](../user/group/subgroups/index.md#create-a-subgroup). Can be `owner` (Owners), or `maintainer` (users with the Maintainer role). |
| `two_factor_grace_period` | integer | no | Time before Two-factor authentication is enforced (in hours). |
| `visibility` | string | no | The visibility level of the group. Can be `private`, `internal`, or `public`. |
| `extra_shared_runners_minutes_limit` **(PREMIUM)** | integer | no | Can be set by administrators only. Additional CI/CD minutes for this group. |
| `file_template_project_id` **(PREMIUM)** | integer | no | The ID of a project to load custom file templates from. |
| `membership_lock` **(PREMIUM)** | boolean | no | Prevent adding new members to projects within this group. |
| `prevent_forking_outside_group` **(PREMIUM)** | boolean | no | When enabled, users can **not** fork projects from this group to external namespaces. |
| `shared_runners_minutes_limit` **(PREMIUM)** | integer | no | Can be set by administrators only. Maximum number of monthly CI/CD minutes for this group. Can be `nil` (default; inherit system default), `0` (unlimited), or `> 0`. |
NOTE:
The `projects` and `shared_projects` attributes in the response are deprecated and [scheduled for removal in API v5](https://gitlab.com/gitlab-org/gitlab/-/issues/213797).

View file

@ -11,8 +11,7 @@ type: reference, api
FLAG:
On self-managed GitLab, by default this feature is not available. To make it available,
ask an administrator to [enable the feature flag](../administration/feature_flags.md) named `ci_secure_files`.
The feature is not ready for production use.
ask an administrator to [enable the feature flag](../administration/feature_flags.md) named `ci_secure_files`. Limited to 100 secure files per project. The feature is not ready for production use.
## List project secure files

View file

@ -2620,9 +2620,11 @@ Multiple runners must exist, or a single runner must be configured to run multip
**Keyword type**: Job keyword. You can use it only as part of a job.
**Possible inputs**:
**Possible inputs**: An array of hashes of variables:
- A numeric value from `2` to `50`.
- The variable names can use only numbers, letters, and underscores (`_`).
- The values must be either a string, or an array of strings.
- The number of permutations cannot exceed 50.
**Example of `parallel:matrix`**:

View file

@ -582,7 +582,8 @@ ServicePing::SubmitService.new(skip_db_write: true).execute
## Manually upload Service Ping payload
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/7388) in GitLab 14.8 with a flag named `admin_application_settings_service_usage_data_center`. Disabled by default.
> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/7388) in GitLab 14.8 with a flag named `admin_application_settings_service_usage_data_center`. Disabled by default.
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/83265) in GitLab 14.10.
Service Ping payload can be uploaded to GitLab even if your application instance doesn't have access to the internet,
or you don't have Service Ping [cron job](#how-service-ping-works) enabled.

View file

@ -161,7 +161,7 @@ The following table lists project permissions available for each role:
| [Projects](project/index.md):<br>Rename project | | | | ✓ | ✓ |
| [Projects](project/index.md):<br>Share (invite) projects with groups | | | | ✓ (*7*) | ✓ (*7*) |
| [Projects](project/index.md):<br>View 2FA status of members | | | | ✓ | ✓ |
| [Projects](project/index.md):<br>Administer project compliance frameworks | | | | | ✓ |
| [Projects](project/index.md):<br>Assign project to a compliance framework | | | | | ✓ |
| [Projects](project/index.md):<br>Archive project | | | | | ✓ |
| [Projects](project/index.md):<br>Change project visibility level | | | | | ✓ |
| [Projects](project/index.md):<br>Delete project | | | | | ✓ |
@ -407,7 +407,7 @@ The following table lists group permissions available for each role:
| List group deploy tokens | | | | ✓ | ✓ |
| Manage [group push rules](group/index.md#group-push-rules) | | | | ✓ | ✓ |
| View/manage group-level Kubernetes cluster | | | | ✓ | ✓ |
| Administer project compliance frameworks | | | | | ✓ |
| Create and manage compliance frameworks | | | | | ✓ |
| Create/Delete group deploy tokens | | | | | ✓ |
| Change group visibility level | | | | | ✓ |
| Delete group | | | | | ✓ |

View file

@ -26,4 +26,21 @@ namespace :dev do
Rails.configuration.eager_load = true
Rails.application.eager_load!
end
databases = ActiveRecord::Tasks::DatabaseTasks.setup_initial_database_yaml
namespace :copy_db do
ActiveRecord::Tasks::DatabaseTasks.for_each(databases) do |name|
next if name == 'main'
desc "Copies the #{name} database from the main database"
task name => :environment do
db_config = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env, name: name)
ApplicationRecord.connection.create_database(db_config.database, template: ApplicationRecord.connection_db_config.database)
rescue ActiveRecord::DatabaseAlreadyExists
warn "Database '#{db_config.database}' already exists"
end
end
end
end

View file

@ -295,13 +295,6 @@ module QA
merge_requests.find { |mr| mr[:title] == title }
end
def runners(tag_list: nil)
url = tag_list ? "#{api_runners_path}?tag_list=#{tag_list.compact.join(',')}" : api_runners_path
response = get(request_url(url, per_page: '100'))
parse_body(response)
end
def registry_repositories
response = get(request_url(api_registry_repositories_path))
parse_body(response)

View file

@ -46,11 +46,12 @@ module QA
end
def remove_via_api!
runners = project.runners(tag_list: @tags)
runners = list_of_runners(tag_list: @tags)
return if runners.blank?
this_runner = runners.find { |runner| runner[:description] == name }
unless this_runner
raise "Project #{project.path_with_namespace} does not have a runner with a description matching #{name} #{"or tags #{@tags}" if @tags&.any?}. Runners available: #{runners}"
end
@ -62,6 +63,16 @@ module QA
Service::DockerRun::GitlabRunner.new(name).remove!
end
def list_of_runners(tag_list: nil)
url = tag_list ? "#{api_post_path}?tag_list=#{tag_list.compact.join(',')}" : api_post_path
response = get(request_url(url, per_page: '100'))
# Capturing 500 error code responses to log this issue better. We can consider cleaning it up once https://gitlab.com/gitlab-org/gitlab/-/issues/331753 is addressed.
raise "Response returned a #{response.code} error code. #{response.body}" if response.code == Support::API::HTTP_STATUS_SERVER_ERROR
parse_body(response)
end
def api_delete_path
"/runners/#{id}"
end
@ -70,6 +81,7 @@ module QA
end
def api_post_path
"/runners"
end
def api_post_body

View file

@ -7,7 +7,7 @@ module QA
let(:api_client) { Runtime::API::Client.new(:gitlab) }
let(:executor) { "qa-runner-#{Time.now.to_i}" }
let(:runner_tags) { ['runner-registration-e2e-test'] }
let(:runner_tags) { ["runner-registration-e2e-test-#{Faker::Alphanumeric.alphanumeric(number: 8)}"] }
let!(:runner) do
Resource::Runner.fabricate! do |runner|
runner.name = executor
@ -20,16 +20,18 @@ module QA
end
# Removing a runner via the UI is covered by `spec/features/runners_spec.rb``
it 'removes the runner', quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/355302', type: :investigating }, testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/354828' do
expect(runner.project.runners.size).to eq(1)
expect(runner.project.runners.first[:description]).to eq(executor)
it 'removes the runner' do
runners = runner.list_of_runners(tag_list: runner_tags)
request = Runtime::API::Request.new(api_client, "runners/#{runner.project.runners.first[:id]}")
expect(runners.size).to eq(1)
expect(runners.first[:description]).to eq(executor)
request = Runtime::API::Request.new(api_client, "runners/#{runners.first[:id]}")
response = delete(request.url)
expect(response.code).to eq(Support::API::HTTP_STATUS_NO_CONTENT)
expect(response.body).to be_empty
expect(runner.project.runners).to be_empty
expect(runner.list_of_runners(tag_list: runner_tags)).to be_empty
end
end
end

View file

@ -2,10 +2,13 @@
module QA
RSpec.describe 'Create' do
describe 'Open a fork in Web IDE', quarantine: {
describe 'Open a fork in Web IDE',
# TODO: remove limitation to only run on main when the test is fixed
only: { pipeline: :main },
quarantine: {
issue: "https://gitlab.com/gitlab-org/gitlab/-/issues/351696",
type: :flaky
} do
} do
let(:parent_project) do
Resource::Project.fabricate_via_api! do |project|
project.name = 'parent-project'

View file

@ -62,7 +62,7 @@ describe('BoardForm', () => {
};
},
provide: {
rootPath: 'root',
boardBaseUrl: 'root',
},
mocks: {
$apollo: {

View file

@ -0,0 +1,88 @@
import { shallowMount } from '@vue/test-utils';
import Vue from 'vue';
import Vuex from 'vuex';
import BoardTopBar from '~/boards/components/board_top_bar.vue';
import BoardAddNewColumnTrigger from '~/boards/components/board_add_new_column_trigger.vue';
import BoardsSelector from '~/boards/components/boards_selector.vue';
import ConfigToggle from '~/boards/components/config_toggle.vue';
import IssueBoardFilteredSearch from '~/boards/components/issue_board_filtered_search.vue';
import NewBoardButton from '~/boards/components/new_board_button.vue';
import ToggleFocus from '~/boards/components/toggle_focus.vue';
describe('BoardTopBar', () => {
let wrapper;
Vue.use(Vuex);
const createStore = ({ mockGetters = {} } = {}) => {
return new Vuex.Store({
state: {},
getters: {
isEpicBoard: () => false,
...mockGetters,
},
});
};
const createComponent = ({ provide = {}, mockGetters = {} } = {}) => {
const store = createStore({ mockGetters });
wrapper = shallowMount(BoardTopBar, {
store,
provide: {
swimlanesFeatureAvailable: false,
canAdminList: false,
isSignedIn: false,
fullPath: 'gitlab-org',
boardType: 'group',
releasesFetchPath: '/releases',
...provide,
},
stubs: { IssueBoardFilteredSearch },
});
};
afterEach(() => {
wrapper.destroy();
});
describe('base template', () => {
beforeEach(() => {
createComponent();
});
it('renders BoardsSelector component', () => {
expect(wrapper.findComponent(BoardsSelector).exists()).toBe(true);
});
it('renders IssueBoardFilteredSearch component', () => {
expect(wrapper.findComponent(IssueBoardFilteredSearch).exists()).toBe(true);
});
it('renders NewBoardButton component', () => {
expect(wrapper.findComponent(NewBoardButton).exists()).toBe(true);
});
it('renders ConfigToggle component', () => {
expect(wrapper.findComponent(ConfigToggle).exists()).toBe(true);
});
it('renders ToggleFocus component', () => {
expect(wrapper.findComponent(ToggleFocus).exists()).toBe(true);
});
it('does not render BoardAddNewColumnTrigger component', () => {
expect(wrapper.findComponent(BoardAddNewColumnTrigger).exists()).toBe(false);
});
});
describe('when user can admin list', () => {
beforeEach(() => {
createComponent({ provide: { canAdminList: true } });
});
it('renders BoardAddNewColumnTrigger component', () => {
expect(wrapper.findComponent(BoardAddNewColumnTrigger).exists()).toBe(true);
});
});
});

View file

@ -105,6 +105,10 @@ describe('BoardsSelector', () => {
apolloProvider: fakeApollo,
propsData: {
throttleDuration,
},
attachTo: document.body,
provide: {
fullPath: '',
boardBaseUrl: `${TEST_HOST}/board/base/url`,
hasMissingBoards: false,
canAdminBoard: true,
@ -112,10 +116,6 @@ describe('BoardsSelector', () => {
scopedIssueBoardFeatureEnabled: true,
weights: [],
},
attachTo: document.body,
provide: {
fullPath: '',
},
});
};

View file

@ -14,10 +14,11 @@ describe('IssueBoardFilter', () => {
const createComponent = ({ isSignedIn = false } = {}) => {
wrapper = shallowMount(IssueBoardFilteredSpec, {
propsData: { fullPath: 'gitlab-org', boardType: 'group' },
provide: {
isSignedIn,
releasesFetchPath: '/releases',
fullPath: 'gitlab-org',
boardType: 'group',
},
});
};

View file

@ -102,6 +102,7 @@ RSpec.describe BoardsHelper do
allow(helper).to receive(:can?).with(user, :create_non_backlog_issues, project_board).and_return(true)
allow(helper).to receive(:can?).with(user, :admin_issue, project_board).and_return(true)
allow(helper).to receive(:can?).with(user, :admin_issue_board_list, project).and_return(false)
allow(helper).to receive(:can?).with(user, :admin_issue_board, project).and_return(false)
end
it 'returns a board_lists_path as lists_endpoint' do
@ -129,12 +130,23 @@ RSpec.describe BoardsHelper do
it 'returns can_admin_list as false by default' do
expect(helper.board_data[:can_admin_list]).to eq('false')
end
it 'returns can_admin_list as true when user can admin the board' do
it 'returns can_admin_list as true when user can admin the board lists' do
allow(helper).to receive(:can?).with(user, :admin_issue_board_list, project).and_return(true)
expect(helper.board_data[:can_admin_list]).to eq('true')
end
end
context 'can_admin_board' do
it 'returns can_admin_board as false by default' do
expect(helper.board_data[:can_admin_board]).to eq('false')
end
it 'returns can_admin_board as true when user can admin the board' do
allow(helper).to receive(:can?).with(user, :admin_issue_board, project).and_return(true)
expect(helper.board_data[:can_admin_board]).to eq('true')
end
end
end
context 'group board' do
@ -146,6 +158,7 @@ RSpec.describe BoardsHelper do
allow(helper).to receive(:can?).with(user, :create_non_backlog_issues, group_board).and_return(true)
allow(helper).to receive(:can?).with(user, :admin_issue, group_board).and_return(true)
allow(helper).to receive(:can?).with(user, :admin_issue_board_list, base_group).and_return(false)
allow(helper).to receive(:can?).with(user, :admin_issue_board, base_group).and_return(false)
end
it 'returns correct path for base group' do
@ -165,7 +178,7 @@ RSpec.describe BoardsHelper do
it 'returns can_admin_list as false by default' do
expect(helper.board_data[:can_admin_list]).to eq('false')
end
it 'returns can_admin_list as true when user can admin the board' do
it 'returns can_admin_list as true when user can admin the board lists' do
allow(helper).to receive(:can?).with(user, :admin_issue_board_list, base_group).and_return(true)
expect(helper.board_data[:can_admin_list]).to eq('true')

View file

@ -7,6 +7,8 @@ RSpec.describe 'dev rake tasks' do
Rake.application.rake_require 'tasks/gitlab/setup'
Rake.application.rake_require 'tasks/gitlab/shell'
Rake.application.rake_require 'tasks/dev'
Rake.application.rake_require 'active_record/railties/databases'
Rake.application.rake_require 'tasks/gitlab/db'
end
describe 'setup' do
@ -37,4 +39,35 @@ RSpec.describe 'dev rake tasks' do
load_task
end
end
describe 'copy_db:ci' do
before do
skip_if_multiple_databases_not_setup
configurations = instance_double(ActiveRecord::DatabaseConfigurations)
allow(ActiveRecord::Base).to receive(:configurations).and_return(configurations)
allow(configurations).to receive(:configs_for).with(env_name: Rails.env, name: 'ci').and_return(ci_configuration)
end
subject(:load_task) { run_rake_task('dev:setup_ci_db') }
let(:ci_configuration) { instance_double(ActiveRecord::DatabaseConfigurations::HashConfig, name: 'ci', database: '__test_db_ci') }
it 'creates the database from main' do
expect(ApplicationRecord.connection).to receive(:create_database).with(
ci_configuration.database,
template: ApplicationRecord.connection_db_config.database
)
run_rake_task('dev:copy_db:ci')
end
context 'when the database already exists' do
it 'prints out a warning' do
expect(ApplicationRecord.connection).to receive(:create_database).and_raise(ActiveRecord::DatabaseAlreadyExists)
expect { run_rake_task('dev:copy_db:ci') }.to output(/Database '#{ci_configuration.database}' already exists/).to_stderr
end
end
end
end