Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2025-05-22 15:12:03 +00:00
parent 40ac69b43f
commit 98b4d737cd
68 changed files with 1284 additions and 837 deletions

View File

@ -7,7 +7,7 @@ workflow:
include: include:
- local: .gitlab/ci/version.yml - local: .gitlab/ci/version.yml
- local: .gitlab/ci/global.gitlab-ci.yml - local: .gitlab/ci/global.gitlab-ci.yml
- component: "gitlab.com/gitlab-org/quality/pipeline-common/allure-report@9.14.0" - component: "gitlab.com/gitlab-org/quality/pipeline-common/allure-report@11.3.0"
inputs: inputs:
job_name: "e2e-test-report" job_name: "e2e-test-report"
job_stage: "report" job_stage: "report"
@ -17,7 +17,7 @@ include:
gitlab_auth_token_variable_name: "PROJECT_TOKEN_FOR_CI_SCRIPTS_API_USAGE" gitlab_auth_token_variable_name: "PROJECT_TOKEN_FOR_CI_SCRIPTS_API_USAGE"
allure_job_name: "${QA_RUN_TYPE}" allure_job_name: "${QA_RUN_TYPE}"
- project: gitlab-org/quality/pipeline-common - project: gitlab-org/quality/pipeline-common
ref: 9.14.0 ref: 11.3.0
file: file:
- /ci/notify-slack.gitlab-ci.yml - /ci/notify-slack.gitlab-ci.yml
- /ci/qa-report.gitlab-ci.yml - /ci/qa-report.gitlab-ci.yml

View File

@ -27,8 +27,6 @@ Layout/LineBreakAfterFinalMixin:
- 'ee/app/workers/active_user_count_threshold_worker.rb' - 'ee/app/workers/active_user_count_threshold_worker.rb'
- 'ee/app/workers/analytics/value_stream_dashboard/count_worker.rb' - 'ee/app/workers/analytics/value_stream_dashboard/count_worker.rb'
- 'ee/app/workers/arkose/blocked_users_report_worker.rb' - 'ee/app/workers/arkose/blocked_users_report_worker.rb'
- 'ee/app/workers/geo/metrics_update_worker.rb'
- 'ee/app/workers/geo/sidekiq_cron_config_worker.rb'
- 'ee/app/workers/gitlab_subscriptions/notify_seats_exceeded_batch_worker.rb' - 'ee/app/workers/gitlab_subscriptions/notify_seats_exceeded_batch_worker.rb'
- 'ee/app/workers/historical_data_worker.rb' - 'ee/app/workers/historical_data_worker.rb'
- 'ee/app/workers/ldap_all_groups_sync_worker.rb' - 'ee/app/workers/ldap_all_groups_sync_worker.rb'

View File

@ -262,7 +262,7 @@ gem 'asciidoctor-kroki', '~> 0.10.0', require: false, feature_category: :markdow
gem 'rouge', '~> 4.5.0', feature_category: :shared gem 'rouge', '~> 4.5.0', feature_category: :shared
gem 'truncato', '~> 0.7.13', feature_category: :team_planning gem 'truncato', '~> 0.7.13', feature_category: :team_planning
gem 'nokogiri', '~> 1.18', feature_category: :shared gem 'nokogiri', '~> 1.18', feature_category: :shared
gem 'gitlab-glfm-markdown', '~> 0.0.30', feature_category: :markdown gem 'gitlab-glfm-markdown', '~> 0.0.31', feature_category: :markdown
gem 'tanuki_emoji', '~> 0.13', feature_category: :markdown gem 'tanuki_emoji', '~> 0.13', feature_category: :markdown
gem 'unicode-emoji', '~> 4.0', feature_category: :markdown gem 'unicode-emoji', '~> 4.0', feature_category: :markdown

View File

@ -223,13 +223,13 @@
{"name":"gitlab-dangerfiles","version":"4.9.1","platform":"ruby","checksum":"296b19d8aca5e4da8d391234914a1c4dfedc29700ddbcd9c554b6ffaa7fdf1b2"}, {"name":"gitlab-dangerfiles","version":"4.9.1","platform":"ruby","checksum":"296b19d8aca5e4da8d391234914a1c4dfedc29700ddbcd9c554b6ffaa7fdf1b2"},
{"name":"gitlab-experiment","version":"0.9.1","platform":"ruby","checksum":"f230ee742154805a755d5f2539dc44d93cdff08c5bbbb7656018d61f93d01f48"}, {"name":"gitlab-experiment","version":"0.9.1","platform":"ruby","checksum":"f230ee742154805a755d5f2539dc44d93cdff08c5bbbb7656018d61f93d01f48"},
{"name":"gitlab-fog-azure-rm","version":"2.2.0","platform":"ruby","checksum":"31aa7c2170f57874053144e7f716ec9e15f32e71ffbd2c56753dce46e2e78ba9"}, {"name":"gitlab-fog-azure-rm","version":"2.2.0","platform":"ruby","checksum":"31aa7c2170f57874053144e7f716ec9e15f32e71ffbd2c56753dce46e2e78ba9"},
{"name":"gitlab-glfm-markdown","version":"0.0.30","platform":"aarch64-linux-gnu","checksum":"faaa675d3934a066b0374a5ba4a1d97ec228195656814d5eb51c5db2356acad8"}, {"name":"gitlab-glfm-markdown","version":"0.0.31","platform":"aarch64-linux-gnu","checksum":"21bd0d9bccaeb1fc9787bbe8b4cabc30688d6800ae2212b7972c9438f0b40f59"},
{"name":"gitlab-glfm-markdown","version":"0.0.30","platform":"aarch64-linux-musl","checksum":"06407c9ce6753b6e6219302c875e0d981c15bdcc98a2df4f064771e5e40859da"}, {"name":"gitlab-glfm-markdown","version":"0.0.31","platform":"aarch64-linux-musl","checksum":"87b3b500dbab5dd003eff79d2909e8b23eb839fbfb49ab342cd173d6b9adb16d"},
{"name":"gitlab-glfm-markdown","version":"0.0.30","platform":"arm64-darwin","checksum":"788065d211288ff60cf3c5d875f678678cf03824d79b5e2d269aaa83c5c4f411"}, {"name":"gitlab-glfm-markdown","version":"0.0.31","platform":"arm64-darwin","checksum":"4f452db9f540ea1af0672042fe046b2b883d897f1325394894376da4e7acc1e8"},
{"name":"gitlab-glfm-markdown","version":"0.0.30","platform":"ruby","checksum":"f3dea33f9fc6d8991fd496631ef36b6e2406392e378d879f320c4c1b645c0066"}, {"name":"gitlab-glfm-markdown","version":"0.0.31","platform":"ruby","checksum":"91c8e9c61c78d49f1e52dbb49e9fd2d790a494a254bc8ad54004dadf091e2d1b"},
{"name":"gitlab-glfm-markdown","version":"0.0.30","platform":"x86_64-darwin","checksum":"583057ac3223900759a2bbc0f7dfe11befba74127217f883cd8ed2de2b7e2251"}, {"name":"gitlab-glfm-markdown","version":"0.0.31","platform":"x86_64-darwin","checksum":"3d9df3278add66356ce07c983b74f6f0bdefcdfb4110f43dd291c43fb69eb6a6"},
{"name":"gitlab-glfm-markdown","version":"0.0.30","platform":"x86_64-linux-gnu","checksum":"d43b185f48577fbe850fa49a5ff28ce2d875a4dd23b1c5333fae2ef4c40bea40"}, {"name":"gitlab-glfm-markdown","version":"0.0.31","platform":"x86_64-linux-gnu","checksum":"c2c4c8f77d078f2afb1420289e32f444e91ece48d8f76a78e43f5b69f5d2248c"},
{"name":"gitlab-glfm-markdown","version":"0.0.30","platform":"x86_64-linux-musl","checksum":"50ac59a1f9545387773fe0daa389ae146c73beb1acff4093011c2a9a1889879a"}, {"name":"gitlab-glfm-markdown","version":"0.0.31","platform":"x86_64-linux-musl","checksum":"cc3e8529f8eaf6be9bfa71509a58396540bb7c2b025e5dd9615a73ad0cf2a6b3"},
{"name":"gitlab-kas-grpc","version":"17.11.2","platform":"ruby","checksum":"f2b9054dcf636346e89549e9478a684a38bf03bf853332e84154ec3a9856ae1c"}, {"name":"gitlab-kas-grpc","version":"17.11.2","platform":"ruby","checksum":"f2b9054dcf636346e89549e9478a684a38bf03bf853332e84154ec3a9856ae1c"},
{"name":"gitlab-labkit","version":"0.37.0","platform":"ruby","checksum":"d2dd0a60db2149a9a8eebf2975dc23f54ac3ceb01bdba732eb1b26b86dfffa70"}, {"name":"gitlab-labkit","version":"0.37.0","platform":"ruby","checksum":"d2dd0a60db2149a9a8eebf2975dc23f54ac3ceb01bdba732eb1b26b86dfffa70"},
{"name":"gitlab-license","version":"2.6.0","platform":"ruby","checksum":"2c1f8ae73835640ec77bf758c1d0c9730635043c01cf77902f7976e826d7d016"}, {"name":"gitlab-license","version":"2.6.0","platform":"ruby","checksum":"2c1f8ae73835640ec77bf758c1d0c9730635043c01cf77902f7976e826d7d016"},

View File

@ -766,7 +766,7 @@ GEM
mime-types mime-types
net-http-persistent (~> 4.0) net-http-persistent (~> 4.0)
nokogiri (~> 1, >= 1.10.8) nokogiri (~> 1, >= 1.10.8)
gitlab-glfm-markdown (0.0.30) gitlab-glfm-markdown (0.0.31)
rb_sys (~> 0.9.109) rb_sys (~> 0.9.109)
gitlab-kas-grpc (17.11.2) gitlab-kas-grpc (17.11.2)
grpc (~> 1.0) grpc (~> 1.0)
@ -2163,7 +2163,7 @@ DEPENDENCIES
gitlab-duo-workflow-service-client (~> 0.1)! gitlab-duo-workflow-service-client (~> 0.1)!
gitlab-experiment (~> 0.9.1) gitlab-experiment (~> 0.9.1)
gitlab-fog-azure-rm (~> 2.2.0) gitlab-fog-azure-rm (~> 2.2.0)
gitlab-glfm-markdown (~> 0.0.30) gitlab-glfm-markdown (~> 0.0.31)
gitlab-housekeeper! gitlab-housekeeper!
gitlab-http! gitlab-http!
gitlab-kas-grpc (~> 17.11.0) gitlab-kas-grpc (~> 17.11.0)

View File

@ -223,13 +223,13 @@
{"name":"gitlab-dangerfiles","version":"4.9.1","platform":"ruby","checksum":"296b19d8aca5e4da8d391234914a1c4dfedc29700ddbcd9c554b6ffaa7fdf1b2"}, {"name":"gitlab-dangerfiles","version":"4.9.1","platform":"ruby","checksum":"296b19d8aca5e4da8d391234914a1c4dfedc29700ddbcd9c554b6ffaa7fdf1b2"},
{"name":"gitlab-experiment","version":"0.9.1","platform":"ruby","checksum":"f230ee742154805a755d5f2539dc44d93cdff08c5bbbb7656018d61f93d01f48"}, {"name":"gitlab-experiment","version":"0.9.1","platform":"ruby","checksum":"f230ee742154805a755d5f2539dc44d93cdff08c5bbbb7656018d61f93d01f48"},
{"name":"gitlab-fog-azure-rm","version":"2.2.0","platform":"ruby","checksum":"31aa7c2170f57874053144e7f716ec9e15f32e71ffbd2c56753dce46e2e78ba9"}, {"name":"gitlab-fog-azure-rm","version":"2.2.0","platform":"ruby","checksum":"31aa7c2170f57874053144e7f716ec9e15f32e71ffbd2c56753dce46e2e78ba9"},
{"name":"gitlab-glfm-markdown","version":"0.0.30","platform":"aarch64-linux-gnu","checksum":"faaa675d3934a066b0374a5ba4a1d97ec228195656814d5eb51c5db2356acad8"}, {"name":"gitlab-glfm-markdown","version":"0.0.31","platform":"aarch64-linux-gnu","checksum":"21bd0d9bccaeb1fc9787bbe8b4cabc30688d6800ae2212b7972c9438f0b40f59"},
{"name":"gitlab-glfm-markdown","version":"0.0.30","platform":"aarch64-linux-musl","checksum":"06407c9ce6753b6e6219302c875e0d981c15bdcc98a2df4f064771e5e40859da"}, {"name":"gitlab-glfm-markdown","version":"0.0.31","platform":"aarch64-linux-musl","checksum":"87b3b500dbab5dd003eff79d2909e8b23eb839fbfb49ab342cd173d6b9adb16d"},
{"name":"gitlab-glfm-markdown","version":"0.0.30","platform":"arm64-darwin","checksum":"788065d211288ff60cf3c5d875f678678cf03824d79b5e2d269aaa83c5c4f411"}, {"name":"gitlab-glfm-markdown","version":"0.0.31","platform":"arm64-darwin","checksum":"4f452db9f540ea1af0672042fe046b2b883d897f1325394894376da4e7acc1e8"},
{"name":"gitlab-glfm-markdown","version":"0.0.30","platform":"ruby","checksum":"f3dea33f9fc6d8991fd496631ef36b6e2406392e378d879f320c4c1b645c0066"}, {"name":"gitlab-glfm-markdown","version":"0.0.31","platform":"ruby","checksum":"91c8e9c61c78d49f1e52dbb49e9fd2d790a494a254bc8ad54004dadf091e2d1b"},
{"name":"gitlab-glfm-markdown","version":"0.0.30","platform":"x86_64-darwin","checksum":"583057ac3223900759a2bbc0f7dfe11befba74127217f883cd8ed2de2b7e2251"}, {"name":"gitlab-glfm-markdown","version":"0.0.31","platform":"x86_64-darwin","checksum":"3d9df3278add66356ce07c983b74f6f0bdefcdfb4110f43dd291c43fb69eb6a6"},
{"name":"gitlab-glfm-markdown","version":"0.0.30","platform":"x86_64-linux-gnu","checksum":"d43b185f48577fbe850fa49a5ff28ce2d875a4dd23b1c5333fae2ef4c40bea40"}, {"name":"gitlab-glfm-markdown","version":"0.0.31","platform":"x86_64-linux-gnu","checksum":"c2c4c8f77d078f2afb1420289e32f444e91ece48d8f76a78e43f5b69f5d2248c"},
{"name":"gitlab-glfm-markdown","version":"0.0.30","platform":"x86_64-linux-musl","checksum":"50ac59a1f9545387773fe0daa389ae146c73beb1acff4093011c2a9a1889879a"}, {"name":"gitlab-glfm-markdown","version":"0.0.31","platform":"x86_64-linux-musl","checksum":"cc3e8529f8eaf6be9bfa71509a58396540bb7c2b025e5dd9615a73ad0cf2a6b3"},
{"name":"gitlab-kas-grpc","version":"17.11.2","platform":"ruby","checksum":"f2b9054dcf636346e89549e9478a684a38bf03bf853332e84154ec3a9856ae1c"}, {"name":"gitlab-kas-grpc","version":"17.11.2","platform":"ruby","checksum":"f2b9054dcf636346e89549e9478a684a38bf03bf853332e84154ec3a9856ae1c"},
{"name":"gitlab-labkit","version":"0.37.0","platform":"ruby","checksum":"d2dd0a60db2149a9a8eebf2975dc23f54ac3ceb01bdba732eb1b26b86dfffa70"}, {"name":"gitlab-labkit","version":"0.37.0","platform":"ruby","checksum":"d2dd0a60db2149a9a8eebf2975dc23f54ac3ceb01bdba732eb1b26b86dfffa70"},
{"name":"gitlab-license","version":"2.6.0","platform":"ruby","checksum":"2c1f8ae73835640ec77bf758c1d0c9730635043c01cf77902f7976e826d7d016"}, {"name":"gitlab-license","version":"2.6.0","platform":"ruby","checksum":"2c1f8ae73835640ec77bf758c1d0c9730635043c01cf77902f7976e826d7d016"},

