Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2024-10-10 18:15:04 +00:00
parent 1252510698
commit 4965365aeb
212 changed files with 2548 additions and 870 deletions

View File

@ -80,6 +80,8 @@ include:
SKIP_IMAGE_VERIFICATION: "true"
# set specific arch list
ARCH_LIST: amd64
# use larger runner for complex rails build jobs
HIGH_CAPACITY_RUNNER_TAG: high-cpu
trigger:
project: '${CI_PROJECT_NAMESPACE}/$[[ inputs.cng_path ]]'
branch: $TRIGGER_BRANCH

View File

@ -16,7 +16,7 @@
- export GOPATH=$CI_PROJECT_DIR/.go
- mkdir -p $GOPATH
- source scripts/utils.sh
- log_disk_usage before_script # https://gitlab.com/gitlab-org/gitlab/-/issues/478880
- log_disk_usage "false" # https://gitlab.com/gitlab-org/gitlab/-/issues/478880
.default-before_script:
before_script:

View File

@ -102,7 +102,7 @@ include:
- rspec_section rspec_parallelized_job "--fail-fast=${RSPEC_FAIL_FAST_THRESHOLD} --tag ~quarantine --tag ~level:background_migration --tag ~click_house"
after_script:
- source scripts/utils.sh
- log_disk_usage after_script # https://gitlab.com/gitlab-org/gitlab/-/issues/478880
- log_disk_usage # https://gitlab.com/gitlab-org/gitlab/-/issues/478880
- bundle exec gem list gitlab_quality-test_tooling
- |
section_start "failed-test-issues" "Report test failures"

View File

