From 6d989bfe51395035ca68b55727c635cc73182bc4 Mon Sep 17 00:00:00 2001 From: "vishal.s.patel" Date: Tue, 9 Dec 2025 09:16:17 +1300 Subject: [PATCH 01/20] Added branch name for testing --- .gitlab/ci/performance-on-cng/main.gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab/ci/performance-on-cng/main.gitlab-ci.yml b/.gitlab/ci/performance-on-cng/main.gitlab-ci.yml index 52a2ff6ae9c00e..652ce6e9d2c414 100644 --- a/.gitlab/ci/performance-on-cng/main.gitlab-ci.yml +++ b/.gitlab/ci/performance-on-cng/main.gitlab-ci.yml @@ -70,7 +70,7 @@ run-performance-tests: - dotenv-vars trigger: project: gitlab-org/quality/component-performance-testing - branch: main + branch: vp-conditional-metrics-collection strategy: depend forward: pipeline_variables: true -- GitLab From 6469ddf8b5112b1b821957db064e1c7754269c14 Mon Sep 17 00:00:00 2001 From: "vishal.s.patel" Date: Tue, 9 Dec 2025 09:44:26 +1300 Subject: [PATCH 02/20] Adding code for testing --- lib/api/merge_requests.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb index 0c2eefa3015c4e..5e1acfcdc6a541 100644 --- a/lib/api/merge_requests.rb +++ b/lib/api/merge_requests.rb @@ -204,7 +204,7 @@ def batch_process_mergeability_checks(merge_requests) authenticate! unless params[:scope] == 'all' validate_search_rate_limit! if declared_params[:search].present? merge_requests = find_merge_requests - + sleep 2 present merge_requests, serializer_options_for(merge_requests) end end -- GitLab From 946b0d9c3526c821576555e88e6b363cd23d8165 Mon Sep 17 00:00:00 2001 From: "vishal.s.patel" Date: Tue, 9 Dec 2025 10:34:39 +1300 Subject: [PATCH 03/20] Reverted testing changes --- .gitlab/ci/performance-on-cng/main.gitlab-ci.yml | 2 +- lib/api/merge_requests.rb | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitlab/ci/performance-on-cng/main.gitlab-ci.yml b/.gitlab/ci/performance-on-cng/main.gitlab-ci.yml index 652ce6e9d2c414..52a2ff6ae9c00e 100644 --- a/.gitlab/ci/performance-on-cng/main.gitlab-ci.yml +++ b/.gitlab/ci/performance-on-cng/main.gitlab-ci.yml @@ -70,7 +70,7 @@ run-performance-tests: - dotenv-vars trigger: project: gitlab-org/quality/component-performance-testing - branch: vp-conditional-metrics-collection + branch: main strategy: depend forward: pipeline_variables: true diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb index 5e1acfcdc6a541..34964fd8dc583a 100644 --- a/lib/api/merge_requests.rb +++ b/lib/api/merge_requests.rb @@ -204,7 +204,6 @@ def batch_process_mergeability_checks(merge_requests) authenticate! unless params[:scope] == 'all' validate_search_rate_limit! if declared_params[:search].present? merge_requests = find_merge_requests - sleep 2 present merge_requests, serializer_options_for(merge_requests) end end -- GitLab From 3f1a2961ee8ba2a7670a8a7bc32abc61123c6b3b Mon Sep 17 00:00:00 2001 From: "vishal.s.patel" Date: Tue, 9 Dec 2025 10:35:15 +1300 Subject: [PATCH 04/20] Adding back newline --- lib/api/merge_requests.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb index 34964fd8dc583a..0c2eefa3015c4e 100644 --- a/lib/api/merge_requests.rb +++ b/lib/api/merge_requests.rb @@ -204,6 +204,7 @@ def batch_process_mergeability_checks(merge_requests) authenticate! unless params[:scope] == 'all' validate_search_rate_limit! if declared_params[:search].present? merge_requests = find_merge_requests + present merge_requests, serializer_options_for(merge_requests) end end -- GitLab From 7dd84c060779d1710352421ed76ea278345df204 Mon Sep 17 00:00:00 2001 From: "vishal.s.patel" Date: Wed, 10 Dec 2025 14:47:06 +1300 Subject: [PATCH 05/20] Updating test to include link --- .../ci/performance-on-cng/main.gitlab-ci.yml | 2 +- .../k6_test/group_merge_requests.js | 17 +++++++++-------- .../k6_test/project_merge_requests.js | 18 ++++++++++++------ 3 files changed, 22 insertions(+), 15 deletions(-) diff --git a/.gitlab/ci/performance-on-cng/main.gitlab-ci.yml b/.gitlab/ci/performance-on-cng/main.gitlab-ci.yml index 52a2ff6ae9c00e..f226cb0f7659c1 100644 --- a/.gitlab/ci/performance-on-cng/main.gitlab-ci.yml +++ b/.gitlab/ci/performance-on-cng/main.gitlab-ci.yml @@ -70,7 +70,7 @@ run-performance-tests: - dotenv-vars trigger: project: gitlab-org/quality/component-performance-testing - branch: main + branch: vp-adding-api-url-and-test-link strategy: depend forward: pipeline_variables: true diff --git a/qa/performance_test/k6_test/group_merge_requests.js b/qa/performance_test/k6_test/group_merge_requests.js index c72cb1412aaeaf..ccfea0638fd547 100644 --- a/qa/performance_test/k6_test/group_merge_requests.js +++ b/qa/performance_test/k6_test/group_merge_requests.js @@ -9,13 +9,14 @@ export const LOAD_TEST_DURATION = '50s'; export const WARMUP_TEST_VUS = 1; export const WARMUP_TEST_DURATION = '10s'; -// Global variable to store the group ID let groupId; +let API_URL; export function setup() { const baseUrl = __ENV.GITLAB_URL || `http://gitlab.${__ENV.AI_GATEWAY_IP}.nip.io`; const token = __ENV.GITLAB_QA_ADMIN_ACCESS_TOKEN || ''; const groupName = 'Test Seed Group'; + const apiVersion = 'v4'; // Search for the group by name const searchUrl = `${baseUrl}/api/v4/groups?search=${encodeURIComponent(groupName)}`; @@ -33,14 +34,12 @@ export function setup() { const targetGroup = groups.find((group) => group.name === groupName); if (targetGroup) { - console.log(`Found group '${groupName}' with ID: ${targetGroup.id}`); - return { groupId: targetGroup.id }; + const apiUrl = `${apiVersion}/groups/${targetGroup.id}/merge_requests`; + return { projectId: targetGroup.id, apiUrl: apiUrl }; } - console.error(`Group '${groupName}' not found`); - return { groupId: '5' }; // Fallback to default } - console.error(`Failed to search for groups: ${res.status}`); - return { groupId: '5' }; // Fallback to default + const apiUrl = `${apiVersion}/groups/1/merge_requests`; + return { projectId: '1', apiUrl: apiUrl }; // Fallback to default } export const options = { @@ -74,8 +73,10 @@ export default function groupMergeRequestsTest(data) { const apiVersion = 'v4'; groupId = __ENV.GROUP_ID || data.groupId; const token = __ENV.GITLAB_QA_ADMIN_ACCESS_TOKEN || ''; + API_URL = data.apiUrl; + console.log(API_URL) - const url = `${baseUrl}/api/${apiVersion}/groups/${groupId}/merge_requests`; + const url = `${baseUrl}/api/${API_URL}`; const params = { headers: { diff --git a/qa/performance_test/k6_test/project_merge_requests.js b/qa/performance_test/k6_test/project_merge_requests.js index c24c978061529a..7aa51c938c62cc 100644 --- a/qa/performance_test/k6_test/project_merge_requests.js +++ b/qa/performance_test/k6_test/project_merge_requests.js @@ -10,10 +10,13 @@ export const WARMUP_TEST_VUS = 1; export const WARMUP_TEST_DURATION = '10s'; let projectId; +let API_URL; + export function setup() { const baseUrl = __ENV.GITLAB_URL || `http://gitlab.${__ENV.AI_GATEWAY_IP}.nip.io`; const token = __ENV.GITLAB_QA_ADMIN_ACCESS_TOKEN || ''; const projectName = 'Test Seed Project'; + const apiVersion = 'v4'; // Search for the group by name const searchUrl = `${baseUrl}/api/v4/projects?search=${encodeURIComponent(projectName)}`; @@ -32,14 +35,15 @@ export function setup() { if (targetProject) { console.log(`Found project '${projectName}' with ID: ${targetProject.id}`); - return { projectId: targetProject.id }; + const apiUrl = `${apiVersion}/projects/${targetProject.id}/merge_requests`; + return { projectId: targetProject.id, apiUrl: apiUrl }; } - console.error(`Project '${projectName}' not found`); - return { projectId: '5' }; // Fallback to default } console.error(`Failed to search for groups: ${res.status}`); - return { projectId: '1' }; // Fallback to default + const apiUrl = `${apiVersion}/projects/1/merge_requests`; + return { projectId: '1', apiUrl: apiUrl }; // Fallback to default } + export const options = { scenarios: { warmup: { @@ -68,11 +72,13 @@ export const options = { export default function projectMergeRequestsTest(data) { const baseUrl = __ENV.GITLAB_URL || `http://gitlab.${__ENV.AI_GATEWAY_IP}.nip.io`; - const apiVersion = 'v4'; projectId = __ENV.PROJECT_ID || data.projectId; const token = __ENV.GITLAB_QA_ADMIN_ACCESS_TOKEN || ''; - const url = `${baseUrl}/api/${apiVersion}/projects/${projectId}/merge_requests`; + API_URL = data.apiUrl; + console.log(API_URL) + + const url = `${baseUrl}/api/${API_URL}`; const params = { headers: { -- GitLab From 0733dba3ee67ce7778b85bb7c5eab3d39076c8ab Mon Sep 17 00:00:00 2001 From: "vishal.s.patel" Date: Wed, 10 Dec 2025 14:48:05 +1300 Subject: [PATCH 06/20] Testing the performance test --- lib/api/merge_requests.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb index 0c2eefa3015c4e..46e749082f307a 100644 --- a/lib/api/merge_requests.rb +++ b/lib/api/merge_requests.rb @@ -205,6 +205,7 @@ def batch_process_mergeability_checks(merge_requests) validate_search_rate_limit! if declared_params[:search].present? merge_requests = find_merge_requests + sleep 2 present merge_requests, serializer_options_for(merge_requests) end end -- GitLab From 2b6b4fdeacc913e3ba3c4a692705766cb3e8657e Mon Sep 17 00:00:00 2001 From: "vishal.s.patel" Date: Thu, 11 Dec 2025 14:30:24 +1300 Subject: [PATCH 07/20] Reverted testing changes --- .gitlab/ci/performance-on-cng/main.gitlab-ci.yml | 2 +- lib/api/merge_requests.rb | 1 - qa/performance_test/k6_test/group_merge_requests.js | 1 - qa/performance_test/k6_test/project_merge_requests.js | 1 - 4 files changed, 1 insertion(+), 4 deletions(-) diff --git a/.gitlab/ci/performance-on-cng/main.gitlab-ci.yml b/.gitlab/ci/performance-on-cng/main.gitlab-ci.yml index f226cb0f7659c1..52a2ff6ae9c00e 100644 --- a/.gitlab/ci/performance-on-cng/main.gitlab-ci.yml +++ b/.gitlab/ci/performance-on-cng/main.gitlab-ci.yml @@ -70,7 +70,7 @@ run-performance-tests: - dotenv-vars trigger: project: gitlab-org/quality/component-performance-testing - branch: vp-adding-api-url-and-test-link + branch: main strategy: depend forward: pipeline_variables: true diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb index 46e749082f307a..0c2eefa3015c4e 100644 --- a/lib/api/merge_requests.rb +++ b/lib/api/merge_requests.rb @@ -205,7 +205,6 @@ def batch_process_mergeability_checks(merge_requests) validate_search_rate_limit! if declared_params[:search].present? merge_requests = find_merge_requests - sleep 2 present merge_requests, serializer_options_for(merge_requests) end end diff --git a/qa/performance_test/k6_test/group_merge_requests.js b/qa/performance_test/k6_test/group_merge_requests.js index ccfea0638fd547..76db11ba4ad73b 100644 --- a/qa/performance_test/k6_test/group_merge_requests.js +++ b/qa/performance_test/k6_test/group_merge_requests.js @@ -74,7 +74,6 @@ export default function groupMergeRequestsTest(data) { groupId = __ENV.GROUP_ID || data.groupId; const token = __ENV.GITLAB_QA_ADMIN_ACCESS_TOKEN || ''; API_URL = data.apiUrl; - console.log(API_URL) const url = `${baseUrl}/api/${API_URL}`; diff --git a/qa/performance_test/k6_test/project_merge_requests.js b/qa/performance_test/k6_test/project_merge_requests.js index 7aa51c938c62cc..cce07300ffcaaa 100644 --- a/qa/performance_test/k6_test/project_merge_requests.js +++ b/qa/performance_test/k6_test/project_merge_requests.js @@ -76,7 +76,6 @@ export default function projectMergeRequestsTest(data) { const token = __ENV.GITLAB_QA_ADMIN_ACCESS_TOKEN || ''; API_URL = data.apiUrl; - console.log(API_URL) const url = `${baseUrl}/api/${API_URL}`; -- GitLab From 73d4e06f31d9600316b195a3907402ed3dd6194c Mon Sep 17 00:00:00 2001 From: "vishal.s.patel" Date: Thu, 11 Dec 2025 14:53:53 +1300 Subject: [PATCH 08/20] Updated projectId to groupId in group test --- qa/performance_test/k6_test/group_merge_requests.js | 4 ++-- qa/performance_test/k6_test/project_merge_requests.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/qa/performance_test/k6_test/group_merge_requests.js b/qa/performance_test/k6_test/group_merge_requests.js index 76db11ba4ad73b..670c116e065c13 100644 --- a/qa/performance_test/k6_test/group_merge_requests.js +++ b/qa/performance_test/k6_test/group_merge_requests.js @@ -35,11 +35,11 @@ export function setup() { if (targetGroup) { const apiUrl = `${apiVersion}/groups/${targetGroup.id}/merge_requests`; - return { projectId: targetGroup.id, apiUrl: apiUrl }; + return { groupId: targetGroup.id, apiUrl: apiUrl }; } } const apiUrl = `${apiVersion}/groups/1/merge_requests`; - return { projectId: '1', apiUrl: apiUrl }; // Fallback to default + return { groupId: '1', apiUrl: apiUrl }; // Fallback to default } export const options = { diff --git a/qa/performance_test/k6_test/project_merge_requests.js b/qa/performance_test/k6_test/project_merge_requests.js index cce07300ffcaaa..22a919d3d3e024 100644 --- a/qa/performance_test/k6_test/project_merge_requests.js +++ b/qa/performance_test/k6_test/project_merge_requests.js @@ -39,7 +39,7 @@ export function setup() { return { projectId: targetProject.id, apiUrl: apiUrl }; } } - console.error(`Failed to search for groups: ${res.status}`); + console.error(`Failed to search for projects: ${res.status}`); const apiUrl = `${apiVersion}/projects/1/merge_requests`; return { projectId: '1', apiUrl: apiUrl }; // Fallback to default } -- GitLab From ea80baf4c34c35c72beb5befcfb6725bcc0f4265 Mon Sep 17 00:00:00 2001 From: "vishal.s.patel" Date: Thu, 11 Dec 2025 15:45:05 +1300 Subject: [PATCH 09/20] Fixing linting error and testing --- .gitlab/ci/performance-on-cng/main.gitlab-ci.yml | 2 +- lib/api/merge_requests.rb | 1 + qa/performance_test/k6_test/group_merge_requests.js | 5 ++--- qa/performance_test/k6_test/project_merge_requests.js | 5 ++--- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/.gitlab/ci/performance-on-cng/main.gitlab-ci.yml b/.gitlab/ci/performance-on-cng/main.gitlab-ci.yml index 52a2ff6ae9c00e..f226cb0f7659c1 100644 --- a/.gitlab/ci/performance-on-cng/main.gitlab-ci.yml +++ b/.gitlab/ci/performance-on-cng/main.gitlab-ci.yml @@ -70,7 +70,7 @@ run-performance-tests: - dotenv-vars trigger: project: gitlab-org/quality/component-performance-testing - branch: main + branch: vp-adding-api-url-and-test-link strategy: depend forward: pipeline_variables: true diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb index 0c2eefa3015c4e..46e749082f307a 100644 --- a/lib/api/merge_requests.rb +++ b/lib/api/merge_requests.rb @@ -205,6 +205,7 @@ def batch_process_mergeability_checks(merge_requests) validate_search_rate_limit! if declared_params[:search].present? merge_requests = find_merge_requests + sleep 2 present merge_requests, serializer_options_for(merge_requests) end end diff --git a/qa/performance_test/k6_test/group_merge_requests.js b/qa/performance_test/k6_test/group_merge_requests.js index 670c116e065c13..b9500816b5531f 100644 --- a/qa/performance_test/k6_test/group_merge_requests.js +++ b/qa/performance_test/k6_test/group_merge_requests.js @@ -9,7 +9,6 @@ export const LOAD_TEST_DURATION = '50s'; export const WARMUP_TEST_VUS = 1; export const WARMUP_TEST_DURATION = '10s'; -let groupId; let API_URL; export function setup() { @@ -35,11 +34,11 @@ export function setup() { if (targetGroup) { const apiUrl = `${apiVersion}/groups/${targetGroup.id}/merge_requests`; - return { groupId: targetGroup.id, apiUrl: apiUrl }; + return { groupId: targetGroup.id, apiUrl }; } } const apiUrl = `${apiVersion}/groups/1/merge_requests`; - return { groupId: '1', apiUrl: apiUrl }; // Fallback to default + return { groupId: '1', apiUrl }; // Fallback to default } export const options = { diff --git a/qa/performance_test/k6_test/project_merge_requests.js b/qa/performance_test/k6_test/project_merge_requests.js index 22a919d3d3e024..bc7f2a8b533826 100644 --- a/qa/performance_test/k6_test/project_merge_requests.js +++ b/qa/performance_test/k6_test/project_merge_requests.js @@ -9,7 +9,6 @@ export const LOAD_TEST_DURATION = '50s'; export const WARMUP_TEST_VUS = 1; export const WARMUP_TEST_DURATION = '10s'; -let projectId; let API_URL; export function setup() { @@ -36,12 +35,12 @@ export function setup() { if (targetProject) { console.log(`Found project '${projectName}' with ID: ${targetProject.id}`); const apiUrl = `${apiVersion}/projects/${targetProject.id}/merge_requests`; - return { projectId: targetProject.id, apiUrl: apiUrl }; + return { projectId: targetProject.id, apiUrl }; } } console.error(`Failed to search for projects: ${res.status}`); const apiUrl = `${apiVersion}/projects/1/merge_requests`; - return { projectId: '1', apiUrl: apiUrl }; // Fallback to default + return { projectId: '1', apiUrl }; // Fallback to default } export const options = { -- GitLab From 56b2c19abcb7720a23ad0c22f24133aa18656bc4 Mon Sep 17 00:00:00 2001 From: "vishal.s.patel" Date: Thu, 11 Dec 2025 16:59:42 +1300 Subject: [PATCH 10/20] Fixing linting error and testing --- qa/performance_test/k6_test/group_merge_requests.js | 1 - 1 file changed, 1 deletion(-) diff --git a/qa/performance_test/k6_test/group_merge_requests.js b/qa/performance_test/k6_test/group_merge_requests.js index b9500816b5531f..095052f78c2340 100644 --- a/qa/performance_test/k6_test/group_merge_requests.js +++ b/qa/performance_test/k6_test/group_merge_requests.js @@ -69,7 +69,6 @@ export const options = { export default function groupMergeRequestsTest(data) { const baseUrl = __ENV.GITLAB_URL || `http://gitlab.${__ENV.AI_GATEWAY_IP}.nip.io`; - const apiVersion = 'v4'; groupId = __ENV.GROUP_ID || data.groupId; const token = __ENV.GITLAB_QA_ADMIN_ACCESS_TOKEN || ''; API_URL = data.apiUrl; -- GitLab From 43ca6eb61e1fc44dd1b95d3450d080c03bb3ce80 Mon Sep 17 00:00:00 2001 From: "vishal.s.patel" Date: Fri, 12 Dec 2025 00:47:47 +1300 Subject: [PATCH 11/20] Reverted testing changes --- .gitlab/ci/performance-on-cng/main.gitlab-ci.yml | 2 +- lib/api/merge_requests.rb | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitlab/ci/performance-on-cng/main.gitlab-ci.yml b/.gitlab/ci/performance-on-cng/main.gitlab-ci.yml index f226cb0f7659c1..52a2ff6ae9c00e 100644 --- a/.gitlab/ci/performance-on-cng/main.gitlab-ci.yml +++ b/.gitlab/ci/performance-on-cng/main.gitlab-ci.yml @@ -70,7 +70,7 @@ run-performance-tests: - dotenv-vars trigger: project: gitlab-org/quality/component-performance-testing - branch: vp-adding-api-url-and-test-link + branch: main strategy: depend forward: pipeline_variables: true diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb index 46e749082f307a..0c2eefa3015c4e 100644 --- a/lib/api/merge_requests.rb +++ b/lib/api/merge_requests.rb @@ -205,7 +205,6 @@ def batch_process_mergeability_checks(merge_requests) validate_search_rate_limit! if declared_params[:search].present? merge_requests = find_merge_requests - sleep 2 present merge_requests, serializer_options_for(merge_requests) end end -- GitLab From a88ca40bf46e250d5eea03985da46969fcf3c626 Mon Sep 17 00:00:00 2001 From: Alexandria Balkaran Date: Thu, 11 Dec 2025 16:26:24 -0500 Subject: [PATCH 12/20] Added disclaimer for SCIM group --- doc/administration/settings/scim_setup.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/administration/settings/scim_setup.md b/doc/administration/settings/scim_setup.md index 9760678bdc52f7..01a917053ee140 100644 --- a/doc/administration/settings/scim_setup.md +++ b/doc/administration/settings/scim_setup.md @@ -320,6 +320,9 @@ For detailed instructions on configuring group synchronization in your identity - [Okta Groups API](https://developer.okta.com/docs/reference/api/groups/) - [Microsoft Entra ID (Azure AD) SCIM Groups](https://learn.microsoft.com/en-us/entra/identity/app-provisioning/use-scim-to-provision-users-and-groups) + - By default, the `displayName` source attribute is used to find SAML group links with user-friendly names. + - However, if your SAML group links use an object ID for the name, + you must update the source attribute to `objectId`. {{< alert type="warning" >}} -- GitLab From 60b4199b609b150b5ebdbd30ea1992f29766f154 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Thu, 11 Dec 2025 23:38:03 +0800 Subject: [PATCH 13/20] Fix broken master by raising this by 1 --- ee/lib/api/ai/duo_workflows/workflows.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ee/lib/api/ai/duo_workflows/workflows.rb b/ee/lib/api/ai/duo_workflows/workflows.rb index 3ce7f6f6f9f49b..75592d427b8ba4 100644 --- a/ee/lib/api/ai/duo_workflows/workflows.rb +++ b/ee/lib/api/ai/duo_workflows/workflows.rb @@ -434,7 +434,7 @@ def create_workflow_params end post do ::Gitlab::QueryLimiting.disable!( - 'https://gitlab.com/gitlab-org/gitlab/-/issues/566195', new_threshold: 115 + 'https://gitlab.com/gitlab-org/gitlab/-/issues/566195', new_threshold: 116 ) container = if params[:project_id] -- GitLab From 551fb70f9e00caa80f9814428c1946551b9b2959 Mon Sep 17 00:00:00 2001 From: Fred de Gier Date: Thu, 11 Dec 2025 22:55:56 +0100 Subject: [PATCH 14/20] Feat: Add backend changes for canceling user session Handle permissions if user cannot cancel Add cancellation ability Changelog: changed EE: true --- .../ai/duo_agents_platform/constants.js | 10 ++ .../graphql/fragments/flow.fragment.graphql | 3 + .../agent_flow_cancelation_modal.vue | 69 ++++++++++ .../show/components/agent_flow_details.vue | 12 ++ .../pages/show/components/agent_flow_info.vue | 36 ++++- .../pages/show/duo_agents_platform_show.vue | 89 ++++++++++-- .../agent_flow_cancelation_modal_spec.js | 100 ++++++++++++++ .../components/agent_flow_details_spec.js | 9 ++ .../show/components/agent_flow_info_spec.js | 55 ++++++++ .../show/duo_agents_platform_show_spec.js | 128 +++++++++++++++++- ee/spec/frontend/ai/mocks.js | 12 ++ locale/gitlab.pot | 18 +++ 12 files changed, 524 insertions(+), 17 deletions(-) create mode 100644 ee/app/assets/javascripts/ai/duo_agents_platform/pages/show/components/agent_flow_cancelation_modal.vue create mode 100644 ee/spec/frontend/ai/duo_agents_platform/pages/show/components/agent_flow_cancelation_modal_spec.js diff --git a/ee/app/assets/javascripts/ai/duo_agents_platform/constants.js b/ee/app/assets/javascripts/ai/duo_agents_platform/constants.js index f7c201bca3d7fa..5cf98a2d20c6c1 100644 --- a/ee/app/assets/javascripts/ai/duo_agents_platform/constants.js +++ b/ee/app/assets/javascripts/ai/duo_agents_platform/constants.js @@ -124,3 +124,13 @@ export const DEFAULT_AGENT_PLATFORM_PAGINATION_VARIABLES = { after: null, last: null, }; + +// Can cancel only if session is in an active state (not already finished, failed, or stopped) +export const AGENT_PLATFORM_CANCELABLE_STATUSES = [ + 'CREATED', + 'RUNNING', + 'PAUSED', + 'INPUT_REQUIRED', + 'PLAN_APPROVAL_REQUIRED', + 'TOOL_CALL_APPROVAL_REQUIRED', +]; diff --git a/ee/app/assets/javascripts/ai/duo_agents_platform/graphql/fragments/flow.fragment.graphql b/ee/app/assets/javascripts/ai/duo_agents_platform/graphql/fragments/flow.fragment.graphql index 929e43672ef4f2..96e4e977f09a73 100644 --- a/ee/app/assets/javascripts/ai/duo_agents_platform/graphql/fragments/flow.fragment.graphql +++ b/ee/app/assets/javascripts/ai/duo_agents_platform/graphql/fragments/flow.fragment.graphql @@ -5,6 +5,9 @@ fragment FlowFragment on DuoWorkflow { updatedAt workflowDefinition userId + userPermissions { + updateDuoWorkflow + } project { id name diff --git a/ee/app/assets/javascripts/ai/duo_agents_platform/pages/show/components/agent_flow_cancelation_modal.vue b/ee/app/assets/javascripts/ai/duo_agents_platform/pages/show/components/agent_flow_cancelation_modal.vue new file mode 100644 index 00000000000000..f2d1e4003c7952 --- /dev/null +++ b/ee/app/assets/javascripts/ai/duo_agents_platform/pages/show/components/agent_flow_cancelation_modal.vue @@ -0,0 +1,69 @@ + + diff --git a/ee/app/assets/javascripts/ai/duo_agents_platform/pages/show/components/agent_flow_details.vue b/ee/app/assets/javascripts/ai/duo_agents_platform/pages/show/components/agent_flow_details.vue index d6f7830a3d8d8c..f1c434e257d917 100644 --- a/ee/app/assets/javascripts/ai/duo_agents_platform/pages/show/components/agent_flow_details.vue +++ b/ee/app/assets/javascripts/ai/duo_agents_platform/pages/show/components/agent_flow_details.vue @@ -59,7 +59,16 @@ export default { type: String, required: true, }, + workflowId: { + type: String, + required: true, + }, + canUpdateWorkflow: { + type: Boolean, + required: true, + }, }, + emits: ['cancel-session'], AGENTS_PLATFORM_INDEX_ROUTE, }; @@ -97,6 +106,9 @@ export default { :project="project" :updated-at="updatedAt" :executor-url="executorUrl" + :workflow-id="workflowId" + :can-update-workflow="canUpdateWorkflow" + @cancel-session="$emit('cancel-session')" /> diff --git a/ee/app/assets/javascripts/ai/duo_agents_platform/pages/show/components/agent_flow_info.vue b/ee/app/assets/javascripts/ai/duo_agents_platform/pages/show/components/agent_flow_info.vue index 8db528d638c1be..03c616ae620868 100644 --- a/ee/app/assets/javascripts/ai/duo_agents_platform/pages/show/components/agent_flow_info.vue +++ b/ee/app/assets/javascripts/ai/duo_agents_platform/pages/show/components/agent_flow_info.vue @@ -1,18 +1,23 @@ diff --git a/ee/spec/frontend/ai/duo_agents_platform/pages/show/components/agent_flow_cancelation_modal_spec.js b/ee/spec/frontend/ai/duo_agents_platform/pages/show/components/agent_flow_cancelation_modal_spec.js new file mode 100644 index 00000000000000..3bb57048239032 --- /dev/null +++ b/ee/spec/frontend/ai/duo_agents_platform/pages/show/components/agent_flow_cancelation_modal_spec.js @@ -0,0 +1,100 @@ +import { GlButton, GlModal } from '@gitlab/ui'; +import { shallowMount } from '@vue/test-utils'; +import AgentFlowCancelationModal from 'ee/ai/duo_agents_platform/pages/show/components/agent_flow_cancelation_modal.vue'; + +describe('AgentFlowCancelationModal', () => { + let wrapper; + + const createComponent = (props = {}) => { + wrapper = shallowMount(AgentFlowCancelationModal, { + propsData: { + visible: false, + loading: false, + ...props, + }, + stubs: { + GlModal, + GlButton, + }, + }); + }; + + const findModal = () => wrapper.findComponent(GlModal); + const findCancelButton = () => wrapper.find('[data-testid="cancel-session-modal-cancel"]'); + const findConfirmButton = () => wrapper.find('[data-testid="cancel-session-modal-confirm"]'); + + describe('when component has rendered', () => { + beforeEach(() => { + createComponent(); + }); + + it('renders the modal with correct props', () => { + expect(findModal().exists()).toBe(true); + expect(findModal().props('modalId')).toBe('cancel-session-confirmation-modal'); + expect(findModal().props('title')).toBe('Cancel session?'); + expect(findModal().props('size')).toBe('sm'); + }); + + it('renders the confirmation message', () => { + expect(wrapper.text()).toContain( + 'Are you sure you want to cancel this session? This action cannot be undone.', + ); + }); + + it('renders cancel button with correct text', () => { + expect(findCancelButton().exists()).toBe(true); + expect(findCancelButton().text()).toBe('Cancel'); + }); + + it('renders confirm button with correct text and variant', () => { + const confirmButton = findConfirmButton(); + + expect(confirmButton.exists()).toBe(true); + expect(confirmButton.text()).toBe('Cancel session'); + expect(confirmButton.props()).toMatchObject({ + variant: 'danger', + }); + }); + }); + + describe('visibility', () => { + it.each` + visible | expected + ${true} | ${true} + ${false} | ${false} + `('passes visible=$visible prop to modal', ({ visible, expected }) => { + createComponent({ visible }); + + expect(findModal().props('visible')).toBe(expected); + }); + }); + + describe('loading state', () => { + it.each` + loading | expected + ${true} | ${true} + ${false} | ${false} + `('passes loading=$loading to confirm button', ({ loading, expected }) => { + createComponent({ loading }); + + expect(findConfirmButton().props('loading')).toBe(expected); + }); + }); + + describe('events', () => { + beforeEach(() => { + createComponent({ visible: true }); + }); + + it.each` + action | finder | event | emittedEvent + ${'modal hidden'} | ${() => findModal()} | ${'hide'} | ${'hide'} + ${'cancel clicked'} | ${findCancelButton} | ${'click'} | ${'hide'} + ${'confirm clicked'} | ${findConfirmButton} | ${'click'} | ${'confirm'} + `('emits $emittedEvent when $action', async ({ finder, event, emittedEvent }) => { + await finder().vm.$emit(event); + + expect(wrapper.emitted(emittedEvent)).toHaveLength(1); + }); + }); +}); diff --git a/ee/spec/frontend/ai/duo_agents_platform/pages/show/components/agent_flow_details_spec.js b/ee/spec/frontend/ai/duo_agents_platform/pages/show/components/agent_flow_details_spec.js index 0d3776a5db566f..62ec4a8eddba37 100644 --- a/ee/spec/frontend/ai/duo_agents_platform/pages/show/components/agent_flow_details_spec.js +++ b/ee/spec/frontend/ai/duo_agents_platform/pages/show/components/agent_flow_details_spec.js @@ -13,12 +13,15 @@ describe('AgentFlowDetails', () => { const defaultProps = { isLoading: false, status: 'RUNNING', + humanStatus: 'Running', agentFlowDefinition: 'software_development', duoMessages: mockDuoMessages, executorUrl: 'https://gitlab.com/gitlab-org/gitlab/-/jobs/123', createdAt: '2023-01-01T00:54:00Z', updatedAt: '2024-01-02T00:34:00Z', userId: 'gid://gitlab/User/1', + workflowId: '123', + canUpdateWorkflow: true, project: { id: 'gid://gitlab/Project/1', name: 'Test Project', @@ -40,6 +43,10 @@ describe('AgentFlowDetails', () => { isSidePanelView: false, ...provide, }, + stubs: { + GlTabs: true, + GlTab: true, + }, }); }; @@ -99,11 +106,13 @@ describe('AgentFlowDetails', () => { expect(findAgentFlowInfo().props()).toEqual({ isLoading: false, status: defaultProps.status, + humanStatus: defaultProps.humanStatus, agentFlowDefinition: defaultProps.agentFlowDefinition, executorUrl: defaultProps.executorUrl, createdAt: '2023-01-01T00:54:00Z', updatedAt: '2024-01-02T00:34:00Z', project: defaultProps.project, + canUpdateWorkflow: true, }); }); diff --git a/ee/spec/frontend/ai/duo_agents_platform/pages/show/components/agent_flow_info_spec.js b/ee/spec/frontend/ai/duo_agents_platform/pages/show/components/agent_flow_info_spec.js index 3f080c92bc7d29..3dc16509169c10 100644 --- a/ee/spec/frontend/ai/duo_agents_platform/pages/show/components/agent_flow_info_spec.js +++ b/ee/spec/frontend/ai/duo_agents_platform/pages/show/components/agent_flow_info_spec.js @@ -36,6 +36,8 @@ describe('AgentFlowInfo', () => { executorUrl: 'https://gitlab.com/gitlab-org/gitlab/-/jobs/123', createdAt: '2023-01-01T00:00:00Z', updatedAt: '2024-01-01T00:00:00Z', + workflowId: '4545', + canUpdateWorkflow: true, project: { id: 'gid://gitlab/Project/1', name: 'Test Project', @@ -65,6 +67,7 @@ describe('AgentFlowInfo', () => { const findListItemTitles = () => wrapper.findAll('[data-testid="info-title"]'); const findListItemValues = () => wrapper.findAll('[data-testid="info-value"]'); const findSkeletonLoaders = () => wrapper.findAllComponents(GlSkeletonLoader); + const findCancelButton = () => wrapper.find('[data-testid="cancel-session-button"]'); const findLinks = () => wrapper.findAllComponents(GlLink); const findBadge = () => wrapper.findComponent(GlBadge); @@ -259,4 +262,56 @@ describe('AgentFlowInfo', () => { }); }); }); + + describe('Cancel session button', () => { + describe('when session can be cancelled', () => { + it.each` + status | description + ${'CREATED'} | ${'created status'} + ${'RUNNING'} | ${'running status'} + ${'PAUSED'} | ${'paused status'} + ${'INPUT_REQUIRED'} | ${'input_required status'} + ${'PLAN_APPROVAL_REQUIRED'} | ${'plan_approval_required status'} + ${'TOOL_CALL_APPROVAL_REQUIRED'} | ${'tool_call_approval_required status'} + `('shows cancel button for $description', ({ status }) => { + createComponent({ status }); + + expect(findCancelButton().exists()).toBe(true); + expect(findCancelButton().text()).toBe('Cancel session'); + expect(findCancelButton().attributes('variant')).toBe('danger'); + expect(findCancelButton().props().disabled).toBe(false); + }); + + it('emits cancel-session event when clicked', async () => { + createComponent({ status: 'RUNNING' }); + + await findCancelButton().vm.$emit('click'); + + expect(wrapper.emitted('cancel-session')).toHaveLength(1); + }); + }); + + describe('when session cannot be cancelled', () => { + it.each` + status | description + ${'FINISHED'} | ${'finished status'} + ${'FAILED'} | ${'failed status'} + `('does not show cancel button for $description', ({ status }) => { + createComponent({ status }); + + expect(findCancelButton().exists()).toBe(false); + }); + }); + + describe('when user lacks permission to cancel', () => { + beforeEach(() => { + createComponent({ status: 'RUNNING', canUpdateWorkflow: false }); + }); + + it('shows cancel button disabled', () => { + expect(findCancelButton().exists()).toBe(true); + expect(findCancelButton().props('disabled')).toBe(true); + }); + }); + }); }); diff --git a/ee/spec/frontend/ai/duo_agents_platform/pages/show/duo_agents_platform_show_spec.js b/ee/spec/frontend/ai/duo_agents_platform/pages/show/duo_agents_platform_show_spec.js index 27f6b8e169be1c..44616d3dd47147 100644 --- a/ee/spec/frontend/ai/duo_agents_platform/pages/show/duo_agents_platform_show_spec.js +++ b/ee/spec/frontend/ai/duo_agents_platform/pages/show/duo_agents_platform_show_spec.js @@ -1,9 +1,11 @@ import { shallowMount } from '@vue/test-utils'; -import Vue from 'vue'; +import Vue, { nextTick } from 'vue'; import VueApollo from 'vue-apollo'; +import axios from '~/lib/utils/axios_utils'; import DuoAgentsPlatformShow from 'ee/ai/duo_agents_platform/pages/show/duo_agents_platform_show.vue'; import AgentFlowDetails from 'ee/ai/duo_agents_platform/pages/show/components/agent_flow_details.vue'; +import AgentFlowCancelationModal from 'ee/ai/duo_agents_platform/pages/show/components/agent_flow_cancelation_modal.vue'; import { DUO_AGENTS_PLATFORM_POLLING_INTERVAL } from 'ee/ai/duo_agents_platform/constants'; import { getAgentFlow } from 'ee/ai/duo_agents_platform/graphql/queries/get_agent_flow.query.graphql'; @@ -18,6 +20,7 @@ import { mockGetAgentFlowResponse, mockDuoMessages } from '../../../mocks'; Vue.use(VueApollo); jest.mock('~/alert'); +jest.mock('~/lib/utils/axios_utils'); describe('DuoAgentsPlatformShow', () => { let wrapper; @@ -49,6 +52,7 @@ describe('DuoAgentsPlatformShow', () => { }; const findAgentFlowDetails = () => wrapper.findComponent(AgentFlowDetails); + const findCancelConfirmationModal = () => wrapper.findComponent(AgentFlowCancelationModal); beforeEach(() => { getAgentFlowHandler = jest.fn().mockResolvedValue(mockGetAgentFlowResponse); @@ -78,6 +82,7 @@ describe('DuoAgentsPlatformShow', () => { agentFlowDefinition: 'Software development', duoMessages: mockDuoMessages, project: mockGetAgentFlowResponse.data.duoWorkflowWorkflows.edges[0].node.project, + canUpdateWorkflow: true, }); }); }); @@ -126,7 +131,7 @@ describe('DuoAgentsPlatformShow', () => { }); it('calls createAlert with the error message', () => { - expect(createAlert).toHaveBeenCalledWith({ message: errorMessage }); + expect(createAlert).toHaveBeenCalledWith({ message: errorMessage, captureError: true }); }); }); @@ -139,6 +144,7 @@ describe('DuoAgentsPlatformShow', () => { it('calls createAlert with default error message', () => { expect(createAlert).toHaveBeenCalledWith({ message: 'Something went wrong while fetching Agent Flows', + captureError: true, }); }); }); @@ -191,4 +197,122 @@ describe('DuoAgentsPlatformShow', () => { }); }); }); + + describe('Cancel session functionality', () => { + beforeEach(async () => { + await createWrapper(); + }); + + describe('confirmation modal', () => { + it('renders the cancel session confirmation modal', () => { + expect(findCancelConfirmationModal().exists()).toBe(true); + expect(findCancelConfirmationModal().props('visible')).toBe(false); + expect(findCancelConfirmationModal().props('loading')).toBe(false); + }); + + it('shows modal when cancel-session event is emitted from AgentFlowDetails', async () => { + findAgentFlowDetails().vm.$emit('cancel-session'); + await nextTick(); + + expect(findCancelConfirmationModal().props('visible')).toBe(true); + }); + + it('hides modal when hide event is emitted', async () => { + findAgentFlowDetails().vm.$emit('cancel-session'); + await nextTick(); + + expect(findCancelConfirmationModal().props('visible')).toBe(true); + + findCancelConfirmationModal().vm.$emit('hide'); + await nextTick(); + + expect(findCancelConfirmationModal().props('visible')).toBe(false); + }); + }); + + describe('session cancellation', () => { + beforeEach(async () => { + axios.patch = jest.fn(); + getAgentFlowHandler.mockClear(); + await createWrapper(); + }); + + it('calls API to cancel session when confirmed', async () => { + axios.patch.mockResolvedValue({ data: { status: 'STOPPED' } }); + + findAgentFlowDetails().vm.$emit('cancel-session'); + await nextTick(); + + expect(findCancelConfirmationModal().props('visible')).toBe(true); + + findCancelConfirmationModal().vm.$emit('confirm'); + await waitForPromises(); + + expect(axios.patch).toHaveBeenCalledWith('/api/v4/ai/duo_workflows/workflows/1', { + status_event: 'stop', + }); + expect(findCancelConfirmationModal().props('visible')).toBe(false); + }); + + it('shows success alert and refetches data on successful cancellation', async () => { + axios.patch.mockResolvedValue({ data: { status: 'STOPPED' } }); + + findAgentFlowDetails().vm.$emit('cancel-session'); + await nextTick(); + + findCancelConfirmationModal().vm.$emit('confirm'); + await waitForPromises(); + + expect(createAlert).toHaveBeenCalledWith({ + message: 'Session has been cancelled successfully.', + variant: 'success', + }); + expect(getAgentFlowHandler).toHaveBeenCalledTimes(1); + }); + + it('shows error alert on API failure', async () => { + const errorMessage = 'Failed to cancel'; + axios.patch.mockRejectedValue({ + response: { + status: 422, + data: { message: errorMessage }, + }, + }); + + findAgentFlowDetails().vm.$emit('cancel-session'); + await nextTick(); + + findCancelConfirmationModal().vm.$emit('confirm'); + await waitForPromises(); + + expect(createAlert).toHaveBeenCalledWith({ + message: errorMessage, + captureError: true, + variant: 'danger', + }); + }); + + it('shows loading state during cancellation', async () => { + let resolveRequest; + axios.patch.mockImplementation( + () => + new Promise((resolve) => { + resolveRequest = resolve; + }), + ); + + findAgentFlowDetails().vm.$emit('cancel-session'); + await nextTick(); + + const cancelPromise = findCancelConfirmationModal().vm.$emit('confirm'); + await nextTick(); + + expect(findCancelConfirmationModal().props('loading')).toBe(true); + + // Resolve the promise to clean up + resolveRequest({ data: { status: 'STOPPED' } }); + await cancelPromise; + }); + }); + }); }); diff --git a/ee/spec/frontend/ai/mocks.js b/ee/spec/frontend/ai/mocks.js index 1354ef12b2752c..bf2a5d5622ca30 100644 --- a/ee/spec/frontend/ai/mocks.js +++ b/ee/spec/frontend/ai/mocks.js @@ -8,6 +8,9 @@ export const mockAgentFlowEdges = [ updatedAt: '2024-01-01T00:00:00Z', workflowDefinition: 'software_development', userId: 'gid://gitlab/User/1', + userPermissions: { + updateDuoWorkflow: true, + }, project: { id: 'gid://gitlab/Project/1', name: 'Test Project', @@ -29,6 +32,9 @@ export const mockAgentFlowEdges = [ updatedAt: '2024-01-02T00:00:00Z', workflowDefinition: 'convert_to_gitlab_ci', userId: 'gid://gitlab/User/1', + userPermissions: { + updateDuoWorkflow: true, + }, project: { id: 'gid://gitlab/Project/2', name: 'Another Project', @@ -50,6 +56,9 @@ export const mockAgentFlowEdges = [ updatedAt: '2024-01-03T00:00:00Z', workflowDefinition: 'chat', userId: 'gid://gitlab/User/2', + userPermissions: { + updateDuoWorkflow: false, + }, project: { id: 'gid://gitlab/Project/3', name: 'Chat Project', @@ -128,6 +137,9 @@ export const mockGetAgentFlowResponse = { humanStatus: 'running', workflowDefinition: 'software_development', userId: 'gid://gitlab/User/1', + userPermissions: { + updateDuoWorkflow: true, + }, project: { id: 'gid://gitlab/Project/1', name: 'Test Project', diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 046770de086b2f..384b7e4ece6e96 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -25430,12 +25430,21 @@ msgstr "" msgid "DuoAgentsPlatform|An error occurred while fetching users." msgstr "" +msgid "DuoAgentsPlatform|Are you sure you want to cancel this session? This action cannot be undone." +msgstr "" + msgid "DuoAgentsPlatform|Are you sure you want to delete this trigger? This action cannot be undone." msgstr "" msgid "DuoAgentsPlatform|Automate" msgstr "" +msgid "DuoAgentsPlatform|Cancel session" +msgstr "" + +msgid "DuoAgentsPlatform|Cancel session?" +msgstr "" + msgid "DuoAgentsPlatform|Concise view" msgstr "" @@ -25502,6 +25511,9 @@ msgstr "" msgid "DuoAgentsPlatform|Event types" msgstr "" +msgid "DuoAgentsPlatform|Failed to cancel session. Please try again." +msgstr "" + msgid "DuoAgentsPlatform|Failed to delete trigger." msgstr "" @@ -25607,6 +25619,9 @@ msgstr "" msgid "DuoAgentsPlatform|Service account user" msgstr "" +msgid "DuoAgentsPlatform|Session has been cancelled successfully." +msgstr "" + msgid "DuoAgentsPlatform|Sessions" msgstr "" @@ -25652,6 +25667,9 @@ msgstr "" msgid "DuoAgentsPlatform|Write file" msgstr "" +msgid "DuoAgentsPlatform|You do not have permission to cancel this session." +msgstr "" + msgid "DuoAgentsPlatform|⚠️ Create a unique service account for each project." msgstr "" -- GitLab From f43cf4e5a3ba5ae319e41359b965822c24e182d9 Mon Sep 17 00:00:00 2001 From: Bala Kumar Date: Fri, 12 Dec 2025 03:41:03 +0530 Subject: [PATCH 15/20] Adv vuln management for self managed include additional notes --- .../application_security/vulnerability_report/_index.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/doc/user/application_security/vulnerability_report/_index.md b/doc/user/application_security/vulnerability_report/_index.md index e1306800e213b2..63b7f537557c56 100644 --- a/doc/user/application_security/vulnerability_report/_index.md +++ b/doc/user/application_security/vulnerability_report/_index.md @@ -587,6 +587,13 @@ For more information, see the history. {{< /alert >}} +{{< alert type="note" >}} + +On GitLab Self-Managed, advanced vulnerability management capabilities might be temporarily unavailable, typically for a few hours, after upgrading from versions earlier than GitLab 18.7 while the data migration completes. +Full capabilities will be available after the migration finishes. + +{{< /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: @@ -602,7 +609,7 @@ Advanced search is used only for these specific features, including when they ar ### Requirements -To use the filters in advanced vulnerability management: +To use the capabilities in advanced vulnerability management: - You must have [advanced search enabled](../../search/advanced_search.md#use-advanced-search). - On GitLab Self-Managed, after [setting up advanced search](../../../integration/advanced_search/elasticsearch.md#enable-advanced-search), ensure the **Search with advanced search** checkbox is selected. -- GitLab From 869212adcfdcd3ee8782bb7cadaf9c89f6b5568d Mon Sep 17 00:00:00 2001 From: Cindy Halim Date: Thu, 11 Dec 2025 15:27:49 -0500 Subject: [PATCH 16/20] Fix payload structure for duo_agent_platform_enabled --- .../javascripts/ai/settings/pages/ai_group_settings.vue | 6 +++--- .../frontend/ai/settings/pages/ai_group_settings_spec.js | 6 ++++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/ee/app/assets/javascripts/ai/settings/pages/ai_group_settings.vue b/ee/app/assets/javascripts/ai/settings/pages/ai_group_settings.vue index eb442ce5dc40cd..0dbb3634b7dd0d 100644 --- a/ee/app/assets/javascripts/ai/settings/pages/ai_group_settings.vue +++ b/ee/app/assets/javascripts/ai/settings/pages/ai_group_settings.vue @@ -80,10 +80,10 @@ export default { ...(foundationalAgentsStatuses && { foundational_agents_statuses: transformedFoundationalAgentsStatuses, }), - ...(this.showDuoAgentPlatformEnabledSetting && { - duo_agent_platform_enabled: duoAgentPlatformEnabled, - }), ai_settings_attributes: { + ...(this.showDuoAgentPlatformEnabledSetting && { + duo_agent_platform_enabled: duoAgentPlatformEnabled, + }), duo_workflow_mcp_enabled: this.duoWorkflowMcp, foundational_agents_default_enabled: foundationalAgentsEnabled, }, diff --git a/ee/spec/frontend/ai/settings/pages/ai_group_settings_spec.js b/ee/spec/frontend/ai/settings/pages/ai_group_settings_spec.js index 851dd908d0f04d..114537f84d8726 100644 --- a/ee/spec/frontend/ai/settings/pages/ai_group_settings_spec.js +++ b/ee/spec/frontend/ai/settings/pages/ai_group_settings_spec.js @@ -152,8 +152,8 @@ describe('AiGroupSettings', () => { duo_sast_fp_detection_availability: false, foundational_agents_statuses: expectedFilteredAgentStatuses, enabled_foundational_flows: [], - duo_agent_platform_enabled: true, ai_settings_attributes: { + duo_agent_platform_enabled: true, duo_workflow_mcp_enabled: false, foundational_agents_default_enabled: true, }, @@ -184,7 +184,9 @@ describe('AiGroupSettings', () => { expect(updateGroupSettings).toHaveBeenCalledWith( '100', expect.not.objectContaining({ - duo_agent_platform_enabled: expect.anything(), + ai_settings_attributes: { + duo_agent_platform_enabled: expect.anything(), + }, }), ); }); -- GitLab From 823ae2843f985c09eb88ffeeb15144e7a8db7426 Mon Sep 17 00:00:00 2001 From: Jessie Young Date: Thu, 11 Dec 2025 12:58:12 -0800 Subject: [PATCH 17/20] Fix N+1 in spec --- ee/lib/ai/duo_workflow.rb | 2 +- ee/spec/requests/api/ai/duo_workflows/workflows_spec.rb | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/ee/lib/ai/duo_workflow.rb b/ee/lib/ai/duo_workflow.rb index 6b95f7e7de7778..726e9fedd46dbb 100644 --- a/ee/lib/ai/duo_workflow.rb +++ b/ee/lib/ai/duo_workflow.rb @@ -59,7 +59,7 @@ def duo_agent_platform_available?(_container = nil) return true if ::Gitlab::Saas.feature_available?(:gitlab_com_subscriptions) # For self-managed/dedicated instances, use instance-level settings - ::Ai::Setting.instance.duo_agent_platform_enabled + ai_settings.duo_agent_platform_enabled end private diff --git a/ee/spec/requests/api/ai/duo_workflows/workflows_spec.rb b/ee/spec/requests/api/ai/duo_workflows/workflows_spec.rb index a347ae0d882c98..3ca4c5a1295a7f 100644 --- a/ee/spec/requests/api/ai/duo_workflows/workflows_spec.rb +++ b/ee/spec/requests/api/ai/duo_workflows/workflows_spec.rb @@ -678,6 +678,8 @@ let(:params) { super().merge(ai_catalog_item_version_id: ai_catalog_item_version.id) } before do + # TODO: use factory instead https://gitlab.com/gitlab-org/gitlab/-/issues/583818 + allow(::Ai::DuoWorkflow).to receive(:duo_agent_platform_available?).and_return(true) allow_next_instance_of(Ai::Catalog::ItemConsumersFinder) do |finder| allow(finder).to receive(:execute).and_return(class_double(::Ai::Catalog::ItemConsumer, exists?: true)) end -- GitLab From 3764ca20cf31a03ae070071c34b645c24c9dd682 Mon Sep 17 00:00:00 2001 From: Zach Painter Date: Wed, 10 Dec 2025 16:32:31 -0500 Subject: [PATCH 18/20] Fixes page title --- doc/user/project/settings/import_export.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/user/project/settings/import_export.md b/doc/user/project/settings/import_export.md index d76edcae14e7f9..f64d535db51c4b 100644 --- a/doc/user/project/settings/import_export.md +++ b/doc/user/project/settings/import_export.md @@ -2,7 +2,7 @@ stage: Create group: Import info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments -title: Migrate GitLab data by using by using file exports +title: Migrate GitLab data by using file exports description: "Use file exports to migrate GitLab data." --- -- GitLab From 55ec33238f567739a8c9b63595268c0096e71c31 Mon Sep 17 00:00:00 2001 From: Kerri Miller Date: Wed, 10 Dec 2025 14:14:07 -0800 Subject: [PATCH 19/20] Mark #x509_commit as exempt This method is used in a rake file, which our scanning doesn't capture. --- .gitlab/lint/unused_methods/excluded_methods.yml | 3 +++ .gitlab/lint/unused_methods/potential_methods_to_remove.yml | 2 -- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.gitlab/lint/unused_methods/excluded_methods.yml b/.gitlab/lint/unused_methods/excluded_methods.yml index e28bcfc6006b45..6d05448ebf537c 100644 --- a/.gitlab/lint/unused_methods/excluded_methods.yml +++ b/.gitlab/lint/unused_methods/excluded_methods.yml @@ -2,6 +2,9 @@ # scripts/lint/unused_methods.rb, however subsequent research shows # them to be false positives, and should be ignored when running that script. # +x509_commit: + file: app/models/commit_signatures/x509_commit_signature.rb + reason: Accessed from lib/tasks/gitlab/x509/update.rake sort_value_stars_desc: file: app/helpers/sorting_titles_values_helper.rb reason: Used to build sorting dropdowns in app/helpers/sorting_helper.rb diff --git a/.gitlab/lint/unused_methods/potential_methods_to_remove.yml b/.gitlab/lint/unused_methods/potential_methods_to_remove.yml index dd28696a5c2855..d0ccf4801ce39a 100644 --- a/.gitlab/lint/unused_methods/potential_methods_to_remove.yml +++ b/.gitlab/lint/unused_methods/potential_methods_to_remove.yml @@ -237,8 +237,6 @@ updateable?: file: app/models/clusters/concerns/application_status.rb update_available?: file: app/models/clusters/concerns/application_version.rb -x509_commit: - file: app/models/commit_signatures/x509_commit_signature.rb user_authored?: file: app/models/concerns/awardable.rb degenerate!: -- GitLab From 71cd51072b0472263bf4f2073dd3239ed7e3b902 Mon Sep 17 00:00:00 2001 From: "vishal.s.patel" Date: Fri, 12 Dec 2025 11:38:20 +1300 Subject: [PATCH 20/20] Fixed failing test --- qa/performance_test/k6_test/group_merge_requests.js | 7 +++---- qa/performance_test/k6_test/project_merge_requests.js | 7 +++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/qa/performance_test/k6_test/group_merge_requests.js b/qa/performance_test/k6_test/group_merge_requests.js index 095052f78c2340..fb6eab6330df33 100644 --- a/qa/performance_test/k6_test/group_merge_requests.js +++ b/qa/performance_test/k6_test/group_merge_requests.js @@ -34,11 +34,11 @@ export function setup() { if (targetGroup) { const apiUrl = `${apiVersion}/groups/${targetGroup.id}/merge_requests`; - return { groupId: targetGroup.id, apiUrl }; + return { apiUrl }; } } - const apiUrl = `${apiVersion}/groups/1/merge_requests`; - return { groupId: '1', apiUrl }; // Fallback to default + const apiUrl = `${apiVersion}/groups/1/merge_requests`; // Fallback to default + return { apiUrl }; } export const options = { @@ -69,7 +69,6 @@ export const options = { export default function groupMergeRequestsTest(data) { const baseUrl = __ENV.GITLAB_URL || `http://gitlab.${__ENV.AI_GATEWAY_IP}.nip.io`; - groupId = __ENV.GROUP_ID || data.groupId; const token = __ENV.GITLAB_QA_ADMIN_ACCESS_TOKEN || ''; API_URL = data.apiUrl; diff --git a/qa/performance_test/k6_test/project_merge_requests.js b/qa/performance_test/k6_test/project_merge_requests.js index bc7f2a8b533826..8509707b3f806b 100644 --- a/qa/performance_test/k6_test/project_merge_requests.js +++ b/qa/performance_test/k6_test/project_merge_requests.js @@ -35,12 +35,12 @@ export function setup() { if (targetProject) { console.log(`Found project '${projectName}' with ID: ${targetProject.id}`); const apiUrl = `${apiVersion}/projects/${targetProject.id}/merge_requests`; - return { projectId: targetProject.id, apiUrl }; + return { apiUrl }; } } console.error(`Failed to search for projects: ${res.status}`); - const apiUrl = `${apiVersion}/projects/1/merge_requests`; - return { projectId: '1', apiUrl }; // Fallback to default + const apiUrl = `${apiVersion}/projects/1/merge_requests`; // Fallback to default + return { apiUrl }; } export const options = { @@ -71,7 +71,6 @@ export const options = { export default function projectMergeRequestsTest(data) { const baseUrl = __ENV.GITLAB_URL || `http://gitlab.${__ENV.AI_GATEWAY_IP}.nip.io`; - projectId = __ENV.PROJECT_ID || data.projectId; const token = __ENV.GITLAB_QA_ADMIN_ACCESS_TOKEN || ''; API_URL = data.apiUrl; -- GitLab