View File

@ -766,7 +766,7 @@ GEM
mime-types mime-types
net-http-persistent (~> 4.0) net-http-persistent (~> 4.0)
nokogiri (~> 1, >= 1.10.8) nokogiri (~> 1, >= 1.10.8)
gitlab-glfm-markdown (0.0.30) gitlab-glfm-markdown (0.0.31)
rb_sys (~> 0.9.109) rb_sys (~> 0.9.109)
gitlab-kas-grpc (17.11.2) gitlab-kas-grpc (17.11.2)
grpc (~> 1.0) grpc (~> 1.0)
@ -2163,7 +2163,7 @@ DEPENDENCIES
gitlab-duo-workflow-service-client (~> 0.1)! gitlab-duo-workflow-service-client (~> 0.1)!
gitlab-experiment (~> 0.9.1) gitlab-experiment (~> 0.9.1)
gitlab-fog-azure-rm (~> 2.2.0) gitlab-fog-azure-rm (~> 2.2.0)
gitlab-glfm-markdown (~> 0.0.30) gitlab-glfm-markdown (~> 0.0.31)
gitlab-housekeeper! gitlab-housekeeper!
gitlab-http! gitlab-http!
gitlab-kas-grpc (~> 17.11.0) gitlab-kas-grpc (~> 17.11.0)

View File