@ -2558,6 +2558,13 @@
- <<: *if-default-refs
changes: *code-backstage-qa-patterns
.static-analysis:rules:ensure-application-settings-have-definition-file:
rules:
- <<: *if-default-refs
changes:
- db/structure.sql
- config/application_setting_columns/*.yml
.static-analysis:rules:haml-lint:
rules:
- <<: *if-default-refs

View File

@ -169,6 +169,16 @@ feature-flags-usage:
paths:
- tmp/feature_flags/
ensure-application-settings-have-definition-file:
image: ruby:${RUBY_VERSION}-alpine
extends:
- .static-analysis-base
- .static-analysis:rules:ensure-application-settings-have-definition-file
variables:
USE_BUNDLE_INSTALL: "false"
script:
- run_timed_command "scripts/cells/ci-ensure-application-settings-have-definition-file.rb"
semgrep-appsec-custom-rules:
stage: lint
extends:

View File

@ -105,11 +105,6 @@ build-cng:
variables:
# use larger runner for complex rails build jobs
HIGH_CAPACITY_RUNNER_TAG: e2e
# quality specific fork, see: https://gitlab.com/gitlab-org/quality/quality-engineering/team-tasks/-/issues/2839
trigger:
project: ${CI_PROJECT_NAMESPACE}/quality/quality-engineering/CNG-mirror
branch: $TRIGGER_BRANCH
strategy: depend
download-knapsack-report:
extends:

37
.lefthook/gitleaks.sh Executable file
View File

@ -0,0 +1,37 @@
#!/bin/bash
set -euo pipefail
MINIMUM_VERSION="v8.20"
SCRIPT_NAME=$(basename "$0")
HOOK_TYPE="${1:-}"
if ! command -v gitleaks &>/dev/null; then
cat >&2 <<EOF
WARNING: gitleaks is not installed. Skipping secrets detection.
Please install at least version v$MINIMUM_VERSION using "asdf install" or see:
https://gitlab.com/gitlab-com/gl-security/security-research/gitleaks-endpoint-installer.
EOF
exit 0
fi
if [ -z "$HOOK_TYPE" ]; then
cat >&2 <<EOF
ERROR: Hook type argument is required.
Usage: ./$SCRIPT_NAME [pre-commit|pre-push]
Please specify 'pre-commit' or 'pre-push' as the argument.
EOF
exit 1
fi
if [ "$HOOK_TYPE" == "pre-commit" ]; then
gitleaks git --pre-commit --staged --no-banner --redact --verbose
elif [ "$HOOK_TYPE" == "pre-push" ]; then
BASE_COMMIT=$(git merge-base origin/master HEAD)
gitleaks git --log-opts="$BASE_COMMIT..HEAD" --no-banner --redact --verbose
else
cat >&2 <<EOF
ERROR: Unsupported hook type '$HOOK_TYPE'.
Usage: ./$SCRIPT_NAME [pre-commit|pre-push]
EOF
exit 1
fi

View File

@ -1986,7 +1986,6 @@ Layout/LineLength:
- 'ee/spec/services/quick_actions/interpret_service_spec.rb'
- 'ee/spec/services/requirements_management/export_csv_service_spec.rb'
- 'ee/spec/services/resource_events/change_weight_service_spec.rb'
- 'ee/spec/services/search/global_service_spec.rb'
- 'ee/spec/services/search/snippet_service_spec.rb'
- 'ee/spec/services/security/ingestion/finding_map_collection_spec.rb'
- 'ee/spec/services/security/ingestion/ingest_report_service_spec.rb'
@ -3865,7 +3864,6 @@ Layout/LineLength:
- 'spec/requests/api/feature_flags_spec.rb'
- 'spec/requests/api/features_spec.rb'
- 'spec/requests/api/files_spec.rb'
- 'spec/requests/api/generic_packages_spec.rb'
- 'spec/requests/api/go_proxy_spec.rb'
- 'spec/requests/api/graphql/boards/board_list_issues_query_spec.rb'
- 'spec/requests/api/graphql/boards/board_list_query_spec.rb'

View File

@ -325,7 +325,6 @@ Rails/StrongParams:
- 'ee/app/controllers/security/projects_controller.rb'
- 'ee/app/controllers/smartcard_controller.rb'
- 'ee/app/controllers/subscriptions/groups_controller.rb'
- 'ee/app/controllers/subscriptions/hand_raise_leads_controller.rb'
- 'ee/app/controllers/subscriptions_controller.rb'
- 'ee/app/controllers/users/base_identity_verification_controller.rb'
- 'ee/app/controllers/users/registrations_identity_verification_controller.rb'

View File

@ -733,7 +733,6 @@ RSpec/ContextWording:
- 'ee/spec/services/requirements_management/export_csv_service_spec.rb'
- 'ee/spec/services/resource_access_tokens/create_service_spec.rb'
- 'ee/spec/services/resource_access_tokens/revoke_service_spec.rb'
- 'ee/spec/services/search/global_service_spec.rb'
- 'ee/spec/services/search/snippet_service_spec.rb'
- 'ee/spec/services/security/ingestion/tasks/ingest_vulnerabilities/create_spec.rb'
- 'ee/spec/services/security/ingestion/tasks/update_vulnerability_uuids_spec.rb'

View File

@ -1 +1 @@
6175a5f017f01e11959045c0dc24d9ca237865e6
39a5e315d1b3e112db13930382d11061bfa95964

View File

@ -292,7 +292,7 @@
{"name":"graphiql-rails","version":"1.10.0","platform":"ruby","checksum":"b557f989a737c8b9e985142609bec52fb1e9393a701eb50e02a7c14422891040"},
{"name":"graphlient","version":"0.8.0","platform":"ruby","checksum":"98c408da1d083454e9f5e274f3b0b6261e2a0c2b5f2ed7b3ef9441d46f8e7cb1"},
{"name":"graphlyte","version":"1.0.0","platform":"ruby","checksum":"b5af4ab67dde6e961f00ea1c18f159f73b52ed11395bb4ece297fe628fa1804d"},
{"name":"graphql","version":"2.3.16","platform":"ruby","checksum":"6ac9966998914c5b470e527c7105d936db70af2306ec0445f88953404bce49c7"},
{"name":"graphql","version":"2.3.17","platform":"ruby","checksum":"5e03655bf8ab07cd9710821f7f991ca6f5e86cf632261350ab185527169554e3"},
{"name":"graphql-client","version":"0.23.0","platform":"ruby","checksum":"f238b8e451676baad06bd15f95396e018192243dcf12c4e6d13fb41d9a2babc1"},
{"name":"graphql-docs","version":"5.0.0","platform":"ruby","checksum":"76baca6e5a803a4b6a9fbbbfdbf16742b7c4c546c8592b6e1a7aa4e79e562d04"},
{"name":"grpc","version":"1.63.0","platform":"aarch64-linux","checksum":"dc75c5fd570b819470781d9512105dddfdd11d984f38b8e60bb946f92d1f79ee"},

View File

@ -919,7 +919,7 @@ GEM
faraday (~> 2.0)
graphql-client
graphlyte (1.0.0)
graphql (2.3.16)
graphql (2.3.17)
base64
fiber-storage
graphql-client (0.23.0)

View File

@ -293,7 +293,7 @@
{"name":"graphiql-rails","version":"1.10.0","platform":"ruby","checksum":"b557f989a737c8b9e985142609bec52fb1e9393a701eb50e02a7c14422891040"},
{"name":"graphlient","version":"0.8.0","platform":"ruby","checksum":"98c408da1d083454e9f5e274f3b0b6261e2a0c2b5f2ed7b3ef9441d46f8e7cb1"},
{"name":"graphlyte","version":"1.0.0","platform":"ruby","checksum":"b5af4ab67dde6e961f00ea1c18f159f73b52ed11395bb4ece297fe628fa1804d"},
{"name":"graphql","version":"2.3.16","platform":"ruby","checksum":"6ac9966998914c5b470e527c7105d936db70af2306ec0445f88953404bce49c7"},
{"name":"graphql","version":"2.3.17","platform":"ruby","checksum":"5e03655bf8ab07cd9710821f7f991ca6f5e86cf632261350ab185527169554e3"},
{"name":"graphql-client","version":"0.23.0","platform":"ruby","checksum":"f238b8e451676baad06bd15f95396e018192243dcf12c4e6d13fb41d9a2babc1"},
{"name":"graphql-docs","version":"5.0.0","platform":"ruby","checksum":"76baca6e5a803a4b6a9fbbbfdbf16742b7c4c546c8592b6e1a7aa4e79e562d04"},
{"name":"grpc","version":"1.63.0","platform":"aarch64-linux","checksum":"dc75c5fd570b819470781d9512105dddfdd11d984f38b8e60bb946f92d1f79ee"},

View File

@ -929,7 +929,7 @@ GEM
faraday (~> 2.0)
graphql-client
graphlyte (1.0.0)
graphql (2.3.16)
graphql (2.3.17)
base64
fiber-storage
graphql-client (0.23.0)

View File

@ -6,7 +6,7 @@ require "guard/rspec/dsl"
cmd = ENV['GUARD_CMD'] || (ENV['SPRING'] ? 'spring rspec' : 'bundle exec rspec')
directories %w[app ee keeps lib rubocop tooling spec]
directories %w[app ee keeps lib rubocop scripts spec tooling]
rspec_context_for = proc do |context_path|
OpenStruct.new(to_s: "spec").tap do |rspec| # rubocop:disable Style/OpenStructUse
@ -45,6 +45,7 @@ guard_setup = proc do |context_path|
watch(%r{^#{context_path}(lib/.+)\.rb$}) { |m| rspec.spec.call(m[1]) }
watch(%r{^#{context_path}(rubocop/.+)\.rb$}) { |m| rspec.spec.call(m[1]) }
watch(%r{^#{context_path}(tooling/.+)\.rb$}) { |m| rspec.spec.call(m[1]) }
watch(%r{^#{context_path}(scripts/.+)\.rb$}) { |m| rspec.spec.call(m[1].tr('-', '_')) }
# Rails files
rails = rails_context_for.call(context_path, %w[erb haml slim])

View File

@ -65,7 +65,7 @@ export default {
<template #header>
<div
class="gl-flex gl-min-h-8 gl-items-center gl-border-b-1 gl-border-b-gray-200 !gl-p-4 gl-border-b-solid"
class="gl-flex gl-min-h-8 gl-items-center gl-border-b-1 gl-border-b-dropdown !gl-p-4 gl-border-b-solid"
>
<span class="gl-grow gl-pr-2 gl-text-sm gl-font-bold">
{{ n__('%d pending comment', '%d pending comments', draftsCount) }}

View File

@ -133,6 +133,7 @@ export default {
:column-index="columnIndex"
@toggleNewForm="toggleNewForm"
@setFilters="$emit('setFilters', $event)"
@cannot-find-active-item="$emit('cannot-find-active-item')"
/>
</div>
<div

View File

@ -7,6 +7,7 @@ import BoardAddNewColumn from 'ee_else_ce/boards/components/board_add_new_column
import BoardAddNewColumnTrigger from '~/boards/components/board_add_new_column_trigger.vue';
import WorkItemDrawer from '~/work_items/components/work_item_drawer.vue';
import { s__ } from '~/locale';
import { removeParams, updateHistory } from '~/lib/utils/url_utility';
import { defaultSortableOptions, DRAG_DELAY } from '~/sortable/constants';
import { mapWorkItemWidgetsToIssuableFields } from '~/issues/list/utils';
import {
@ -19,6 +20,7 @@ import {
DEFAULT_BOARD_LIST_ITEMS_SIZE,
BoardType,
} from 'ee_else_ce/boards/constants';
import { DETAIL_VIEW_QUERY_PARAM_NAME } from '~/work_items/constants';
import { calculateNewPosition } from 'ee_else_ce/boards/boards_util';
import { setError } from '../graphql/cache_updates';
import BoardColumn from './board_column.vue';
@ -88,6 +90,7 @@ export default {
return {
boardHeight: null,
highlightedLists: [],
columnsThatCannotFindActiveItem: 0,
};
},
computed: {
@ -245,6 +248,14 @@ export default {
isLastList(index) {
return this.boardListsToUse.length - 1 === index;
},
handleCannotFindActiveItem() {
this.columnsThatCannotFindActiveItem += 1;
if (this.columnsThatCannotFindActiveItem === this.boardListsToUse.length) {
updateHistory({
url: removeParams([DETAIL_VIEW_QUERY_PARAM_NAME]),
});
}
},
},
};
</script>
@ -281,6 +292,7 @@ export default {
@setActiveList="$emit('setActiveList', $event)"
@setFilters="$emit('setFilters', $event)"
@addNewListAfter="$emit('setAddColumnFormVisibility', $event)"
@cannot-find-active-item="handleCannotFindActiveItem"
/>
<transition mode="out-in" name="slide" @after-enter="afterFormEnters">

View File

@ -7,6 +7,7 @@ import { ESC_KEY_CODE } from '~/lib/utils/keycodes';
import { defaultSortableOptions, DRAG_DELAY } from '~/sortable/constants';
import { sortableStart, sortableEnd } from '~/sortable/utils';
import Tracking from '~/tracking';
import { getParameterByName } from '~/lib/utils/url_utility';
import listQuery from 'ee_else_ce/boards/graphql/board_lists_deferred.query.graphql';
import setActiveBoardItemMutation from 'ee_else_ce/boards/graphql/client/set_active_board_item.mutation.graphql';
import BoardNewIssue from 'ee_else_ce/boards/components/board_new_issue.vue';
@ -18,6 +19,7 @@ import {
listIssuablesQueries,
ListType,
} from 'ee_else_ce/boards/constants';
import { DETAIL_VIEW_QUERY_PARAM_NAME } from '~/work_items/constants';
import {
addItemToList,
removeItemFromList,
@ -90,6 +92,7 @@ export default {
addItemToListInProgress: false,
updateIssueOrderInProgress: false,
dragCancelled: false,
hasMadeDrawerAttempt: false,
};
},
apollo: {
@ -128,6 +131,28 @@ export default {
message: s__('Boards|An error occurred while fetching a list. Please try again.'),
});
},
result({ data }) {
if (this.hasMadeDrawerAttempt) {
return;
}
const queryParam = getParameterByName(DETAIL_VIEW_QUERY_PARAM_NAME);
if (!data || !queryParam) {
return;
}
const { iid, full_path: fullPath } = JSON.parse(atob(queryParam));
const boardItem = this.boardListItems.find(
(item) => item.iid === iid && item.referencePath.includes(fullPath),
);
if (boardItem) {
this.setActiveWorkItem(boardItem);
} else {
this.$emit('cannot-find-active-item');
}
this.hasMadeDrawerAttempt = true;
},
},
toList: {
query() {
@ -603,16 +628,19 @@ export default {
});
} finally {
this.addItemToListInProgress = false;
this.$apollo.mutate({
mutation: setActiveBoardItemMutation,
variables: {
boardItem: issuable,
listId: this.list.id,
isIssue: this.isIssueBoard,
},
});
this.setActiveWorkItem(issuable);
}
},
setActiveWorkItem(boardItem) {
this.$apollo.mutate({
mutation: setActiveBoardItemMutation,
variables: {
boardItem,
listId: this.list.id,
isIssue: this.isIssueBoard,
},
});
},
},
};
</script>

View File

@ -145,14 +145,18 @@ export default {
return Number(this.pageInfo.hasNextPage);
},
fields() {
return [
this.canBulkDestroyArtifacts && {
key: 'checkbox',
label: '',
thClass: 'gl-w-1/20',
},
...this.$options.fields,
];
if (this.canBulkDestroyArtifacts) {
return [
{
key: 'checkbox',
label: '',
thClass: 'gl-w-1/20',
},
...this.$options.fields,
];
}
return this.$options.fields;
},
anyArtifactsSelected() {
return Boolean(this.selectedArtifacts.length);

View File

@ -180,7 +180,7 @@ export default {
:data-testid="testid"
>
<template #list-item>
<div class="-gl-my-1 -gl-ml-2 gl-flex gl-items-center gl-justify-between">
<div class="-gl-my-2 -gl-ml-2 gl-flex gl-items-center gl-justify-between">
<job-name-component
v-gl-tooltip.viewport.left
:title="tooltipText"

View File

@ -137,7 +137,7 @@ export default {
<template #header>
<div
class="gl-flex gl-min-h-8 gl-items-center gl-border-b-1 gl-border-b-gray-200 !gl-p-4 gl-text-sm gl-font-bold gl-leading-1 gl-border-b-solid"
class="gl-flex gl-min-h-8 gl-items-center gl-border-b-1 gl-border-b-dropdown !gl-p-4 gl-text-sm gl-font-bold gl-leading-1 gl-border-b-solid"
>
<template v-if="isLoading">
<span>{{ $options.i18n.stage }}</span>

View File

@ -133,7 +133,7 @@ export default {
<template #header>
<div
data-testid="pipeline-stage-dropdown-menu-title"
class="gl-flex gl-min-h-8 gl-items-center gl-border-b-1 gl-border-b-gray-200 !gl-p-4 gl-text-sm gl-font-bold gl-leading-1 gl-border-b-solid"
class="gl-flex gl-min-h-8 gl-items-center gl-border-b-1 gl-border-b-dropdown !gl-p-4 gl-text-sm gl-font-bold gl-leading-1 gl-border-b-solid"
>
<span>{{ dropdownHeaderText }}</span>
</div>

View File

@ -144,11 +144,11 @@ export default {
<template #header>
<div
aria-hidden="true"
class="gl-flex gl-min-h-8 gl-items-center gl-border-b-1 gl-border-b-gray-200 !gl-p-4 gl-text-sm gl-font-bold gl-text-gray-900 gl-border-b-solid"
class="gl-flex gl-min-h-8 gl-items-center gl-border-b-1 gl-border-b-dropdown !gl-p-4 gl-text-sm gl-font-bold gl-text-gray-900 gl-border-b-solid"
>
{{ $options.i18n.downloadArtifacts }}
</div>
<div v-if="hasArtifacts" class="gl-border-b-1 gl-border-b-gray-200 gl-border-b-solid">
<div v-if="hasArtifacts" class="gl-border-b-1 gl-border-b-dropdown gl-border-b-solid">
<gl-search-box-by-type
ref="searchInput"
v-model.trim="searchQuery"

View File

@ -89,7 +89,7 @@ export default {
<template v-if="shouldRenderCreateButton" #footer>
<gl-button
category="tertiary"
class="!gl-justify-start !gl-rounded-tl-none !gl-rounded-tr-none !gl-border-t-1 gl-border-t-gray-200 !gl-pl-7 gl-border-t-solid"
class="!gl-justify-start !gl-rounded-tl-none !gl-rounded-tr-none !gl-border-t-1 gl-border-t-dropdown !gl-pl-7 gl-border-t-solid"
:class="{ 'gl-mt-3': !filteredResults.length }"
@click="selectAgent(searchTerm)"
>

View File

@ -170,7 +170,7 @@ export default {
<gl-search-box-by-type
ref="searchValue"
v-model="searchValue"
class="add-reaction-search gl-border-b-1 gl-border-b-gray-200 gl-border-b-solid"
class="add-reaction-search gl-border-b-1 gl-border-b-dropdown gl-border-b-solid"
borderless
autofocus
debounce="500"
@ -218,7 +218,7 @@ export default {
<template v-if="newCustomEmojiPath" #footer>
<div
class="gl-flex gl-flex-col gl-border-t-1 gl-border-t-gray-200 !gl-p-2 !gl-pt-0 gl-border-t-solid"
class="gl-flex gl-flex-col gl-border-t-1 gl-border-t-dropdown !gl-p-2 !gl-pt-0 gl-border-t-solid"
>
<gl-button
:href="newCustomEmojiPath"

View File

@ -37,7 +37,7 @@ export default {
'gl-text-base',
'gl-font-normal',
'gl-leading-normal',
'gl-shadow-inner-1-gray-200',
'gl-shadow-inner-1-dropdown',
'gl-py-3',
'gl-px-4',
'gl-mb-0',

View File

@ -135,7 +135,7 @@ export default {
<template v-if="shouldRenderSelectButton" #footer>
<gl-button
category="tertiary"
class="!gl-justify-start !gl-rounded-tl-none !gl-rounded-tr-none !gl-border-t-1 gl-border-t-gray-200 !gl-pl-7 gl-border-t-solid"
class="!gl-justify-start !gl-rounded-tl-none !gl-rounded-tr-none !gl-border-t-1 gl-border-t-dropdown !gl-pl-7 gl-border-t-solid"
:class="{ 'gl-mt-3': !filteredNamespacesList.length }"
@click="onSelect(searchTerm)"
>

View File

@ -324,7 +324,7 @@ export default {
<gl-dropdown
:text="__('Recent searches')"
class="filtered-search-history-dropdown-wrapper"
toggle-class="filtered-search-history-dropdown-toggle-button !gl-shadow-none !gl-border-r-gray-200 !gl-border-1 !gl-rounded-none"
toggle-class="filtered-search-history-dropdown-toggle-button !gl-shadow-none !gl-border-r-dropdown !gl-border-1 !gl-rounded-none"
:disabled="loading"
>
<div v-if="!$options.hasLocalStorage" class="gl-px-5">

View File

@ -99,7 +99,7 @@ export default {
<template #footer>
<div
v-if="isCreateEnvironmentShown"
class="gl-border-t-1 gl-border-t-gray-200 gl-p-2 gl-border-t-solid"
class="gl-border-t-1 gl-border-t-dropdown gl-p-2 gl-border-t-solid"
>
<gl-button
category="tertiary"

View File

@ -25,10 +25,11 @@ export default {
</script>
<template>
<settings-block id="incident-management-settings" data-testid="incidents-settings-content">
<template #title>
<span ref="sectionHeader">{{ $options.i18n.headerText }}</span>
</template>
<settings-block
id="incident-management-settings"
:title="$options.i18n.headerText"
data-testid="incidents-settings-content"
>
<template #description>
<span ref="sectionSubHeader">{{ $options.i18n.subHeaderText }}</span>
</template>

View File

@ -48,7 +48,12 @@ import axios from '~/lib/utils/axios_utils';
import { fetchPolicies } from '~/lib/graphql';
import { isPositiveInteger } from '~/lib/utils/number_utils';
import { scrollUp } from '~/lib/utils/scroll_utils';
import { getParameterByName, joinPaths } from '~/lib/utils/url_utility';
import {
getParameterByName,
joinPaths,
removeParams,
updateHistory,
} from '~/lib/utils/url_utility';
import { __ } from '~/locale';
import {
OPERATORS_IS,
@ -85,8 +90,12 @@ import IssuableList from '~/vue_shared/issuable/list/components/issuable_list_ro
import { DEFAULT_PAGE_SIZE, issuableListTabs } from '~/vue_shared/issuable/list/constants';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import NewResourceDropdown from '~/vue_shared/components/new_resource_dropdown/new_resource_dropdown.vue';
import { WORK_ITEM_TYPE_ENUM_OBJECTIVE } from '~/work_items/constants';
import {
WORK_ITEM_TYPE_ENUM_OBJECTIVE,
DETAIL_VIEW_QUERY_PARAM_NAME,
} from '~/work_items/constants';
import WorkItemDrawer from '~/work_items/components/work_item_drawer.vue';
import { makeDrawerUrlParam } from '~/work_items/utils';
import {
CREATED_DESC,
i18n,
@ -244,6 +253,7 @@ export default {
}
this.pageInfo = data[this.namespace]?.issues.pageInfo ?? {};
this.exportCsvPathWithQuery = this.getExportCsvPathWithQuery();
this.checkDrawerParams();
},
error(error) {
this.issuesError = this.$options.i18n.errorFetchingIssues;
@ -538,7 +548,10 @@ export default {
return this.tabCounts[this.state] ?? 0;
},
urlParams() {
return {
const show = this.activeIssuable
? makeDrawerUrlParam(this.activeIssuable, this.fullPath)
: undefined;
const base = {
sort: urlSortParams[this.sortKey],
state: this.state,
...this.urlFilterParams,
@ -547,6 +560,10 @@ export default {
page_after: this.pageParams.afterCursor ?? undefined,
page_before: this.pageParams.beforeCursor ?? undefined,
};
if (show) {
return { ...base, show };
}
return base;
},
// due to the issues with cache-and-network, we need this hack to check if there is any data for the query in the cache.
// if we have cached data, we disregard the loading state
@ -577,6 +594,11 @@ export default {
if (newValue.fullPath !== oldValue.fullPath) {
this.updateData(getParameterByName(PARAM_SORT));
}
if (newValue.query.show) {
this.checkDrawerParams();
} else {
this.activeIssuable = null;
}
},
},
created() {
@ -820,7 +842,6 @@ export default {
handleSelectIssuable(issuable) {
this.activeIssuable = {
...issuable,
fullPath: this.fullPath,
};
},
updateIssuablesCache(workItem) {
@ -831,9 +852,13 @@ export default {
});
const activeIssuable = issuesList[this.namespace].issues.nodes.find(
(issue) => issue.iid === workItem.iid,
(issue) => getIdFromGraphQLId(issue.id) === getIdFromGraphQLId(workItem.id),
);
if (!activeIssuable) {
return;
}
// when we change issuable state, it's moved to a different tab
// to ensure that we show 20 items of the first page, we need to refetch issuables
if (!activeIssuable.state.includes(workItem.state.toLowerCase())) {
@ -883,6 +908,29 @@ export default {
client.writeQuery({ query: getIssuesQuery, variables: this.queryVariables, data });
},
checkDrawerParams() {
const queryParam = getParameterByName(DETAIL_VIEW_QUERY_PARAM_NAME);
if (this.activeIssuable || !queryParam) {
return;
}
const params = JSON.parse(atob(queryParam));
if (params.id) {
const issue = this.issues.find((i) => getIdFromGraphQLId(i.id) === params.id);
if (issue) {
this.activeIssuable = {
...issue,
// we need fullPath here to prevent cache invalidation
fullPath: params.full_path,
};
} else {
updateHistory({
url: removeParams([DETAIL_VIEW_QUERY_PARAM_NAME]),
});
}
}
},
},
};
</script>

View File

@ -314,7 +314,7 @@ export default {
<template #footer>
<div
class="gl-flex gl-flex-col gl-border-t-1 gl-border-t-gray-200 !gl-p-2 !gl-pt-0 gl-border-t-solid"
class="gl-flex gl-flex-col gl-border-t-1 gl-border-t-dropdown !gl-p-2 !gl-pt-0 gl-border-t-solid"
>
<gl-button
category="tertiary"

View File

@ -44,8 +44,11 @@ export default {
},
},
watch: {
count(newVal) {
this.open = newVal > 0;
count: {
handler(newVal) {
this.open = newVal > 0;
},
immediate: true,
},
},
methods: {

View File

@ -187,7 +187,7 @@ export default {
<template v-if="directlyInviteMembers" #footer>
<div
class="gl-flex gl-flex-col gl-border-t-1 gl-border-t-gray-200 !gl-p-2 !gl-pt-0 gl-border-t-solid"
class="gl-flex gl-flex-col gl-border-t-1 gl-border-t-dropdown !gl-p-2 !gl-pt-0 gl-border-t-solid"
>
<invite-members-trigger
trigger-element="button"

View File

@ -171,7 +171,7 @@ export default {
</template>
<template #footer>
<div
class="gl-flex gl-flex-col gl-border-t-1 gl-border-t-gray-200 !gl-p-2 !gl-pt-0 gl-border-t-solid"
class="gl-flex gl-flex-col gl-border-t-1 gl-border-t-dropdown !gl-p-2 !gl-pt-0 gl-border-t-solid"
>
<gl-button
v-for="(item, idx) in extraLinks"

View File

@ -214,11 +214,11 @@ export default {
@action="setPromoteModalVisibility(true)"
/>
<gl-disclosure-dropdown-group v-if="canReadMilestone" bordered class="!gl-border-t-gray-200">
<gl-disclosure-dropdown-group v-if="canReadMilestone" bordered class="!gl-border-t-dropdown">
<gl-disclosure-dropdown-item :item="copyIdItem" :data-clipboard-text="id" />
</gl-disclosure-dropdown-group>
<gl-disclosure-dropdown-group v-if="showDelete" bordered class="!gl-border-t-gray-200">
<gl-disclosure-dropdown-group v-if="showDelete" bordered class="!gl-border-t-dropdown">
<gl-disclosure-dropdown-item :item="deleteItem" @action="setDeleteModalVisibility(true)" />
</gl-disclosure-dropdown-group>

View File

@ -16,8 +16,7 @@ export default {
</script>
<template>
<settings-block id="organization-settings-advanced">
<template #title>{{ $options.i18n.settingsBlock.title }}</template>
<settings-block id="organization-settings-advanced" :title="$options.i18n.settingsBlock.title">
<template #description>{{ $options.i18n.settingsBlock.description }}</template>
<template #default>
<change-url />

View File

@ -100,8 +100,11 @@ export default {
</script>
<template>
<settings-block id="organization-settings" default-expanded>
<template #title>{{ $options.i18n.settingsBlock.title }}</template>
<settings-block
id="organization-settings"
:title="$options.i18n.settingsBlock.title"
default-expanded
>
<template #description>{{ $options.i18n.settingsBlock.description }}</template>
<template #default>
<form-errors-alert v-model="errors" :scroll-on-error="true" />

View File

@ -48,8 +48,7 @@ export default {
</script>
<template>
<settings-block id="organization-settings-visibility">
<template #title>{{ $options.i18n.settingsBlock.title }}</template>
<settings-block id="organization-settings-visibility" :title="$options.i18n.settingsBlock.title">
<template #description>{{ $options.i18n.settingsBlock.description }}</template>
<template #default>
<gl-form :id="$options.formId">

View File

@ -70,7 +70,7 @@ export default {
<div data-testid="tag-name-search">
<gl-search-box-by-type
:value="query"
class="gl-border-b-1 gl-border-gray-200 gl-border-b-solid"
class="gl-border-b-1 gl-border-dropdown gl-border-b-solid"
borderless
autofocus
@input="onSearchBoxInput"
@ -95,7 +95,7 @@ export default {
{{ $options.i18n.noResults }}
</div>
</div>
<div class="gl-border-t-1 gl-border-gray-200 gl-py-3 gl-border-t-solid">
<div class="gl-border-t-1 gl-border-dropdown gl-py-3 gl-border-t-solid">
<gl-button
category="tertiary"
class="!gl-justify-start !gl-rounded-none"

View File

@ -240,7 +240,7 @@ export default {
v-if="isFocused"
v-outside.click.focusin="closeDropdown"
data-testid="header-search-dropdown-menu"
class="header-search-dropdown-menu gl-absolute gl-z-2 gl-mt-3 !gl-w-full !gl-min-w-full !gl-max-w-none gl-overflow-y-auto gl-rounded-base gl-border-1 gl-border-solid gl-border-gray-200 gl-bg-white gl-shadow-x0-y2-b4-s0"
class="header-search-dropdown-menu gl-absolute gl-z-2 gl-mt-3 !gl-w-full !gl-min-w-full !gl-max-w-none gl-overflow-y-auto gl-rounded-base gl-border-1 gl-border-solid gl-border-dropdown gl-bg-white gl-shadow-x0-y2-b4-s0"
>
<div class="header-search-dropdown-content gl-py-2">
<dropdown-keyboard-navigation

View File

@ -18,8 +18,14 @@ export function isExpanded(sectionArg) {
export function expandSection(sectionArg) {
const $section = $(sectionArg);
const title = $section.find('.js-settings-toggle-trigger-only').text();
$section.find('.js-settings-toggle:not(.js-settings-toggle-trigger-only)').text(__('Collapse'));
$section
.find('.js-settings-toggle:not(.js-settings-toggle-trigger-only) .gl-button-text')
.text(__('Collapse'));
$section
.find('.js-settings-toggle:not(.js-settings-toggle-trigger-only)')
.attr('aria-label', `${__('Collapse')} ${title}`);
$section.addClass('expanded');
if (!$section.hasClass('no-animate')) {
$section
@ -34,8 +40,15 @@ export function expandSection(sectionArg) {
export function closeSection(sectionArg) {
const $section = $(sectionArg);
const title = $section.find('.js-settings-toggle-trigger-only').text();
$section
.find('.js-settings-toggle:not(.js-settings-toggle-trigger-only) .gl-button-text')
.text(__('Expand'));
$section
.find('.js-settings-toggle:not(.js-settings-toggle-trigger-only)')
.attr('aria-label', `${__('Expand')} ${title}`);
$section.find('.js-settings-toggle:not(.js-settings-toggle-trigger-only)').text(__('Expand'));
$section.removeClass('expanded');
if (!$section.hasClass('no-animate')) {
$section

View File

@ -68,7 +68,7 @@ export default {
</button>
</template>
<template #header>
<span class="gl-border-b-1 gl-border-gray-200 gl-p-4 gl-border-b-solid">
<span class="gl-border-b-1 gl-border-dropdown gl-p-4 gl-border-b-solid">
{{ $options.i18n.header }}
</span>
</template>

View File

@ -163,7 +163,7 @@ export default {
</template>
<template v-if="!organizationSwitchingEnabled" #footer>
<div class="gl-border-t gl-mt-2 gl-border-t-gray-200 gl-px-4 gl-pt-3">
<div class="gl-border-t gl-mt-2 gl-border-t-dropdown gl-px-4 gl-pt-3">
<div class="gl-text-sm gl-font-bold">
{{ $options.i18n.switchOrganizations }}
</div>

View File

@ -1,5 +1,13 @@
<script>
import { GlLoadingIcon, GlKeysetPagination, GlButton, GlBadge, GlTab, GlTabs } from '@gitlab/ui';
import {
GlLoadingIcon,
GlKeysetPagination,
GlLink,
GlButton,
GlBadge,
GlTab,
GlTabs,
} from '@gitlab/ui';
import * as Sentry from '~/sentry/sentry_browser_wrapper';
import { createAlert } from '~/alert';
import { s__ } from '~/locale';
@ -14,6 +22,7 @@ const STATUS_BY_TAB = [['pending'], ['done'], ['pending', 'done']];
export default {
components: {
GlLink,
GlLoadingIcon,
GlKeysetPagination,
GlButton,
@ -209,6 +218,12 @@ export default {
@prev="prevPage"
@next="nextPage"
/>
<div class="gl-mt-5 gl-text-center">
<gl-link href="https://gitlab.com/gitlab-org/gitlab/-/issues/498315" target="_blank">{{
s__('Todos|Leave feedback')
}}</gl-link>
</div>
</div>
</div>
</div>

View File

@ -79,9 +79,9 @@ export default {
type: String,
required: false,
},
pipelineIid: {
type: Number,
required: false,
pipelineMiniGraphVariables: {
type: Object,
required: true,
},
buildsWithCoverage: {
type: Array,
@ -128,10 +128,6 @@ export default {
required: false,
default: false,
},
sourceProjectFullPath: {
type: String,
required: true,
},
targetProjectFullPath: {
type: String,
required: true,
@ -155,13 +151,14 @@ export default {
return this.hasPipeline && !this.ciStatus;
},
status() {
return this.pipeline.details && this.pipeline.details.status
? this.pipeline.details.status
: {};
return this.pipeline?.details?.status || {};
},
artifacts() {
return this.pipeline?.details?.artifacts;
},
hasArtifacts() {
return Boolean(this.pipeline?.details?.artifacts?.length);
},
hasStages() {
return this.pipeline?.details?.stages?.length > 0;
},
@ -210,9 +207,6 @@ export default {
this.buildsWithCoverage.length,
);
},
pipelineMiniGraphQueryId() {
return this.pipelineIid?.toString() || null;
},
isMergeTrain() {
return Boolean(this.pipeline.flags?.merge_train_pipeline);
},
@ -307,9 +301,9 @@ export default {
<div class="gl-inline-flex gl-grow gl-items-center gl-justify-between">
<div>
<pipeline-mini-graph
v-if="isGraphQLPipelineMiniGraph && pipelineMiniGraphQueryId"
:iid="pipelineMiniGraphQueryId"
:full-path="sourceProjectFullPath"
v-if="isGraphQLPipelineMiniGraph && pipelineMiniGraphVariables.iid"
:iid="pipelineMiniGraphVariables.iid"
:full-path="pipelineMiniGraphVariables.fullPath"
:is-merge-train="isMergeTrain"
:pipeline-etag="pipelineEtag"
/>
@ -323,6 +317,7 @@ export default {
/>
</div>
<pipeline-artifacts
v-if="hasArtifacts"
:pipeline-id="pipeline.id"
:artifacts="artifacts"
class="gl-ml-3"

View File

@ -1,8 +1,14 @@
<script>
import { sanitize } from '~/lib/dompurify';
import { n__ } from '~/locale';
import { n__, __ } from '~/locale';
import { createAlert } from '~/alert';
import { convertToGraphQLId } from '~/graphql_shared/utils';
import { TYPENAME_CI_PIPELINE } from '~/graphql_shared/constants';
import { getQueryHeaders, toggleQueryPollingByVisibility } from '~/ci/pipeline_details/graph/utils';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { PIPELINE_MINI_GRAPH_POLL_INTERVAL } from '~/ci/pipeline_details/constants';
import MergeRequestStore from '../stores/mr_widget_store';
import getMergePipeline from '../queries/get_merge_pipeline.query.graphql';
import ArtifactsApp from './artifacts_list_app.vue';
import DeploymentList from './deployment/deployment_list.vue';
import MrWidgetContainer from './mr_widget_container.vue';
@ -38,7 +44,39 @@ export default {
default: false,
},
},
data() {
return {
mergePipeline: {},
};
},
apollo: {
mergePipeline: {
context() {
return getQueryHeaders(this.mr.pipelineEtag);
},
query: getMergePipeline,
skip() {
return !this.useMergePipelineQuery;
},
variables() {
return {
fullPath: this.mr.targetProjectFullPath,
id: convertToGraphQLId(TYPENAME_CI_PIPELINE, this.mr.mergePipeline.id),
};
},
pollInterval: PIPELINE_MINI_GRAPH_POLL_INTERVAL,
update({ project }) {
return project?.pipeline || {};
},
error() {
createAlert({ message: __('There was a problem fetching the merge pipeline.') });
},
},
},
computed: {
useMergePipelineQuery() {
return this.isPostMerge && this.glFeatures?.ciGraphqlPipelineMiniGraph;
},
branch() {
return this.isPostMerge ? this.mr.targetBranch : this.mr.sourceBranch;
},
@ -57,6 +95,17 @@ export default {
pipeline() {
return this.isPostMerge ? this.mr.mergePipeline : this.mr.pipeline;
},
pipelineMiniGraphVariables() {
return this.isPostMerge
? {
fullPath: this.mergePipeline?.project?.fullPath,
iid: this.mergePipeline?.iid,
}
: {
fullPath: this.mr.pipelineProjectPath || '',
iid: this.mr.pipelineIid || '',
};
},
showCollapsedDeployments() {
return this.deployments.length > 3;
},
@ -74,6 +123,11 @@ export default {
return this.isPostMerge ? this.mr?.mergePipeline?.details?.status?.text : this.mr.ciStatus;
},
},
mounted() {
if (this.useMergePipelineQuery) {
toggleQueryPollingByVisibility(this.$apollo.queries.mergePipeline);
}
},
};
</script>
<template>
@ -82,7 +136,7 @@ export default {
:pipeline="pipeline"
:pipeline-coverage-delta="mr.pipelineCoverageDelta"
:pipeline-etag="mr.pipelineEtag"
:pipeline-iid="mr.pipelineIid"
:pipeline-mini-graph-variables="pipelineMiniGraphVariables"
:builds-with-coverage="mr.buildsWithCoverage"
:ci-status="ciStatus"
:has-ci="mr.hasCI"
@ -95,7 +149,6 @@ export default {
:retargeted="mr.retargeted"
:target-project-id="mr.targetProjectId"
:iid="mr.iid"
:source-project-full-path="mr.sourceProjectFullPath"
:target-project-full-path="mr.targetProjectFullPath"
/>
<template #footer>

View File

@ -0,0 +1,13 @@
query getMergePipeline($fullPath: ID!, $id: CiPipelineID!) {
project(fullPath: $fullPath) {
id
pipeline(id: $id) {
id
iid
project {
id
fullPath
}
}
}
}

View File

@ -3,6 +3,7 @@ import { STATUS_CLOSED, STATUS_MERGED, STATUS_OPEN } from '~/issues/constants';
import { formatDate, getTimeago, newDate, timeagoLanguageCode } from '~/lib/utils/datetime_utility';
import { machine } from '~/lib/utils/finite_state_machine';
import { badgeState } from '~/merge_requests/components/merge_request_header.vue';
import { cleanLeadingSeparator } from '~/lib/utils/url_utility';
import {
MTWPS_MERGE_STRATEGY,
MT_MERGE_STRATEGY,
@ -141,7 +142,10 @@ export default class MergeRequestStore {
this.isPipelineSkipped = this.ciStatus === 'skipped';
this.pipelineDetailedStatus = pipelineStatus;
this.isPipelineActive = data.pipeline ? data.pipeline.active : false;
this.pipelineIid = data.pipeline?.iid;
this.pipelineIid = data.pipeline?.iid?.toString() || '';
this.pipelineProjectPath = data.pipeline?.project_path
? cleanLeadingSeparator(data.pipeline?.project_path)
: '';
this.isPipelineBlocked =
data.only_allow_merge_if_pipeline_succeeds && pipelineStatus?.group === 'manual';
this.faviconOverlayPath = data.favicon_overlay_path;

View File

@ -47,9 +47,11 @@ export default {
>
<gl-sprintf :message="$options.i18n.message">
<template #link="{ content }">
<help-page-link href="subscriptions/gitlab_com/index" anchor="change-the-linked-group">{{
content
}}</help-page-link>
<help-page-link
href="subscriptions/gitlab_com/index"
anchor="link-subscription-to-a-group"
>{{ content }}</help-page-link
>
</template>
</gl-sprintf>
</gl-modal>

View File

@ -173,7 +173,7 @@ export default {
>
<template #header>
<div
class="gl-min-h-8 gl-border-b-1 gl-border-b-gray-200 !gl-p-4 gl-text-sm gl-font-bold gl-border-b-solid"
class="gl-min-h-8 gl-border-b-1 gl-border-b-dropdown !gl-p-4 gl-text-sm gl-font-bold gl-border-b-solid"
>
{{ __('Manage') }}
</div>

View File

@ -35,6 +35,12 @@ export default {
toggleButtonText() {
return this.expanded ? this.$options.i18n.collapseText : this.$options.i18n.expandText;
},
toggleButtonAriaLabel() {
return `${this.toggleButtonText} ${this.$scopedSlots.title || this.title}`;
},
expandedClass() {
return this.expanded ? 'expanded' : '';
},
collapseId() {
return this.id || uniqueId('settings-block-');
},
@ -54,8 +60,24 @@ export default {
</script>
<template>
<section :id="id" class="vue-settings-block settings no-animate">
<div class="gl-flex gl-items-start gl-justify-between">
<section :id="id" class="vue-settings-block settings no-animate" :class="expandedClass">
<div class="gl-flex gl-items-start gl-justify-between gl-gap-x-3">
<div class="-gl-mr-3 gl-shrink-0 gl-px-2 gl-py-0 sm:gl-mr-0 sm:gl-p-2">
<gl-button
category="tertiary"
size="small"
class="settings-toggle gl-shrink-0 !gl-pl-2 !gl-pr-0"
icon="chevron-lg-right"
button-text-classes="gl-sr-only"
:aria-label="toggleButtonAriaLabel"
:aria-expanded="ariaExpanded"
:aria-controls="collapseId"
data-testid="settings-block-toggle"
@click="toggleExpanded"
>
{{ toggleButtonText }}
</gl-button>
</div>
<div class="gl-grow">
<h2
role="button"
@ -63,34 +85,16 @@ export default {
class="gl-heading-2 !gl-mb-2 gl-cursor-pointer"
:aria-expanded="ariaExpanded"
:aria-controls="collapseId"
data-testid="settings-block-title"
@click="toggleExpanded"
>
<slot v-if="$scopedSlots.title" name="title"></slot>
<template v-else>{{ title }}</template>
{{ title }}
</h2>
<p class="gl-m-0 gl-text-subtle"><slot name="description"></slot></p>
</div>
<div class="gl-shrink-0 gl-px-2">
<gl-button
class="gl-min-w-12 gl-shrink-0"
:aria-expanded="ariaExpanded"
:aria-controls="collapseId"
data-testid="settings-block-toggle"
@click="toggleExpanded"
>
<span aria-hidden="true">
{{ toggleButtonText }}
</span>
<span class="gl-sr-only">
{{ toggleButtonText }}
<slot v-if="$scopedSlots.title" name="title"></slot>
<template v-else>{{ title }}</template>
</span>
</gl-button>
</div>
</div>
<gl-collapse :id="collapseId" v-model="expanded" data-testid="settings-block-content">
<div class="gl-pt-5">
<div class="gl-pl-7 gl-pt-5 sm:gl-pl-8">
<slot></slot>
</div>
</gl-collapse>

View File

@ -100,7 +100,9 @@ export default {
return this.issuable.iid;
},
workItemFullPath() {
return this.issuable.namespace?.fullPath;
return (
this.issuable.namespace?.fullPath || this.issuable.reference?.split(this.issuableSymbol)[0]
);
},
author() {
return this.issuable.author || {};
@ -267,6 +269,7 @@ export default {
return;
}
this.$emit('select-issuable', {
id: this.issuable.id,
iid: this.issuableIid,
webUrl: this.issuable.webUrl,
fullPath: this.workItemFullPath,

View File

@ -6,6 +6,7 @@ import PageSizeSelector from '~/vue_shared/components/page_size_selector.vue';
import { updateHistory, setUrlParams } from '~/lib/utils/url_utility';
import { __ } from '~/locale';
import { DRAG_DELAY } from '~/sortable/constants';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import FilteredSearchBar from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
@ -307,7 +308,9 @@ export default {
this.$emit('page-size-change', newPageSize);
},
isIssuableActive(issuable) {
return Boolean(issuable.iid === this.activeIssuable?.iid);
return Boolean(
getIdFromGraphQLId(issuable.id) === getIdFromGraphQLId(this.activeIssuable?.id),
);
},
},
PAGE_SIZE_STORAGE_KEY,

View File

@ -244,7 +244,7 @@ export default {
<slot name="list-item" :item="item">{{ item.text }}</slot>
</template>
<template v-if="showFooter" #footer>
<div class="gl-border-t-1 gl-border-t-gray-200 !gl-p-2 gl-border-t-solid">
<div class="gl-border-t-1 gl-border-t-dropdown !gl-p-2 gl-border-t-solid">
<slot name="footer"></slot>
</div>
</template>

View File

@ -482,7 +482,7 @@ export default {
});
},
openInModal({ event, modalWorkItem, context }) {
if (!this.workItemsAlphaEnabled || context === LINKED_ITEMS_ANCHOR) {
if (!this.workItemsAlphaEnabled || context === LINKED_ITEMS_ANCHOR || this.isDrawer) {
return;
}
@ -896,7 +896,7 @@ export default {
</section>
</section>
<work-item-detail-modal
v-if="!isModal"
v-if="!isModal && !isDrawer"
ref="modal"
:parent-id="workItem.id"
:work-item-id="modalWorkItemId"

View File

@ -5,8 +5,10 @@ import { __ } from '~/locale';
import deleteWorkItemMutation from '~/work_items/graphql/delete_work_item.mutation.graphql';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { TYPE_EPIC, TYPE_ISSUE } from '~/issues/constants';
import { DETAIL_VIEW_QUERY_PARAM_NAME } from '~/work_items/constants';
import * as Sentry from '~/sentry/sentry_browser_wrapper';
import { visitUrl } from '~/lib/utils/url_utility';
import { visitUrl, setUrlParams, updateHistory, removeParams } from '~/lib/utils/url_utility';
import { makeDrawerItemFullPath, makeDrawerUrlParam } from '../utils';
export default {
name: 'WorkItemDrawer',
@ -51,14 +53,7 @@ export default {
},
computed: {
activeItemFullPath() {
if (this.activeItem?.fullPath) {
return this.activeItem.fullPath;
}
const delimiter = this.issuableType === TYPE_EPIC ? '&' : '#';
if (!this.activeItem.referencePath) {
return undefined;
}
return this.activeItem.referencePath.split(delimiter)[0];
return makeDrawerItemFullPath(this.activeItem, this.fullPath, this.issuableType);
},
modalIsGroup() {
return this.issuableType.toLowerCase() === TYPE_EPIC;
@ -75,6 +70,17 @@ export default {
);
},
},
watch: {
activeItem: {
deep: true,
immediate: true,
handler(newValue) {
if (newValue?.iid) {
this.setDrawerParams();
}
},
},
},
methods: {
async deleteWorkItem({ workItemId }) {
try {
@ -119,6 +125,17 @@ export default {
this.copyTooltipText = this.$options.i18n.copyTooltipText;
}, 2000);
},
setDrawerParams() {
const params = makeDrawerUrlParam(this.activeItem, this.fullPath, this.issuableType);
updateHistory({
// we're using `show` to match the modal view parameter
url: setUrlParams({ [DETAIL_VIEW_QUERY_PARAM_NAME]: params }),
});
},
handleClose() {
updateHistory({ url: removeParams([DETAIL_VIEW_QUERY_PARAM_NAME]) });
this.$emit('close');
},
handleClickOutside(event) {
for (const selector of this.$options.defaultExcludedSelectors) {
const excludedElements = document.querySelectorAll(selector);
@ -136,7 +153,7 @@ export default {
}
}
}
this.$emit('close');
this.handleClose();
},
},
i18n: {
@ -164,7 +181,7 @@ export default {
header-sticky
header-height="calc(var(--top-bar-height) + var(--performance-bar-height))"
class="gl-w-full gl-leading-reset lg:gl-w-[480px] xl:gl-w-[768px] min-[1440px]:gl-w-[912px]"
@close="$emit('close')"
@close="handleClose"
>
<template #title>
<div class="gl-text gl-flex gl-w-full gl-items-center gl-gap-x-2 xl:gl-px-4">

View File

@ -15,7 +15,7 @@ import {
convertToUrlParams,
} from 'ee_else_ce/issues/list/utils';
import { TYPENAME_USER } from '~/graphql_shared/constants';
import { convertToGraphQLId } from '~/graphql_shared/utils';
import { convertToGraphQLId, getIdFromGraphQLId } from '~/graphql_shared/utils';
import {
STATUS_ALL,
STATUS_CLOSED,
@ -65,8 +65,13 @@ import IssuableList from '~/vue_shared/issuable/list/components/issuable_list_ro
import WorkItemDrawer from '~/work_items/components/work_item_drawer.vue';
import { DEFAULT_PAGE_SIZE, issuableListTabs } from '~/vue_shared/issuable/list/constants';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { getParameterByName } from '~/lib/utils/url_utility';
import { STATE_CLOSED, STATE_OPEN, WORK_ITEM_TYPE_ENUM_EPIC } from '../constants';
import { getParameterByName, removeParams, updateHistory } from '~/lib/utils/url_utility';
import {
STATE_CLOSED,
STATE_OPEN,
WORK_ITEM_TYPE_ENUM_EPIC,
DETAIL_VIEW_QUERY_PARAM_NAME,
} from '../constants';
import getWorkItemsQuery from '../graphql/list/get_work_items.query.graphql';
import getWorkItemStateCountsQuery from '../graphql/list/get_work_item_state_counts.query.graphql';
import { sortOptions, urlSortParams } from './list/constants';
@ -163,6 +168,7 @@ export default {
document.title = `Issues · ${data.project.name} · GitLab`;
}
}
this.checkDrawerParams();
},
error(error) {
this.error = s__(
@ -416,6 +422,11 @@ export default {
if (newValue.fullPath !== oldValue.fullPath) {
this.updateData(getParameterByName(PARAM_SORT));
}
if (newValue.query[DETAIL_VIEW_QUERY_PARAM_NAME] && !this.$apollo.queries.workItems.loading) {
this.checkDrawerParams();
} else {
this.activeItem = null;
}
},
},
created() {
@ -564,6 +575,29 @@ export default {
this.sortKey = deriveSortKey({ sort, sortMap: urlSortParams });
this.state = state || STATUS_OPEN;
},
checkDrawerParams() {
const queryParam = getParameterByName(DETAIL_VIEW_QUERY_PARAM_NAME);
if (!queryParam) {
return;
}
const params = JSON.parse(atob(queryParam));
if (params.id) {
const issue = this.workItems.find((i) => getIdFromGraphQLId(i.id) === params.id);
if (issue) {
this.activeItem = {
...issue,
// we need fullPath here to prevent cache invalidation
fullPath: params.full_path,
};
} else {
updateHistory({
url: removeParams([DETAIL_VIEW_QUERY_PARAM_NAME]),
});
}
}
},
},
};
</script>

View File

@ -2,6 +2,7 @@ import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import { queryToObject } from '~/lib/utils/url_utility';
import AccessorUtilities from '~/lib/utils/accessor';
import { parseBoolean } from '~/lib/utils/common_utils';
import { TYPE_EPIC, TYPE_ISSUE } from '~/issues/constants';
import {
NEW_WORK_ITEM_IID,
@ -248,6 +249,40 @@ export const getShowLabelsFromLocalStorage = (showLabelsLocalStorageKey, default
return null;
};
/**
* @param {{fullPath?: string, referencePath?: string}} activeItem
* @param {string} fullPath
* @param {string} issuableType
* @returns {string}
*/
export const makeDrawerItemFullPath = (activeItem, fullPath, issuableType = TYPE_ISSUE) => {
if (activeItem?.fullPath) {
return activeItem.fullPath;
}
const delimiter = issuableType === TYPE_EPIC ? '&' : '#';
if (!activeItem?.referencePath) {
return fullPath;
}
return activeItem.referencePath.split(delimiter)[0];
};
/**
* since legacy epics don't have GID matching the work item ID, we need additional parameters
* @param {{iid: string, id: string}} activeItem
* @param {string} fullPath
* @param {string} issuableType
* @returns {{iid: string, full_path: string, id: number}}
*/
export const makeDrawerUrlParam = (activeItem, fullPath, issuableType = TYPE_ISSUE) => {
return btoa(
JSON.stringify({
iid: activeItem.iid,
full_path: makeDrawerItemFullPath(activeItem, fullPath, issuableType),
id: getIdFromGraphQLId(activeItem.id),
}),
);
};
export const getNewWorkItemAutoSaveKey = (fullPath, workItemType) => {
if (!workItemType || !fullPath) return '';
return `new-${fullPath}-${workItemType.toLowerCase()}-draft`;

View File

@ -42,6 +42,14 @@
&.animating {
overflow: hidden;
}
.settings-toggle svg {
@apply gl-transition;
}
&.expanded .settings-toggle svg {
transform: rotate(90deg);
}
}
.vue-settings-block {
@ -163,4 +171,4 @@
background-color: var(--orange-50, $orange-50);
border: 1px solid var(--orange-200, $orange-200);
border-radius: $gl-border-radius-base;
}
}

