Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2024-02-01 18:09:41 +00:00
parent 14c802c4d3
commit 8318fc4ad7
110 changed files with 1079 additions and 514 deletions

View File

@ -56,6 +56,7 @@ rails-production-server-boot-puma-cng:
ruby_syntax:
extends:
- .preflight-job-base
- .preflight:rules:ruby_syntax
before_script:
- source scripts/utils.sh
image: ${GITLAB_DEPENDENCY_PROXY_ADDRESS}ruby:${RUBY_VERSION}

View File

@ -327,6 +327,14 @@
.lib-gitlab-patterns: &lib-gitlab-patterns
- "{,ee/,jh/}lib/{,ee/,jh/}gitlab/**/*"
# Patterns to match pure Ruby code
.ruby-patterns: &ruby-patterns
- "**/Rakefile"
- "**/Dangerfile"
- "**/Gemfile"
- "**/Guardfile"
- "**/*.rb"
# Backend patterns + .ci-patterns
.backend-patterns: &backend-patterns
- "{,jh/}Gemfile{,.lock}"
@ -2847,7 +2855,7 @@
.preflight:rules:ruby_syntax:
rules:
- <<: *if-default-refs
changes: *backend-patterns
changes: *ruby-patterns
.preflight:rules:no-ee-check:
rules:

View File

@ -819,13 +819,6 @@ Layout/ArgumentAlignment:
- 'ee/db/geo/migrate/20180405074130_add_partial_index_project_repository_verification.rb'
- 'ee/db/geo/post_migrate/20210217020154_add_unique_index_on_container_repository_registry.rb'
- 'ee/db/geo/post_migrate/20210217020156_add_unique_index_on_terraform_state_version_registry.rb'
- 'ee/lib/ee/gitlab/background_migration/backfill_epic_cache_counts.rb'
- 'ee/lib/ee/gitlab/background_migration/backfill_project_statistics_container_repository_size.rb'
- 'ee/lib/ee/gitlab/background_migration/backfill_project_statistics_storage_size_without_uploads_size.rb'
- 'ee/lib/ee/gitlab/background_migration/migrate_shared_vulnerability_scanners.rb'
- 'ee/lib/ee/gitlab/background_migration/migrate_vulnerabilities_feedback_to_vulnerabilities_state_transition.rb'
- 'ee/lib/ee/gitlab/background_migration/populate_latest_pipeline_ids.rb'
- 'ee/lib/ee/gitlab/background_migration/populate_resolved_on_default_branch_column.rb'
- 'ee/lib/ee/gitlab/import_sources.rb'
- 'ee/lib/ee/gitlab/middleware/read_only/controller.rb'
- 'ee/lib/ee/gitlab/scim/group/deprovisioning_service.rb'
@ -857,15 +850,6 @@ Layout/ArgumentAlignment:
- 'ee/spec/lib/ee/gitlab/application_context_spec.rb'
- 'ee/spec/lib/ee/gitlab/auth/ldap/sync/admin_users_spec.rb'
- 'ee/spec/lib/ee/gitlab/auth/ldap/sync/group_spec.rb'
- 'ee/spec/lib/ee/gitlab/background_migration/backfill_epic_cache_counts_spec.rb'
- 'ee/spec/lib/ee/gitlab/background_migration/backfill_project_statistics_storage_size_without_uploads_size_spec.rb'
- 'ee/spec/lib/ee/gitlab/background_migration/delete_invalid_epic_issues_spec.rb'
- 'ee/spec/lib/ee/gitlab/background_migration/fix_approval_project_rules_without_protected_branches_spec.rb'
- 'ee/spec/lib/ee/gitlab/background_migration/fix_security_scan_statuses_spec.rb'
- 'ee/spec/lib/ee/gitlab/background_migration/migrate_shared_vulnerability_scanners_spec.rb'
- 'ee/spec/lib/ee/gitlab/background_migration/migrate_vulnerabilities_feedback_to_vulnerabilities_state_transition_spec.rb'
- 'ee/spec/lib/ee/gitlab/background_migration/populate_approval_merge_request_rules_with_security_orchestration_spec.rb'
- 'ee/spec/lib/ee/gitlab/background_migration/populate_approval_project_rules_with_security_orchestration_spec.rb'
- 'ee/spec/lib/ee/gitlab/checks/push_rule_check_spec.rb'
- 'ee/spec/lib/ee/gitlab/checks/push_rules/commit_check_spec.rb'
- 'ee/spec/lib/ee/gitlab/ci/config/entry/bridge_spec.rb'

View File

@ -522,7 +522,7 @@ group :test do
# Moved in `test` because https://gitlab.com/gitlab-org/gitlab/-/issues/217527
gem 'derailed_benchmarks', require: false # rubocop:todo Gemfile/MissingFeatureCategory
gem 'gitlab_quality-test_tooling', '~> 1.14.1', require: false, feature_category: :tooling
gem 'gitlab_quality-test_tooling', '~> 1.14.2', require: false, feature_category: :tooling
end
gem 'octokit', '~> 6.0' # rubocop:todo Gemfile/MissingFeatureCategory

View File

@ -227,7 +227,7 @@
{"name":"gitlab-styles","version":"11.0.0","platform":"ruby","checksum":"0dd8ec066ce9955ac51d3616c6bfded30f75bb526f39ff392ece6f43d5b9406b"},
{"name":"gitlab_chronic_duration","version":"0.12.0","platform":"ruby","checksum":"0d766944d415b5c831f176871ee8625783fc0c5bfbef2d79a3a616f207ffc16d"},
{"name":"gitlab_omniauth-ldap","version":"2.2.0","platform":"ruby","checksum":"bb4d20acb3b123ed654a8f6a47d3fac673ece7ed0b6992edb92dca14bad2838c"},
{"name":"gitlab_quality-test_tooling","version":"1.14.1","platform":"ruby","checksum":"667ccdd211ad1f3ba5bcba7e11e9d65e7d774e39bcaa1cbb667e58925fba3430"},
{"name":"gitlab_quality-test_tooling","version":"1.14.2","platform":"ruby","checksum":"9ee343328d5e755b54d97729adc5a743abd83b05e9b28d9bbbe74f393b6cf658"},
{"name":"globalid","version":"1.1.0","platform":"ruby","checksum":"b337e1746f0c8cb0a6c918234b03a1ddeb4966206ce288fbb57779f59b2d154f"},
{"name":"gon","version":"6.4.0","platform":"ruby","checksum":"e3a618d659392890f1aa7db420f17c75fd7d35aeb5f8fe003697d02c4b88d2f0"},
{"name":"google-apis-androidpublisher_v3","version":"0.34.0","platform":"ruby","checksum":"d7e1d7dd92f79c498fe2082222a1740d788e022e660c135564b3fd299cab5425"},

View File

@ -741,7 +741,7 @@ GEM
omniauth (>= 1.3, < 3)
pyu-ruby-sasl (>= 0.0.3.3, < 0.1)
rubyntlm (~> 0.5)
gitlab_quality-test_tooling (1.14.1)
gitlab_quality-test_tooling (1.14.2)
activesupport (>= 6.1, < 7.1)
amatch (~> 0.4.1)
gitlab (~> 4.19)
@ -1933,7 +1933,7 @@ DEPENDENCIES
gitlab-utils!
gitlab_chronic_duration (~> 0.12)
gitlab_omniauth-ldap (~> 2.2.0)
gitlab_quality-test_tooling (~> 1.14.1)
gitlab_quality-test_tooling (~> 1.14.2)
gon (~> 6.4.0)
google-apis-androidpublisher_v3 (~> 0.34.0)
google-apis-cloudbilling_v1 (~> 0.21.0)

View File