@ -1,3 +1,7 @@
# @graphql-eslint/eslint-plugin@4.0.0 reports missing IDs in `FragmentDefinition`. For now, we are
# ignoring the newly uncovered error on this fragment. Please consider addressing the violation
# if you are modifying this file and it turns out selecting the ID makes sense here.
# eslint-disable-next-line @graphql-eslint/require-selections
fragment BaseCiGroupVariable on CiGroupVariable { fragment BaseCiGroupVariable on CiGroupVariable {
description description
environmentScope environmentScope

View File

@ -1,3 +1,7 @@
# @graphql-eslint/eslint-plugin@4.0.0 reports missing IDs in `FragmentDefinition`. For now, we are
# ignoring the newly uncovered error on this fragment. Please consider addressing the violation
# if you are modifying this file and it turns out selecting the ID makes sense here.
# eslint-disable-next-line @graphql-eslint/require-selections
fragment BaseCiInstanceVariable on CiInstanceVariable { fragment BaseCiInstanceVariable on CiInstanceVariable {
description description
protected protected

View File

@ -1,3 +1,7 @@
# @graphql-eslint/eslint-plugin@4.0.0 reports missing IDs in `FragmentDefinition`. For now, we are
# ignoring the newly uncovered error on this fragment. Please consider addressing the violation
# if you are modifying this file and it turns out selecting the ID makes sense here.
# eslint-disable-next-line @graphql-eslint/require-selections
fragment BaseCiProjectVariable on CiProjectVariable { fragment BaseCiProjectVariable on CiProjectVariable {
description description
environmentScope environmentScope

View File

@ -27,7 +27,7 @@ query getJobs(
} }
nodes { nodes {
artifacts { artifacts {
# eslint-disable-next-line @graphql-eslint/require-id-when-available # eslint-disable-next-line @graphql-eslint/require-selections
nodes { nodes {
downloadPath downloadPath
fileType fileType

View File

@ -11,7 +11,7 @@ query getPipelineJobs($fullPath: ID!, $iid: ID!, $after: String) {
} }
nodes { nodes {
artifacts { artifacts {
# eslint-disable-next-line @graphql-eslint/require-id-when-available # eslint-disable-next-line @graphql-eslint/require-selections
nodes { nodes {
downloadPath downloadPath
fileType fileType

View File

@ -1,5 +1,9 @@
#import "./list_item_shared.fragment.graphql" #import "./list_item_shared.fragment.graphql"
fragment ListItem on CiRunner { fragment ListItem on CiRunner {
# @graphql-eslint/eslint-plugin@4.0.0 reports missing IDs in `FragmentDefinition`. For now, we are
# ignoring the newly uncovered error on this fragment. Please consider addressing the violation
# if you are modifying this file and it turns out selecting the ID makes sense here.
# eslint-disable-next-line @graphql-eslint/require-selections
...ListItemShared ...ListItemShared
} }

View File

@ -21,6 +21,10 @@ fragment ListItemShared on CiRunner {
} }
managers(first: 1) { managers(first: 1) {
count count
# @graphql-eslint/eslint-plugin@4.0.0 reports missing IDs in `FragmentDefinition`. For now, we are
# ignoring the newly uncovered error on this fragment. Please consider addressing the violation
# if you are modifying this file and it turns out selecting the ID makes sense here.
# eslint-disable-next-line @graphql-eslint/require-selections
nodes { nodes {
version version
ipAddress ipAddress

View File

@ -1,3 +1,7 @@
# @graphql-eslint/eslint-plugin@4.0.0 reports missing IDs in `FragmentDefinition`. For now, we are
# ignoring the newly uncovered error on this fragment. Please consider addressing the violation
# if you are modifying this file and it turns out selecting the ID makes sense here.
# eslint-disable-next-line @graphql-eslint/require-selections
fragment ApprovalSummary on Deployment { fragment ApprovalSummary on Deployment {
iid iid
} }

View File

@ -1,3 +1,7 @@
# @graphql-eslint/eslint-plugin@4.0.0 reports missing IDs in `FragmentDefinition`. For now, we are
# ignoring the newly uncovered error on this fragment. Please consider addressing the violation
# if you are modifying this file and it turns out selecting the ID makes sense here.
# eslint-disable-next-line @graphql-eslint/require-selections
fragment UserAvailability on User { fragment UserAvailability on User {
status { status {
availability availability

View File

@ -1,3 +1,7 @@
# @graphql-eslint/eslint-plugin@4.0.0 reports missing IDs in `FragmentDefinition`. For now, we are
# ignoring the newly uncovered error on this fragment. Please consider addressing the violation
# if you are modifying this file and it turns out selecting the ID makes sense here.
# eslint-disable-next-line @graphql-eslint/require-selections
fragment IncidentFields on Issue { fragment IncidentFields on Issue {
severity severity
escalationStatus escalationStatus

View File

@ -1,3 +1,7 @@
# @graphql-eslint/eslint-plugin@4.0.0 reports missing IDs in `FragmentDefinition`. For now, we are
# ignoring the newly uncovered error on this fragment. Please consider addressing the violation
# if you are modifying this file and it turns out selecting the ID makes sense here.
# eslint-disable-next-line @graphql-eslint/require-selections
fragment MergeRequestApprovalFragment on MergeRequest { fragment MergeRequestApprovalFragment on MergeRequest {
approved approved
approvedBy { approvedBy {

View File

@ -14,7 +14,7 @@ query securityReportsDownloadPaths(
id id
name name
artifacts { artifacts {
# eslint-disable-next-line @graphql-eslint/require-id-when-available # eslint-disable-next-line @graphql-eslint/require-selections
nodes { nodes {
downloadPath downloadPath
fileType fileType

View File

@ -5,6 +5,10 @@ fragment JobArtifacts on Pipeline {
id id
name name
artifacts { artifacts {
# @graphql-eslint/eslint-plugin@4.0.0 reports missing IDs in `FragmentDefinition`. For now, we are
# ignoring the newly uncovered error on this fragment. Please consider addressing the violation
# if you are modifying this file and it turns out selecting the ID makes sense here.
# eslint-disable-next-line @graphql-eslint/require-selections
nodes { nodes {
downloadPath downloadPath
fileType fileType

View File

@ -14,7 +14,7 @@ query securityReportDownloadPaths(
id id
name name
artifacts { artifacts {
# eslint-disable-next-line @graphql-eslint/require-id-when-available # eslint-disable-next-line @graphql-eslint/require-selections
nodes { nodes {
downloadPath downloadPath
fileType fileType

View File

@ -4,7 +4,7 @@ query getPipelineCorpuses($projectPath: ID!, $iid: ID, $reportTypes: [SecurityRe
project(fullPath: $projectPath) { project(fullPath: $projectPath) {
id id
pipeline(iid: $iid) { pipeline(iid: $iid) {
# eslint-disable-next-line @graphql-eslint/require-id-when-available # eslint-disable-next-line @graphql-eslint/require-selections
...JobArtifacts ...JobArtifacts
} }
} }

View File

@ -1,6 +1,10 @@
#import "ee_else_ce/work_items/graphql/work_item_metadata_widgets.fragment.graphql" #import "ee_else_ce/work_items/graphql/work_item_metadata_widgets.fragment.graphql"
#import "ee_else_ce/work_items/graphql/work_item_metadata_widgets_extras.fragment.graphql" #import "ee_else_ce/work_items/graphql/work_item_metadata_widgets_extras.fragment.graphql"
# @graphql-eslint/eslint-plugin@4.0.0 reports missing IDs in `FragmentDefinition`. For now, we are
# ignoring the newly uncovered error on this fragment. Please consider addressing the violation
# if you are modifying this file and it turns out selecting the ID makes sense here.
# eslint-disable-next-line @graphql-eslint/require-selections
fragment WorkItemLinkedItemsFragment on WorkItem { fragment WorkItemLinkedItemsFragment on WorkItem {
widgets { widgets {
... on WorkItemWidgetLinkedItems { ... on WorkItemWidgetLinkedItems {

View File

@ -9,6 +9,10 @@ fragment WorkItemWidgets on WorkItemWidget {
description description
descriptionHtml descriptionHtml
lastEditedAt lastEditedAt
# @graphql-eslint/eslint-plugin@4.0.0 reports missing IDs in `FragmentDefinition`. For now, we are
# ignoring the newly uncovered error on this fragment. Please consider addressing the violation
# if you are modifying this file and it turns out selecting the ID makes sense here.
# eslint-disable-next-line @graphql-eslint/require-selections
lastEditedBy { lastEditedBy {
name name
webPath webPath

View File

@ -16,6 +16,7 @@ module Mutations
type: GraphQL::Types::ID, type: GraphQL::Types::ID,
required: true, required: true,
description: 'Full path of the namespace on which the preference is set.' description: 'Full path of the namespace on which the preference is set.'
argument :work_item_type_id, argument :work_item_type_id,
type: ::Types::GlobalIDType[::WorkItems::Type], type: ::Types::GlobalIDType[::WorkItems::Type],
required: false, required: false,
@ -27,6 +28,11 @@ module Mutations
required: false, required: false,
default_value: :created_asc default_value: :created_asc
argument :display_settings,
type: GraphQL::Types::JSON,
description: 'Display settings for the work item lists.',
required: false
field :user_preferences, field :user_preferences,
type: ::Types::WorkItems::UserPreference, type: ::Types::WorkItems::UserPreference,
description: 'User preferences.' description: 'User preferences.'

View File

@ -23,6 +23,10 @@ fragment LinkedPipelineData on Pipeline {
userPermissions { userPermissions {
updatePipeline updatePipeline
} }
# @graphql-eslint/eslint-plugin@4.0.0 reports missing IDs in `FragmentDefinition`. For now, we are
# ignoring the newly uncovered error on this fragment. Please consider addressing the violation
# if you are modifying this file and it turns out selecting the ID makes sense here.
# eslint-disable-next-line @graphql-eslint/require-selections
status: detailedStatus { status: detailedStatus {
__typename __typename
group group
@ -59,13 +63,13 @@ query getPipelineDetails($projectPath: ID!, $iid: ID!) {
downstream { downstream {
__typename __typename
nodes { nodes {
# eslint-disable-next-line @graphql-eslint/require-id-when-available -- for detailedStatus.id # eslint-disable-next-line @graphql-eslint/require-selections -- for detailedStatus.id
...LinkedPipelineData ...LinkedPipelineData
name name
} }
} }
upstream { upstream {
# eslint-disable-next-line @graphql-eslint/require-id-when-available -- for detailedStatus.id # eslint-disable-next-line @graphql-eslint/require-selections -- for detailedStatus.id
...LinkedPipelineData ...LinkedPipelineData
} }
stages { stages {
@ -74,7 +78,7 @@ query getPipelineDetails($projectPath: ID!, $iid: ID!) {
__typename __typename
id id
name name
# eslint-disable-next-line @graphql-eslint/require-id-when-available # eslint-disable-next-line @graphql-eslint/require-selections
status: detailedStatus { status: detailedStatus {
__typename __typename
action { action {
@ -91,7 +95,7 @@ query getPipelineDetails($projectPath: ID!, $iid: ID!) {
nodes { nodes {
__typename __typename
id id
# eslint-disable-next-line @graphql-eslint/require-id-when-available # eslint-disable-next-line @graphql-eslint/require-selections
status: detailedStatus { status: detailedStatus {
__typename __typename
label label
@ -122,7 +126,7 @@ query getPipelineDetails($projectPath: ID!, $iid: ID!) {
...CiNeeds ...CiNeeds
} }
} }
# eslint-disable-next-line @graphql-eslint/require-id-when-available # eslint-disable-next-line @graphql-eslint/require-selections
status: detailedStatus { status: detailedStatus {
__typename __typename
icon icon

View File

@ -22,6 +22,11 @@ module Types
null: true, null: true,
description: 'Sort order for work item lists.' description: 'Sort order for work item lists.'
field :display_settings,
type: GraphQL::Types::JSON,
null: true,
description: 'Display settings for the work item lists.'
def sort def sort
object.sort&.to_sym object.sort&.to_sym
end end

View File

@ -63,6 +63,10 @@ module Approvable
def eligible_for_unapproval_by?(user) def eligible_for_unapproval_by?(user)
user && approved_by?(user) && user.can?(:approve_merge_request, self) user && approved_by?(user) && user.can?(:approve_merge_request, self)
end end
def approvals_for_user_ids(user_ids)
approvals.where(user_id: user_ids)
end
end end
Approvable.prepend_mod Approvable.prepend_mod

View File

@ -89,6 +89,7 @@ module MergeRequests
merge_request_activity_counter.track_reviewers_changed_action(user: current_user) merge_request_activity_counter.track_reviewers_changed_action(user: current_user)
trigger_merge_request_reviewers_updated(merge_request) trigger_merge_request_reviewers_updated(merge_request)
set_reviewers_approved(merge_request, new_reviewers) if new_reviewers.any?
set_first_reviewer_assigned_at_metrics(merge_request) if new_reviewers.any? set_first_reviewer_assigned_at_metrics(merge_request) if new_reviewers.any?
trigger_user_merge_request_updated(merge_request) trigger_user_merge_request_updated(merge_request)
end end
@ -218,6 +219,13 @@ module MergeRequests
# Implemented in EE # Implemented in EE
end end
def set_reviewers_approved(merge_request, new_reviewers)
approval_users = merge_request.approvals_for_user_ids(new_reviewers.map(&:id))
merge_request.merge_request_reviewers_with(approval_users.select(:user_id))
.update_all(state: :approved)
end
def merge_request_metrics_service(merge_request) def merge_request_metrics_service(merge_request)
MergeRequestMetricsService.new(merge_request.metrics) MergeRequestMetricsService.new(merge_request.metrics)
end end

View File

@ -1,13 +1,16 @@
- if @project.pages_deployed? - if @project.pages_deployed?
- if can?(current_user, :remove_pages, @project) = render ::Layouts::CrudComponent.new(s_('GitLabPages|Delete Pages'),
.gl-bg-red-50.gl-shadow-inner-l-3-red-600.gl-py-5.gl-px-6.gl-mt-5 options: { class: 'gl-mt-5' },
%h4.gl-text-lg.gl-mt-0= s_('GitLabPages|Remove pages') body_options: { class: '!gl-m-0 gl-p-5 gl-bg-feedback-danger' }) do |c|
%p= s_('GitLabPages|Removing pages will prevent them from being exposed to the public internet.') - c.with_body do
= render Pajamas::ButtonComponent.new(href: project_pages_path(@project), - if can?(current_user, :remove_pages, @project)
variant: :danger, %p
method: :delete, = s_('GitLabPages|This action will permanently delete all Pages deployments.')
button_options: { data: { confirm: s_('GitLabPages|Are you sure?'), 'confirm-btn-variant': 'danger' }, "aria-label": s_('GitLabPages|Remove pages') }) do = s_('GitLabPages|This is permanent and cannot be undone. To deploy this Pages site again, run a new pipeline.')
= s_('GitLabPages|Remove pages') = render Pajamas::ButtonComponent.new(href: project_pages_path(@project),
- else variant: :danger,
.nothing-here-block method: :delete,
= s_('GitLabPages|Only project maintainers can remove pages') button_options: { data: { confirm: s_('GitLabPages|Are you sure?'), 'confirm-btn-variant': 'danger' }, "aria-label": s_('GitLabPages|Remove pages') }) do
= s_('GitLabPages|Delete Pages')
- else
= s_('GitLabPages|Only project maintainers can remove pages.')

View File

@ -5,5 +5,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/178281
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/514208 rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/514208
milestone: '17.11' milestone: '17.11'
group: group::source code group: group::source code
type: gitlab_com_derisk type: beta
default_enabled: false default_enabled: true

View File

@ -1119,8 +1119,8 @@ Settings.cell['enabled'] ||= false # All Cells Features are disabled by default
Settings.cell['id'] ||= nil Settings.cell['id'] ||= nil
Settings.cell['database'] ||= {} Settings.cell['database'] ||= {}
Settings.cell.database['skip_sequence_alteration'] ||= false Settings.cell.database['skip_sequence_alteration'] ||= false
# NOTE: `topology_service_client` is the configuration to use going forward as per https://docs.gitlab.com/administration/cells/#configuration
# Topology Service Client Settings # We continue to be backwards compatible and support `topology_service` as a top-level key.
Settings.cell['topology_service_client'] ||= Settings.respond_to?(:topology_service) ? Settings.topology_service || {} : {} Settings.cell['topology_service_client'] ||= Settings.respond_to?(:topology_service) ? Settings.topology_service || {} : {}
Settings.cell.topology_service_client['address'] ||= 'topology-service.gitlab.example.com:443' Settings.cell.topology_service_client['address'] ||= 'topology-service.gitlab.example.com:443'
Settings.cell.topology_service_client['ca_file'] ||= '/home/git/gitlab/config/topology-service-ca.pem' Settings.cell.topology_service_client['ca_file'] ||= '/home/git/gitlab/config/topology-service-ca.pem'

View File

@ -20,7 +20,7 @@ if Gitlab.config.cell.enabled
Settings.required_topology_service_settings.each do |setting| Settings.required_topology_service_settings.each do |setting|
setting_value = Gitlab.config.cell.topology_service_client.send(setting) setting_value = Gitlab.config.cell.topology_service_client.send(setting)
print_error.call("Topology Service setting '#{setting}' is not set.") if setting_value.blank? print_error.call("Topology Service Client setting '#{setting}' is not set.") if setting_value.blank?
end end
elsif Gitlab.config.cell.id.present? elsif Gitlab.config.cell.id.present?
print_error.call("Cell ID is set but Cell is not enabled.") print_error.call("Cell ID is set but Cell is not enabled.")

View File

@ -0,0 +1,18 @@
---
key_path: counts.total_top_level_groups
description: Counts the number of top-level groups on the instance
product_group: organizations
product_categories:
- groups_and_projects
value_type: number
status: active
milestone: "18.1"
instrumentation_class: CountTopLevelGroupsMetric
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/190217
time_frame: all
data_source: database
data_category: optional
tiers:
- free
- premium
- ultimate

View File

@ -1,8 +1,9 @@
--- ---
migration_job_name: BackfillStatusPagePublishedIncidentsNamespaceId migration_job_name: BackfillStatusPagePublishedIncidentsNamespaceId
description: Backfills sharding key `status_page_published_incidents.namespace_id` from `issues`. description: Backfills sharding key `status_page_published_incidents.namespace_id`
from `issues`.
feature_category: incident_management feature_category: incident_management
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/174868 introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/174868
milestone: '17.7' milestone: '17.7'
queued_migration_version: 20241205143060 queued_migration_version: 20241205143060
finalized_by: # version of the migration that finalized this BBM finalized_by: '20250521232727'

View File

@ -0,0 +1,21 @@
# frozen_string_literal: true
class FinalizeHkBackfillStatusPagePublishedIncidentsNamespaceId < Gitlab::Database::Migration[2.3]
milestone '18.1'
disable_ddl_transaction!
restrict_gitlab_migration gitlab_schema: :gitlab_main_cell
def up
ensure_batched_background_migration_is_finished(
job_class_name: 'BackfillStatusPagePublishedIncidentsNamespaceId',
table_name: :status_page_published_incidents,
column_name: :id,
job_arguments: [:namespace_id, :issues, :namespace_id, :issue_id],
finalize: true
)
end
def down; end
end

View File

@ -0,0 +1 @@
790cec3f4a91a1db687211c1c6013683339e66d39ceb32fd486015b00069b294

View File

@ -13001,6 +13001,7 @@ Input type: `WorkItemUserPreferenceUpdateInput`
| Name | Type | Description | | Name | Type | Description |
| ---- | ---- | ----------- | | ---- | ---- | ----------- |
| <a id="mutationworkitemuserpreferenceupdateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. | | <a id="mutationworkitemuserpreferenceupdateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationworkitemuserpreferenceupdatedisplaysettings"></a>`displaySettings` | [`JSON`](#json) | Display settings for the work item lists. |
| <a id="mutationworkitemuserpreferenceupdatenamespacepath"></a>`namespacePath` | [`ID!`](#id) | Full path of the namespace on which the preference is set. | | <a id="mutationworkitemuserpreferenceupdatenamespacepath"></a>`namespacePath` | [`ID!`](#id) | Full path of the namespace on which the preference is set. |
| <a id="mutationworkitemuserpreferenceupdatesort"></a>`sort` | [`WorkItemSort`](#workitemsort) | Sort order for work item lists. | | <a id="mutationworkitemuserpreferenceupdatesort"></a>`sort` | [`WorkItemSort`](#workitemsort) | Sort order for work item lists. |
| <a id="mutationworkitemuserpreferenceupdateworkitemtypeid"></a>`workItemTypeId` | [`WorkItemsTypeID`](#workitemstypeid) | Global ID of a work item type. | | <a id="mutationworkitemuserpreferenceupdateworkitemtypeid"></a>`workItemTypeId` | [`WorkItemsTypeID`](#workitemstypeid) | Global ID of a work item type. |
@ -21551,6 +21552,7 @@ Counts for each analyzer status in the group and subgroups.
| <a id="analyzergroupstatustypefailure"></a>`failure` | [`Int!`](#int) | Number of analyzers failed. | | <a id="analyzergroupstatustypefailure"></a>`failure` | [`Int!`](#int) | Number of analyzers failed. |
| <a id="analyzergroupstatustypenamespaceid"></a>`namespaceId` | [`Int!`](#int) | Namespace ID. | | <a id="analyzergroupstatustypenamespaceid"></a>`namespaceId` | [`Int!`](#int) | Namespace ID. |
| <a id="analyzergroupstatustypesuccess"></a>`success` | [`Int!`](#int) | Number of analyzers succeeded. | | <a id="analyzergroupstatustypesuccess"></a>`success` | [`Int!`](#int) | Number of analyzers succeeded. |
| <a id="analyzergroupstatustypeupdatedat"></a>`updatedAt` | [`ISO8601DateTime!`](#iso8601datetime) | Timestamp of when the status was last updated. |
### `AnalyzerProjectStatusType` ### `AnalyzerProjectStatusType`
@ -21565,6 +21567,7 @@ Analyzer status (success/fail) for projects.
| <a id="analyzerprojectstatustypelastcall"></a>`lastCall` | [`Time!`](#time) | Last time analyzer was called. | | <a id="analyzerprojectstatustypelastcall"></a>`lastCall` | [`Time!`](#time) | Last time analyzer was called. |
| <a id="analyzerprojectstatustypeprojectid"></a>`projectId` | [`Int!`](#int) | Project ID. | | <a id="analyzerprojectstatustypeprojectid"></a>`projectId` | [`Int!`](#int) | Project ID. |
| <a id="analyzerprojectstatustypestatus"></a>`status` | [`AnalyzerStatusEnum!`](#analyzerstatusenum) | Analyzer status. | | <a id="analyzerprojectstatustypestatus"></a>`status` | [`AnalyzerStatusEnum!`](#analyzerstatusenum) | Analyzer status. |
| <a id="analyzerprojectstatustypeupdatedat"></a>`updatedAt` | [`ISO8601DateTime!`](#iso8601datetime) | Timestamp of when the status was last updated. |
### `AncestorType` ### `AncestorType`
@ -42070,6 +42073,7 @@ Represents Depth limit reached for the allowed work item type.
| Name | Type | Description | | Name | Type | Description |
| ---- | ---- | ----------- | | ---- | ---- | ----------- |
| <a id="workitemtypesuserpreferencedisplaysettings"></a>`displaySettings` | [`JSON`](#json) | Display settings for the work item lists. |
| <a id="workitemtypesuserpreferencenamespace"></a>`namespace` | [`Namespace!`](#namespace) | Namespace for the user preference. | | <a id="workitemtypesuserpreferencenamespace"></a>`namespace` | [`Namespace!`](#namespace) | Namespace for the user preference. |
| <a id="workitemtypesuserpreferencesort"></a>`sort` | [`WorkItemSort`](#workitemsort) | Sort order for work item lists. | | <a id="workitemtypesuserpreferencesort"></a>`sort` | [`WorkItemSort`](#workitemsort) | Sort order for work item lists. |
| <a id="workitemtypesuserpreferenceworkitemtype"></a>`workItemType` | [`WorkItemType`](#workitemtype) | Type assigned to the work item. | | <a id="workitemtypesuserpreferenceworkitemtype"></a>`workItemType` | [`WorkItemType`](#workitemtype) | Type assigned to the work item. |

View File

@ -839,10 +839,11 @@ space by truncating tables. This results in a smaller data set: For example,
the data in `users` table on CI database is no longer read and also no the data in `users` table on CI database is no longer read and also no
longer updated. So this data can be removed by truncating the tables. longer updated. So this data can be removed by truncating the tables.
For this purpose, GitLab provides two Rake tasks, one for each database: For this purpose, GitLab provides separate Rake tasks, one for each database:
- `gitlab:db:truncate_legacy_tables:main` will truncate the CI tables in Main database. - `gitlab:db:truncate_legacy_tables:main` will truncate the legacy tables in Main database.
- `gitlab:db:truncate_legacy_tables:ci` will truncate the Main tables in CI database. - `gitlab:db:truncate_legacy_tables:ci` will truncate the legacy tables in CI database.
- `gitlab:db:truncate_legacy_tables:sec` will truncate the legacy tables in Sec database.
{{< alert type="note" >}} {{< alert type="note" >}}

View File

@ -21,8 +21,9 @@ title: AI impact analytics
{{< /history >}} {{< /history >}}
The primary goal of AI impact analytics is to measure GitLab Duo's impact on software development lifecycle (SDLC) performance. AI impact analytics measure the impact of GitLab Duo on software development lifecycle (SDLC) performance.
This dashboard provides visibility into key SDLC metrics in the context of AI adoption, helping you measure which metrics have improved as a result of AI investments. This dashboard provides visibility into key SDLC metrics in the context of AI adoption for projects or groups.
You can use the dashboard to measure which metrics have improved from your AI investments.
Use AI impact analytics for: Use AI impact analytics for:
@ -32,30 +33,32 @@ Use AI impact analytics for:
To learn how you can optimize your license utilization, To learn how you can optimize your license utilization,
see [GitLab Duo add-ons](../../subscriptions/subscription-add-ons.md). see [GitLab Duo add-ons](../../subscriptions/subscription-add-ons.md).
To learn more about AI impact analytics, see the blog post
[Developing GitLab Duo: AI impact analytics dashboard measures the ROI of AI](https://about.gitlab.com/blog/2024/05/15/developing-gitlab-duo-ai-impact-analytics-dashboard-measures-the-roi-of-ai/).
For a click-through demo, see the [AI impact analytics product tour](https://gitlab.navattic.com/ai-impact). For a click-through demo, see the [AI impact analytics product tour](https://gitlab.navattic.com/ai-impact).
<i class="fa-youtube-play" aria-hidden="true"></i> <i class="fa-youtube-play" aria-hidden="true"></i>
For an overview, see [GitLab Duo AI Impact Dashboard](https://youtu.be/FxSWX64aUOE?si=7Yfc6xHm63c3BRwn). For an overview, see [GitLab Duo AI Impact Dashboard](https://youtu.be/FxSWX64aUOE?si=7Yfc6xHm63c3BRwn).
<!-- Video published on 2025-03-06 --> <!-- Video published on 2025-03-06 -->
## AI impact metrics ## Key metrics
AI impact analytics displays key metrics and metric trends for a project or group.
### Key metrics
- **Duo seats: Assigned and used**: Percentage of users that are assigned a Duo seat and used at least one AI feature in the last 30 days. - **Duo seats: Assigned and used**: Percentage of users that are assigned a Duo seat and used at least one AI feature in the last 30 days.
It is calculated as the number of users with Duo seats that use AI features divided by the total number of assigned Duo seats. It is calculated as the number of users with Duo seats that use AI features divided by the total number of assigned Duo seats.
- **Code Suggestions: Unique users**: Percentage of users that engage with Code Suggestions every month. It is calculated as the number of monthly unique Code Suggestions users divided by total monthly [unique contributors](../profile/contributions_calendar.md#user-contribution-events). Only unique code contributors, meaning users with `pushed` events, are included in the calculation. - **Code Suggestions: Unique users**: Percentage of users that engage with Code Suggestions every month.
It is calculated as the number of monthly unique Code Suggestions users divided by total monthly [unique contributors](../profile/contributions_calendar.md#user-contribution-events).
Only unique code contributors (users with `pushed` events) are included in the calculation.
- **Code Suggestions: Acceptance rate**: Percentage of code suggestions provided by GitLab Duo that have been accepted by code contributors in the last 30 days. - **Code Suggestions: Acceptance rate**: Percentage of code suggestions provided by GitLab Duo that have been accepted by code contributors in the last 30 days.
It is calculated as the number of accepted code suggestions divided by the total number of generated code suggestions. It is calculated as the number of accepted code suggestions divided by the total number of generated code suggestions.
- **Duo Chat: Unique users**: Percentage of users that engage with GitLab Duo Chat every month. It is calculated as the number of monthly unique GitLab Duo Chat users divided by the total GitLab Duo assigned users. - **Duo Chat: Unique users**: Percentage of users that engage with GitLab Duo Chat every month.
It is calculated as the number of monthly unique GitLab Duo Chat users divided by the total GitLab Duo assigned users.
### Metric trends ## Metric trends
The **Metric trends** table displays metrics for the last six months, with monthly values, percentage changes in the past six months, and trend sparklines. The **Metric trends** table displays metrics for the last six months, with monthly values, percentage changes in the past six months, and trend sparklines.
#### Lifecycle metrics ### Lifecycle metrics
- [**Cycle time**](../group/value_stream_analytics/_index.md#lifecycle-metrics) - [**Cycle time**](../group/value_stream_analytics/_index.md#lifecycle-metrics)
- [**Lead time**](../group/value_stream_analytics/_index.md#lifecycle-metrics) - [**Lead time**](../group/value_stream_analytics/_index.md#lifecycle-metrics)
@ -63,13 +66,19 @@ The **Metric trends** table displays metrics for the last six months, with month
- [**Change failure rate**](dora_metrics.md#change-failure-rate) - [**Change failure rate**](dora_metrics.md#change-failure-rate)
- [**Critical vulnerabilities over time**](../application_security/vulnerability_report/_index.md) - [**Critical vulnerabilities over time**](../application_security/vulnerability_report/_index.md)
#### AI usage metrics ### AI usage metrics
**Code Suggestions usage**: Monthly user engagement with AI Code Suggestions. **Code Suggestions usage**: Monthly user engagement with AI Code Suggestions.
- The month-over-month comparison of the AI Usage unique users rate gives a more accurate indication of this metric, as it eliminates factors such as developer experience level and project type or complexity. The month-over-month comparison of the AI Usage unique users rate gives a more accurate indication of this metric,
- The baseline for the AI Usage trend is the total number of code contributors, not just users with GitLab Duo seats. This baseline gives a more accurate representation of AI usage by team members. To learn more about AI impact analytics, see the blog post [Developing GitLab Duo: AI impact analytics dashboard measures the ROI of AI](https://about.gitlab.com/blog/2024/05/15/developing-gitlab-duo-ai-impact-analytics-dashboard-measures-the-roi-of-ai/). because it eliminates factors such as developer experience level and project type or complexity.
- To analyze the performance of teams that use AI versus teams that don't, you can create a custom [Value Streams Dashboard Scheduled Report](https://gitlab.com/explore/catalog/components/vsd-reports-generator) based on the AI impact view of projects and groups with and without GitLab Duo.
The baseline for the AI Usage trend is the total number of code contributors, not just users with GitLab Duo seats.
This baseline gives a more accurate representation of AI usage by team members.
To analyze the performance of teams that use AI versus teams that don't, you can create a custom
[Value Streams Dashboard Scheduled Report](https://gitlab.com/explore/catalog/components/vsd-reports-generator)
based on the AI impact view of projects and groups with and without GitLab Duo.
{{< alert type="note" >}} {{< alert type="note" >}}

View File

@ -129,7 +129,7 @@ You can filter by:
- **Report Type**: For more details, see [Report Type filter](#report-type-filter) - **Report Type**: For more details, see [Report Type filter](#report-type-filter)
- **Scanner**: For more details, see [Scanner filter](#scanner-filter) - **Scanner**: For more details, see [Scanner filter](#scanner-filter)
- **Activity**: For more details, see [Activity filter](#activity-filter). - **Activity**: For more details, see [Activity filter](#activity-filter).
- **Identifier**: Filter by the vulnerability's identifier (available only for projects, support for groups is proposed in [issue 508713](https://gitlab.com/gitlab-org/gitlab/-/issues/508713)). - **Identifier**: Filter by the vulnerability's identifier (requires [advanced vulnerability management](#advanced-vulnerability-management). Without advanced vulnerability management, availability is restricted to projects and groups with a maximum of 20,000 vulnerabilities).
- **Project**: Filter vulnerabilities in specific projects (available only for groups). - **Project**: Filter vulnerabilities in specific projects (available only for groups).
<!-- vale gitlab_base.SubstitutionWarning = YES --> <!-- vale gitlab_base.SubstitutionWarning = YES -->
@ -244,6 +244,7 @@ The **GitLab Duo (AI)** filter is available when:
- Non-OWASP category in OWASP top 10 grouping [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/442526) in GitLab 17.1 [with a flag](../../../administration/feature_flags.md) named `owasp_top_10_null_filtering`. Disabled by default. - Non-OWASP category in OWASP top 10 grouping [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/442526) in GitLab 17.1 [with a flag](../../../administration/feature_flags.md) named `owasp_top_10_null_filtering`. Disabled by default.
- Non-OWASP category in OWASP top 10 grouping [enabled on GitLab Self-Managed, and GitLab Dedicated](https://gitlab.com/gitlab-org/gitlab/-/issues/463783) in GitLab 17.5. - Non-OWASP category in OWASP top 10 grouping [enabled on GitLab Self-Managed, and GitLab Dedicated](https://gitlab.com/gitlab-org/gitlab/-/issues/463783) in GitLab 17.5.
- Non-OWASP category in OWASP top 10 grouping [generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/463783) in GitLab 17.6. Feature flag `owasp_top_10_null_filtering` removed. - Non-OWASP category in OWASP top 10 grouping [generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/463783) in GitLab 17.6. Feature flag `owasp_top_10_null_filtering` removed.
- OWASP 2021 top 10 grouping [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/466034) on GitLab.com and GitLab Dedicated in GitLab 18.1 [with a flag](../../../administration/feature_flags.md) named `vulnerability_report_owasp_2021`. Disabled by default. Feature also requires [advanced vulnerability management](#advanced-vulnerability-management).
{{< /history >}} {{< /history >}}
@ -256,6 +257,7 @@ You can group by:
- Report Type - Report Type
- Scanner - Scanner
- OWASP top 10 2017 - OWASP top 10 2017
- OWASP top 10 2021 (requires [advanced vulnerability management](#advanced-vulnerability-management))
### Group vulnerabilities ### Group vulnerabilities
@ -530,6 +532,38 @@ To add a vulnerability manually:
The newly-created vulnerability's detail page is opened. The newly-created vulnerability's detail page is opened.
## Advanced vulnerability management
{{< history >}}
- Ingestion of vulnerability data into advanced search [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/536299) in GitLab 18.1 [with a flag](../../../administration/feature_flags.md) named `vulnerability_es_ingestion`. Available in GitLab.com and GitLab Dedicated. Disabled by default.
- Filters for OWASP 2021 grouping and identifiers in advanced search [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/537673) with the feature flag `advanced_vulnerability_management`. Available in GitLab.com and GitLab Dedicated. Disabled by default.
{{< /history >}}
{{< alert type="flag" >}}
Advanced vulnerability management is controlled by feature flags.
For more information, see the history.
{{< /alert >}}
GitLab primarily uses PostgreSQL for filtering in the vulnerability report. Due to database indexing limitations and performance challenges when applying multiple filters, GitLab uses [advanced search](../../search/advanced_search.md) for specific vulnerability management features.
Advanced search powers the following features:
1. Grouping data by OWASP 2021 categories in the vulnerability report for a project or group.
1. Filtering based on a vulnerability's identifier in the vulnerability report for a project or group.
Advanced search is used only for these specific features, including when they are combined with other [filters](#filter-vulnerabilities). Other filters, when used independently, continue to use the standard PostgreSQL filtering.
### Requirements
To use the filters in advanced vulnerability management:
- You must use GitLab.com or a GitLab Dedicated instance with [advanced search enabled](../../search/advanced_search.md#enable-advanced-search). This feature is not supported on GitLab Self-Managed, but support is proposed [issue 525484](https://gitlab.com/gitlab-org/gitlab/-/issues/525484).
- You must be in the vulnerability report for a project or group. This feature is not supported in the security dashboard, but support is proposed in [issue 537807](https://gitlab.com/gitlab-org/gitlab/-/issues/537807).
## Operational vulnerabilities ## Operational vulnerabilities
The **Operational vulnerabilities** tab lists vulnerabilities found by [Operational container scanning](../../clusters/agent/vulnerabilities.md). The **Operational vulnerabilities** tab lists vulnerabilities found by [Operational container scanning](../../clusters/agent/vulnerabilities.md).

View File

@ -69,13 +69,19 @@ If the case of `404.html`, there are different scenarios. For example:
You can configure redirects for your site using a `_redirects` file. For more information, see You can configure redirects for your site using a `_redirects` file. For more information, see
[Create redirects for GitLab Pages](redirects.md). [Create redirects for GitLab Pages](redirects.md).
## Remove your pages ## Delete a Pages site
To remove your pages: Permanently delete all Pages deployments for a project.
This is permanent and cannot be undone.
To delete your pages:
1. On the left sidebar, select **Search or go to** and find your project. 1. On the left sidebar, select **Search or go to** and find your project.
1. Select **Deploy > Pages**. 1. Select **Deploy > Pages**.
1. Select **Remove pages**. 1. Select **Delete pages**.
Your Pages site is no longer deployed.
To deploy this Pages site again, run a new pipeline.
## Subdomains of subdomains ## Subdomains of subdomains

View File

@ -55,7 +55,7 @@ To create a parallel deployment:
pages: pages:
stage: deploy stage: deploy
script: script:
- echo "Pages accessible through ${CI_PAGES_URL}/${CI_COMMIT_BRANCH}" - echo "Pages accessible through ${CI_PAGES_URL}"
pages: # specifies that this is a Pages job and publishes the default public directory pages: # specifies that this is a Pages job and publishes the default public directory
path_prefix: "$CI_COMMIT_BRANCH" path_prefix: "$CI_COMMIT_BRANCH"
``` ```

View File

@ -23,7 +23,7 @@ Use advanced search to find exactly what you need across your entire GitLab inst
With advanced search: With advanced search:
- Identify code patterns across all projects to refactor shared components more efficiently. - Identify code patterns across all projects to refactor shared components more efficiently.
- Locate security vulnerabilities in dependencies across your entire organization at once. - Locate security vulnerabilities across your entire organization's codebase and dependencies using [advanced vulnerability management](../application_security/vulnerability_report/_index.md#advanced-vulnerability-management).
- Track usage of deprecated functions or libraries throughout all repositories. - Track usage of deprecated functions or libraries throughout all repositories.
- Find discussions buried in issues, merge requests, or comments. - Find discussions buried in issues, merge requests, or comments.
- Discover existing solutions instead of reinventing functionality that already exists. - Discover existing solutions instead of reinventing functionality that already exists.

View File

@ -4,7 +4,8 @@ import { existsSync } from 'node:fs';
import localRules from 'eslint-plugin-local-rules'; import localRules from 'eslint-plugin-local-rules';
import js from '@eslint/js'; import js from '@eslint/js';
import { FlatCompat } from '@eslint/eslintrc'; import { FlatCompat } from '@eslint/eslintrc';
import * as graphqlEslint from '@graphql-eslint/eslint-plugin'; // eslint-disable-next-line import/no-unresolved
import graphqlPlugin from '@graphql-eslint/eslint-plugin';
import * as todoLists from './.eslint_todo/index.mjs'; import * as todoLists from './.eslint_todo/index.mjs';
const { dirname } = import.meta; const { dirname } = import.meta;
@ -108,7 +109,7 @@ export default [
], ],
}, },
...compat.extends(...extendConfigs), ...compat.extends(...extendConfigs),
...compat.plugins('no-jquery', '@graphql-eslint'), ...compat.plugins('no-jquery'),
{ {
rules: { rules: {
'no-unused-vars': [ 'no-unused-vars': [
@ -589,11 +590,9 @@ export default [
files: ['**/*.graphql'], files: ['**/*.graphql'],
languageOptions: { languageOptions: {
ecmaVersion: 5,
sourceType: 'script',
parserOptions: { parserOptions: {
parser: { ...graphqlEslint, meta: { name: '@graphql-eslint' } }, parser: graphqlPlugin.parser,
graphQLConfig: { graphQLConfig: {
documents: '{,ee/,jh/}app/**/*.graphql', documents: '{,ee/,jh/}app/**/*.graphql',
schema: './tmp/tests/graphql/gitlab_schema_apollo.graphql', schema: './tmp/tests/graphql/gitlab_schema_apollo.graphql',
@ -601,12 +600,16 @@ export default [
}, },
}, },
plugins: {
'@graphql-eslint': graphqlPlugin,
},
rules: { rules: {
'filenames/match-regex': 'off', 'filenames/match-regex': 'off',
'spaced-comment': 'off', 'spaced-comment': 'off',
'@graphql-eslint/no-anonymous-operations': 'error', '@graphql-eslint/no-anonymous-operations': 'error',
'@graphql-eslint/unique-operation-name': 'error', '@graphql-eslint/unique-operation-name': 'error',
'@graphql-eslint/require-id-when-available': 'error', '@graphql-eslint/require-selections': 'error',
'@graphql-eslint/no-unused-variables': 'error', '@graphql-eslint/no-unused-variables': 'error',
'@graphql-eslint/no-unused-fragments': 'error', '@graphql-eslint/no-unused-fragments': 'error',
'@graphql-eslint/no-duplicate-fields': 'error', '@graphql-eslint/no-duplicate-fields': 'error',
@ -622,7 +625,7 @@ export default [
], ],
rules: { rules: {
'@graphql-eslint/require-id-when-available': 'off', '@graphql-eslint/require-selections': 'off',
}, },
}, },
{ {

View File

@ -2,9 +2,10 @@
module ActiveContext module ActiveContext
class Embeddings class Embeddings
def self.generate_embeddings(content, model: nil, primitive: 'semantic_search_issue', user: nil) def self.generate_embeddings(content, unit_primitive:, model: nil, user: nil)
action = 'embedding'
embeddings = Gitlab::Llm::VertexAi::Embeddings::Text embeddings = Gitlab::Llm::VertexAi::Embeddings::Text
.new(content, user: user, tracking_context: { action: 'embedding' }, unit_primitive: primitive, model: model) .new(content, user: user, tracking_context: { action: action }, unit_primitive: unit_primitive, model: model)
.execute .execute
embeddings.all?(Array) ? embeddings : [embeddings] embeddings.all?(Array) ? embeddings : [embeddings]

View File

@ -14,6 +14,7 @@ module ActiveContext
class_methods do class_methods do
def apply_embeddings( def apply_embeddings(
refs:, refs:,
unit_primitive:,
content_field: :content, content_field: :content,
content_method: nil, content_method: nil,
remove_content: true remove_content: true
@ -44,7 +45,8 @@ module ActiveContext
versions = items.first[:versions] versions = items.first[:versions]
contents = items.map { |item| item[:doc][content_field] } contents = items.map { |item| item[:doc][content_field] }
embeddings_by_version = generate_embeddings_for_each_version(versions, contents) embeddings_by_version = generate_embeddings_for_each_version(versions: versions, contents: contents,
unit_primitive: unit_primitive)
# Apply the generated embeddings back to each document # Apply the generated embeddings back to each document
items.each.with_index do |item, index| items.each.with_index do |item, index|
@ -75,9 +77,10 @@ module ActiveContext
end end
end end
def generate_embeddings_for_each_version(versions, contents) def generate_embeddings_for_each_version(versions:, contents:, unit_primitive:)
versions.each_with_object({}) do |version, embeddings_by_version| versions.each_with_object({}) do |version, embeddings_by_version|
embedding = ActiveContext::Embeddings.generate_embeddings(contents, model: version[:model]) embedding = ActiveContext::Embeddings.generate_embeddings(contents, model: version[:model],
unit_primitive: unit_primitive)
embeddings_by_version[version[:field]] = embedding embeddings_by_version[version[:field]] = embedding
end end
end end

View File

@ -6,7 +6,7 @@ RSpec.describe ActiveContext::Preprocessors::Embeddings do
include ::ActiveContext::Preprocessors::Embeddings include ::ActiveContext::Preprocessors::Embeddings
add_preprocessor :embeddings do |refs| add_preprocessor :embeddings do |refs|
apply_embeddings(refs: refs, content_method: :embedding_content) apply_embeddings(refs: refs, content_method: :embedding_content, unit_primitive: 'test_unit_primitive')
end end
def embedding_content def embedding_content
@ -29,6 +29,7 @@ RSpec.describe ActiveContext::Preprocessors::Embeddings do
let(:object_id) { 5 } let(:object_id) { 5 }
let(:collection_name) { 'mock_collection' } let(:collection_name) { 'mock_collection' }
let(:vectors) { [1.0, 2.0] } let(:vectors) { [1.0, 2.0] }
let(:unit_primitive) { 'test_unit_primitive' }
let(:vertex_blank_error) { StandardError.new('The text content is empty.') } let(:vertex_blank_error) { StandardError.new('The text content is empty.') }
before do before do
@ -61,7 +62,7 @@ RSpec.describe ActiveContext::Preprocessors::Embeddings do
it 'generates and sets embeddings for each document' do it 'generates and sets embeddings for each document' do
expect(ActiveContext::Embeddings).to receive(:generate_embeddings) expect(ActiveContext::Embeddings).to receive(:generate_embeddings)
.once.with(['Some content'], model: nil).and_return([vectors]) .once.with(['Some content'], model: nil, unit_primitive: unit_primitive).and_return([vectors])
expect(preprocessed_reference.documents).to match_array([{ embedding: vectors }]) expect(preprocessed_reference.documents).to match_array([{ embedding: vectors }])
end end
@ -86,7 +87,7 @@ RSpec.describe ActiveContext::Preprocessors::Embeddings do
it 'generates and sets embeddings for each document' do it 'generates and sets embeddings for each document' do
expect(ActiveContext::Embeddings).to receive(:generate_embeddings) expect(ActiveContext::Embeddings).to receive(:generate_embeddings)
.once.with(['Other content'], model: nil).and_return([vectors]) .once.with(['Other content'], model: nil, unit_primitive: unit_primitive).and_return([vectors])
expect(preprocessed_reference.documents).to match_array([{ embedding: vectors }]) expect(preprocessed_reference.documents).to match_array([{ embedding: vectors }])
end end
@ -109,7 +110,7 @@ RSpec.describe ActiveContext::Preprocessors::Embeddings do
it 'generates and sets embeddings for each document' do it 'generates and sets embeddings for each document' do
expect(ActiveContext::Embeddings).to receive(:generate_embeddings) expect(ActiveContext::Embeddings).to receive(:generate_embeddings)
.once.with(['Some content'], model: nil).and_return([vectors]) .once.with(['Some content'], model: nil, unit_primitive: unit_primitive).and_return([vectors])
expect(preprocessed_reference.documents).to match_array([{ embedding: vectors }]) expect(preprocessed_reference.documents).to match_array([{ embedding: vectors }])
end end
@ -121,7 +122,8 @@ RSpec.describe ActiveContext::Preprocessors::Embeddings do
include ::ActiveContext::Preprocessors::Embeddings include ::ActiveContext::Preprocessors::Embeddings
add_preprocessor :embeddings do |refs| add_preprocessor :embeddings do |refs|
apply_embeddings(refs: refs, content_method: :embedding_content, remove_content: false) apply_embeddings(refs: refs, content_method: :embedding_content, remove_content: false,
unit_primitive: 'test_unit_primitive')
end end
def embedding_content def embedding_content
@ -146,7 +148,8 @@ RSpec.describe ActiveContext::Preprocessors::Embeddings do
include ::ActiveContext::Preprocessors::Embeddings include ::ActiveContext::Preprocessors::Embeddings
add_preprocessor :embeddings do |refs| add_preprocessor :embeddings do |refs|
apply_embeddings(refs: refs, content_field: :other_field, content_method: :embedding_content) apply_embeddings(refs: refs, content_field: :other_field, content_method: :embedding_content,
unit_primitive: 'test_unit_primitive')
end end
def embedding_content def embedding_content
@ -172,7 +175,7 @@ RSpec.describe ActiveContext::Preprocessors::Embeddings do
it 'generates and sets embeddings for each document' do it 'generates and sets embeddings for each document' do
expect(ActiveContext::Embeddings).to receive(:generate_embeddings) expect(ActiveContext::Embeddings).to receive(:generate_embeddings)
.once.with(['Some other content'], model: nil).and_return([vectors]) .once.with(['Some other content'], model: nil, unit_primitive: unit_primitive).and_return([vectors])
expect(preprocessed_reference.documents).to match_array([{ embedding: vectors }]) expect(preprocessed_reference.documents).to match_array([{ embedding: vectors }])
end end
@ -195,7 +198,7 @@ RSpec.describe ActiveContext::Preprocessors::Embeddings do
it 'generates and sets embeddings for each document' do it 'generates and sets embeddings for each document' do
expect(ActiveContext::Embeddings).to receive(:generate_embeddings) expect(ActiveContext::Embeddings).to receive(:generate_embeddings)
.once.with(['Some content'], model: nil).and_return([vectors]) .once.with(['Some content'], model: nil, unit_primitive: unit_primitive).and_return([vectors])
expect(preprocessed_reference.documents).to match_array([{ embedding: vectors }]) expect(preprocessed_reference.documents).to match_array([{ embedding: vectors }])
end end
@ -207,8 +210,8 @@ RSpec.describe ActiveContext::Preprocessors::Embeddings do
include ::ActiveContext::Preprocessors::Embeddings include ::ActiveContext::Preprocessors::Embeddings
add_preprocessor :embeddings do |refs| add_preprocessor :embeddings do |refs|
apply_embeddings(refs: refs, content_field: :other_field, apply_embeddings(refs: refs, content_field: :other_field, content_method: :embedding_content,
content_method: :embedding_content, remove_content: false) remove_content: false, unit_primitive: 'test_unit_primitive')
end end
def embedding_content def embedding_content
@ -236,7 +239,8 @@ RSpec.describe ActiveContext::Preprocessors::Embeddings do
include ::ActiveContext::Preprocessors::Embeddings include ::ActiveContext::Preprocessors::Embeddings
add_preprocessor :embeddings do |refs| add_preprocessor :embeddings do |refs|
apply_embeddings(refs: refs, content_method: :embedding_content) apply_embeddings(refs: refs, content_method: :embedding_content,
unit_primitive: 'test_unit_primitive')
end end
end end
end end
@ -258,7 +262,7 @@ RSpec.describe ActiveContext::Preprocessors::Embeddings do
include ::ActiveContext::Preprocessors::Embeddings include ::ActiveContext::Preprocessors::Embeddings
add_preprocessor :embeddings do |refs| add_preprocessor :embeddings do |refs|
apply_embeddings(refs: refs) apply_embeddings(refs: refs, unit_primitive: 'test_unit_primitive')
end end
end end
end end
@ -288,7 +292,7 @@ RSpec.describe ActiveContext::Preprocessors::Embeddings do
it 'generates and sets embeddings for each document' do it 'generates and sets embeddings for each document' do
expect(ActiveContext::Embeddings).to receive(:generate_embeddings) expect(ActiveContext::Embeddings).to receive(:generate_embeddings)
.once.with(['Other content'], model: nil).and_return([vectors]) .once.with(['Other content'], model: nil, unit_primitive: unit_primitive).and_return([vectors])
expect(preprocessed_reference.documents).to match_array([{ embedding: vectors }]) expect(preprocessed_reference.documents).to match_array([{ embedding: vectors }])
end end
@ -315,7 +319,8 @@ RSpec.describe ActiveContext::Preprocessors::Embeddings do
include ::ActiveContext::Preprocessors::Embeddings include ::ActiveContext::Preprocessors::Embeddings
add_preprocessor :embeddings do |refs| add_preprocessor :embeddings do |refs|
apply_embeddings(refs: refs, content_field: :other_field) apply_embeddings(refs: refs, content_field: :other_field,
unit_primitive: 'test_unit_primitive')
end end
end end
end end
@ -331,7 +336,7 @@ RSpec.describe ActiveContext::Preprocessors::Embeddings do
it 'generates and sets embeddings for each document' do it 'generates and sets embeddings for each document' do
expect(ActiveContext::Embeddings).to receive(:generate_embeddings) expect(ActiveContext::Embeddings).to receive(:generate_embeddings)
.once.with(['Some other content'], model: nil).and_return([vectors]) .once.with(['Some other content'], model: nil, unit_primitive: unit_primitive).and_return([vectors])
expect(preprocessed_reference.documents).to match_array([{ embedding: vectors }]) expect(preprocessed_reference.documents).to match_array([{ embedding: vectors }])
end end

View File

@ -1,5 +1,5 @@
variables: variables:
DAST_AUTO_DEPLOY_IMAGE_VERSION: 'v2.128.0' DAST_AUTO_DEPLOY_IMAGE_VERSION: 'v2.130.0'
.dast-auto-deploy: .dast-auto-deploy:
image: "${CI_TEMPLATE_REGISTRY_HOST}/gitlab-org/cluster-integration/auto-deploy-image:${DAST_AUTO_DEPLOY_IMAGE_VERSION}" image: "${CI_TEMPLATE_REGISTRY_HOST}/gitlab-org/cluster-integration/auto-deploy-image:${DAST_AUTO_DEPLOY_IMAGE_VERSION}"

View File

@ -1,5 +1,5 @@
variables: variables:
AUTO_DEPLOY_IMAGE_VERSION: 'v2.128.0' AUTO_DEPLOY_IMAGE_VERSION: 'v2.130.0'
.auto-deploy: .auto-deploy:
image: "${CI_TEMPLATE_REGISTRY_HOST}/gitlab-org/cluster-integration/auto-deploy-image:${AUTO_DEPLOY_IMAGE_VERSION}" image: "${CI_TEMPLATE_REGISTRY_HOST}/gitlab-org/cluster-integration/auto-deploy-image:${AUTO_DEPLOY_IMAGE_VERSION}"

View File

@ -1,5 +1,5 @@
variables: variables:
AUTO_DEPLOY_IMAGE_VERSION: 'v2.128.0' AUTO_DEPLOY_IMAGE_VERSION: 'v2.130.0'
.auto-deploy: .auto-deploy:
image: "${CI_TEMPLATE_REGISTRY_HOST}/gitlab-org/cluster-integration/auto-deploy-image:${AUTO_DEPLOY_IMAGE_VERSION}" image: "${CI_TEMPLATE_REGISTRY_HOST}/gitlab-org/cluster-integration/auto-deploy-image:${AUTO_DEPLOY_IMAGE_VERSION}"

View File

@ -0,0 +1,15 @@
# frozen_string_literal: true
module Gitlab
module Usage
module Metrics
module Instrumentations
class CountTopLevelGroupsMetric < DatabaseMetric
operation :count, column: :id
relation { Group.top_level }
end
end
end
end
end

View File

@ -3,7 +3,7 @@
namespace :gitlab do namespace :gitlab do
namespace :db do namespace :db do
namespace :truncate_legacy_tables do namespace :truncate_legacy_tables do
desc "GitLab | DB | Truncate CI Tables on Main" desc "GitLab | DB | Truncate Legacy Tables on Main"
task :main, [:min_batch_size] => [:environment, 'gitlab:db:validate_config'] do |_t, args| task :main, [:min_batch_size] => [:environment, 'gitlab:db:validate_config'] do |_t, args|
args.with_defaults(min_batch_size: 5) args.with_defaults(min_batch_size: 5)
Gitlab::Database::TablesTruncate.new( Gitlab::Database::TablesTruncate.new(
@ -15,7 +15,7 @@ namespace :gitlab do
).execute ).execute
end end
desc "GitLab | DB | Truncate Main Tables on CI" desc "GitLab | DB | Truncate Legacy Tables on CI"
task :ci, [:min_batch_size] => [:environment, 'gitlab:db:validate_config'] do |_t, args| task :ci, [:min_batch_size] => [:environment, 'gitlab:db:validate_config'] do |_t, args|
args.with_defaults(min_batch_size: 5) args.with_defaults(min_batch_size: 5)
Gitlab::Database::TablesTruncate.new( Gitlab::Database::TablesTruncate.new(
@ -26,6 +26,18 @@ namespace :gitlab do
until_table: ENV['UNTIL_TABLE'] until_table: ENV['UNTIL_TABLE']
).execute ).execute
end end
desc "GitLab | DB | Truncate Legacy Tables on Sec"
task :sec, [:min_batch_size] => [:environment, 'gitlab:db:validate_config'] do |_t, args|
args.with_defaults(min_batch_size: 5)
Gitlab::Database::TablesTruncate.new(
database_name: 'sec',
min_batch_size: args.min_batch_size.to_i,
logger: Logger.new($stdout),
dry_run: ENV['DRY_RUN'] == 'true',
until_table: ENV['UNTIL_TABLE']
).execute
end
end end
end end
end end

View File

@ -28375,6 +28375,9 @@ msgstr ""
msgid "GitLabPages|DNS check unsuccessful" msgid "GitLabPages|DNS check unsuccessful"
msgstr "" msgstr ""
msgid "GitLabPages|Delete Pages"
msgstr ""
msgid "GitLabPages|Domain ownership verification required. Add a TXT record to verify ownership at your domain provider." msgid "GitLabPages|Domain ownership verification required. Add a TXT record to verify ownership at your domain provider."
msgstr "" msgstr ""
@ -28405,7 +28408,7 @@ msgstr ""
msgid "GitLabPages|New domain" msgid "GitLabPages|New domain"
msgstr "" msgstr ""
msgid "GitLabPages|Only project maintainers can remove pages" msgid "GitLabPages|Only project maintainers can remove pages."
msgstr "" msgstr ""
msgid "GitLabPages|Pages" msgid "GitLabPages|Pages"
@ -28429,9 +28432,6 @@ msgstr ""
msgid "GitLabPages|Remove pages" msgid "GitLabPages|Remove pages"
msgstr "" msgstr ""
msgid "GitLabPages|Removing pages will prevent them from being exposed to the public internet."
msgstr ""
msgid "GitLabPages|Restrict access to only project members on all group projects" msgid "GitLabPages|Restrict access to only project members on all group projects"
msgstr "" msgstr ""
@ -28447,6 +28447,12 @@ msgstr ""
msgid "GitLabPages|Support for domains and certificates is disabled. Ask your GitLab administrator to enable it." msgid "GitLabPages|Support for domains and certificates is disabled. Ask your GitLab administrator to enable it."
msgstr "" msgstr ""
msgid "GitLabPages|This action will permanently delete all Pages deployments."
msgstr ""
msgid "GitLabPages|This is permanent and cannot be undone. To deploy this Pages site again, run a new pipeline."
msgstr ""
msgid "GitLabPages|Updating your Pages configuration…" msgid "GitLabPages|Updating your Pages configuration…"
msgstr "" msgstr ""
@ -43224,7 +43230,10 @@ msgstr ""
msgid "Owasp|A9:2017 Using Components with Known Vulnerabilities" msgid "Owasp|A9:2017 Using Components with Known Vulnerabilities"
msgstr "" msgstr ""
msgid "Owasp|Non-OWASP Top 10" msgid "Owasp|Non-OWASP Top 10 2017"
msgstr ""
msgid "Owasp|Non-OWASP Top 10 2021"
msgstr "" msgstr ""
msgid "Owned by %{image_tag}" msgid "Owned by %{image_tag}"

View File

@ -56,7 +56,7 @@
"@babel/preset-env": "^7.23.7", "@babel/preset-env": "^7.23.7",
"@csstools/postcss-global-data": "^2.1.1", "@csstools/postcss-global-data": "^2.1.1",
"@cubejs-client/core": "^1.0.0", "@cubejs-client/core": "^1.0.0",
"@floating-ui/dom": "^1.6.13", "@floating-ui/dom": "^1.7.0",
"@gitlab/application-sdk-browser": "^0.3.4", "@gitlab/application-sdk-browser": "^0.3.4",
"@gitlab/at.js": "1.5.7", "@gitlab/at.js": "1.5.7",
"@gitlab/cluster-client": "^3.0.0", "@gitlab/cluster-client": "^3.0.0",
@ -82,41 +82,41 @@
"@snowplow/browser-plugin-timezone": "^3.24.2", "@snowplow/browser-plugin-timezone": "^3.24.2",
"@snowplow/browser-tracker": "^3.24.2", "@snowplow/browser-tracker": "^3.24.2",
"@sourcegraph/code-host-integration": "0.0.95", "@sourcegraph/code-host-integration": "0.0.95",
"@tiptap/core": "^2.11.7", "@tiptap/core": "^2.12.0",
"@tiptap/extension-blockquote": "^2.11.7", "@tiptap/extension-blockquote": "^2.12.0",
"@tiptap/extension-bold": "^2.11.7", "@tiptap/extension-bold": "^2.12.0",
"@tiptap/extension-bubble-menu": "^2.11.7", "@tiptap/extension-bubble-menu": "^2.12.0",
"@tiptap/extension-bullet-list": "^2.11.7", "@tiptap/extension-bullet-list": "^2.12.0",
"@tiptap/extension-code": "^2.11.7", "@tiptap/extension-code": "^2.12.0",
"@tiptap/extension-code-block": "^2.11.7", "@tiptap/extension-code-block": "^2.12.0",
"@tiptap/extension-code-block-lowlight": "^2.11.7", "@tiptap/extension-code-block-lowlight": "^2.12.0",
"@tiptap/extension-document": "^2.11.7", "@tiptap/extension-document": "^2.12.0",
"@tiptap/extension-dropcursor": "^2.11.7", "@tiptap/extension-dropcursor": "^2.12.0",
"@tiptap/extension-gapcursor": "^2.11.7", "@tiptap/extension-gapcursor": "^2.12.0",
"@tiptap/extension-hard-break": "^2.11.7", "@tiptap/extension-hard-break": "^2.12.0",
"@tiptap/extension-heading": "^2.11.7", "@tiptap/extension-heading": "^2.12.0",
"@tiptap/extension-highlight": "^2.11.7", "@tiptap/extension-highlight": "^2.12.0",
"@tiptap/extension-history": "^2.11.7", "@tiptap/extension-history": "^2.12.0",
"@tiptap/extension-horizontal-rule": "^2.11.7", "@tiptap/extension-horizontal-rule": "^2.12.0",
"@tiptap/extension-image": "^2.11.7", "@tiptap/extension-image": "^2.12.0",
"@tiptap/extension-italic": "^2.11.7", "@tiptap/extension-italic": "^2.12.0",
"@tiptap/extension-link": "^2.11.7", "@tiptap/extension-link": "^2.12.0",
"@tiptap/extension-list-item": "^2.11.7", "@tiptap/extension-list-item": "^2.12.0",
"@tiptap/extension-ordered-list": "^2.11.7", "@tiptap/extension-ordered-list": "^2.12.0",
"@tiptap/extension-paragraph": "^2.11.7", "@tiptap/extension-paragraph": "^2.12.0",
"@tiptap/extension-strike": "^2.11.7", "@tiptap/extension-strike": "^2.12.0",
"@tiptap/extension-subscript": "^2.11.7", "@tiptap/extension-subscript": "^2.12.0",
"@tiptap/extension-superscript": "^2.11.7", "@tiptap/extension-superscript": "^2.12.0",
"@tiptap/extension-table": "^2.11.7", "@tiptap/extension-table": "^2.12.0",
"@tiptap/extension-table-cell": "^2.11.7", "@tiptap/extension-table-cell": "^2.12.0",
"@tiptap/extension-table-header": "^2.11.7", "@tiptap/extension-table-header": "^2.12.0",
"@tiptap/extension-table-row": "^2.11.7", "@tiptap/extension-table-row": "^2.12.0",
"@tiptap/extension-task-item": "^2.11.7", "@tiptap/extension-task-item": "^2.12.0",
"@tiptap/extension-task-list": "^2.11.7", "@tiptap/extension-task-list": "^2.12.0",
"@tiptap/extension-text": "^2.11.7", "@tiptap/extension-text": "^2.12.0",
"@tiptap/pm": "^2.11.7", "@tiptap/pm": "^2.12.0",
"@tiptap/suggestion": "^2.11.7", "@tiptap/suggestion": "^2.12.0",
"@tiptap/vue-2": "^2.11.7", "@tiptap/vue-2": "^2.12.0",
"@vue/apollo-components": "^4.0.0-beta.4", "@vue/apollo-components": "^4.0.0-beta.4",
"@vue/apollo-option": "^4.0.0-beta.4", "@vue/apollo-option": "^4.0.0-beta.4",
"apollo-upload-client": "15.0.0", "apollo-upload-client": "15.0.0",
@ -251,7 +251,7 @@
"@gitlab/eslint-plugin": "20.7.1", "@gitlab/eslint-plugin": "20.7.1",
"@gitlab/noop": "^1.0.1", "@gitlab/noop": "^1.0.1",
"@gitlab/stylelint-config": "6.2.2", "@gitlab/stylelint-config": "6.2.2",
"@graphql-eslint/eslint-plugin": "3.20.1", "@graphql-eslint/eslint-plugin": "4.4.0",
"@originjs/vite-plugin-commonjs": "^1.0.3", "@originjs/vite-plugin-commonjs": "^1.0.3",
"@pinia/testing": "^0.1.5", "@pinia/testing": "^0.1.5",
"@rollup/plugin-graphql": "^2.0.5", "@rollup/plugin-graphql": "^2.0.5",
@ -277,7 +277,7 @@
"custom-jquery-matchers": "^2.1.0", "custom-jquery-matchers": "^2.1.0",
"dependency-cruiser": "^16.9.0", "dependency-cruiser": "^16.9.0",
"eslint": "9.25.1", "eslint": "9.25.1",
"eslint-formatter-gitlab": "^5.1.0", "eslint-formatter-gitlab": "^6.0.0",
"eslint-import-resolver-jest": "3.0.2", "eslint-import-resolver-jest": "3.0.2",
"eslint-import-resolver-webpack": "0.13.10", "eslint-import-resolver-webpack": "0.13.10",
"eslint-plugin-import": "^2.31.0", "eslint-plugin-import": "^2.31.0",

View File

@ -1,22 +0,0 @@
diff --git a/node_modules/@graphql-eslint/eslint-plugin/cjs/utils.js b/node_modules/@graphql-eslint/eslint-plugin/cjs/utils.js
index bd7f92a..1ee1ca1 100644
--- a/node_modules/@graphql-eslint/eslint-plugin/cjs/utils.js
+++ b/node_modules/@graphql-eslint/eslint-plugin/cjs/utils.js
@@ -52,7 +52,7 @@ var import_chalk = __toESM(require("chalk"));
var import_graphql = require("graphql");
var import_lodash = __toESM(require("lodash.lowercase"));
function requireSiblingsOperations(ruleId, context) {
- const { siblingOperations } = context.parserServices;
+ const { siblingOperations } = context.sourceCode.parserServices;
if (!siblingOperations.available) {
throw new Error(
`Rule \`${ruleId}\` requires \`parserOptions.operations\` to be set and loaded. See https://bit.ly/graphql-eslint-operations for more info`
@@ -61,7 +61,7 @@ function requireSiblingsOperations(ruleId, context) {
return siblingOperations;
}
function requireGraphQLSchemaFromContext(ruleId, context) {
- const { schema } = context.parserServices;
+ const { schema } = context.sourceCode.parserServices;
if (!schema) {
throw new Error(
`Rule \`${ruleId}\` requires \`parserOptions.schema\` to be set and loaded. See https://bit.ly/graphql-eslint-schema for more info`

View File

@ -85,7 +85,7 @@ RSpec.describe '1_settings', feature_category: :shared do
} }
end end
context 'when legacy topology service config is provided' do context 'when legacy topology service client config is provided as a top-level key' do
before do before do
stub_config({ cell: { enabled: true, id: 1 }, topology_service: config }) stub_config({ cell: { enabled: true, id: 1 }, topology_service: config })
load_settings load_settings
@ -96,6 +96,18 @@ RSpec.describe '1_settings', feature_category: :shared do
it { expect(Settings.cell.topology_service_client.certificate_file).to eq(config[:certificate_file]) } it { expect(Settings.cell.topology_service_client.certificate_file).to eq(config[:certificate_file]) }
it { expect(Settings.cell.topology_service_client.private_key_file).to eq(config[:private_key_file]) } it { expect(Settings.cell.topology_service_client.private_key_file).to eq(config[:private_key_file]) }
end end
context 'when topology service client config is provided as a key nested' do
before do
stub_config({ cell: { enabled: true, id: 1, topology_service_client: config } })
load_settings
end
it { expect(Settings.cell.topology_service_client.address).to eq(config[:address]) }
it { expect(Settings.cell.topology_service_client.ca_file).to eq(config[:ca_file]) }
it { expect(Settings.cell.topology_service_client.certificate_file).to eq(config[:certificate_file]) }
it { expect(Settings.cell.topology_service_client.private_key_file).to eq(config[:private_key_file]) }
end
end end
describe 'Pages custom domains settings' do describe 'Pages custom domains settings' do

View File

@ -121,7 +121,7 @@ RSpec.describe 'validate database config', feature_category: :cell do
end end
it 'raises exception about missing topology service client config' do it 'raises exception about missing topology service client config' do
expect { validate_config }.to raise_error("Topology Service setting 'address' is not set.#{dev_message}") expect { validate_config }.to raise_error("Topology Service Client setting 'address' is not set.#{dev_message}")
end end
it_behaves_like 'with SKIP_CELL_CONFIG_VALIDATION=true' it_behaves_like 'with SKIP_CELL_CONFIG_VALIDATION=true'

View File

@ -0,0 +1,13 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::Usage::Metrics::Instrumentations::CountTopLevelGroupsMetric, feature_category: :groups_and_projects do
let_it_be(:top_level_group) { create(:group) }
let_it_be(:subgroup) { create(:group, :nested) }
let_it_be(:project_in_group_namespace) { create(:project, group: subgroup) }
let_it_be(:project_in_user_namespace) { create(:project) }
let(:expected_value) { 2 }
it_behaves_like 'a correct instrumented metric value', { time_frame: 'all', data_source: 'database' }
end

View File

@ -106,6 +106,19 @@ RSpec.describe Approvable do
end end
end end
describe '#approvals_for_user_ids' do
let_it_be(:user) { create(:user) }
let_it_be(:merge_request) { create(:merge_request) }
let_it_be(:approval) { create(:approval, merge_request: merge_request, user: user) }
let_it_be(:approval2) { create(:approval, merge_request: merge_request, user: create(:user)) }
subject(:approvals) { merge_request.approvals_for_user_ids([user.id]) }
it 'returns approvals by user' do
is_expected.to contain_exactly(approval)
end
end
describe '.not_approved_by_users_with_usernames' do describe '.not_approved_by_users_with_usernames' do
subject { MergeRequest.not_approved_by_users_with_usernames([user.username, user2.username]) } subject { MergeRequest.not_approved_by_users_with_usernames([user.username, user2.username]) }

View File

@ -59,5 +59,41 @@ RSpec.describe WorkItems::UserPreference, type: :model, feature_category: :team_
MESSAGE MESSAGE
end end
end end
describe 'validate display_settings' do
before do
allow(WorkItems::SortingKeys).to receive(:available?).and_return(true)
end
it 'is valid with an empty display settings hash' do
preferences = described_class.new(namespace: namespace, display_settings: {})
expect(preferences).to be_valid
end
it 'is invalid with properties not defined in the schema' do
invalid_display_settings = { 'invalidProperty' => 'some_value' }
preferences = described_class.new(namespace: namespace, display_settings: invalid_display_settings)
expect(preferences).not_to be_valid
expect(preferences.errors[:display_settings]).to include('must be a valid json schema')
end
it 'is valid with a valid display_settings hash' do
valid_display_settings = { 'shouldOpenItemsInSidePanel' => true }
preferences = described_class.new(namespace: namespace, display_settings: valid_display_settings)
expect(preferences).to be_valid
expect(preferences.display_settings).to eq(valid_display_settings)
end
it 'is invalid with a wrong type as per schema validation' do
invalid_display_settings = { 'shouldOpenItemsInSidePanel' => 'string_type' }
preferences = described_class.new(namespace: namespace, display_settings: invalid_display_settings)
expect(preferences).not_to be_valid
expect(preferences.errors[:display_settings]).to include('must be a valid json schema')
end
end
end end
end end

View File

@ -31,5 +31,13 @@ RSpec.describe 'Updating an existing HTTP Integration', feature_category: :incid
let(:mutation_response) { graphql_mutation_response(:http_integration_update) } let(:mutation_response) { graphql_mutation_response(:http_integration_update) }
before do
allow_unlimited_graphql_complexity
allow_unlimited_graphql_depth
allow_unlimited_validation_timeout
# Optional, if you suspect recursion issues, though less common for timeouts:
# allow_high_graphql_recursion
end
it_behaves_like 'updating an existing HTTP integration' it_behaves_like 'updating an existing HTTP integration'
end end

View File

@ -10,12 +10,14 @@ RSpec.describe 'Update work items user preferences', feature_category: :team_pla
let_it_be(:work_item_type) { WorkItems::Type.default_by_type(:incident) } let_it_be(:work_item_type) { WorkItems::Type.default_by_type(:incident) }
let(:sorting_value) { 'CREATED_ASC' } let(:sorting_value) { 'CREATED_ASC' }
let(:display_settings) { { 'shouldOpenItemsInSidePanel' => true } }
let(:input) do let(:input) do
{ {
namespacePath: namespace.full_path, namespacePath: namespace.full_path,
workItemTypeId: work_item_type&.to_gid, workItemTypeId: work_item_type&.to_gid,
sort: sorting_value sort: sorting_value,
displaySettings: display_settings
} }
end end
@ -30,6 +32,7 @@ RSpec.describe 'Update work items user preferences', feature_category: :team_pla
name name
} }
sort sort
displaySettings
} }
FIELDS FIELDS
end end
@ -66,7 +69,8 @@ RSpec.describe 'Update work items user preferences', feature_category: :team_pla
'workItemType' => { 'workItemType' => {
'name' => work_item_type.name 'name' => work_item_type.name
}, },
'sort' => sorting_value 'sort' => sorting_value,
'displaySettings' => display_settings
) )
end end
@ -74,7 +78,8 @@ RSpec.describe 'Update work items user preferences', feature_category: :team_pla
let(:input) do let(:input) do
{ {
namespacePath: namespace.full_path, namespacePath: namespace.full_path,
sort: sorting_value sort: sorting_value,
displaySettings: display_settings
} }
end end
@ -89,7 +94,8 @@ RSpec.describe 'Update work items user preferences', feature_category: :team_pla
'path' => namespace.path 'path' => namespace.path
}, },
'workItemType' => nil, 'workItemType' => nil,
'sort' => sorting_value 'sort' => sorting_value,
'displaySettings' => display_settings
) )
end end
end end
@ -109,6 +115,20 @@ RSpec.describe 'Update work items user preferences', feature_category: :team_pla
expect(mutation_response['userPreferences']).to be_nil expect(mutation_response['userPreferences']).to be_nil
end end
end end
context 'when display settings are not valid' do
let_it_be(:display_settings) { { 'shouldOpenItemsInSidePanel' => 'test' } }
it 'updates the user preferences successfully' do
post_graphql_mutation(mutation, current_user: user)
expect(response).to have_gitlab_http_status(:success)
expect(graphql_errors).to be_blank
expect(mutation_response['errors']).to include(
'Display settings must be a valid json schema'
)
expect(mutation_response['userPreferences']).to be_nil
end
end
end end
end end

View File

@ -307,6 +307,14 @@ RSpec.describe MergeRequests::UpdateService, :mailer, feature_category: :code_re
update_merge_request(reviewer_ids: [user.id]) update_merge_request(reviewer_ids: [user.id])
end end
it 'sets reviewer state as approved if user has previously approved' do
create(:approval, merge_request: merge_request, user: user)
update_merge_request(reviewer_ids: [user.id])
expect(merge_request.find_reviewer(user)).to be_approved
end
end end
it 'creates a resource label event' do it 'creates a resource label event' do

View File

@ -6,8 +6,18 @@ RSpec.describe 'gitlab:db:truncate_legacy_tables', :silence_stdout, :reestablish
:suppress_gitlab_schemas_validate_connection, feature_category: :cell do :suppress_gitlab_schemas_validate_connection, feature_category: :cell do
let(:main_connection) { ApplicationRecord.connection } let(:main_connection) { ApplicationRecord.connection }
let(:ci_connection) { Ci::ApplicationRecord.connection } let(:ci_connection) { Ci::ApplicationRecord.connection }
let(:sec_connection) { SecApplicationRecord.connection }
let(:test_gitlab_main_table) { '_test_gitlab_main_table' } let(:test_gitlab_main_table) { '_test_gitlab_main_table' }
let(:test_gitlab_ci_table) { '_test_gitlab_ci_table' } let(:test_gitlab_ci_table) { '_test_gitlab_ci_table' }
let(:test_gitlab_sec_table) { '_test_gitlab_sec_table' }
let(:databases) { %i[main ci] }
let(:tables_to_schema) do
{
test_gitlab_main_table => :gitlab_main,
test_gitlab_ci_table => :gitlab_ci,
test_gitlab_sec_table => :gitlab_main
}
end
before(:all) do before(:all) do
Rake.application.rake_require 'active_record/railties/databases' Rake.application.rake_require 'active_record/railties/databases'
@ -19,20 +29,21 @@ RSpec.describe 'gitlab:db:truncate_legacy_tables', :silence_stdout, :reestablish
before do before do
skip_if_shared_database(:ci) skip_if_shared_database(:ci)
execute_on_each_database(<<~SQL) execute_on_each_database(<<~SQL, databases: databases)
CREATE TABLE #{test_gitlab_main_table} (id integer NOT NULL); CREATE TABLE #{test_gitlab_main_table} (id integer NOT NULL);
INSERT INTO #{test_gitlab_main_table} VALUES(generate_series(1, 50)); INSERT INTO #{test_gitlab_main_table} VALUES(generate_series(1, 50));
SQL SQL
execute_on_each_database(<<~SQL) execute_on_each_database(<<~SQL, databases: databases)
CREATE TABLE #{test_gitlab_ci_table} (id integer NOT NULL); CREATE TABLE #{test_gitlab_ci_table} (id integer NOT NULL);
INSERT INTO #{test_gitlab_ci_table} VALUES(generate_series(1, 50)); INSERT INTO #{test_gitlab_ci_table} VALUES(generate_series(1, 50));
SQL SQL
execute_on_each_database(<<~SQL, databases: databases)
CREATE TABLE #{test_gitlab_sec_table} (id integer NOT NULL);
INSERT INTO #{test_gitlab_sec_table} VALUES(generate_series(1, 50));
SQL
allow(Gitlab::Database::GitlabSchema).to receive(:tables_to_schema).and_return( allow(Gitlab::Database::GitlabSchema).to receive(:tables_to_schema).and_return(
{ tables_to_schema
test_gitlab_main_table => :gitlab_main,
test_gitlab_ci_table => :gitlab_ci
}
) )
end end
@ -61,6 +72,40 @@ RSpec.describe 'gitlab:db:truncate_legacy_tables', :silence_stdout, :reestablish
database_name: "ci", database_name: "ci",
with_retries: false with_retries: false
).lock_writes ).lock_writes
# Locking sec table on the ci database
Gitlab::Database::LockWritesManager.new(
table_name: test_gitlab_sec_table,
connection: ci_connection,
database_name: "ci",
with_retries: false
).lock_writes
if database_exists?('sec')
# Locking sec table on the main database
Gitlab::Database::LockWritesManager.new(
table_name: test_gitlab_sec_table,
connection: main_connection,
database_name: "main",
with_retries: false
).lock_writes
# Locking ci table on the sec database
Gitlab::Database::LockWritesManager.new(
table_name: test_gitlab_ci_table,
connection: sec_connection,
database_name: "sec",
with_retries: false
).lock_writes
# Locking main table on the sec database
Gitlab::Database::LockWritesManager.new(
table_name: test_gitlab_main_table,
connection: sec_connection,
database_name: "sec",
with_retries: false
).lock_writes
end
end end
it 'calls TablesTruncate with the correct parameters and default minimum batch size' do it 'calls TablesTruncate with the correct parameters and default minimum batch size' do
@ -75,10 +120,20 @@ RSpec.describe 'gitlab:db:truncate_legacy_tables', :silence_stdout, :reestablish
truncate_legacy_tables truncate_legacy_tables
end end
it 'truncates the legacy table' do it 'truncates the legacy tables' do
expect do legacy_tables.each do |legacy_table|
truncate_legacy_tables expect(
end.to change { connection.select_value("SELECT count(*) from #{legacy_table}") }.from(50).to(0) connection.select_value("SELECT count(*) from #{legacy_table}")
).to eq(50)
end
truncate_legacy_tables
legacy_tables.each do |legacy_table|
expect(
connection.select_value("SELECT count(*) from #{legacy_table}")
).to eq(0)
end
end end
it 'does not truncate the table that belongs to the connection schema' do it 'does not truncate the table that belongs to the connection schema' do
@ -95,19 +150,23 @@ RSpec.describe 'gitlab:db:truncate_legacy_tables', :silence_stdout, :reestablish
it 'does not truncate any tables' do it 'does not truncate any tables' do
expect do expect do
truncate_legacy_tables truncate_legacy_tables
end.not_to change { connection.select_value("SELECT count(*) from #{legacy_table}") } end.not_to change {
legacy_tables.map do |legacy_table|
connection.select_value("SELECT count(*) from #{legacy_table}")
end
}
end end
it 'prints the truncation sql statement to the output' do it 'prints the truncation sql statement to the output' do
expect do expect do
truncate_legacy_tables truncate_legacy_tables
end.to output(/TRUNCATE TABLE #{legacy_table} RESTRICT/).to_stdout end.to output(/TRUNCATE TABLE #{legacy_tables.join(', ')} RESTRICT/).to_stdout
end end
end end
context 'when passing until_table parameter via environment variable' do context 'when passing until_table parameter via environment variable' do
before do before do
stub_env('UNTIL_TABLE', legacy_table) stub_env('UNTIL_TABLE', legacy_tables.first)
end end
it 'sends the table name to TablesTruncate' do it 'sends the table name to TablesTruncate' do
@ -116,7 +175,7 @@ RSpec.describe 'gitlab:db:truncate_legacy_tables', :silence_stdout, :reestablish
min_batch_size: 5, min_batch_size: 5,
logger: anything, logger: anything,
dry_run: false, dry_run: false,
until_table: legacy_table until_table: legacy_tables.first
).and_call_original ).and_call_original
truncate_legacy_tables truncate_legacy_tables
@ -131,18 +190,55 @@ RSpec.describe 'gitlab:db:truncate_legacy_tables', :silence_stdout, :reestablish
let(:connection) { ApplicationRecord.connection } let(:connection) { ApplicationRecord.connection }
let(:database_name) { 'main' } let(:database_name) { 'main' }
let(:active_table) { test_gitlab_main_table } let(:active_table) { test_gitlab_main_table }
let(:legacy_table) { test_gitlab_ci_table } let(:legacy_tables) { [test_gitlab_ci_table] }
it_behaves_like 'truncating legacy tables' it_behaves_like 'truncating legacy tables'
end end
context 'when truncating main tables on the ci database' do context 'when truncating main tables on the ci database' do
subject(:truncate_legacy_tables) { run_rake_task('gitlab:db:truncate_legacy_tables:ci') } subject(:truncate_legacy_tables) do
# These are purposefully cross-DB to properly test multi-schema truncation
Gitlab::Database.allow_cross_joins_across_databases(
url: 'https://gitlab.com/gitlab-org/gitlab/-/merge_requests/189237'
) do
run_rake_task('gitlab:db:truncate_legacy_tables:ci')
end
end
let(:connection) { Ci::ApplicationRecord.connection } let(:connection) { Ci::ApplicationRecord.connection }
let(:database_name) { 'ci' } let(:database_name) { 'ci' }
let(:active_table) { test_gitlab_ci_table } let(:active_table) { test_gitlab_ci_table }
let(:legacy_table) { test_gitlab_main_table } let(:legacy_tables) { [test_gitlab_main_table, test_gitlab_sec_table] }
it_behaves_like 'truncating legacy tables'
end
context 'when truncating both ci and main tables on the sec database' do
subject(:truncate_legacy_tables) do
# These are purposefully cross-DB to properly test multi-schema truncation
Gitlab::Database.allow_cross_joins_across_databases(
url: 'https://gitlab.com/gitlab-org/gitlab/-/merge_requests/189237'
) do
run_rake_task('gitlab:db:truncate_legacy_tables:sec')
end
end
let(:connection) { SecApplicationRecord.connection }
let(:database_name) { 'sec' }
let(:active_table) { test_gitlab_sec_table }
let(:legacy_tables) { [test_gitlab_ci_table, test_gitlab_main_table] }
let(:databases) { %i[ci main sec] }
let(:tables_to_schema) do
{
test_gitlab_main_table => :gitlab_main,
test_gitlab_ci_table => :gitlab_ci,
test_gitlab_sec_table => :gitlab_sec
}
end
before do
skip unless database_exists?(:sec)
end
it_behaves_like 'truncating legacy tables' it_behaves_like 'truncating legacy tables'
end end

View File

@ -72,7 +72,8 @@ RSpec.describe CI::ChangedFiles, feature_category: :tooling do
describe '#run_eslint_for_changed_files' do describe '#run_eslint_for_changed_files' do
let(:eslint_command) do let(:eslint_command) do
['yarn', 'run', 'lint:eslint', '--no-warn-ignored', '--format', 'gitlab', 'file1.js', 'file2.vue'] ['yarn', 'run', 'lint:eslint', '--no-warn-ignored', '--no-error-on-unmatched-pattern', '--format', 'gitlab',
'file1.js', 'file2.vue']
end end
let(:console_message) { /Running ESLint for changed files.../i } let(:console_message) { /Running ESLint for changed files.../i }
@ -120,7 +121,7 @@ RSpec.describe CI::ChangedFiles, feature_category: :tooling do
context 'when a single todo file has been changed' do context 'when a single todo file has been changed' do
let(:eslint_command) do let(:eslint_command) do
['yarn', 'run', 'lint:eslint', '--no-warn-ignored', '--format', 'gitlab', ['yarn', 'run', 'lint:eslint', '--no-warn-ignored', '--no-error-on-unmatched-pattern', '--format', 'gitlab',
'.eslint_todo/vue-no-unused-properties.mjs', '.eslint_todo/vue-no-unused-properties.mjs',
'app/assets/javascripts/add_context_commits_modal/components/add_context_commits_modal_wrapper.vue', 'app/assets/javascripts/add_context_commits_modal/components/add_context_commits_modal_wrapper.vue',
'app/assets/javascripts/admin/abuse_report/components/notes/abuse_report_comment_form.vue', 'app/assets/javascripts/admin/abuse_report/components/notes/abuse_report_comment_form.vue',
@ -174,7 +175,7 @@ RSpec.describe CI::ChangedFiles, feature_category: :tooling do
context 'when several todo files have been changed' do context 'when several todo files have been changed' do
let(:eslint_command) do let(:eslint_command) do
['yarn', 'run', 'lint:eslint', '--no-warn-ignored', '--format', 'gitlab', ['yarn', 'run', 'lint:eslint', '--no-warn-ignored', '--no-error-on-unmatched-pattern', '--format', 'gitlab',
'.eslint_todo/vue-no-unused-properties.mjs', '.eslint_todo/vue-no-unused-properties.mjs',
'app/assets/javascripts/projects/project_new.js', 'app/assets/javascripts/projects/project_new.js',
'app/assets/javascripts/add_context_commits_modal/components/add_context_commits_modal_wrapper.vue', 'app/assets/javascripts/add_context_commits_modal/components/add_context_commits_modal_wrapper.vue',
@ -240,7 +241,7 @@ RSpec.describe CI::ChangedFiles, feature_category: :tooling do
context 'when todo files have been changed but no ignored file was removed from them' do context 'when todo files have been changed but no ignored file was removed from them' do
let(:eslint_command) do let(:eslint_command) do
['yarn', 'run', 'lint:eslint', '--no-warn-ignored', '--format', 'gitlab', ['yarn', 'run', 'lint:eslint', '--no-warn-ignored', '--no-error-on-unmatched-pattern', '--format', 'gitlab',
'.eslint_todo/vue-no-unused-properties.mjs'] '.eslint_todo/vue-no-unused-properties.mjs']
end end

View File

@ -45,7 +45,13 @@ module CI
return 0 return 0
end end
command = ["yarn", "run", "lint:eslint", "--no-warn-ignored", "--format", "gitlab", *files] command = [
"yarn", "run", "lint:eslint",
"--no-warn-ignored",
"--no-error-on-unmatched-pattern",
"--format", "gitlab",
*files
]
system(*command) system(*command)
last_command_status.exitstatus last_command_status.exitstatus

1309
yarn.lock

File diff suppressed because it is too large Load Diff