View File

@ -2,15 +2,15 @@
- if callout?
= callout
.gl-flex.gl-justify-between.gl-items-start.gl-gap-x-3.gl-pt-5
.gl-shrink-0.gl-px-2.gl-py-0.-gl-mr-3.sm:gl-p-2.sm:gl-mr-0
= render Pajamas::ButtonComponent.new(category: :tertiary, size: :small, icon: 'chevron-lg-right', icon_classes: '!-gl-mx-2', button_text_classes: 'gl-sr-only', button_options: @button_options.merge(class: 'settings-toggle js-settings-toggle', 'aria-label': aria_label)) do
= button_text
.gl-grow
%h2{ class: title_classes }
= heading || @heading
- if description || @description
%p.gl-text-subtle.gl-m-0
= description || @description
.gl-shrink-0.gl-px-2
= render Pajamas::ButtonComponent.new(button_options: @button_options.merge(class: 'gl-min-w-12 js-settings-toggle')) do
= button_text
.settings-content
.gl-mt-5
.gl-pl-7.sm:gl-pl-8.gl-mt-5
= body

View File

@ -41,5 +41,9 @@ module Layouts
def button_text
@expanded ? _('Collapse') : _('Expand')
end
def aria_label
@expanded ? "#{_('Collapse')} #{@heading}" : "#{_('Expand')} #{@heading}"
end
end
end