@ -14,7 +14,9 @@ import { mapActions, mapGetters, mapState } from 'vuex';
import * as Sentry from '~/sentry/sentry_browser_wrapper';
import { createAlert, VARIANT_SUCCESS } from '~/alert';
import { EVENT_ISSUABLE_VUE_APP_CHANGE } from '~/issuable/constants';
import { ISSUABLE_EDIT_DESCRIPTION } from '~/behaviors/shortcuts/keybindings';
import { keysFor, ISSUABLE_EDIT_DESCRIPTION } from '~/behaviors/shortcuts/keybindings';
import { shouldDisableShortcuts } from '~/behaviors/shortcuts/shortcuts_toggle';
import { sanitize } from '~/lib/dompurify';
import { STATUS_CLOSED, TYPE_ISSUE, issuableTypeText } from '~/issues/constants';
import { ISSUE_STATE_EVENT_CLOSE, ISSUE_STATE_EVENT_REOPEN } from '~/issues/show/constants';
import { capitalizeFirstCharacter } from '~/lib/utils/text_utility';
@ -192,10 +194,14 @@ export default {
};
},
editShortcutKey() {
return ISSUABLE_EDIT_DESCRIPTION.defaultKeys[0];
return shouldDisableShortcuts() ? null : keysFor(ISSUABLE_EDIT_DESCRIPTION)[0];
},
editTooltip() {
return `${this.$options.i18n.editTitleAndDescription} <kbd class="glat gl-ml-1" aria-hidden=true>${this.editShortcutKey}</kbd>`;
const description = this.$options.i18n.editTitleAndDescription;
const key = this.editShortcutKey;
return shouldDisableShortcuts()
? description
: sanitize(`${description} <kbd class="flat gl-ml-1" aria-hidden=true>${key}</kbd>`);
},
},
created() {

View File

@ -17,15 +17,14 @@ import { RESOURCE_TYPE_GROUPS, RESOURCE_TYPE_PROJECTS } from '../../constants';
import GroupsView from '../../shared/components/groups_view.vue';
import ProjectsView from '../../shared/components/projects_view.vue';
import { onPageChange } from '../../shared/utils';
import { QUERY_PARAM_END_CURSOR, QUERY_PARAM_START_CURSOR } from '../../shared/constants';
import {
DISPLAY_LISTBOX_ITEMS,
QUERY_PARAM_END_CURSOR,
QUERY_PARAM_START_CURSOR,
SORT_DIRECTION_ASC,
SORT_DIRECTION_DESC,
SORT_ITEMS,
SORT_ITEM_CREATED,
FILTERED_SEARCH_TERM_KEY,
} from '../constants';
SORT_ITEM_NAME,
} from '../../shared/constants';
import { DISPLAY_LISTBOX_ITEMS, SORT_ITEMS, FILTERED_SEARCH_TERM_KEY } from '../constants';
export default {
i18n: {
@ -60,10 +59,13 @@ export default {
return this.$options.sortItems.find((sortItem) => sortItem.value === this.sortName);
},
sortName() {
return this.$route.query.sort_name || SORT_ITEM_CREATED.value;
return this.$route.query.sort_name || SORT_ITEM_NAME.value;
},
sortDirection() {
return this.$route.query.sort_direction || SORT_DIRECTION_ASC;
},
isAscending() {
return this.$route.query.sort_direction !== SORT_DIRECTION_DESC;
return this.sortDirection !== SORT_DIRECTION_DESC;
},
sortText() {
return this.activeSortItem.text;
@ -91,6 +93,21 @@ export default {
? display
: RESOURCE_TYPE_GROUPS;
},
search() {
return (
this.filteredSearchValue.find((token) => token.type === FILTERED_SEARCH_TERM)?.value
?.data || ''
);
},
routeQueryWithoutPagination() {
const {
[QUERY_PARAM_START_CURSOR]: startCursor,
[QUERY_PARAM_END_CURSOR]: endCursor,
...routeQuery
} = this.$route.query;
return routeQuery;
},
},
methods: {
pushQuery(query) {
@ -110,11 +127,11 @@ export default {
return;
}
this.pushQuery({ ...this.$route.query, sort_name: sortValue });
this.pushQuery({ ...this.routeQueryWithoutPagination, sort_name: sortValue });
},
onSortDirectionChange(isAscending) {
this.pushQuery({
...this.$route.query,
...this.routeQueryWithoutPagination,
sort_direction: isAscending ? SORT_DIRECTION_ASC : SORT_DIRECTION_DESC,
});
},
@ -182,6 +199,9 @@ export default {
list-item-class="gl-px-5"
:start-cursor="startCursor"
:end-cursor="endCursor"
:search="search"
:sort-name="sortName"
:sort-direction="sortDirection"
@page-change="onPageChange"
/>
</div>

View File

@ -1,4 +1,5 @@
import { __ } from '~/locale';
import { SORT_ITEM_NAME, SORT_ITEM_CREATED_AT, SORT_ITEM_UPDATED_AT } from '../shared/constants';
export const DISPLAY_QUERY_GROUPS = 'groups';
export const DISPLAY_QUERY_PROJECTS = 'projects';
@ -16,12 +17,4 @@ export const DISPLAY_LISTBOX_ITEMS = [
},
];
export const SORT_DIRECTION_ASC = 'asc';
export const SORT_DIRECTION_DESC = 'desc';
export const SORT_ITEM_CREATED = {
value: 'created',
text: __('Created'),
};
export const SORT_ITEMS = [SORT_ITEM_CREATED];
export const SORT_ITEMS = [SORT_ITEM_NAME, SORT_ITEM_CREATED_AT, SORT_ITEM_UPDATED_AT];

View File

@ -5,6 +5,7 @@ import { s__, __ } from '~/locale';
import GroupsList from '~/vue_shared/components/groups_list/groups_list.vue';
import { DEFAULT_PER_PAGE } from '~/api';
import groupsQuery from '../graphql/queries/groups.query.graphql';
import { SORT_ITEM_NAME, SORT_DIRECTION_ASC } from '../constants';
import { formatGroups } from '../utils';
export default {
@ -57,32 +58,25 @@ export default {
required: false,
default: DEFAULT_PER_PAGE,
},
search: {
type: String,
required: false,
default: '',
},
sortName: {
type: String,
required: false,
default: SORT_ITEM_NAME.value,
},
sortDirection: {
type: String,
required: false,
default: SORT_DIRECTION_ASC,
},
},
data() {
const baseData = {
groups: {},
};
if (!this.startCursor && !this.endCursor) {
return {
...baseData,
pagination: {
first: this.perPage,
after: null,
last: null,
before: null,
},
};
}
return {
...baseData,
pagination: {
first: this.endCursor && this.perPage,
after: this.endCursor,
last: this.startCursor && this.perPage,
before: this.startCursor,
},
groups: {},
};
},
apollo: {
@ -91,6 +85,8 @@ export default {
variables() {
return {
id: this.organizationGid,
search: this.search,
sort: this.sort,
...this.pagination,
};
},
@ -104,13 +100,6 @@ export default {
pageInfo,
};
},
result() {
this.$emit('page-change', {
endCursor: this.pagination.after,
startCursor: this.pagination.before,
hasPreviousPage: this.pageInfo.hasPreviousPage,
});
},
error(error) {
createAlert({ message: this.$options.i18n.errorMessage, error, captureError: true });
},
@ -123,6 +112,26 @@ export default {
pageInfo() {
return this.groups.pageInfo || {};
},
pagination() {
if (!this.startCursor && !this.endCursor) {
return {
first: this.perPage,
after: null,
last: null,
before: null,
};
}
return {
first: this.endCursor && this.perPage,
after: this.endCursor,
last: this.startCursor && this.perPage,
before: this.startCursor,
};
},
sort() {
return `${this.sortName}_${this.sortDirection}`.toUpperCase();
},
isLoading() {
return this.$apollo.queries.groups.loading;
},
@ -147,20 +156,16 @@ export default {
},
methods: {
onNext(endCursor) {
this.pagination = {
first: this.perPage,
after: endCursor,
last: null,
before: null,
};
this.$emit('page-change', {
endCursor,
startCursor: null,
});
},
onPrev(startCursor) {
this.pagination = {
first: null,
after: null,
last: this.perPage,
before: startCursor,
};
this.$emit('page-change', {
endCursor: null,
startCursor,
});
},
},
};

View File

@ -4,6 +4,7 @@ import { s__, __ } from '~/locale';
import ProjectsList from '~/vue_shared/components/projects_list/projects_list.vue';
import { DEFAULT_PER_PAGE } from '~/api';
import { createAlert } from '~/alert';
import { SORT_ITEM_NAME, SORT_DIRECTION_ASC } from '../constants';
import projectsQuery from '../graphql/queries/projects.query.graphql';
import { formatProjects } from '../utils';
@ -61,32 +62,25 @@ export default {
required: false,
default: DEFAULT_PER_PAGE,
},
search: {
type: String,
required: false,
default: '',
},
sortName: {
type: String,
required: false,
default: SORT_ITEM_NAME.value,
},
sortDirection: {
type: String,
required: false,
default: SORT_DIRECTION_ASC,
},
},
data() {
const baseData = {
projects: {},
};
if (!this.startCursor && !this.endCursor) {
return {
...baseData,
pagination: {
first: this.perPage,
after: null,
last: null,
before: null,
},
};
}
return {
...baseData,
pagination: {
first: this.endCursor && this.perPage,
after: this.endCursor,
last: this.startCursor && this.perPage,
before: this.startCursor,
},
projects: {},
};
},
apollo: {
@ -127,6 +121,23 @@ export default {
pageInfo() {
return this.projects.pageInfo || {};
},
pagination() {
if (!this.startCursor && !this.endCursor) {
return {
first: this.perPage,
after: null,
last: null,
before: null,
};
}
return {
first: this.endCursor && this.perPage,
after: this.endCursor,
last: this.startCursor && this.perPage,
before: this.startCursor,
};
},
isLoading() {
return this.$apollo.queries.projects.loading;
},
@ -151,20 +162,16 @@ export default {
},
methods: {
onNext(endCursor) {
this.pagination = {
first: this.perPage,
after: endCursor,
last: null,
before: null,
};
this.$emit('page-change', {
endCursor,
startCursor: null,
});
},
onPrev(startCursor) {
this.pagination = {
first: null,
after: null,
last: this.perPage,
before: startCursor,
};
this.$emit('page-change', {
endCursor: null,
startCursor,
});
},
},
};

View File

@ -1,5 +1,5 @@
import { formValidators } from '@gitlab/ui/dist/utils';
import { s__ } from '~/locale';
import { s__, __ } from '~/locale';
export const FORM_FIELD_NAME = 'name';
export const FORM_FIELD_ID = 'id';
@ -26,3 +26,21 @@ export const FORM_FIELD_DESCRIPTION_VALIDATORS = [
export const QUERY_PARAM_START_CURSOR = 'start_cursor';
export const QUERY_PARAM_END_CURSOR = 'end_cursor';
export const SORT_DIRECTION_ASC = 'asc';
export const SORT_DIRECTION_DESC = 'desc';
export const SORT_ITEM_NAME = {
value: 'name',
text: __('Name'),
};
export const SORT_ITEM_CREATED_AT = {
value: 'created_at',
text: __('Created'),
};
export const SORT_ITEM_UPDATED_AT = {
value: 'updated_at',
text: __('Updated'),
};

View File

@ -1,6 +1,8 @@
#import "~/graphql_shared/fragments/page_info.fragment.graphql"
query getOrganizationGroups(
$id: OrganizationsOrganizationID!
$search: String
$sort: OrganizationGroupSort
$first: Int
$last: Int
$before: String
@ -8,7 +10,14 @@ query getOrganizationGroups(
) {
organization(id: $id) {
id
groups(first: $first, last: $last, before: $before, after: $after) {
groups(
first: $first
search: $search
sort: $sort
last: $last
before: $before
after: $after
) {
nodes {
id
fullName

View File

@ -39,10 +39,9 @@ export const formatGroups = (groups) =>
export const onPageChange = ({
startCursor,
endCursor,
hasPreviousPage,
routeQuery: { start_cursor, end_cursor, ...routeQuery },
}) => {
if (startCursor && hasPreviousPage) {
if (startCursor) {
return {
...routeQuery,
[QUERY_PARAM_START_CURSOR]: startCursor,

View File

@ -6,7 +6,9 @@ import { TYPE_ALERT, TYPE_ISSUE, TYPE_MERGE_REQUEST } from '~/issues/constants';
import { __, n__ } from '~/locale';
import UserSelect from '~/vue_shared/components/user_select/user_select.vue';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { ISSUE_MR_CHANGE_ASSIGNEE } from '~/behaviors/shortcuts/keybindings';
import { keysFor, ISSUE_MR_CHANGE_ASSIGNEE } from '~/behaviors/shortcuts/keybindings';
import { shouldDisableShortcuts } from '~/behaviors/shortcuts/shortcuts_toggle';
import { sanitize } from '~/lib/dompurify';
import { assigneesQueries } from '../../queries/constants';
import SidebarEditableItem from '../sidebar_editable_item.vue';
import SidebarAssigneesRealtime from './assignees_realtime.vue';
@ -158,10 +160,17 @@ export default {
return this.issuable?.author;
},
assigneeShortcutDescription() {
return ISSUE_MR_CHANGE_ASSIGNEE.description;
return shouldDisableShortcuts() ? null : ISSUE_MR_CHANGE_ASSIGNEE.description;
},
assigneeShortcutKey() {
return ISSUE_MR_CHANGE_ASSIGNEE.defaultKeys[0];
return shouldDisableShortcuts() ? null : keysFor(ISSUE_MR_CHANGE_ASSIGNEE)[0];
},
assigneeTooltip() {
const description = this.assigneeShortcutDescription;
const key = this.assigneeShortcutKey;
return shouldDisableShortcuts()
? null
: sanitize(`${description} <kbd class="flat gl-ml-1" aria-hidden=true>${key}</kbd>`);
},
},
watch: {
@ -253,7 +262,7 @@ export default {
:loading="isSettingAssignees"
:initial-loading="isAssigneesLoading"
:title="assigneeText"
:edit-tooltip="`${assigneeShortcutDescription} <kbd class='flat ml-1' aria-hidden=true>${assigneeShortcutKey}</kbd>`"
:edit-tooltip="assigneeTooltip"
:edit-aria-label="assigneeShortcutDescription"
:edit-keyshortcuts="assigneeShortcutKey"
:is-dirty="isDirty"

View File

@ -7,7 +7,9 @@ import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { TYPE_EPIC, TYPE_ISSUE, TYPE_MERGE_REQUEST, TYPE_TEST_CASE } from '~/issues/constants';
import { __ } from '~/locale';
import { ISSUABLE_CHANGE_LABEL } from '~/behaviors/shortcuts/keybindings';
import { keysFor, ISSUABLE_CHANGE_LABEL } from '~/behaviors/shortcuts/keybindings';
import { shouldDisableShortcuts } from '~/behaviors/shortcuts/shortcuts_toggle';
import { sanitize } from '~/lib/dompurify';
import { issuableLabelsQueries } from '../../../queries/constants';
import SidebarEditableItem from '../../sidebar_editable_item.vue';
import { DEBOUNCE_DROPDOWN_DELAY, VARIANT_SIDEBAR } from './constants';
@ -161,10 +163,17 @@ export default {
return this.issuableSupportsLockOnMerge || this.issuable?.supportsLockOnMerge;
},
labelShortcutDescription() {
return ISSUABLE_CHANGE_LABEL.description;
return shouldDisableShortcuts() ? null : ISSUABLE_CHANGE_LABEL.description;
},
labelShortcutKey() {
return ISSUABLE_CHANGE_LABEL.defaultKeys[0];
return shouldDisableShortcuts() ? null : keysFor(ISSUABLE_CHANGE_LABEL)[0];
},
labelTooltip() {
const description = this.labelShortcutDescription;
const key = this.labelShortcutKey;
return shouldDisableShortcuts()
? null
: sanitize(`${description} <kbd class="flat gl-ml-1" aria-hidden=true>${key}</kbd>`);
},
},
apollo: {
@ -382,7 +391,7 @@ export default {
<sidebar-editable-item
ref="editable"
:title="__('Labels')"
:edit-tooltip="`${labelShortcutDescription} <kbd class='flat ml-1' aria-hidden=true>${labelShortcutKey}</kbd>`"
:edit-tooltip="labelTooltip"
:edit-aria-label="labelShortcutDescription"
:edit-keyshortcuts="labelShortcutKey"
:loading="isLoading"

View File

@ -268,11 +268,6 @@ export default {
)
);
},
autoMergeStateVisible() {
if (!window.gon?.features?.mergeBlockedComponent) return false;
return this.mr.state === 'autoMergeEnabled' || this.mr.machineValue === 'AUTO_MERGE';
},
},
watch: {
'mr.machineValue': {
@ -581,13 +576,15 @@ export default {
</div>
<div class="mr-widget-section" data-testid="mr-widget-content">
<mr-widget-auto-merge-enabled
v-if="autoMergeStateVisible"
:mr="mr"
:service="service"
class="gl-border-b-1 gl-border-b-solid gl-border-gray-100"
/>
<merge-checks v-if="mergeBlockedComponentEnabled" :mr="mr" :service="service" />
<template v-if="mergeBlockedComponentEnabled">
<mr-widget-auto-merge-enabled
v-if="mr.autoMergeEnabled"
:mr="mr"
:service="service"
class="gl-border-b-1 gl-border-b-solid gl-border-gray-100"
/>
<merge-checks :mr="mr" :service="service" />
</template>
<component :is="componentName" v-else :mr="mr" :service="service" />
<ready-to-merge
v-if="mr.commitsCount"

View File

@ -39,7 +39,7 @@ export default function deviseState() {
return stateKey.shaMismatch;
}
if (this.autoMergeEnabled && !this.mergeError) {
return stateKey.autoMergeEnabled;
return window.gon?.features?.mergeBlockedComponent ? null : stateKey.autoMergeEnabled;
}
if (
this.detailedMergeStatus === DETAILED_MERGE_STATUS.MERGEABLE ||

View File

@ -11,7 +11,8 @@ import { s__, __ } from '~/locale';
import { visitUrl } from '~/lib/utils/url_utility';
import Tracking from '~/tracking';
import ConfirmForkModal from '~/vue_shared/components/web_ide/confirm_fork_modal.vue';
import { GO_TO_PROJECT_WEBIDE } from '~/behaviors/shortcuts/keybindings';
import { keysFor, GO_TO_PROJECT_WEBIDE } from '~/behaviors/shortcuts/keybindings';
import { shouldDisableShortcuts } from '~/behaviors/shortcuts/shortcuts_toggle';
import { KEY_EDIT, KEY_WEB_IDE, KEY_GITPOD, KEY_PIPELINE_EDITOR } from './constants';
export const i18n = {
@ -198,8 +199,11 @@ export default {
...handleOptions,
};
},
shortcutsDisabled() {
return shouldDisableShortcuts();
},
webIdeActionShortcutKey() {
return GO_TO_PROJECT_WEBIDE.defaultKeys[0];
return keysFor(GO_TO_PROJECT_WEBIDE)[0];
},
webIdeActionText() {
if (this.webIdeText) {
@ -368,7 +372,9 @@ export default {
<span data-testid="action-primary-text" class="gl-font-weight-bold">{{
action.text
}}</span>
<kbd v-if="action.shortcut" class="flat">{{ action.shortcut }}</kbd>
<kbd v-if="action.shortcut && !shortcutsDisabled" class="flat">{{
action.shortcut
}}</kbd>
</span>
<span data-testid="action-secondary-text" class="gl-font-sm gl-text-secondary">
{{ action.secondaryText }}

View File

@ -1,5 +1,5 @@
.whats-new-drawer {
@include gl-shadow-none;
box-shadow: none;
overflow-y: hidden;
width: 500px;

View File

@ -478,7 +478,7 @@ span.idiff {
overflow: hidden;
.tree-list-parent::before {
@include gl-content-empty;
content: '';
position: absolute;
z-index: 1;
pointer-events: none;

View File

@ -296,7 +296,7 @@ label {
border-color: var(--gray-700, $gray-700);
input {
@include gl-shadow-none;
box-shadow: none;
}
}

View File

@ -51,7 +51,7 @@
font-family: $monospace-font;
white-space: nowrap;
display: flex;
@include gl-justify-content-end;
justify-content: flex-end;
i,
svg {
@ -115,12 +115,12 @@ td.line-numbers {
.line-numbers:not(.line-links) a:focus-within::before,
.line-links:hover a::before,
.line-links:focus-within a::before {
@include gl-visibility-visible;
visibility: visible;
}
.file-line-num {
@include gl-justify-content-end;
justify-content: flex-end;
flex-grow: 1;
padding-right: $gl-spacing-scale-3;
}

View File

@ -48,7 +48,7 @@
// - app/helpers/ci/status_helper.rb
.ci-icon-gl-icon-wrapper {
border-radius: $gl-border-radius-full;
@include gl-line-height-0;
line-height: 0;
}
// Makes the borderless CI icons appear slightly bigger than the default 16px.

View File

@ -14,11 +14,11 @@
// GitLab UI's label component
.gl-label,
.gl-label-sm {
@include gl-vertical-align-bottom;
vertical-align: bottom;
&:focus:active {
@include gl-reset-color;
@include gl-shadow-none;
color: inherit;
box-shadow: none;
outline: none;
}
@ -51,6 +51,6 @@
}
.md code {
@include gl-vertical-align-bottom;
vertical-align: bottom;
}
}

View File

@ -134,8 +134,8 @@ body {
.gl--flex-full {
display: flex;
@include gl-align-items-stretch;
@include gl-overflow-hidden;
align-items: stretch;
overflow: hidden;
}
.fullscreen-layout {

View File

@ -125,7 +125,7 @@
}
.suggestions.md > .markdown-code-block {
@include gl-static;
position: static;
}
.md-suggestion-header {

View File

@ -47,7 +47,7 @@
.dropdown-menu {
width: 100%;
@include gl-max-w-none;
max-width: none;
}
.gl-dropdown-item-check-icon {

View File

@ -1,7 +1,7 @@
[data-editor-loading] {
position: relative;
display: flex;
@include gl-justify-content-center;
justify-content: center;
align-items: center;
z-index: 0;
@ -29,7 +29,7 @@
display: flex;
.md {
@include gl-overflow-scroll;
overflow: scroll;
padding-left: $gl-spacing-scale-6;
padding-right: $gl-spacing-scale-6;
padding-top: $gl-spacing-scale-4;
@ -38,7 +38,7 @@
}
.gl-source-editor {
@include gl-order-n1;
order: -1;
border-radius: 0 0 $border-radius-default $border-radius-default;
}
}
@ -70,11 +70,11 @@
.margin-view-overlays {
.line-numbers {
display: flex;
@include gl-justify-content-end;
justify-content: flex-end;
position: relative;
&::before {
@include gl-visibility-hidden;
visibility: hidden;
align-self: center;
background-color: $gray-400;
margin-right: $gl-spacing-scale-2;
@ -88,16 +88,16 @@
}
&:hover {
@include gl-text-decoration-underline;
text-decoration: underline;
cursor: pointer !important;
}
&:hover::before {
@include gl-visibility-visible;
visibility: visible;
}
&:focus::before {
@include gl-visibility-visible;
visibility: visible;
outline: auto;
}
@ -112,11 +112,11 @@
// Remove custom focus from element
.inputarea {
@include gl-shadow-none;
box-shadow: none;
}
}
.active-line-text {
background-color: $orange-600;
@include gl-opacity-3;
opacity: 0.3;
}

View File

@ -245,7 +245,7 @@ $super-sidebar-transition-hint-duration: $super-sidebar-transition-duration / 4;
.user-bar-dropdown-toggle {
padding: $gl-spacing-scale-2;
@include gl-border-none;
border-style: none;
&[aria-expanded='true'] {
background-color: var(--super-sidebar-user-bar-button-hover-bg);

View File

@ -146,7 +146,7 @@
p {
line-height: 1.5;
@include gl-reset-color;
color: inherit;
&:last-child {
margin: 0;

View File

@ -112,7 +112,7 @@
@mixin line-link($color, $icon) {
&::before {
@include gl-visibility-hidden;
visibility: hidden;
align-self: center;
margin-right: $gl-spacing-scale-1;
width: $gl-spacing-scale-5;

View File

@ -8,6 +8,10 @@ Shared styles for system note dot and icon styles used for MR, Issue, Work Item
margin-left: 12px;
margin-right: 8px;
border: 2px solid var(--gray-50, $gray-50);
.gl-dark .modal-body & {
border-color: var(--gray-100, $gray-100);
}
}
.system-note-icon {

View File

@ -8,7 +8,7 @@
}
.toggle-sidebar-mobile-button {
@include gl-right-0;
right: 0;
}
.dropdown-menu-toggle {

View File

@ -143,7 +143,7 @@
.user-avatar-link {
&:not(:last-of-type) {
@include gl-mr-n3;
margin-right: -$gl-spacing-scale-3;
}
}

View File

@ -85,7 +85,7 @@
top: $calc-application-header-height;
@include media-breakpoint-up(lg) {
@include gl-border-l-0;
border-left-width: 0;
}
&.right-sidebar-collapsed {

View File

@ -78,7 +78,7 @@ $t-gray-a-16-design-pin: rgba($black, 0.16);
}
&.inactive {
@include gl-opacity-5;
opacity: 0.5;
&:hover {
opacity: 1;

View File

@ -3,7 +3,7 @@
.file-editor {
#editor,
.editor {
@include gl-border-0;
border-width: 0;
margin: 0;
padding: 0;
position: relative;
@ -12,7 +12,7 @@
.editor-loading-content {
height: 100%;
@include gl-border-0;
border-width: 0;
}
}

View File

@ -28,7 +28,7 @@ $stroke-size: 1px;
.escalation-rule-row {
@media (max-width: $breakpoint-lg) {
@include gl-flex-wrap;
flex-wrap: wrap;
}
}

View File

@ -12,7 +12,7 @@
color: var(--gray-500, $gray-500);
> .gl-tab-counter-badge {
@include gl-reset-color;
color: inherit;
font-size: $gl-font-size-sm;
background-color: var(--gray-50, $gray-50);
}
@ -21,7 +21,7 @@
@include media-breakpoint-down(xs) {
.list-header {
@include gl-flex-direction-column-reverse;
flex-direction: column-reverse;
}
.create-incident-button {

View File

@ -58,7 +58,7 @@
.timeline-entry:not(:last-child) {
.timeline-event-border {
padding-bottom: $gl-spacing-scale-3;
@include gl-border-gray-50;
border-color: $gray-50;
border-width: $gl-border-size-1;
border-bottom-style: solid;
}
@ -68,7 +68,7 @@
.timeline-entry:last-child,
.create-timeline-event {
.timeline-event-bottom-border {
@include gl-border-b;
border-bottom: solid $gl-border-size-1 $border-color;
padding-top: $gl-spacing-scale-5;
}
}

View File

@ -93,7 +93,7 @@
padding-left: $gl-spacing-scale-1;
padding-right: $gl-spacing-scale-2;
height: $gl-spacing-scale-5;
@include gl-min-w-5;
min-width: $gl-spacing-scale-5;
line-height: 14px;
}
}

View File

@ -343,7 +343,7 @@ $comparison-empty-state-height: 62px;
}
.survey-slide-up-enter-active {
@include gl-transition-slow;
transition: all $gl-transition-duration-slow ease;
}
.mr-compare-dropdown {

View File

@ -847,13 +847,13 @@ $tabs-holder-z-index: 250;
}
.mr-widget-extension-icon::before {
@include gl-content-empty;
content: '';
position: absolute;
@include gl-left-50p;
@include gl-top-half;
@include gl-opacity-3;
left: 50%;
top: 50%;
opacity: 0.3;
border-style: solid;
@include gl-border-4;
border-width: $gl-border-size-4;
border-radius: $gl-border-radius-full;
width: 24px;
@ -862,11 +862,11 @@ $tabs-holder-z-index: 250;
}
.mr-widget-extension-icon::after {
@include gl-content-empty;
content: '';
position: absolute;
border-radius: $gl-border-radius-full;
@include gl-left-50p;
@include gl-top-half;
left: 50%;
top: 50%;
width: 16px;
height: 16px;
@ -981,7 +981,8 @@ $tabs-holder-z-index: 250;
.detail-page-description,
.merge-request-tabs-container {
&.is-merge-request {
@include gl-mx-auto;
margin-left: auto;
margin-right: auto;
max-width: $fixed-layout-width - ($container-margin-xl * 2);
}
}
@ -989,8 +990,9 @@ $tabs-holder-z-index: 250;
.container-fluid.diffs-container-limited {
.flash-container {
@include gl-mx-auto;
@include gl-max-w-container-xl;
margin-left: auto;
margin-right: auto;
max-width: $container-xl;
padding-left: $gl-spacing-scale-5;
padding-right: $gl-spacing-scale-5;
}
@ -1042,7 +1044,7 @@ $tabs-holder-z-index: 250;
.mr-ready-merge-related-links a,
.mr-widget-merge-details a,
.mr-widget-author {
@include gl-text-decoration-underline;
text-decoration: underline;
&:hover,
&:focus {
@ -1066,28 +1068,28 @@ $tabs-holder-z-index: 250;
}
.mr-widget-status-icon-level-1::before {
@include gl-content-empty;
content: '';
position: absolute;
left: 0;
top: 0;
@include gl-bottom-0;
@include gl-right-0;
@include gl-opacity-3;
bottom: 0;
right: 0;
opacity: 0.3;
border-radius: $gl-border-radius-full;
border-style: solid;
@include gl-border-4;
border-width: $gl-border-size-4;
}
.mr-widget-status-icon-level-1::after {
@include gl-content-empty;
content: '';
position: absolute;
border-radius: $gl-border-radius-full;
border-style: solid;
@include gl-border-4;
@include gl-left-2;
@include gl-right-2;
@include gl-top-2;
@include gl-bottom-2;
border-width: $gl-border-size-4;
left: $gl-spacing-scale-2;
right: $gl-spacing-scale-2;
top: $gl-spacing-scale-2;
bottom: $gl-spacing-scale-2;
}
.memory-graph-container {
@ -1202,11 +1204,11 @@ $tabs-holder-z-index: 250;
.discussion-collapsible {
margin: 0;
@include gl-border-l-0;
@include gl-border-r-0;
border-left-width: 0;
border-right-width: 0;
border-bottom-width: 0;
@include gl-rounded-top-left-none;
@include gl-rounded-top-right-none;
border-top-left-radius: 0;
border-top-right-radius: 0;
}
}

View File

@ -66,7 +66,7 @@ $column-right-gradient: linear-gradient(to right, $gradient-dark-gray 0%, $gradi
.list-section .details-cell {
&::after {
height: 100%;
@include gl-content-empty;
content: '';
position: absolute;
top: 0;
right: -$grid-size;

View File

@ -6,7 +6,7 @@
}
.dashboard-card {
@include gl-cursor-grab;
cursor: grab;
&-header {
&-warning {

View File

@ -271,15 +271,15 @@
.projects-list {
@include basic-list;
@include gl-display-table;
display: table;
.project-row {
@include gl-display-table-row;
display: table-row;
}
.project-cell {
@include gl-display-table-cell;
@include gl-vertical-align-top;
display: table-cell;
vertical-align: top;
padding-top: $gl-spacing-scale-4;
padding-bottom: $gl-spacing-scale-4;
border-bottom: 1px solid var(--gray-50, $gray-50);
@ -287,7 +287,7 @@
.project-row:last-of-type {
.project-cell {
@include gl-border-none;
border-style: none;
}
}
@ -303,7 +303,7 @@
}
.controls {
@include gl-line-height-42;
line-height: $gl-line-height-42;
}
}
@ -323,7 +323,7 @@
&:not(.compact) {
.controls {
@include media-breakpoint-up(lg) {
@include gl-justify-content-start;
justify-content: flex-start;
padding-right: $gl-spacing-scale-9;
&:not(.with-pipeline-status) {
@ -339,7 +339,7 @@
.project-details {
p,
.commit-row-message {
@include gl-white-space-normal;
white-space: normal;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
/* stylelint-disable-next-line value-no-vendor-prefix */
@ -350,7 +350,7 @@
.controls {
@include media-breakpoint-up(sm) {
@include gl-justify-content-end;
justify-content: flex-end;
}
.icon-wrapper {
@ -376,8 +376,8 @@
&.compact {
.description {
width: 100%;
@include gl-display-table;
@include gl-table-layout-fixed;
display: table;
table-layout: fixed;
}
.avatar-container {
@ -409,7 +409,7 @@
@include media-breakpoint-down(md) {
.updated-note {
margin-top: $gl-spacing-scale-3;
@include gl-text-right;
text-align: right;
}
}
@ -431,7 +431,7 @@
@include media-breakpoint-down(xs) {
.updated-note {
margin-top: 0;
@include gl-text-left;
text-align: left;
}
}
}

View File

@ -325,7 +325,7 @@ $language-filter-max-height: 20rem;
pre {
padding: 0; // This overrides the existing style that will add space between each line.
.line {
@include gl-word-break-word;
word-break: break-word;
white-space: break-spaces;
}
}

View File

@ -29,7 +29,7 @@
&.todo-pending.done-reversible {
.todo-item,
.todo-timestamp {
@include gl-opacity-5;
opacity: 0.5;
}
.todo-avatar {
@ -38,8 +38,8 @@
&:hover {
border-top-width: $gl-border-size-1;
@include gl-border-t-transparent;
@include gl-border-t-solid;
border-top-color: transparent;
border-top-style: solid;
}
}
}
@ -49,12 +49,12 @@
.todo-label a::before {
// Make area of the todo item clickable by expanding the area around the todo link
@include gl-content-empty;
content: '';
position: absolute;
left: 0;
@include gl-right-0;
right: 0;
top: 0;
@include gl-bottom-0;
bottom: 0;
z-index: 9;
}
}
@ -64,9 +64,9 @@
@include media-breakpoint-up(sm) {
margin-right: 0;
@include gl-text-overflow-ellipsis;
@include gl-white-space-nowrap;
@include gl-overflow-hidden;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
}
@ -82,7 +82,7 @@
padding-left: $gl-spacing-scale-1;
padding-right: $gl-spacing-scale-1;
margin: 0;
@include gl-border-0;
border-width: 0;
border-radius: $gl-border-radius-base;
display: inline-flex;
background: var(--gray-50, $gray-50);
@ -106,7 +106,7 @@
.todo-actions {
position: absolute;
@include gl-right-0;
right: 0;
@include media-breakpoint-up(sm) {
position: relative;

View File

@ -135,10 +135,10 @@ ul.related-merge-requests > li gl-emoji {
@include media-breakpoint-down(xs) {
.btn.btn-confirm {
@include gl-justify-content-start;
justify-content: flex-start;
&.dropdown-toggle {
@include gl-flex-grow-0;
flex-grow: 0;
}
}
}

View File

@ -34,6 +34,10 @@ $system-note-icon-m-left: $avatar-m-left + $icon-size-diff / $avatar-m-ratio;
.main-notes-list::before {
background: var(--gray-50, $gray-50);
.gl-dark .modal-body & {
background: var(--gray-100, $gray-100);
}
}
.timeline-entry:not(.draft-note):last-child::before {
@ -42,6 +46,10 @@ $system-note-icon-m-left: $avatar-m-left + $icon-size-diff / $avatar-m-ratio;
.gl-dark & {
background: var(--gray-10);
}
.gl-dark .modal-body & {
background: var(--gray-50, $gray-50);
}
&.note-comment {
top: 30px;

View File

@ -130,7 +130,7 @@
}
.gl-avatar {
@include gl-border-none;
border-style: none;
box-shadow: inset 0 0 0 1px rgba($gray-950, $gl-avatar-border-opacity);
}

View File

@ -0,0 +1,20 @@
.gl-single-stat.gl-display-flex.gl-flex-direction-column.gl-p-2
.gl-display-flex.gl-align-items-center.gl-text-gray-700.gl-mb-2
- if title_icon?
= sprite_icon(@title_icon, css_class: 'gl-mr-2')
%span.gl-font-base.gl-font-weight-normal{ data: { testid: 'title-text' } }
= @title
.gl-single-stat-content.gl-display-flex.gl-align-items-baseline.gl-font-weight-bold.gl-text-gray-900
%span.gl-single-stat-number.gl-line-height-1{ class: unit_class, data: { testid: 'displayValue' } }
%span{ data: { testid: 'non-animated-value' } }
= @stat_value
- if unit?
%span.gl-font-sm.gl-mx-2.gl-transition-medium.gl-opacity-10{ data: { testid: 'unit' } }
= @unit
- if meta_icon? && !meta_text?
= sprite_icon(@meta_icon, css_class: @text_color)
- elsif meta_text?
= render Pajamas::BadgeComponent.new(@meta_text,
variant: @variant,
icon: @meta_icon,
data: { testid: 'meta-badge' })

View File

@ -0,0 +1,56 @@
# frozen_string_literal: true
module Pajamas
class SingleStatComponent < Pajamas::Component
# @param [String] title
# @param [String] stat_value
# @param [String] unit
# @param [String] title_icon
# @param [String] meta_text
# @param [String] meta_icon
# @param [Symbol] variant
def initialize(
title: nil,
stat_value: nil,
unit: nil,
title_icon: nil,
meta_text: nil,
meta_icon: nil,
text_color: nil,
variant: :muted
)
@title = title
@stat_value = stat_value
@unit = unit
@title_icon = title_icon.to_s.presence
@meta_text = meta_text
@meta_icon = meta_icon
@text_color = text_color
@variant = filter_attribute(variant.to_sym, Pajamas::BadgeComponent::VARIANT_OPTIONS, default: :muted)
end
private
delegate :sprite_icon, to: :helpers
def unit_class
"gl-mr-2" unless unit?
end
def unit?
@unit
end
def title_icon?
@title_icon
end
def meta_icon?
@meta_icon
end
def meta_text?
@meta_text
end
end
end

View File

@ -5,7 +5,11 @@ module PreviewMarkdown
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def preview_markdown
result = PreviewMarkdownService.new(@project, current_user, markdown_service_params).execute
result = PreviewMarkdownService.new(
container: resource_parent,
current_user: current_user,
params: markdown_service_params
).execute
render json: {
body: view_context.markdown(result[:text], markdown_context_params),
@ -19,6 +23,10 @@ module PreviewMarkdown
private
def resource_parent
@project
end
def projects_filter_params
{
issuable_reference_expansion_enabled: true,

View File

@ -389,9 +389,9 @@ class GroupsController < Groups::ApplicationController
end
end
override :markdown_service_params
def markdown_service_params
params.merge(group: group)
override :resource_parent
def resource_parent
group
end
override :has_project_list?
@ -412,4 +412,4 @@ class GroupsController < Groups::ApplicationController
end
end
GroupsController.prepend_mod_with('GroupsController')
GroupsController.prepend_mod

View File

@ -165,7 +165,7 @@ class Projects::MergeRequests::DraftsController < Projects::MergeRequests::Appli
def render_draft_note(note)
params = { target_id: merge_request.id, target_type: 'MergeRequest', text: note.note }
result = PreviewMarkdownService.new(@project, current_user, params).execute
result = PreviewMarkdownService.new(container: @project, current_user: current_user, params: params).execute
markdown_params = { markdown_engine: result[:markdown_engine], issuable_reference_expansion_enabled: true }
note.rendered_note = view_context.markdown(result[:text], markdown_params)

View File

@ -274,7 +274,7 @@ module MergeRequestsHelper
link_to_author = link_to_member(project, merge_request.author, size: 24, extra_class: 'gl-font-weight-bold gl-mr-2', avatar: false)
copy_action_description = _('Copy branch name')
copy_action_shortcut = 'b'
copy_button_title = "#{copy_action_description} <kbd class='flat ml-1'>#{copy_action_shortcut}</kbd>"
copy_button_title = "#{copy_action_description} <kbd class='flat ml-1' aria-hidden=true>#{copy_action_shortcut}</kbd>"
copy_button = clipboard_button(text: merge_request.source_branch, title: copy_button_title, aria_keyshortcuts: copy_action_shortcut, aria_label: copy_action_description, class: 'gl-display-none! gl-md-display-inline-block! js-source-branch-copy')
target_branch = link_to merge_request.target_branch, project_tree_path(merge_request.target_project, merge_request.target_branch), title: merge_request.target_branch, class: 'ref-container gl-display-inline-block gl-text-truncate gl-max-w-26 gl-mx-2'

View File

@ -23,11 +23,10 @@ module Ci
scope :order_by_created_at_asc, -> { reorder(created_at: :asc) }
scope :order_by_created_at_desc, -> { reorder(created_at: :desc) }
# After we denormalize the `released_at` column, we won't need to use `joins(:release)` and keyset_order_*
scope :order_by_released_at_asc, -> { joins(:release).keyset_order_by_released_at_asc }
scope :order_by_released_at_desc, -> { joins(:release).keyset_order_by_released_at_desc }
scope :order_by_released_at_asc, -> { reorder(released_at: :asc) }
scope :order_by_released_at_desc, -> { reorder(released_at: :desc) }
delegate :sha, :released_at, :author_id, to: :release
delegate :sha, :author_id, to: :release
before_create :sync_with_release
after_destroy :update_catalog_resource
@ -52,12 +51,9 @@ module Ci
catalog_resources_table = Ci::Catalog::Resource.arel_table
catalog_resources_id_list = catalog_resources.map { |resource| "(#{resource.id})" }.join(',')
# We need to use an alias for the `releases` table here so that it does not
# conflict with `joins(:release)` in the `order_by_released_at_*` scope.
join_query = Ci::Catalog::Resources::Version
.where(catalog_resources_table[:id].eq(arel_table[:catalog_resource_id]))
.joins("INNER JOIN releases AS rel ON rel.id = #{table_name}.release_id")
.order(Arel.sql('rel.released_at DESC'))
.order_by_released_at_desc
.limit(1)
Ci::Catalog::Resources::Version
@ -65,46 +61,6 @@ module Ci
.joins("INNER JOIN LATERAL (#{join_query.to_sql}) #{table_name} ON TRUE")
end
def keyset_order_by_released_at_asc
keyset_order = Gitlab::Pagination::Keyset::Order.build([
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: :released_at,
column_expression: Release.arel_table[:released_at],
order_expression: Release.arel_table[:released_at].asc,
nullable: :not_nullable,
distinct: false
),
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: :id,
order_expression: Release.arel_table[:id].asc,
nullable: :not_nullable,
distinct: true
)
])
reorder(keyset_order)
end
def keyset_order_by_released_at_desc
keyset_order = Gitlab::Pagination::Keyset::Order.build([
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: :released_at,
column_expression: Release.arel_table[:released_at],
order_expression: Release.arel_table[:released_at].desc,
nullable: :not_nullable,
distinct: false
),
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: :id,
order_expression: Release.arel_table[:id].desc,
nullable: :not_nullable,
distinct: true
)
])
reorder(keyset_order)
end
def order_by(order)
case order.to_s
when 'created_asc' then order_by_created_at_asc

View File

@ -17,7 +17,11 @@ module Ci
@removed_count = 0
@locked_count = 0
@start_at = Time.current
@loop_limit = Feature.enabled?(:ci_job_artifacts_backlog_large_loop_limit) ? LARGE_LOOP_LIMIT : LOOP_LIMIT
@loop_limit = if Feature.enabled?(:ci_job_artifacts_backlog_large_loop_limit, type: :ops)
LARGE_LOOP_LIMIT
else
LOOP_LIMIT
end
end
def execute
@ -35,9 +39,9 @@ module Ci
unknown_status_build_ids = safely_ordered_ci_job_artifacts_locked_unknown_relation.pluck_job_id.uniq
locked_pipe_build_ids = ::Ci::Build
.with_pipeline_locked_artifacts
.id_in(unknown_status_build_ids)
.pluck_primary_key
.with_pipeline_locked_artifacts
.id_in(unknown_status_build_ids)
.pluck_primary_key
@locked_count += update_unknown_artifacts(locked_pipe_build_ids, Ci::JobArtifact.lockeds[:artifacts_locked])

View File

@ -2,6 +2,8 @@
module Groups
class CreateService < Groups::BaseService
include Organization::CurrentOrganization
def initialize(user, params = {})
@current_user = user
@params = params.dup
@ -15,6 +17,8 @@ module Groups
@group = Group.new(params.except(*::NamespaceSetting.allowed_namespace_settings_params))
set_organization unless @params[:organization_id]
@group.build_namespace_settings
handle_namespace_settings
@ -96,8 +100,6 @@ module Groups
# We are unsetting this here to match behavior of invalid parent_id above and protect against possible
# committing to the database of a value that isn't allowed.
@group.organization = nil
message = s_("CreateGroup|You don't have permission to create a group in the provided organization.")
@group.errors.add(:organization_id, message)
return false
end
@ -105,6 +107,25 @@ module Groups
true
end
def can_create_group_in_organization?
return true if can?(current_user, :create_group, @group.organization)
message = s_("CreateGroup|You don't have permission to create a group in the provided organization.")
@group.errors.add(:organization_id, message)
false
end
def matches_parent_organization?
return true if @group.parent_id.blank?
return true if @group.parent.organization_id == @group.organization_id
message = s_("CreateGroup|You can't create a group in a different organization than the parent group.")
@group.errors.add(:organization_id, message)
false
end
def organization_setting_valid?
# we check for the params presence explicitly since:
# 1. We have a default organization_id at db level set and organization exists and may not have the entry
@ -115,7 +136,7 @@ module Groups
return true if params[:organization_id].blank?
return true if @group.organization.blank?
can?(current_user, :create_group, @group.organization)
can_create_group_in_organization? && matches_parent_organization?
end
def can_use_visibility_level?
@ -139,6 +160,14 @@ module Groups
@group.shared_runners_enabled = @group.parent.shared_runners_enabled
@group.allow_descendants_override_disabled_shared_runners = @group.parent.allow_descendants_override_disabled_shared_runners
end
def set_organization
if @group.parent_id
@group.organization = @group.parent.organization
elsif current_organization
@group.organization = current_organization
end
end
end
end

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true
class PreviewMarkdownService < BaseService
class PreviewMarkdownService < BaseContainerService
def execute
text, commands = explain_quick_actions(params[:text])
users = find_user_references(text)
@ -55,7 +55,7 @@ class PreviewMarkdownService < BaseService
def find_commands_target
QuickActions::TargetService
.new(container: project, current_user: current_user, params: { group: params[:group] })
.new(container: container, current_user: current_user)
.execute(target_type, target_id)
end
@ -68,4 +68,4 @@ class PreviewMarkdownService < BaseService
end
end
PreviewMarkdownService.prepend_mod_with('PreviewMarkdownService')
PreviewMarkdownService.prepend_mod

View File

@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/429557
milestone: '16.6'
type: development
group: group::code review
default_enabled: false
default_enabled: true

View File

@ -3,6 +3,6 @@ name: ci_job_artifacts_backlog_large_loop_limit
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/76509
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/347151
milestone: '14.10'
type: development
type: ops
group: group::pipeline security
default_enabled: false

View File

@ -0,0 +1,15 @@
- title: "`omniauth-azure-oauth2` gem is deprecated"
# The milestones for the deprecation announcement, and the removal.
removal_milestone: "17.0"
announcement_milestone: "16.9"
# Change breaking_change to false if needed.
breaking_change: true
# The stage and GitLab username of the person reporting the change,
# and a link to the deprecation issue
reporter: hsutor
stage: govern
issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/408989
body: | # (required) Don't change this line.
GitLab users can use the `omniauth-azure-oauth2` gem to authenticate with GitLab. In 17.0, this gem will be replaced with the `omniauth_openid_connect` gem. The new gem contains all of the same features as the old gem, but also has upstream maintenance and is better for security and centralized maintenance.
This change requires that users re-connect to the OAuth2 provider at time of migration. To avoid disruption, [add `omniauth_openid_connect` as a new provider](https://docs.gitlab.com/ee/administration/auth/oidc.html#configure-multiple-openid-connect-providers) any time before 17.0. Users will see a new login button and have to manually reconnect their credentials. If you do not implement the `omniauth_openid_connect` gem before 17.0, users will no longer be able to sign in using the Azure login button, and will have to sign in using their username and password, until the correct gem is implemented by the administrator.

View File

@ -2,6 +2,7 @@
table_name: p_ci_pipeline_variables
classes:
- Ci::PipelineVariable
- Ci::PipelineVariable::Partitioned
feature_categories:
- continuous_integration
description: Routing table for ci_pipeline_variables

View File

@ -0,0 +1,52 @@
# frozen_string_literal: true
class NpmSettingsToDependencyProxyPackagesSettings < Gitlab::Database::Migration[2.2]
disable_ddl_transaction!
milestone '16.9'
TABLE_NAME = :dependency_proxy_packages_settings
def up
# using the table name so that Migration/AddLimitToTextColumns cop will not make rubocop fail.
change_table :dependency_proxy_packages_settings do |t|
t.text :npm_external_registry_url, null: true
t.binary :encrypted_npm_external_registry_basic_auth, null: true
t.binary :encrypted_npm_external_registry_basic_auth_iv, null: true
t.binary :encrypted_npm_external_registry_auth_token, null: true
t.binary :encrypted_npm_external_registry_auth_token_iv, null: true
end
# using the table name so that Migration/AddLimitToTextColumns cop will not make rubocop fail.
add_text_limit :dependency_proxy_packages_settings, :npm_external_registry_url, 255
constraint = check_constraint_name(TABLE_NAME.to_s, 'encrypted_npm_external_registry_basic_auth', 'max_length')
add_check_constraint(TABLE_NAME, 'octet_length(encrypted_npm_external_registry_basic_auth) <= 1020', constraint)
constraint = check_constraint_name(TABLE_NAME.to_s, 'encrypted_npm_external_registry_basic_auth_iv', 'max_length')
add_check_constraint(TABLE_NAME, 'octet_length(encrypted_npm_external_registry_basic_auth_iv) <= 1020', constraint)
constraint = check_constraint_name(TABLE_NAME.to_s, 'encrypted_npm_external_registry_auth_token', 'max_length')
add_check_constraint(TABLE_NAME, 'octet_length(encrypted_npm_external_registry_auth_token) <= 1020', constraint)
constraint = check_constraint_name(TABLE_NAME.to_s, 'encrypted_npm_external_registry_auth_token_iv', 'max_length')
add_check_constraint(TABLE_NAME, 'octet_length(encrypted_npm_external_registry_auth_token_iv) <= 1020', constraint)
constraint = check_constraint_name(TABLE_NAME.to_s, 'npm_credentials', 'one_set_or_empty')
add_check_constraint(
TABLE_NAME,
'num_nulls(encrypted_npm_external_registry_basic_auth, encrypted_npm_external_registry_auth_token) > 0',
constraint
)
end
def down
change_table TABLE_NAME do |t|
t.remove :npm_external_registry_url,
:encrypted_npm_external_registry_basic_auth,
:encrypted_npm_external_registry_basic_auth_iv,
:encrypted_npm_external_registry_auth_token,
:encrypted_npm_external_registry_auth_token_iv
end
end
end

View File

@ -0,0 +1 @@
de80bafabfdf3fb99d98f6dd2b2e38d41968913d68511543d7442958d8a7b864

View File

@ -16303,10 +16303,21 @@ CREATE TABLE dependency_proxy_packages_settings (
encrypted_maven_external_registry_username_iv bytea,
encrypted_maven_external_registry_password bytea,
encrypted_maven_external_registry_password_iv bytea,
npm_external_registry_url text,
encrypted_npm_external_registry_basic_auth bytea,
encrypted_npm_external_registry_basic_auth_iv bytea,
encrypted_npm_external_registry_auth_token bytea,
encrypted_npm_external_registry_auth_token_iv bytea,
CONSTRAINT check_12c046b67f CHECK ((char_length(npm_external_registry_url) <= 255)),
CONSTRAINT check_14a2818907 CHECK (((num_nulls(encrypted_maven_external_registry_username, encrypted_maven_external_registry_password) = 0) OR (num_nulls(encrypted_maven_external_registry_username, encrypted_maven_external_registry_password) = 2))),
CONSTRAINT check_353c7ecafd CHECK ((octet_length(encrypted_maven_external_registry_username) <= 1020)),
CONSTRAINT check_48643112c8 CHECK ((octet_length(encrypted_npm_external_registry_auth_token) <= 1020)),
CONSTRAINT check_54126e21c1 CHECK ((octet_length(encrypted_npm_external_registry_basic_auth) <= 1020)),
CONSTRAINT check_7fafb5606e CHECK ((octet_length(encrypted_npm_external_registry_basic_auth_iv) <= 1020)),
CONSTRAINT check_93afb1690f CHECK ((num_nulls(encrypted_npm_external_registry_basic_auth, encrypted_npm_external_registry_auth_token) > 0)),
CONSTRAINT check_ac55c514a5 CHECK ((char_length(maven_external_registry_url) <= 255)),
CONSTRAINT check_c6f700648d CHECK ((octet_length(encrypted_maven_external_registry_password) <= 1020)),
CONSTRAINT check_c8613a3d35 CHECK ((octet_length(encrypted_npm_external_registry_auth_token_iv) <= 1020)),
CONSTRAINT check_cdf5f9a434 CHECK ((octet_length(encrypted_maven_external_registry_password_iv) <= 1020)),
CONSTRAINT check_fd5def68ba CHECK ((octet_length(encrypted_maven_external_registry_username_iv) <= 1020))
);

View File

@ -1,12 +1,13 @@
---
# Warning: gitlab.VersionHistory
# Warning: gitlab.HistoryItems
#
# Ensures version history items are properly formatted.
# Ensures history items are properly formatted.
#
extends: existence
message: "Version history items should always start with '> -', even if there is only one item."
message: "History items must always start with '> -', one item per line, even if there is only one item."
link: https://docs.gitlab.com/ee/development/documentation/versions.html#add-a-version-history-item
level: warning
level: error
scope: raw
raw:
- '(?m)(?<=^#+[^\n]*\n\n)> [^-]'
- '(?m)(?<=^#+[^\n]*\n\n)> [^-]|'
- '^> - [^\n]*\n[^\n>`]'

View File

@ -276,7 +276,7 @@ After configuring LDAP, to test the configuration, use the
### Basic configuration settings
> The `hosts` configuration setting was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/139) in GitLab 14.7.
> - The `hosts` configuration setting was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/139) in GitLab 14.7.
The following basic settings are available:

View File

@ -125,6 +125,7 @@ Example response:
"external_pipeline_validation_service_token": null,
"external_pipeline_validation_service_url": null,
"jira_connect_application_key": null,
"jira_connect_public_key_storage_enabled": false,
"jira_connect_proxy_url": null,
"silent_mode_enabled": false,
"package_registry_allow_anyone_to_pull_option": true,
@ -269,6 +270,7 @@ Example response:
"external_pipeline_validation_service_url": null,
"can_create_group": false,
"jira_connect_application_key": "123",
"jira_connect_public_key_storage_enabled": true,
"jira_connect_proxy_url": "http://gitlab.example.com",
"user_defaults_to_private_profile": true,
"projects_api_rate_limit_unauthenticated": 400,
@ -467,8 +469,9 @@ listed in the descriptions of the relevant settings.
| `import_sources` | array of strings | no | Sources to allow project import from, possible values: `github`, `bitbucket`, `bitbucket_server`, `fogbugz`, `git`, `gitlab_project`, `gitea`, and `manifest`. |
| `invisible_captcha_enabled` | boolean | no | Enable Invisible CAPTCHA spam detection during sign-up. Disabled by default. |
| `issues_create_limit` | integer | no | Max number of issue creation requests per minute per user. Disabled by default.|
| `jira_connect_application_key` | String | no | Application ID of the OAuth application that should be used to authenticate with the GitLab for Jira Cloud app |
| `jira_connect_proxy_url` | String | no | URL of the GitLab instance that should be used as a proxy for the GitLab for Jira Cloud app |
| `jira_connect_application_key` | String | no | ID of the OAuth application used to authenticate with the GitLab for Jira Cloud app. |
| `jira_connect_public_key_storage_enabled` | boolean | no | Enable public key storage for the GitLab for Jira Cloud app. |
| `jira_connect_proxy_url` | String | no | URL of the GitLab instance used as a proxy for the GitLab for Jira Cloud app. |
| `keep_latest_artifact` | boolean | no | Prevent the deletion of the artifacts from the most recent successful jobs, regardless of the expiry time. Enabled by default. |
| `local_markdown_version` | integer | no | Increase this value when any cached Markdown should be invalidated. |
| `mailgun_signing_key` | string | no | The Mailgun HTTP webhook signing key for receiving events from webhook. |

View File

@ -471,7 +471,7 @@ stop_review:
#### Run a pipeline job when environment is stopped
> Feature flag `environment_stop_actions_include_all_finished_deployments` [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/435128) in GitLab 16.9. Disabled by default.
> - Feature flag `environment_stop_actions_include_all_finished_deployments` [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/435128) in GitLab 16.9. Disabled by default.
FLAG:
On self-managed GitLab, by default this feature is not available. To make it available, an administrator can [enable the feature flag](../../administration/feature_flags.md) named `environment_stop_actions_include_all_finished_deployments`.

View File

@ -363,8 +363,12 @@ downstream projects. On self-managed instances, an administrator can change this
### How pipeline duration is calculated
Total running time for a given pipeline excludes retries and pending
(queued) time.
The total running time for a given pipeline excludes:
- The duration of the initial run for any job that is retried or manually re-run.
- Any pending (queue) time.
That means that if a job is retried or manually re-run, only the duration of the latest run is included in the total running time.
Each job is represented as a `Period`, which consists of:
@ -373,26 +377,32 @@ Each job is represented as a `Period`, which consists of:
A simple example is:
- A (1, 3)
- B (2, 4)
- A (0, 2)
- A' (2, 4)
- This is retrying A
- B (1, 3)
- C (6, 7)
In the example:
- A begins at 1 and ends at 3.
- B begins at 2 and ends at 4.
- A begins at 0 and ends at 2.
- A' begins at 2 and ends at 4.
- B begins at 1 and ends at 3.
- C begins at 6 and ends at 7.
Visually, it can be viewed as:
```plaintext
0 1 2 3 4 5 6 7
AAAAAAA
BBBBBBB
AAAAAAA
BBBBBBB
A'A'A'A
CCCC
```
The union of A, B, and C is (1, 4) and (6, 7). Therefore, the total running time is:
Because A is retried, we ignore it and count only job A'.
The union of B, A', and C is (1, 4) and (6, 7). Therefore, the total
running time is:
```plaintext
(4 - 1) + (7 - 6) => 4

View File

@ -2441,9 +2441,12 @@ job1:
- [GitLab Runner configuration](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-runners-section)
### `identity_provider` **(EXPERIMENT)**
### `identity_provider`
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/142054) in GitLab 16.9. This feature is an [Experiment](../../policy/experiment-beta-support.md).
DETAILS:
**Status:** Experiment
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/142054) in GitLab 16.9. This feature is an [Experiment](../../policy/experiment-beta-support.md).
FLAG:
On GitLab.com, this feature is not available.

View File

@ -462,7 +462,6 @@ Elasticsearch is a distributed RESTful search engine built for the cloud.
- [Source](../install/installation.md#install-gitaly)
- Layer: Core Service (Data)
- Process: `gitaly`
- GitLab.com: [Service Architecture](https://handbook.gitlab.com/handbook/engineering/infrastructure/production/architecture/#service-architecture)
Gitaly is a service designed by GitLab to remove our need for NFS for Git storage in distributed deployments of GitLab (think GitLab.com or High Availability Deployments). As of 11.3.0, this service handles all Git level access in GitLab. You can read more about the project [in the project's README](https://gitlab.com/gitlab-org/gitaly).
@ -474,7 +473,6 @@ Gitaly is a service designed by GitLab to remove our need for NFS for Git storag
- [Source](../install/installation.md#install-gitaly)
- Layer: Core Service (Data)
- Process: `praefect`
- GitLab.com: [Service Architecture](https://handbook.gitlab.com/handbook/engineering/infrastructure/production/architecture/#service-architecture)
Praefect is a transparent proxy between each Git client and the Gitaly coordinating the replication of
repository updates to secondary nodes.
@ -553,7 +551,6 @@ GitLab CI/CD is the open-source continuous integration service included with Git
- [Source](../install/installation.md#install-gitlab-shell)
- [GDK](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/gitlab.yml.example)
- Layer: Core Service (Processor)
- GitLab.com: [Service Architecture](https://handbook.gitlab.com/handbook/engineering/infrastructure/production/architecture/#service-architecture)
[GitLab Shell](gitlab_shell/index.md) is a program designed at GitLab to handle SSH-based `git` sessions, and modifies the list of authorized keys. GitLab Shell is not a Unix shell nor a replacement for Bash or Zsh.
@ -566,7 +563,6 @@ GitLab CI/CD is the open-source continuous integration service included with Git
- [Source](../install/installation.md#install-gitlab-workhorse)
- Layer: Core Service (Processor)
- Process: `gitlab-workhorse`
- GitLab.com: [Service Architecture](https://handbook.gitlab.com/handbook/engineering/infrastructure/production/architecture/#service-architecture)
[GitLab Workhorse](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/development/workhorse) is a program designed at GitLab to help alleviate pressure from Puma. You can read more about the [historical reasons for developing](https://about.gitlab.com/blog/2016/04/12/a-brief-history-of-gitlab-workhorse/). It's designed to act as a smart reverse proxy to help speed up GitLab as a whole.
@ -640,7 +636,6 @@ MinIO is an object storage server released under the GNU AGPL v3.0. It is compat
- [Source](../install/installation.md#10-nginx)
- Layer: Core Service (Processor)
- Process: `nginx`
- GitLab.com: [Service Architecture](https://handbook.gitlab.com/handbook/engineering/infrastructure/production/architecture/#service-architecture)
NGINX has an Ingress port for all HTTP requests and routes them to the appropriate sub-systems within GitLab. We are bundling an unmodified version of the popular open source webserver.
@ -733,7 +728,6 @@ Prometheus is a time-series tool that helps GitLab administrators expose metrics
- [Source](../install/installation.md#8-redis)
- Layer: Core Service (Data)
- Process: `redis`
- GitLab.com: [Service Architecture](https://handbook.gitlab.com/handbook/engineering/infrastructure/production/architecture/#service-architecture)
Redis is packaged to provide a place to store:

View File

@ -40,7 +40,7 @@ when FIPS mode is enabled.
| Ubuntu 20.04 Libgcrypt Cryptographic Module | [3902](https://csrc.nist.gov/projects/cryptographic-module-validation-program/certificate/3902) | EC2 instances | `gpg`, `sshd` |
| Amazon Linux 2 Kernel Crypto API Cryptographic Module | [3709](https://csrc.nist.gov/projects/cryptographic-module-validation-program/certificate/3709) | EKS nodes | Linux kernel |
| Amazon Linux 2 OpenSSL Cryptographic Module | [3553](https://csrc.nist.gov/projects/cryptographic-module-validation-program/certificate/3553) | EKS nodes | NGINX |
| RedHat Enterprise Linux 8 OpenSSL Cryptographic Module | [4271](https://csrc.nist.gov/projects/cryptographic-module-validation-program/certificate/4271) | EKS nodes | UBI containers: Workhorse, Pages, container registry, Rails (Puma/Sidekiq), Security Analyzers |
| RedHat Enterprise Linux 8 OpenSSL Cryptographic Module | [4271](https://csrc.nist.gov/projects/cryptographic-module-validation-program/certificate/4271) | EKS nodes | UBI containers: Workhorse, Pages, container registry, Rails (Puma/Sidekiq), Security Analyzers, `gitlab-sshd` |
| RedHat Enterprise Linux 8 Libgcrypt Cryptographic Module | [3784](https://csrc.nist.gov/projects/cryptographic-module-validation-program/certificate/3784) | EKS nodes | UBI containers: GitLab Shell, `gpg` |
### Supported Operating Systems

View File

@ -587,7 +587,7 @@ If these commands return `undercover: ✅ No coverage is missing in latest chang
### `pajamas_adoption` job
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/141368) in GitLab 16.8.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/141368) in GitLab 16.8.
The `pajamas_adoption` job runs the [Pajamas Adoption Scanner](https://gitlab-org.gitlab.io/frontend/pajamas-adoption-scanner/) in merge requests to prevent regressions in the adoption of the [Pajamas Design System](https://design.gitlab.com/).

View File

@ -261,6 +261,7 @@ The following Elasticsearch settings are available:
| `Maximum bulk request size (MiB)` | Used by the GitLab Ruby and Go-based indexer processes. This setting indicates how much data must be collected (and stored in memory) in a given indexing process before submitting the payload to the Elasticsearch Bulk API. For the GitLab Go-based indexer, you should use this setting with `Bulk request concurrency`. `Maximum bulk request size (MiB)` must accommodate the resource constraints of both the Elasticsearch hosts and the hosts running the GitLab Go-based indexer from either the `gitlab-rake` command or the Sidekiq tasks. |
| `Bulk request concurrency` | The Bulk request concurrency indicates how many of the GitLab Go-based indexer processes (or threads) can run in parallel to collect data to subsequently submit to the Elasticsearch Bulk API. This increases indexing performance, but fills the Elasticsearch bulk requests queue faster. This setting should be used together with the Maximum bulk request size setting (see above) and needs to accommodate the resource constraints of both the Elasticsearch hosts and the hosts running the GitLab Go-based indexer either from the `gitlab-rake` command or the Sidekiq tasks. |
| `Client request timeout` | Elasticsearch HTTP client request timeout value in seconds. `0` means using the system default timeout value, which depends on the libraries that GitLab application is built upon. |
| `Code indexing concurrency` | Maximum number of Elasticsearch code indexing background jobs allowed to run concurrently. This only applies to repository indexing operations. |
WARNING:
Increasing the values of `Maximum bulk request size (MiB)` and `Bulk request concurrency` can negatively impact

View File

@ -9,7 +9,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
There are cases where GitLab would like to validate the edge-cases of scale, support, and maintenance burden of features in their current form for every designed use case.
There are also scenarios where a feature is not complete enough to be considered an [MVC](https://handbook.gitlab.com/handbook/product/product-principles/#the-minimal-viable-change-mvc).
In these cases, GitLab has the option to release features as Experiment, Beta, or Limited Availability, and users can opt-in and test the new experience.
In these cases, GitLab has the option to release features as Experiments or Beta features, and users can opt-in and test the new experience.
Features might not be fully documented or supported in the Experiment or Beta phases.
Please note that some features may not be aligned to these recommendations if they were developed before the recommendations were in place or if the group determined an alternative implementation approach was needed.
@ -92,11 +92,11 @@ The experimental features are only shown when people/organizations opt-in to exp
All features that are available on GitLab.com are considered "in production".
Because all Experiment, Beta, and Generally Available features are available on GitLab.com, they are all considered to be in production.
## Experiment, Beta and Limited Availability Exit Criteria
## Experiment and Beta Exit Criteria
To ensure the phases before General Availability are as short as possible each phase of Experiment, Beta and LA should include exit criteria.
This encourages rapid iteration and reduces [cycle time](https://handbook.gitlab.com/handbook/values/#reduce-cycle-time).
GitLab Product Managers will take the following into account when deciding what exit criteria to apply to their Experimental, Beta, and Limited Availability features:
GitLab Product Managers will take the following into account when deciding what exit criteria to apply to their Experimental and Beta features:
- **Time**: Define an end date at which point the feature will be General Availability.
- Consider setting a time-bound target metric that will define readiness for exit into GA (e.g. X number of customers retained MoM over 6 months after launch of Experiment, X% growth of free and paid users in three months since launch Beta, etc.)

View File

@ -12,4 +12,4 @@ Choose and manage the subscription that's right for you and your organization.
|--|--|--|
| [**Choose a GitLab subscription**](choosing_subscription.md) **{chevron-right}**<br><br> Options for accessing GitLab. | [**GitLab SaaS subscription**](gitlab_com/index.md) **{chevron-right}**<br><br> Seat usage, compute minutes, storage limits, renewal info. | [**GitLab self-managed subscription**](self_managed/index.md) **{chevron-right}**<br><br> Billable users, renewal and upgrade info. |
| [**GitLab Dedicated**](gitlab_dedicated/index.md) **{chevron-right}**<br><br> Available features and benefits. | [**Community programs**](community_programs.md) **{chevron-right}**<br><br> Education, Open Source, Startups. | [**The Customers Portal**](customers_portal.md) **{chevron-right}**<br><br> Payment and company details. |
| [**Quarterly reconciliation and annual true-ups**](quarterly_reconciliation.md) **{chevron-right}**<br><br> Billing examples.| [**Compute quota**](../ci/pipelines/cicd_minutes.md) **{chevron-right}**<br><br> Calculations, quotas, purchase information. | |
| [**Quarterly reconciliation and annual true-ups**](quarterly_reconciliation.md) **{chevron-right}**<br><br> Billing examples.| [**Compute quota**](../ci/pipelines/cicd_minutes.md) **{chevron-right}**<br><br> Calculations, quotas, purchase information. | [**Subscription add-ons**](subscription-add-ons.md)<br><br> Purchase add-ons for AI features. |

View File

@ -0,0 +1,53 @@
---
stage: Fulfillment
group: Purchase
description: Subscription add-ons, GitLab Duo Pro, seat assignment
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
# Subscription add-ons
DETAILS:
**Tier:** Premium, Ultimate
**Offering:** SaaS, self-managed
You can purchase subscription add-ons to give users in your organization access to more GitLab features.
Subscription add-ons are purchased as additional seats in your subscription.
Access to features provided by subscription add-ons is managed through seat assignment. Subscription
add-ons can be assigned to billable users only.
## Assign GitLab Duo Pro seats
Prerequisites:
- You must purchase the GitLab Duo Pro add-on from the [GitLab Sales Team](https://about.gitlab.com/solutions/gitlab-duo-pro/sales/).
After you purchase GitLab Duo Pro, you can assign seats to billable users to grant access to the add-on.
### For GitLab.com
1. On the left sidebar, select **Search or go to** and find your group.
1. Select **Settings > Usage Quotas**.
<!-- vale gitlab.Substitutions = NO -->
1. Select the **Duo Pro** tab.
<!-- vale gitlab.Substitutions = YES -->
1. To the right of the user, turn on the toggle to assign or unassign
GitLab Duo Pro.
### For self-managed
Prerequisites:
- You must be an administrator.
1. On the left sidebar, at the bottom, select **Admin Area**.
<!-- vale gitlab.Substitutions = NO -->
1. Select **Duo Pro**.
- If the **Duo Pro** menu item is not available, synchronize your subscription
after purchase:
1. On the left sidebar, select **Subscription**.
1. In **Subscription details**, to the right of **Last sync**, select
synchronize subscription (**{retry}**).
<!-- vale gitlab.Substitutions = YES -->
1. To the right of the user, turn on the toggle to assign or unassign
GitLab Duo Pro.

View File

@ -1664,6 +1664,22 @@ Multiple DORA metrics can now be queried simultaneously using a new metrics fiel
<div class="deprecation breaking-change" data-milestone="17.0">
### `omniauth-azure-oauth2` gem is deprecated
<div class="deprecation-notes">
- Announced in GitLab <span class="milestone">16.9</span>
- Removal in GitLab <span class="milestone">17.0</span> ([breaking change](https://docs.gitlab.com/ee/update/terminology.html#breaking-change))
- To discuss this change or learn more, see the [deprecation issue](https://gitlab.com/gitlab-org/gitlab/-/issues/408989).
</div>
GitLab users can use the `omniauth-azure-oauth2` gem to authenticate with GitLab. In 17.0, this gem will be replaced with the `omniauth_openid_connect` gem. The new gem contains all of the same features as the old gem, but also has upstream maintenance and is better for security and centralized maintenance.
This change requires that users re-connect to the OAuth2 provider at time of migration. To avoid disruption, [add `omniauth_openid_connect` as a new provider](https://docs.gitlab.com/ee/administration/auth/oidc.html#configure-multiple-openid-connect-providers) any time before 17.0. Users will see a new login button and have to manually reconnect their credentials. If you do not implement the `omniauth_openid_connect` gem before 17.0, users will no longer be able to sign in using the Azure login button, and will have to sign in using their username and password, until the correct gem is implemented by the administrator.
</div>
<div class="deprecation breaking-change" data-milestone="17.0">
### `postgres_exporter['per_table_stats']` configuration setting
<div class="deprecation-notes">

View File

@ -876,7 +876,7 @@ reported.
### View details of an API Fuzzing vulnerability
> Introduced in GitLab 13.7.
> - Introduced in GitLab 13.7.
Faults detected by API Fuzzing occur in the live web application, and require manual investigation
to determine if they are vulnerabilities. Fuzzing faults are included as vulnerabilities with a

View File

@ -15,10 +15,8 @@ To run a DAST scan:
## Create a DAST CI/CD job
> - This template was [updated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/62597) to DAST_VERSION: 2 in
GitLab 14.0.
> - This template was [updated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/87183) to DAST_VERSION: 3 in
GitLab 15.0.
> - This template was [updated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/62597) to DAST_VERSION: 2 in GitLab 14.0.
> - This template was [updated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/87183) to DAST_VERSION: 3 in GitLab 15.0.
To add DAST scanning to your application, use the DAST job defined
in the GitLab DAST CI/CD template file. Updates to the template are provided with GitLab

View File

@ -300,19 +300,13 @@ actions:
## Understanding scan result policy approvals
> - The branch comparison logic for `scan_finding` was [changed](https://gitlab.com/gitlab-org/gitlab/-/issues/428518) in GitLab 16.8 [with a flag](../../../administration/feature_flags.md) named `scan_result_policy_merge_base_pipeline`. Disabled by default.
FLAG:
On self-managed GitLab, by default this feature is not available. To make it available, an administrator can [enable the feature flag](../../../administration/feature_flags.md) named `scan_result_policy_merge_base_pipeline`.
On GitLab.com, this feature is not available.
> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/435297) in GitLab 16.9. Feature flag `scan_result_policy_merge_base_pipeline` removed.
### Scope of scan result policy comparison
- To determine when approval is required on a merge request, we compare completed pipelines for each supported pipeline source for the source and target branch (for example, `feature`/`main`). This ensures the most comprehensive evaluation of scan results.
- For the source branch, the comparison pipeline is its latest completed `HEAD` pipeline.
- For `license_finding` rules, we compare to a common ancestor's latest completed pipeline.
- For `scan_finding` rules, the comparison pipeline may differ:
- If the `scan_result_policy_merge_base_pipeline` feature flag is enabled, we compare to a common ancestor's latest completed pipeline.
- Otherwise, we compare to the target branch's latest completed `HEAD` pipeline.
- For the target branch, we compare to a common ancestor's latest completed pipeline.
- Scan result policies considers all supported pipeline sources (based on the [`CI_PIPELINE_SOURCE` variable](../../../ci/variables/predefined_variables.md)) when comparing results from both the source and target branches when determining if a merge request requires approval. Pipeline sources `webide` and `parent_pipeline` are not supported.
### Accepting risk and ignoring vulnerabilities in future merge requests

View File

@ -19,9 +19,11 @@ To temporarily grant Git access to your projects, you can use SSH certificates.
## Add a CA certificate to a top-level group
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/421915) in GitLab 16.4 [with a flag](../feature_flags.md) named `ssh_certificates_rest_endpoints`. Disabled by default.
> - [Enabled on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/424501) in GitLab 16.9.
FLAG:
On GitLab.com, this feature is not available.
On GitLab.com, this feature is available.
Prerequisites:
- You must have the Owner role for the group.
@ -64,9 +66,10 @@ The user certificates can only be used to access the projects within the top-lev
## Enforce SSH certificates
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/421915) in GitLab 16.7 [with a flag](../feature_flags.md) named `enforce_ssh_certificates_via_settings`. Disabled by default.
> - [Enabled on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/426235) in GitLab 16.9.
FLAG:
On GitLab.com, this feature is not available.
On GitLab.com, this feature is available.
You can enforce usage of SSH certificates and forbid users from authenticating using SSH
keys and access tokens.

View File

@ -485,7 +485,7 @@ system note in the OKR's comments, for example:
## Lock discussion
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/398649) in GitLab 16.9 [with a flag](../administration/feature_flags.md) named `work_items_mvc`. Disabled by default.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/398649) in GitLab 16.9 [with a flag](../administration/feature_flags.md) named `work_items_mvc`. Disabled by default.
FLAG:
On self-managed GitLab, by default this feature is not available. To make it available, an administrator can [enable the feature flag](../administration/feature_flags.md) named `work_items_mvc`.

View File

@ -455,3 +455,27 @@ GitLab has approved this rule automatically to unblock the merge request.
This issue occurs when an approval rule uses a Code Owner that is not a direct member of the project.
The workaround is to check that the group or user has been invited to the project.
### User or group not shown when viewing Code Owners for a directory
Code Owners might not show the intended user or group based on your configured rules when viewing a directory,
but correctly show the Code Owners for files beneath the directory.
For example:
```plaintext
* @dev-team
docs/ @tech-writer-team
```
All files beneath the `docs/` directory show `@tech-writer-team` as Code Owners, but the directory itself will show `@dev-team`.
This behavior occurs when viewing a directory because the [syntax rule](../../project/codeowners/reference.md#directory-paths)
applies to all files beneath the directory, which does not include the directory itself. To resolve this, update the `CODEOWNERS` file to include the
directory specifically along with all files beneath the directory. For example:
```plaintext
* @dev-team
docs @tech-writer-team
docs/ @tech-writer-team
```

View File

@ -34,7 +34,7 @@ use the default approval rules from the target (upstream) project, not the sourc
## Add an approval rule
> Approval rules for all protected branches introduced in GitLab 15.3.
> - Approval rules for all protected branches introduced in GitLab 15.3.
Prerequisites:

View File

@ -320,6 +320,7 @@ For a web developer writing a webpage for your company's website:
> - [Enabled on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/387070) in GitLab 16.0.
> - [Enabled on self-managed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/126998) in GitLab 16.3 by default.
> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/132355) in GitLab 16.5. Feature flag `mr_activity_filters` removed.
> - Filtering bot comments [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/128473) in GitLab 16.9.
To understand the history of a merge request, filter its activity feed to show you
only the items that are relevant to you.
@ -335,6 +336,7 @@ only the items that are relevant to you.
- Assignees & Reviewers
- Approvals
- Bot comments
- Comments
- Commits & branches
- Edits

View File

@ -64,7 +64,7 @@ Each link as an asset has the following attributes:
|-------------|--------------------------------------------------------------------------------------------------------------|----------|
| `name` | The name of the link. | Yes |
| `url` | The URL to download a file. | Yes |
| `filepath` | The redirect link to the `url`. See [this section](#permanent-links-to-release-assets) for more information. | No |
| `filepath` | The redirect link to the `url`. Must start with a slash (`/`). See [this section](#permanent-links-to-release-assets) for more information. | No |
| `link_type` | The content kind of what users can download via `url`. See [this section](#link-types) for more information. | No |
#### Permanent link to latest release
@ -101,7 +101,7 @@ to use the same URL. This is defined during [link creation](../../../api/release
The format of the URL is:
```plaintext
https://host/namespace/project/-/releases/:release/downloads/:filepath
https://host/namespace/project/-/releases/:release/downloads:filepath
```
If you have an asset for the `v11.9.0-rc2` release in the `gitlab-org`
@ -128,22 +128,24 @@ If the release is private, you need to provide a Personal Access Token with eith
a `private_token` query parameter or a `HTTP_PRIVATE_TOKEN` header when making the request. For example:
```shell
curl --location --output filename "https://gitlab.example.com/my-group/my-project/-/releases/:release/downloads/:filepath?private_token=<your_access_token>"
curl --location --output filename --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/my-group/my-project/-/releases/:release/downloads/:filepath"
curl --location --output filename "https://gitlab.example.com/my-group/my-project/-/releases/myrelease/downloads</path-to-file>?private_token=<your_access_token>"
curl --location --output filename --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/my-group/my-project/-/releases/myrelease/downloads</path-to-file>
```
#### Permanent links to latest release assets
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/16821) in GitLab 14.9.
The `filepath` from [permanent links to release assets](#permanent-links-to-release-assets) can be used in combination with [permanent link to the latest release](#permanent-link-to-latest-release). It is useful when we want to link a permanent URL to download an asset from the *latest release*.
You can use the `filepath` from [permanent links to release assets](#permanent-links-to-release-assets) in combination with a [permanent link to the latest release](#permanent-link-to-latest-release). The `filepath` must start with a slash (`/`).
The format of the URL is:
```plaintext
https://host/namespace/project/-/releases/permalink/latest/downloads/:filepath
https://host/namespace/project/-/releases/permalink/latest/downloads:filepath
```
You can use this format to provide a permanent link to an asset from the latest release.
If you have an asset with [`filepath`](../../../api/releases/links.md#create-a-release-link) for the `v11.9.0-rc2` latest release in the `gitlab-org`
namespace and `gitlab-runner` project on `gitlab.com`, for example:

View File

@ -492,7 +492,7 @@ system note in the task's comments, for example:
## Lock discussion
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/398649) in GitLab 16.9 [with a flag](../administration/feature_flags.md) named `work_items_mvc`. Disabled by default.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/398649) in GitLab 16.9 [with a flag](../administration/feature_flags.md) named `work_items_mvc`. Disabled by default.
FLAG:
On self-managed GitLab, by default this feature is not available. To make it available, an administrator can [enable the feature flag](../administration/feature_flags.md) named `work_items_mvc`.

View File

@ -210,8 +210,9 @@ module API
optional :group_runner_token_expiration_interval, type: Integer, desc: 'Token expiration interval for group runners, in seconds'
optional :project_runner_token_expiration_interval, type: Integer, desc: 'Token expiration interval for project runners, in seconds'
optional :pipeline_limit_per_project_user_sha, type: Integer, desc: "Maximum number of pipeline creation requests allowed per minute per user and commit. Set to 0 for unlimited requests per minute."
optional :jira_connect_application_key, type: String, desc: "Application ID of the OAuth application that should be used to authenticate with the GitLab for Jira Cloud app"
optional :jira_connect_proxy_url, type: String, desc: "URL of the GitLab instance that should be used as a proxy for the GitLab for Jira Cloud app"
optional :jira_connect_application_key, type: String, desc: "ID of the OAuth application used to authenticate with the GitLab for Jira Cloud app."
optional :jira_connect_public_key_storage_enabled, type: Boolean, desc: 'Enable public key storage for the GitLab for Jira Cloud app.'
optional :jira_connect_proxy_url, type: String, desc: "URL of the GitLab instance used as a proxy for the GitLab for Jira Cloud app."
optional :bulk_import_concurrent_pipeline_batch_limit, type: Integer, desc: 'Maximum simultaneous Direct Transfer pipeline batches to process'
optional :bulk_import_enabled, type: Boolean, desc: 'Enable migrating GitLab groups and projects by direct transfer'
optional :bulk_import_max_download_file, type: Integer, desc: 'Maximum download file size in MB when importing from source GitLab instances by direct transfer'

View File

@ -19,7 +19,7 @@ module Gitlab
delegate :instrument, to: :logger
# We try to keep the number of parallel HTTP requests to a minimum to avoid overloading IO.
MAX_PARALLEL_REMOTE_REQUESTS = 2
MAX_PARALLEL_REMOTE_REQUESTS = 4
def initialize(
project: nil, pipeline: nil, sha: nil, user: nil, parent_pipeline: nil, variables: nil,

View File

@ -14703,6 +14703,9 @@ msgstr ""
msgid "CreateGitTag|Set tag message"
msgstr ""
msgid "CreateGroup|You can't create a group in a different organization than the parent group."
msgstr ""
msgid "CreateGroup|You don't have permission to create a group in the provided organization."
msgstr ""

View File

@ -61,7 +61,7 @@
"@gitlab/favicon-overlay": "2.0.0",
"@gitlab/fonts": "^1.3.0",
"@gitlab/svgs": "3.80.0",
"@gitlab/ui": "^72.12.1",
"@gitlab/ui": "^73.1.1",
"@gitlab/visual-review-tools": "1.7.3",
"@gitlab/web-ide": "^0.0.1-dev-20240125064919",
"@mattiasbuelens/web-streams-adapter": "^0.1.0",

View File

@ -0,0 +1,64 @@
# frozen_string_literal: true
require "spec_helper"
RSpec.describe Pajamas::SingleStatComponent, type: :component, feature_category: :shared do
let(:title) { "Single Stat" }
let(:stat_value) { "9,000" }
let(:title_icon) { nil }
let(:unit) { nil }
let(:meta_text) { nil }
let(:variant) { :success }
before do
render_inline(described_class.new(
title: title,
title_icon: title_icon,
stat_value: stat_value,
unit: unit,
meta_text: meta_text
))
end
context "with default props" do
it 'shows title' do
expect(page).to have_css('[data-testid=title-text]', text: title)
end
it 'shows stat_value' do
expect(page).to have_css('[data-testid=non-animated-value]', text: stat_value)
end
it 'does not show unit' do
expect(page).not_to have_css('[data-testid=unit]')
end
it 'does not show meta badge' do
expect(page).not_to have_css('[data-testid=meta-badge]')
end
end
context "with title_icon" do
let(:title_icon) { :tanuki }
it 'shows icon' do
expect(page).to have_css('svg[data-testid=tanuki-icon]')
end
end
context 'with unit' do
let(:unit) { 'KB' }
it 'shows unit' do
expect(page).to have_css('[data-testid=unit]', text: unit)
end
end
context 'with meta_text' do
let(:meta_text) { "You're doing great!" }
it 'shows badge with text' do
expect(page).to have_css('[data-testid=meta-badge]', text: meta_text)
end
end
end

View File

@ -0,0 +1,38 @@
# frozen_string_literal: true
module Pajamas
class SingleStatComponentPreview < ViewComponent::Preview
# SingleStat
# ---
#
# See its design reference [here](https://design.gitlab.com/data-visualization/single-stat).
#
# @param title text
# @param stat_value text
# @param unit text
# @param title_icon text
# @param meta_text text
# @param meta_icon text
# @param variant select {{ Pajamas::BadgeComponent::VARIANT_OPTIONS }}
# @param hide_units toggle
def default(
title: 'Single stat',
stat_value: '9,001',
unit: '',
title_icon: 'chart',
meta_text: '',
meta_icon: 'check-circle',
variant: :default
)
render Pajamas::SingleStatComponent.new(
title: title,
stat_value: stat_value,
unit: unit,
title_icon: title_icon,
meta_text: meta_text,
meta_icon: meta_icon,
variant: variant
)
end
end
end

View File

@ -3,11 +3,13 @@ import App from '~/organizations/groups_and_projects/components/app.vue';
import GroupsView from '~/organizations/shared/components/groups_view.vue';
import ProjectsView from '~/organizations/shared/components/projects_view.vue';
import { RESOURCE_TYPE_GROUPS, RESOURCE_TYPE_PROJECTS } from '~/organizations/constants';
import { SORT_ITEMS } from '~/organizations/groups_and_projects/constants';
import {
SORT_ITEM_CREATED,
SORT_ITEM_NAME,
SORT_ITEM_CREATED_AT,
SORT_DIRECTION_DESC,
SORT_ITEMS,
} from '~/organizations/groups_and_projects/constants';
SORT_DIRECTION_ASC,
} from '~/organizations/shared/constants';
import FilteredSearchBar from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
import {
FILTERED_SEARCH_TERM,
@ -21,6 +23,9 @@ describe('GroupsAndProjectsApp', () => {
const routerMock = {
push: jest.fn(),
};
const mockEndCursor = 'mockEndCursor';
const mockStartCursor = 'mockStartCursor';
let wrapper;
const createComponent = ({ routeQuery = { search: 'foo' } } = {}) => {
@ -45,11 +50,24 @@ describe('GroupsAndProjectsApp', () => {
'when `display` query string is $display',
({ display, expectedComponent, expectedDisplayListboxSelectedProp }) => {
beforeEach(() => {
createComponent({ routeQuery: { display } });
createComponent({
routeQuery: {
display,
search: 'foo',
start_cursor: mockStartCursor,
end_cursor: mockEndCursor,
},
});
});
it('renders expected component', () => {
expect(wrapper.findComponent(expectedComponent).exists()).toBe(true);
it('renders expected component with correct props', () => {
expect(wrapper.findComponent(expectedComponent).props()).toMatchObject({
startCursor: mockStartCursor,
endCursor: mockEndCursor,
search: 'foo',
sortName: SORT_ITEM_NAME.value,
sortDirection: SORT_DIRECTION_ASC,
});
});
it('renders display listbox with correct props', () => {
@ -88,8 +106,8 @@ describe('GroupsAndProjectsApp', () => {
expect(findSort().props()).toMatchObject({
isAscending: true,
text: SORT_ITEM_CREATED.text,
sortBy: SORT_ITEM_CREATED.value,
text: 'Name',
sortBy: SORT_ITEM_NAME.value,
sortOptions: SORT_ITEMS,
});
});
@ -124,28 +142,50 @@ describe('GroupsAndProjectsApp', () => {
describe('when sort item is changed', () => {
beforeEach(() => {
createComponent();
createComponent({
routeQuery: {
display: RESOURCE_TYPE_PROJECTS,
start_cursor: mockStartCursor,
end_cursor: mockEndCursor,
search: 'foo',
},
});
findSort().vm.$emit('sortByChange', SORT_ITEM_CREATED.value);
findSort().vm.$emit('sortByChange', SORT_ITEM_CREATED_AT.value);
});
it('updates `sort_name` query string', () => {
expect(routerMock.push).toHaveBeenCalledWith({
query: { sort_name: SORT_ITEM_CREATED.value, search: 'foo' },
query: {
display: RESOURCE_TYPE_PROJECTS,
sort_name: SORT_ITEM_CREATED_AT.value,
search: 'foo',
},
});
});
});
describe('when sort direction is changed', () => {
beforeEach(() => {
createComponent();
createComponent({
routeQuery: {
display: RESOURCE_TYPE_PROJECTS,
start_cursor: mockStartCursor,
end_cursor: mockEndCursor,
search: 'foo',
},
});
findSort().vm.$emit('sortDirectionChange', false);
});
it('updates `sort_direction` query string', () => {
expect(routerMock.push).toHaveBeenCalledWith({
query: { sort_direction: SORT_DIRECTION_DESC, search: 'foo' },
query: {
display: RESOURCE_TYPE_PROJECTS,
sort_direction: SORT_DIRECTION_DESC,
search: 'foo',
},
});
});
});
@ -163,9 +203,6 @@ describe('GroupsAndProjectsApp', () => {
});
describe('when page is changed', () => {
const mockEndCursor = 'mockEndCursor';
const mockStartCursor = 'mockStartCursor';
describe('when going to next page', () => {
beforeEach(() => {
createComponent({ routeQuery: { display: RESOURCE_TYPE_PROJECTS } });
@ -203,28 +240,6 @@ describe('GroupsAndProjectsApp', () => {
query: { display: RESOURCE_TYPE_PROJECTS, start_cursor: mockStartCursor },
});
});
describe('when going to the first page', () => {
it('removes `start_cursor` and `end_cursor` query string', () => {
createComponent({
routeQuery: {
display: RESOURCE_TYPE_PROJECTS,
start_cursor: mockStartCursor,
end_cursor: mockEndCursor,
},
});
findProjectsView().vm.$emit('page-change', {
endCursor: null,
startCursor: mockStartCursor,
hasPreviousPage: false,
});
expect(routerMock.push).toHaveBeenCalledWith({
query: { display: RESOURCE_TYPE_PROJECTS },
});
});
});
});
});
});

Some files were not shown because too many files have changed in this diff Show More