View File

@ -3,6 +3,7 @@
module StreamDiffs
extend ActiveSupport::Concern
include ActionController::Live
include DiffHelper
def diffs
return render_404 unless rapid_diffs_enabled?
@ -11,7 +12,7 @@ module StreamDiffs
offset = { offset_index: params.permit(:offset)[:offset].to_i }
stream_diff_files(options.merge(offset))
stream_diff_files(streaming_diff_options.merge(offset))
rescue StandardError => e
Gitlab::AppLogger.error("Error streaming diffs: #{e.message}")
response.stream.write e.message
@ -29,8 +30,8 @@ module StreamDiffs
raise NotImplementedError
end
def options
{}
def streaming_diff_options
diff_options
end
def view

View File

@ -41,6 +41,7 @@ class GroupsController < Groups::ApplicationController
push_force_frontend_feature_flag(:work_items_beta, group.work_items_beta_feature_flag_enabled?)
push_force_frontend_feature_flag(:work_items_alpha, group.work_items_alpha_feature_flag_enabled?)
push_frontend_feature_flag(:issues_grid_view)
push_frontend_feature_flag(:issues_list_drawer, group)
push_force_frontend_feature_flag(:namespace_level_work_items, group.namespace_work_items_enabled?)
end

View File

@ -10,11 +10,12 @@ module Projects
commit
end
def options
opts = diff_options
opts[:offset_index] = params.permit(:offset)[:offset].to_i
def streaming_diff_options
opts = super
opts[:ignore_whitespace_change] = true if params.permit(:format)[:format] == 'diff'
opts[:use_extra_viewer_as_main] = false
opts
end
end

View File

@ -11,10 +11,6 @@ module Projects
@merge_request
end
def options
{}
end
def stream_diff_files(options)
if !!ActiveModel::Type::Boolean.new.cast(params[:diff_blobs])
stream_diff_blobs(options)

View File

@ -11,6 +11,7 @@ class ProjectSetting < ApplicationRecord
belongs_to :project, inverse_of: :project_setting
scope :for_projects, ->(projects) { where(project_id: projects) }
scope :with_namespace, -> { joins(project: :namespace) }
attr_encrypted :cube_api_key,
mode: :per_attribute_iv,

View File

@ -12,6 +12,10 @@ class MergeRequests::PipelineEntity < Grape::Entity
project_pipeline_path(pipeline.project, pipeline)
end
expose :project_path do |pipeline|
project_path(pipeline.project)
end
expose :flags do
expose :merged_result_pipeline?, as: :merge_request_pipeline # deprecated, use merged_result_pipeline going forward
expose :merged_result_pipeline?, as: :merged_result_pipeline

View File

@ -6,6 +6,7 @@ module Import
MEMBER_DELETE_BATCH_SIZE = 1_000
GROUP_FINDER_MEMBER_RELATIONS = %i[direct inherited shared_from_groups].freeze
PROJECT_FINDER_MEMBER_RELATIONS = %i[direct inherited invited_groups shared_into_ancestors].freeze
RELATION_BATCH_SLEEP = 5
def initialize(import_source_user)
@import_source_user = import_source_user
@ -73,6 +74,9 @@ module Import
user_reference_column: user_reference_column
) do |model_relation, placeholder_references|
reassign_placeholder_records_batch(model_relation, placeholder_references, user_reference_column)
# TODO: Remove with https://gitlab.com/gitlab-org/gitlab/-/issues/493977
Kernel.sleep RELATION_BATCH_SLEEP
end
rescue NameError => e
::Import::Framework::Logger.error(

View File

@ -4,7 +4,7 @@
id: 'js-gitpod-settings',
expanded: expanded) do |c|
- c.with_description do
#js-gitpod-settings-help-text{ data: {"message" => gitpod_enable_description, "message-url" => "https://gitpod.io/" } }
%span#js-gitpod-settings-help-text{ data: {"message" => gitpod_enable_description, "message-url" => "https://gitpod.io/" } }
= link_to sprite_icon('question-o'), help_page_path('integration/gitpod.md'), target: '_blank', class: 'has-tooltip', title: _('More information')
- c.with_body do
= gitlab_ui_form_for @application_setting, url: general_admin_application_settings_path(anchor: 'js-gitpod-settings'), html: { class: 'fieldset-form', id: 'gitpod-settings' } do |f|

View File

@ -3,6 +3,6 @@
- if group.linked_to_subscription?
= render Pajamas::AlertComponent.new(variant: :tip, dismissible: false, alert_options: { class: 'gl-mb-5', data: { testid: 'group-has-linked-subscription-alert' }}) do |c|
- c.with_body do
= html_escape(_("This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group.")) % { linkStart: "<a href=\"#{help_page_path('subscriptions/gitlab_com/index.md', anchor: 'change-the-linked-group')}\">".html_safe, linkEnd: '</a>'.html_safe }
= html_escape(_("This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group.")) % { linkStart: "<a href=\"#{help_page_path('subscriptions/gitlab_com/index.md', anchor: 'link-subscription-to-a-group')}\">".html_safe, linkEnd: '</a>'.html_safe }
.js-confirm-danger{ data: group_confirm_modal_data(group: group, remove_form_id: remove_form_id) }

View File

@ -21,5 +21,5 @@
- if group.paid?
= render Pajamas::AlertComponent.new(variant: :tip, dismissible: false, alert_options: { class: 'gl-mb-5' }) do |c|
- c.with_body do
= html_escape(_("This group can't be transferred because it is linked to a subscription. To transfer this group, %{linkStart}link the subscription%{linkEnd} with a different group.")) % { linkStart: "<a href=\"#{help_page_path('subscriptions/gitlab_com/index.md', anchor: 'change-the-linked-group')}\">".html_safe, linkEnd: '</a>'.html_safe }
= html_escape(_("This group can't be transferred because it is linked to a subscription. To transfer this group, %{linkStart}link the subscription%{linkEnd} with a different group.")) % { linkStart: "<a href=\"#{help_page_path('subscriptions/gitlab_com/index.md', anchor: 'link-subscription-to-a-group')}\">".html_safe, linkEnd: '</a>'.html_safe }
.js-transfer-group-form{ data: initial_data }

View File

@ -12,6 +12,9 @@ module Import
sidekiq_options retry: 5, dead: false
sidekiq_options max_retries_after_interruption: 20
# TODO: Remove with https://gitlab.com/gitlab-org/gitlab/-/issues/493977
concurrency_limit -> { 4 }
sidekiq_retries_exhausted do |msg, exception|
new.perform_failure(exception, msg['args'])
end

View File

@ -5,7 +5,7 @@ class PostReceive
idempotent!
deduplicate :none
data_consistency :sticky, feature_flag: :load_balanacing_for_sticky_post_receive_worker
data_consistency :sticky
sidekiq_options retry: 3
include Gitlab::Experiment::Dsl

View File

@ -0,0 +1,12 @@
---
api_type:
attr: allow_top_level_group_owners_to_create_service_accounts
clusterwide: false
column: allow_top_level_group_owners_to_create_service_accounts
db_type: boolean
default: 'false'
description:
encrypted: false
gitlab_com_different_than_default: false
jihu: false
not_null: true

View File

@ -8,6 +8,6 @@ default: "'{}'::jsonb"
description: "[Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/408314) in
GitLab 17.0. For available options, see [Options for `default_branch_protection_defaults`](groups.md#options-for-default_branch_protection_defaults)."
encrypted: false
gitlab_com_different_than_default: false
gitlab_com_different_than_default: true
jihu: false
not_null: true

View File

@ -10,6 +10,6 @@ description: Specifies whether users who have not confirmed their email should b
`unconfirmed_users_delete_after_days` days. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/352514)
in GitLab 16.1. Self-managed, Premium and Ultimate only.
encrypted: false
gitlab_com_different_than_default: false
gitlab_com_different_than_default: true
jihu: false
not_null: true

View File

@ -1,11 +1,14 @@
---
api_type:
api_type: boolean
attr: pre_receive_secret_detection_enabled
clusterwide: true
column: pre_receive_secret_detection_enabled
db_type: boolean
default: 'false'
description:
description: Allow projects to enable secret push protection. This does not enable
secret push protection. When you enable this feature, you accept the [GitLab Testing
Agreement](https://handbook.gitlab.com/handbook/legal/testing-agreement/). Ultimate
only.
encrypted: false
gitlab_com_different_than_default: true
jihu: false

View File

@ -1,9 +0,0 @@
---
name: load_balanacing_for_sticky_post_receive_worker
feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/402254
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/167317
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/495040
milestone: '17.5'
group: group::source code
type: worker
default_enabled: false

View File

@ -0,0 +1,37 @@
- title: "GitLab Runner Docker Machine executor is deprecated"
# The milestones for the deprecation announcement, and the removal.
removal_milestone: "20.0"
announcement_milestone: "17.5"
# 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: DarrenEastman
stage: verify
issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/498268
# Use the impact calculator https://gitlab-com.gitlab.io/gl-infra/breaking-change-impact-calculator/?
impact: medium
scope: [instance, group, project]
resolution_role: [Admin, Owner, Maintainer]
manual_task: true
body: | # (required) Don't change this line.
The [GitLab Runner Docker Machine executor](https://docs.gitlab.com/runner/executors/docker_machine.html) is deprecated and will be fully removed from the product as a supported feature in GitLab 20.0 (May 2027). The replacement for Docker Machine, [GitLab Runner Autoscaler](https://docs.gitlab.com/runner/runner_autoscale/) with GitLab developed plugins for Amazon Web Services (AWS) EC2, Google Compute Engine (GCE) and Microsoft Azure virtual machines (VMs) is generally available. With this announcement, the GitLab Runner team will no longer accept community contributions for the GitLab maintained Docker Machine fork, or resolve newly identified bugs.
# ==============================
# OPTIONAL END-OF-SUPPORT FIELDS
# ==============================
#
# If an End of Support period applies:
# 1) Share this announcement in the `#spt_managers` Support channel in Slack
# 2) Mention `@gitlab-com/support` in this merge request.
#
# When support for this feature ends, in XX.YY milestone format.
end_of_support_milestone:
# Array of tiers the feature is currently available to,
# like [Free, Silver, Gold, Core, Premium, Ultimate]
tiers:
# Links to documentation and thumbnail image
documentation_url:
image_url:
# Use the youtube thumbnail URL with the structure of https://img.youtube.com/vi/UNIQUEID/hqdefault.jpg
video_url:

View File

@ -0,0 +1,8 @@
---
migration_job_name: RequeueBackfillPCiPipelineVariablesProjectId
description: Requeue backfill sharding key `p_ci_pipeline_variables.project_id` from `p_ci_pipelines` for gitlab.com
feature_category: continuous_integration
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/168493
milestone: '17.5'
queued_migration_version: 20241008064311
finalized_by: # version of the migration that finalized this BBM

View File

@ -0,0 +1,13 @@
# frozen_string_literal: true
class AddTempMigratingColumnToNamespaceHistoricalStatistics < Gitlab::Database::Migration[2.2]
milestone '17.5'
def up
add_column :vulnerability_namespace_historical_statistics, :migrating, :boolean, default: false, null: false
end
def down
remove_column :vulnerability_namespace_historical_statistics, :migrating
end
end

View File

@ -0,0 +1,36 @@
# frozen_string_literal: true
class RequeueBackfillPCiPipelineVariablesProjectId < Gitlab::Database::Migration[2.2]
milestone '17.5'
restrict_gitlab_migration gitlab_schema: :gitlab_ci
MIGRATION = "BackfillPCiPipelineVariablesProjectId"
DELAY_INTERVAL = 2.minutes
BATCH_SIZE = 75_000
SUB_BATCH_SIZE = 250
TABLE_NAME = :p_ci_pipeline_variables
BATCH_COLUMN = :id
JOB_ARGS = %i[project_id p_ci_pipelines project_id pipeline_id partition_id]
def up
return unless Gitlab.com_except_jh?
delete_batched_background_migration(MIGRATION, TABLE_NAME, BATCH_COLUMN, JOB_ARGS)
queue_batched_background_migration(
MIGRATION,
TABLE_NAME,
BATCH_COLUMN,
*JOB_ARGS,
job_interval: DELAY_INTERVAL,
batch_size: BATCH_SIZE,
sub_batch_size: SUB_BATCH_SIZE
)
end
def down
return unless Gitlab.com_except_jh?
delete_batched_background_migration(MIGRATION, TABLE_NAME, BATCH_COLUMN, JOB_ARGS)
end
end

View File

@ -0,0 +1 @@
5485b5a71ea889318831649bbdbc829b0565e7c01126cf9907544f6e39450bae

View File

@ -0,0 +1 @@
201b3763cefe1ada643eb517712f354aa4c10a7c872d99357e6e2b48848bf2d8

View File

@ -20658,7 +20658,8 @@ CREATE TABLE vulnerability_namespace_historical_statistics (
unknown integer DEFAULT 0 NOT NULL,
info integer DEFAULT 0 NOT NULL,
date date NOT NULL,
letter_grade smallint NOT NULL
letter_grade smallint NOT NULL,
migrating boolean DEFAULT false NOT NULL
);
CREATE SEQUENCE vulnerability_namespace_historical_statistics_id_seq

View File

@ -182,6 +182,8 @@ Backups do not include:
- [Mattermost data](../../integration/mattermost/index.md#back-up-gitlab-mattermost)
- Redis (and thus Sidekiq jobs)
- [Object storage](#object-storage) on Linux package (Omnibus) / Docker / Self-compiled installations
- [Global server hooks](../server_hooks.md#create-global-server-hooks-for-all-repositories)
- [File hooks](../file_hooks.md)
WARNING:
GitLab does not back up any configuration files (`/etc/gitlab`), TLS keys and certificates, or system

View File

@ -454,8 +454,12 @@ You can use the [AWS CLI](https://aws.amazon.com/cli/) to verify that access to
The S3 bucket contains a combination of **infrastructure logs** and **application logs** from the GitLab [log system](../../administration/logs/index.md). The logs in the bucket are encrypted using an AWS KMS key that is managed by GitLab. If you choose to enable [BYOK](../../administration/dedicated/create_instance.md#encrypted-data-at-rest-byok), the application logs are not encrypted with the key you provide.
<!-- vale gitlab_base.Spelling = NO -->
The logs in the S3 bucket are organized by date in `YYYY/MM/DD/HH` format. For example, there would be a directory like `2023/10/12/13`. That directory would contain the logs from October 12, 2023 at 1300 UTC. The logs are streamed into the bucket with [Amazon Kinesis Data Firehose](https://aws.amazon.com/firehose/).
<!-- vale gitlab_base.Spelling = YES -->
## Troubleshooting
### Outbound Private Link

View File

@ -596,7 +596,7 @@ If one or more of these values is significantly high, this could indicate a prob
If you get a **secondary** site in a broken state and want to reset the replication state,
to start again from scratch, there are a few steps that can help you:
1. Stop Sidekiq and the Geo LogCursor.
1. Stop Sidekiq and the Geo Log Cursor.
It's possible to make Sidekiq stop gracefully, but making it stop getting new jobs and
wait until the current jobs to finish processing.

View File

@ -59,7 +59,7 @@ When running Gitaly in Kubernetes, you must:
A pod can rotate for many reasons. Understanding and planing the service lifecycle helps minimize disruption.
For example, in Gitaly's case, a Kubernetes `StatefulSet` rotates on `spec.template` object changes, which can happen during Helm Chart upgrades (labels, or image tag) or pod resource requests or limits updates.
For example, with Gitaly, a Kubernetes `StatefulSet` rotates on `spec.template` object changes, which can happen during Helm Chart upgrades (labels, or image tag) or pod resource requests or limits updates.
This section focuses on common pod disruption cases and how to address them.

View File

@ -177,7 +177,7 @@ staggered across Gitaly nodes so the scheduled housekeeping is not running
simultaneously on multiple nodes.
If a scheduled housekeeping run reaches the `duration` specified, the running tasks are
gracefully cancelled. On subsequent scheduled housekeeping runs, Gitaly randomly shuffles
gracefully canceled. On subsequent scheduled housekeeping runs, Gitaly randomly shuffles
the repository list to process.
The following snippet enables daily background repository maintenance starting at

View File

@ -178,7 +178,7 @@ For more information, see [user deactivation emails](../administration/settings/
To deactivate users with the GitLab API, see [deactivate user](../api/user_moderation.md#deactivate-a-user). For information about permanent user restrictions, see [block and unblock users](#block-and-unblock-users).
To remove a user from a GitLab.com subscription, see
[Remove users from your subscription](../subscriptions/gitlab_com/index.md#remove-users-from-your-subscription).
[Remove users from your subscription](../subscriptions/gitlab_com/index.md#remove-users-from-subscription).
### Automatically deactivate dormant users

View File

@ -30,9 +30,13 @@ to the UI. This is properly balanced and scheduled, and therefore is
a better indicator of effective uptime. You can also monitor the sign-in
page `/users/sign_in` endpoint.
<!-- vale gitlab_base.Spelling = NO -->
On GitLab.com, tools such as [Pingdom](https://www.pingdom.com/) and
Apdex measurements are used to determine uptime.
<!-- vale gitlab_base.Spelling = YES -->
## IP allowlist
To access monitoring resources, the requesting client IP needs to be included in the allowlist.

View File

@ -561,7 +561,7 @@ To enable it:
1. On the left sidebar, at the bottom, select **Admin**.
1. Select **Settings > Preferences**.
1. Expand **Pages**.
1. Enter the email address for receiving notifications and accept Let's Encrypt's Terms of Service.
1. Enter the email address for receiving notifications and accept the Terms of Service for Let's Encrypt.
1. Select **Save changes**.
### Access control

View File

@ -1526,8 +1526,8 @@ input/output operations per second (IOPS) for read operations and 2,000 IOPS for
write operations. If you're running the environment on a Cloud provider,
refer to their documentation about how to configure IOPS correctly.
Gitaly servers must not be exposed to the public internet, as Gitaly's network
traffic is unencrypted by default. The use of a firewall is highly recommended
Gitaly servers must not be exposed to the public internet, as network traffic
on Gitaly is unencrypted by default. The use of a firewall is highly recommended
to restrict access to the Gitaly server. Another option is to
[use TLS](#gitaly-cluster-tls-support).

View File

@ -1532,8 +1532,8 @@ input/output operations per second (IOPS) for read operations and 2,000 IOPS for
write operations. If you're running the environment on a Cloud provider,
refer to their documentation about how to configure IOPS correctly.
Gitaly servers must not be exposed to the public internet, as Gitaly's network
traffic is unencrypted by default. The use of a firewall is highly recommended
Gitaly servers must not be exposed to the public internet, as network traffic
on Gitaly is unencrypted by default. The use of a firewall is highly recommended
to restrict access to the Gitaly server. Another option is to
[use TLS](#gitaly-cluster-tls-support).

View File

@ -444,8 +444,8 @@ Be sure to note the following items:
- A GitLab server can use one or more Gitaly server nodes.
- Gitaly addresses must be specified to be correctly resolvable for *all*
Gitaly clients.
- Gitaly servers must not be exposed to the public internet, as Gitaly's network
traffic is unencrypted by default. The use of a firewall is highly recommended
- Gitaly servers must not be exposed to the public internet, as network traffic
on Gitaly is unencrypted by default. The use of a firewall is highly recommended
to restrict access to the Gitaly server. Another option is to
[use TLS](#gitaly-tls-support).

View File

@ -1362,8 +1362,8 @@ input/output operations per second (IOPS) for read operations and 2,000 IOPS for
write operations. If you're running the environment on a Cloud provider,
refer to their documentation about how to configure IOPS correctly.
Gitaly servers must not be exposed to the public internet, as Gitaly's network
traffic is unencrypted by default. The use of a firewall is highly recommended
Gitaly servers must not be exposed to the public internet, as network traffic
on Gitaly is unencrypted by default. The use of a firewall is highly recommended
to restrict access to the Gitaly server. Another option is to
[use TLS](#gitaly-cluster-tls-support).
@ -2127,13 +2127,13 @@ If not stated below, no other modifications are supported for lower use counts.
- Combining select nodes: The following specific components are supported to be combined onto the same nodes to reduce complexity at the cost of some performance:
- GitLab Rails and Sidekiq: Sidekiq nodes can be removed, and the component instead enabled on the GitLab Rails nodes.
- PostgreSQL and PgBouncer: PgBouncer nodes could be removed and instead be enabled on PostgreSQL nodes with the Internal Load Balancer pointing to them. However, to enable [Database Load Balancing](../postgresql/database_load_balancing.md), a separate PgBouncer array is still required.
- Reducing the node counts: Some node types do not need consensus and can run with fewer nodes (but more than one for redundancy). This will also lead to reduced performance.
- Reducing the node counts: Some node types do not need consensus and can run with fewer nodes (but more than one for redundancy):
- GitLab Rails and Sidekiq: Stateless services don't have a minimum node count. Two are enough for redundancy.
- PostgreSQL and PgBouncer: A quorum is not strictly necessary. Two PostgreSQL nodes and two PgBouncer nodes are enough for redundancy.
- Consul, Redis Sentinel, and Praefect: Require an odd number, and a minimum of three nodes, for a voting quorum.
- Running select components in reputable Cloud PaaS solutions: The following specific components are supported to be run on reputable Cloud Provider PaaS solutions. By doing this, additional dependent components can also be removed:
- PostgreSQL: Can be run on reputable Cloud PaaS solutions such as Google Cloud SQL or Amazon RDS. In this setup, the PgBouncer and Consul nodes are no longer required:
- Consul may still be desired if [Prometheus](../monitoring/prometheus/index.md) auto discovery is a requirement, otherwise you would need to [manually add scrape configurations](../monitoring/prometheus/index.md#adding-custom-scrape-configurations) for all nodes.
- As Redis Sentinel runs on the same box as Consul in this architecture, it may need to be run on a separate box if Redis is still being run using the Linux package.
- Redis: Can be run on reputable Cloud PaaS solutions such as Google Memorystore and AWS ElastiCache. In this setup, the Redis Sentinel is no longer required.
## Cloud Native Hybrid reference architecture with Helm Charts (alternative)

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