From 5d7f5cb05d43967d074dbe7b025c6697c121d562 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Thu, 11 Jul 2024 18:26:32 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- .../javascripts/issuable/issuable_context.js | 4 - .../notes/components/note_actions.vue | 9 - .../charts/components/pipeline_charts.vue | 19 +- .../charts/components/statistics_list.vue | 31 +- .../assignees/sidebar_assignees.vue | 149 ---- .../javascripts/sidebar/mount_sidebar.js | 61 +- .../merge_requests/drafts_controller.rb | 2 +- app/helpers/issuables_helper.rb | 7 - app/models/concerns/has_user_type.rb | 3 +- app/models/import/namespace_import_user.rb | 12 + app/models/namespace.rb | 2 + app/models/project.rb | 1 + app/models/user.rb | 3 +- app/policies/base_policy.rb | 5 + app/services/draft_notes/publish_service.rb | 22 +- app/services/notes/build_service.rb | 5 +- app/services/notes/create_service.rb | 7 +- .../issuable/_sidebar_assignees.html.haml | 39 - ...ck_view_all_link_in_pipeline_analytics.yml | 18 + ...viewer_assignment_timestamp_in_metrics.yml | 2 +- .../development/wiki_front_matter.yml | 4 +- .../development/wiki_front_matter_title.yml | 2 +- ...all_link_in_pipeline_analytics_monthly.yml | 22 + ..._all_link_in_pipeline_analytics_weekly.yml | 22 + db/docs/namespace_import_users.yml | 12 + ...145111_add_namespace_import_users_table.rb | 19 + ...n_key_on_user_to_namespace_import_users.rb | 17 + ..._on_namespace_to_namespace_import_users.rb | 17 + ..._add_seat_control_to_namespace_settings.rb | 9 + db/schema_migrations/20240701145111 | 1 + db/schema_migrations/20240701151120 | 1 + db/schema_migrations/20240701151442 | 1 + db/schema_migrations/20240709173654 | 1 + db/structure.sql | 31 + doc/api/graphql/reference/index.md | 1 + .../decisions/001_policy_on_the_client.md | 48 +- .../img/architecture.jpg | Bin 255392 -> 0 bytes .../blueprints/ai_context_management/index.md | 293 +------- doc/architecture/blueprints/autoflow/index.md | 213 +----- .../blueprints/autoflow/relation_to_ci.md | 68 +- .../blueprints/cdot_plan_managment/index.md | 178 +---- .../blueprints/cells/topology_service.md | 4 +- .../blueprints/ci_build_speed/benchmark.md | 87 +-- .../blueprints/ci_build_speed/index.md | 76 +- .../blueprints/custom_models/index.md | 252 +------ .../blueprints/disaster_recovery/index.md | 90 +-- .../blueprints/disaster_recovery/regional.md | 166 +---- .../blueprints/disaster_recovery/zonal.md | 124 +--- .../duo-workflow-architecture-gitlab-com.png | Bin 96843 -> 0 bytes ...orkflow-architecture-self-managed-full.png | Bin 100628 -> 0 bytes ...rkflow-architecture-self-managed-mixed.png | Bin 126440 -> 0 bytes .../duo-workflow-without-executor.png | Bin 47740 -> 0 bytes .../diagrams/duo_workflow_auth.png | Bin 54849 -> 0 bytes .../blueprints/duo_workflow/index.md | 688 +----------------- .../epss/decisions/002_use_new_bucket.md | 31 +- doc/architecture/blueprints/epss/index.md | 282 +------ .../gitlab_duo_rag/elasticsearch.md | 44 +- .../blueprints/gitlab_duo_rag/index.md | 40 +- .../blueprints/gitlab_duo_rag/postgresql.md | 98 +-- .../gitlab_duo_rag/vertex_ai_search.md | 137 +--- .../blueprints/gitlab_rag/elasticsearch.md | 224 +----- .../gitlab_rag/img/blog_figure-1.jpg | Bin 24155 -> 0 bytes .../img/elasticsearch_hybrid_search.png | Bin 22564 -> 0 bytes .../img/elasticsearch_rag_hosted_models.png | Bin 27082 -> 0 bytes .../img/elasticsearch_vector_store.png | Bin 16664 -> 0 bytes .../blueprints/gitlab_rag/index.md | 308 +------- .../blueprints/gitlab_rag/postgresql.md | 143 +--- .../blueprints/gitlab_rag/vertex_ai_search.md | 90 +-- .../blueprints/gitlab_steps/data.drawio.png | Bin 19270 -> 0 bytes .../decisions/001_initial_support.md | 33 +- .../blueprints/gitlab_steps/gitlab-ci.md | 250 +------ .../blueprints/gitlab_steps/implementation.md | 342 +-------- .../blueprints/gitlab_steps/index.md | 242 +----- .../gitlab_steps/runner-integration.md | 296 +------- .../gitlab_steps/service-deployment.md | 21 +- .../gitlab_steps/step-definition.md | 371 +--------- .../step-runner-sequence.drawio.png | Bin 32938 -> 0 bytes .../gitlab_steps/steps-syntactic-sugar.md | 69 +- .../blueprints/gitlab_xray_rag/index.md | 289 +------- .../modular_monolith/bounded_contexts.md | 55 +- .../001_modular_application_domain.md | 56 +- .../002_bounded_contexts_definition.md | 64 +- .../decisions/003_stewardship.md | 63 +- .../hexagonal_architecture.png | Bin 33135 -> 0 bytes .../hexagonal_monolith/index.md | 221 +----- .../blueprints/modular_monolith/index.md | 139 +--- .../modular_monolith/packages_extraction.md | 55 +- .../modular_monolith/proof_of_concepts.md | 137 +--- .../blueprints/modular_monolith/references.md | 73 +- .../img/pipeline_mini_graph.png | Bin 4285 -> 0 bytes .../blueprints/pipeline_mini_graph/index.md | 157 +--- .../documentation/testing/index.md | 49 +- doc/user/gitlab_duo/index.md | 40 +- doc/user/group/epics/epic_work_items.md | 5 + .../group/value_stream_analytics/index.md | 3 + doc/user/profile/achievements.md | 78 +- doc/user/profile/index.md | 2 +- lib/api/draft_notes.rb | 2 +- lib/gitlab/import/import_user_creator.rb | 77 ++ lib/tasks/gitlab/populate_job_traces.rake | 180 +++++ locale/gitlab.pot | 44 +- ...and_terminate_workspace_shared_examples.rb | 7 +- .../import/namespace_import_users.rb | 8 + spec/factories/users.rb | 4 + .../user_views_open_merge_request_spec.rb | 16 - spec/frontend/fixtures/merge_requests.rb | 12 +- .../statistics_list_spec.js.snap | 34 +- .../charts/components/pipeline_charts_spec.js | 7 +- .../charts/components/statistics_list_spec.js | 23 + .../projects/pipelines/charts/mock_data.js | 3 +- .../assignees/sidebar_assignees_spec.js | 102 --- spec/frontend/users_select/test_helper.js | 5 +- spec/helpers/issuables_helper_spec.rb | 29 - .../gitlab/import/import_user_creator_spec.rb | 87 +++ spec/models/concerns/has_user_type_spec.rb | 2 +- .../import/namespace_import_user_spec.rb | 35 + spec/models/user_spec.rb | 10 +- spec/policies/base_policy_spec.rb | 12 + spec/policies/group_policy_spec.rb | 10 +- .../draft_notes/publish_service_spec.rb | 33 +- spec/services/notes/build_service_spec.rb | 26 +- .../gitlab/populate_job_traces_rake_spec.rb | 109 +++ 122 files changed, 1330 insertions(+), 6902 deletions(-) delete mode 100644 app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.vue create mode 100644 app/models/import/namespace_import_user.rb create mode 100644 config/events/click_view_all_link_in_pipeline_analytics.yml create mode 100644 config/metrics/counts_28d/count_distinct_user_id_from_click_view_all_link_in_pipeline_analytics_monthly.yml create mode 100644 config/metrics/counts_7d/count_distinct_user_id_from_click_view_all_link_in_pipeline_analytics_weekly.yml create mode 100644 db/docs/namespace_import_users.yml create mode 100644 db/migrate/20240701145111_add_namespace_import_users_table.rb create mode 100644 db/migrate/20240701151120_add_foreign_key_on_user_to_namespace_import_users.rb create mode 100644 db/migrate/20240701151442_add_foreign_key_on_namespace_to_namespace_import_users.rb create mode 100644 db/migrate/20240709173654_add_seat_control_to_namespace_settings.rb create mode 100644 db/schema_migrations/20240701145111 create mode 100644 db/schema_migrations/20240701151120 create mode 100644 db/schema_migrations/20240701151442 create mode 100644 db/schema_migrations/20240709173654 delete mode 100644 doc/architecture/blueprints/ai_context_management/img/architecture.jpg delete mode 100644 doc/architecture/blueprints/duo_workflow/diagrams/duo-workflow-architecture-gitlab-com.png delete mode 100644 doc/architecture/blueprints/duo_workflow/diagrams/duo-workflow-architecture-self-managed-full.png delete mode 100644 doc/architecture/blueprints/duo_workflow/diagrams/duo-workflow-architecture-self-managed-mixed.png delete mode 100644 doc/architecture/blueprints/duo_workflow/diagrams/duo-workflow-without-executor.png delete mode 100644 doc/architecture/blueprints/duo_workflow/diagrams/duo_workflow_auth.png delete mode 100644 doc/architecture/blueprints/gitlab_rag/img/blog_figure-1.jpg delete mode 100644 doc/architecture/blueprints/gitlab_rag/img/elasticsearch_hybrid_search.png delete mode 100644 doc/architecture/blueprints/gitlab_rag/img/elasticsearch_rag_hosted_models.png delete mode 100644 doc/architecture/blueprints/gitlab_rag/img/elasticsearch_vector_store.png delete mode 100644 doc/architecture/blueprints/gitlab_steps/data.drawio.png delete mode 100644 doc/architecture/blueprints/gitlab_steps/step-runner-sequence.drawio.png delete mode 100644 doc/architecture/blueprints/modular_monolith/hexagonal_monolith/hexagonal_architecture.png delete mode 100644 doc/architecture/blueprints/pipeline_mini_graph/img/pipeline_mini_graph.png create mode 100644 lib/gitlab/import/import_user_creator.rb create mode 100644 lib/tasks/gitlab/populate_job_traces.rake create mode 100644 spec/factories/import/namespace_import_users.rb delete mode 100644 spec/frontend/sidebar/components/assignees/sidebar_assignees_spec.js create mode 100644 spec/lib/gitlab/import/import_user_creator_spec.rb create mode 100644 spec/models/import/namespace_import_user_spec.rb create mode 100644 spec/tasks/gitlab/populate_job_traces_rake_spec.rb diff --git a/app/assets/javascripts/issuable/issuable_context.js b/app/assets/javascripts/issuable/issuable_context.js index ef49bd29a40..24812172279 100644 --- a/app/assets/javascripts/issuable/issuable_context.js +++ b/app/assets/javascripts/issuable/issuable_context.js @@ -5,7 +5,6 @@ import UsersSelect from '~/users_select'; export default class IssuableContext { constructor(currentUser) { - this.userSelect = new UsersSelect(currentUser); this.reviewersSelect = new UsersSelect(currentUser, '.js-reviewer-search'); this.reviewersSelect.dropdowns.forEach((glDropdownInstance) => { @@ -34,9 +33,6 @@ export default class IssuableContext { $('.issuable-sidebar .inline-update').on('change', 'select', function onClickSelect() { return $(this).submit(); }); - $('.issuable-sidebar .inline-update').on('change', '.js-assignee', function onClickAssignee() { - return $(this).submit(); - }); $(document) .off('click', '.issuable-sidebar .dropdown-content a') .on('click', '.issuable-sidebar .dropdown-content a', (e) => e.preventDefault()); diff --git a/app/assets/javascripts/notes/components/note_actions.vue b/app/assets/javascripts/notes/components/note_actions.vue index 9aa8d30e8f3..d5476635b16 100644 --- a/app/assets/javascripts/notes/components/note_actions.vue +++ b/app/assets/javascripts/notes/components/note_actions.vue @@ -12,7 +12,6 @@ import resolvedStatusMixin from '~/batch_comments/mixins/resolved_status'; import { createAlert } from '~/alert'; import { TYPE_ISSUE } from '~/issues/constants'; import { __, sprintf } from '~/locale'; -import eventHub from '~/sidebar/event_hub'; import UserAccessRoleBadge from '~/vue_shared/components/user_access_role_badge.vue'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import { splitCamelCase } from '~/lib/utils/text_utility'; @@ -151,9 +150,6 @@ export default { showDeleteAction() { return this.canDelete && !this.canReportAsAbuse && !this.noteUrl; }, - isAuthoredByCurrentUser() { - return this.authorId === this.currentUserId; - }, currentUserId() { return this.getUserDataByProp('id'); }, @@ -165,9 +161,6 @@ export default { ? __('Unassign from commenting user') : __('Assign to commenting user'); }, - sidebarAction() { - return this.isUserAssigned ? 'sidebar.addAssignee' : 'sidebar.removeAssignee'; - }, targetType() { return this.getNoteableData.targetType; }, @@ -228,8 +221,6 @@ export default { }, handleAssigneeUpdate(assignees) { this.$emit('updateAssignees', assignees); - eventHub.$emit(this.sidebarAction, this.author); - eventHub.$emit('sidebar.saveAssignees'); }, assignUser() { let { assignees } = this; diff --git a/app/assets/javascripts/projects/pipelines/charts/components/pipeline_charts.vue b/app/assets/javascripts/projects/pipelines/charts/components/pipeline_charts.vue index 54ff4f8b354..b46045da269 100644 --- a/app/assets/javascripts/projects/pipelines/charts/components/pipeline_charts.vue +++ b/app/assets/javascripts/projects/pipelines/charts/components/pipeline_charts.vue @@ -158,21 +158,26 @@ export default { }; }, successRatio() { - const { successfulPipelines, failedPipelines } = this.counts; + const { successfulPipelines, totalPipelines } = this.counts; const successfulCount = successfulPipelines?.count; - const failedCount = failedPipelines?.count; - const ratio = (successfulCount / (successfulCount + failedCount)) * 100; + const totalCount = totalPipelines?.count || 0; - return failedCount === 0 ? 100 : ratio; + return totalCount === 0 ? 100 : (successfulCount / totalCount) * 100; + }, + failureRatio() { + const { failedPipelines, totalPipelines } = this.counts; + const failedCount = failedPipelines?.count; + const totalCount = totalPipelines?.count || 0; + + return totalCount === 0 ? 0 : (failedCount / totalCount) * 100; }, formattedCounts() { - const { totalPipelines, successfulPipelines, failedPipelines } = this.counts; + const { totalPipelines } = this.counts; return { total: totalPipelines?.count, - success: successfulPipelines?.count, - failed: failedPipelines?.count, successRatio: this.successRatio, + failureRatio: this.failureRatio, }; }, areaCharts() { diff --git a/app/assets/javascripts/projects/pipelines/charts/components/statistics_list.vue b/app/assets/javascripts/projects/pipelines/charts/components/statistics_list.vue index bf68dc7159c..131908ddfdf 100644 --- a/app/assets/javascripts/projects/pipelines/charts/components/statistics_list.vue +++ b/app/assets/javascripts/projects/pipelines/charts/components/statistics_list.vue @@ -4,7 +4,7 @@ import { GlSingleStat } from '@gitlab/ui/dist/charts'; import { SUPPORTED_FORMATS, getFormatter } from '~/lib/utils/unit_format'; import { s__, formatNumber } from '~/locale'; -const defaultPrecision = 2; +const defaultPrecision = 0; export default { components: { @@ -38,21 +38,16 @@ export default { value: formatNumber(this.counts.total), }, { - label: s__('PipelineCharts|Success ratio'), + label: s__('PipelineCharts|Failure rate'), + identifier: 'failure-ratio', + value: formatPercent(this.counts.failureRatio, defaultPrecision), + link: this.failedPipelinesLink, + }, + { + label: s__('PipelineCharts|Success rate'), identifier: 'success-ratio', value: formatPercent(this.counts.successRatio, defaultPrecision), }, - { - label: s__('PipelineCharts|Successful pipelines'), - identifier: 'successful-pipelines', - value: formatNumber(this.counts.success), - }, - { - label: s__('PipelineCharts|Failed pipelines'), - identifier: 'failed-pipelines', - value: formatNumber(this.counts.failed), - link: this.failedPipelinesLink, - }, ]; }, }, @@ -74,9 +69,13 @@ export default { :should-animate="true" use-delimiters /> - {{ - s__('Pipeline|See details') - }} + {{ s__('Pipeline|View all') }} diff --git a/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.vue b/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.vue deleted file mode 100644 index 897cd3583c8..00000000000 --- a/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.vue +++ /dev/null @@ -1,149 +0,0 @@ - - - diff --git a/app/assets/javascripts/sidebar/mount_sidebar.js b/app/assets/javascripts/sidebar/mount_sidebar.js index 7a6d890ba9e..e86dadc3b2c 100644 --- a/app/assets/javascripts/sidebar/mount_sidebar.js +++ b/app/assets/javascripts/sidebar/mount_sidebar.js @@ -8,7 +8,6 @@ import { isInDesignPage, isInIncidentPage, isInIssuePage, - isInMRPage, parseBoolean, } from '~/lib/utils/common_utils'; import { __ } from '~/locale'; @@ -16,7 +15,6 @@ import { apolloProvider } from '~/graphql_shared/issuable_client'; import Translate from '~/vue_shared/translate'; import UserSelect from '~/vue_shared/components/user_select/user_select.vue'; import CollapsedAssigneeList from './components/assignees/collapsed_assignee_list.vue'; -import SidebarAssignees from './components/assignees/sidebar_assignees.vue'; import SidebarAssigneesWidget from './components/assignees/sidebar_assignees_widget.vue'; import SidebarConfidentialityWidget from './components/confidential/sidebar_confidentiality_widget.vue'; import CopyEmailToClipboard from './components/copy/copy_email_to_clipboard.vue'; @@ -84,51 +82,6 @@ function mountSidebarTodoWidget() { }); } -function getSidebarAssigneeAvailabilityData() { - const sidebarAssigneeEl = document.querySelectorAll('.js-sidebar-assignee-data input'); - return Array.from(sidebarAssigneeEl) - .map((el) => el.dataset) - .reduce( - (acc, { username, availability = '' }) => ({ - ...acc, - [username]: availability, - }), - {}, - ); -} - -function mountSidebarAssigneesDeprecated(mediator) { - const el = document.querySelector('.js-sidebar-assignees-root'); - - if (!el) { - return null; - } - - const { id, iid, fullPath } = getSidebarOptions(); - const assigneeAvailabilityStatus = getSidebarAssigneeAvailabilityData(); - - return new Vue({ - el, - name: 'SidebarAssigneesRoot', - apolloProvider, - render: (createElement) => - createElement(SidebarAssignees, { - props: { - mediator, - issuableIid: String(iid), - projectPath: fullPath, - field: el.dataset.field, - issuableType: - isInIssuePage() || isInIncidentPage() || isInDesignPage() - ? TYPE_ISSUE - : TYPE_MERGE_REQUEST, - issuableId: id, - assigneeAvailabilityStatus, - }, - }), - }); -} - function mountSidebarAssigneesWidget() { const el = document.querySelector('.js-sidebar-assignees-root'); @@ -172,12 +125,6 @@ function mountSidebarAssigneesWidget() { }, }), }); - - const assigneeDropdown = document.querySelector('.js-sidebar-assignee-dropdown'); - - if (assigneeDropdown) { - trackShowInviteMemberLink(assigneeDropdown); - } } function mountSidebarReviewers(mediator) { @@ -801,15 +748,9 @@ export function mountAssigneesDropdown() { }); } -const isAssigneesWidgetShown = isInIssuePage() || isInDesignPage() || isInMRPage(); - export function mountSidebar(mediator, store) { mountSidebarTodoWidget(); - if (isAssigneesWidgetShown) { - mountSidebarAssigneesWidget(); - } else { - mountSidebarAssigneesDeprecated(mediator); - } + mountSidebarAssigneesWidget(); mountSidebarReviewers(mediator); mountSidebarCrmContacts(); mountSidebarLabelsWidget(); diff --git a/app/controllers/projects/merge_requests/drafts_controller.rb b/app/controllers/projects/merge_requests/drafts_controller.rb index d64dae17dee..e84a795c826 100644 --- a/app/controllers/projects/merge_requests/drafts_controller.rb +++ b/app/controllers/projects/merge_requests/drafts_controller.rb @@ -57,7 +57,7 @@ class Projects::MergeRequests::DraftsController < Projects::MergeRequests::Appli end def publish - result = DraftNotes::PublishService.new(merge_request, current_user).execute(draft_note(allow_nil: true)) + result = DraftNotes::PublishService.new(merge_request, current_user).execute(draft: draft_note(allow_nil: true)) if create_note_params[:note] ::Notes::CreateService.new(@project, current_user, create_note_params).execute diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb index 7704436298a..85c50682032 100644 --- a/app/helpers/issuables_helper.rb +++ b/app/helpers/issuables_helper.rb @@ -206,13 +206,6 @@ module IssuablesHelper finder.class.scalar_params.any? { |p| params[p].present? } end - def assignee_sidebar_data(assignee, merge_request: nil) - { avatar_url: assignee.avatar_url, name: assignee.name, username: assignee.username }.tap do |data| - data[:can_merge] = merge_request.can_be_merged_by?(assignee) if merge_request - data[:availability] = assignee.status.availability if assignee.association(:status).loaded? && assignee.status&.availability - end - end - def issuable_squash_option?(issuable, project) if issuable.persisted? issuable.squash diff --git a/app/models/concerns/has_user_type.rb b/app/models/concerns/has_user_type.rb index a0d06f275d5..0ea29c70a92 100644 --- a/app/models/concerns/has_user_type.rb +++ b/app/models/concerns/has_user_type.rb @@ -20,7 +20,8 @@ module HasUserType service_account: 13, llm_bot: 14, placeholder: 15, - duo_code_review_bot: 16 + duo_code_review_bot: 16, + import_user: 17 }.with_indifferent_access.freeze BOT_USER_TYPES = %w[ diff --git a/app/models/import/namespace_import_user.rb b/app/models/import/namespace_import_user.rb new file mode 100644 index 00000000000..3b11075e173 --- /dev/null +++ b/app/models/import/namespace_import_user.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +module Import + class NamespaceImportUser < ApplicationRecord + self.table_name = 'namespace_import_users' + + belongs_to :import_user, class_name: 'User', foreign_key: :user_id, inverse_of: :namespace_import_user + belongs_to :namespace + + validates :namespace_id, :user_id, presence: true + end +end diff --git a/app/models/namespace.rb b/app/models/namespace.rb index b852029fe09..406f7a16df6 100644 --- a/app/models/namespace.rb +++ b/app/models/namespace.rb @@ -111,6 +111,8 @@ class Namespace < ApplicationRecord has_many :jira_connect_subscriptions, class_name: 'JiraConnectSubscription', foreign_key: :namespace_id, inverse_of: :namespace has_many :import_source_users, class_name: 'Import::SourceUser', foreign_key: :namespace_id, inverse_of: :namespace + has_one :namespace_import_user, class_name: 'Import::NamespaceImportUser', foreign_key: :namespace_id, inverse_of: :namespace + has_one :import_user, class_name: 'User', through: :namespace_import_user, foreign_key: :user_id validates :owner, presence: true, if: ->(n) { n.owner_required? } validates :name, diff --git a/app/models/project.rb b/app/models/project.rb index 6eb69a2a93f..051b5d9e2ec 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -532,6 +532,7 @@ class Project < ApplicationRecord delegate :name, to: :owner, allow_nil: true, prefix: true delegate :jira_dvcs_server_last_sync_at, to: :feature_usage delegate :last_pipeline, to: :commit, allow_nil: true + delegate :import_user, to: :root_ancestor with_options to: :team do delegate :members, prefix: true diff --git a/app/models/user.rb b/app/models/user.rb index 649d7c7d29c..8fd6cf82fe7 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -268,6 +268,7 @@ class User < MainClusterwide::ApplicationRecord has_many :created_custom_emoji, class_name: 'CustomEmoji', inverse_of: :creator has_many :bulk_imports + has_one :namespace_import_user, class_name: 'Import::NamespaceImportUser', inverse_of: :import_user has_many :custom_attributes, class_name: 'UserCustomAttribute' has_one :trusted_with_spam_attribute, -> { UserCustomAttribute.trusted_with_spam }, class_name: 'UserCustomAttribute' @@ -2263,7 +2264,7 @@ class User < MainClusterwide::ApplicationRecord end def terms_accepted? - return true if project_bot? || service_account? || security_policy_bot? + return true if project_bot? || service_account? || security_policy_bot? || import_user? if Feature.enabled?(:enforce_acceptance_of_changed_terms) !!ApplicationSetting::Term.latest&.accepted_by_user?(self) diff --git a/app/policies/base_policy.rb b/app/policies/base_policy.rb index 0be0a8c71be..c28d80ddf6b 100644 --- a/app/policies/base_policy.rb +++ b/app/policies/base_policy.rb @@ -51,6 +51,10 @@ class BasePolicy < DeclarativePolicy::Base with_options scope: :user, score: 0 condition(:placeholder_user) { @user.try(:placeholder?) || false } + desc "Import user" + with_options scope: :user, score: 0 + condition(:import_user) { @user.try(:import_user?) || false } + desc "User email is unconfirmed or user account is locked" with_options scope: :user, score: 0 condition(:inactive) { @user&.confirmation_required_on_sign_in? || @user&.access_locked? } @@ -91,6 +95,7 @@ class BasePolicy < DeclarativePolicy::Base condition(:is_gitlab_com, score: 0, scope: :global) { ::Gitlab.com? } rule { placeholder_user }.prevent_all + rule { import_user }.prevent_all private diff --git a/app/services/draft_notes/publish_service.rb b/app/services/draft_notes/publish_service.rb index 89893c375fa..e2398ac74fc 100644 --- a/app/services/draft_notes/publish_service.rb +++ b/app/services/draft_notes/publish_service.rb @@ -2,13 +2,15 @@ module DraftNotes class PublishService < DraftNotes::BaseService - def execute(draft = nil) - return error('Not allowed to create notes') unless can?(current_user, :create_note, merge_request) + def execute(draft: nil, executing_user: nil) + executing_user ||= current_user + + return error('Not allowed to create notes') unless can?(executing_user, :create_note, merge_request) if draft - publish_draft_note(draft) + publish_draft_note(draft, executing_user) else - publish_draft_notes + publish_draft_notes(executing_user) merge_request_activity_counter.track_publish_review_action(user: current_user) end @@ -20,14 +22,14 @@ module DraftNotes private - def publish_draft_note(draft) - create_note_from_draft(draft) + def publish_draft_note(draft, executing_user) + create_note_from_draft(draft, executing_user) draft.delete MergeRequests::ResolvedDiscussionNotificationService.new(project: project, current_user: current_user).execute(merge_request) end - def publish_draft_notes + def publish_draft_notes(executing_user) return if draft_notes.blank? review = Review.create!(author: current_user, merge_request: merge_request, project: project) @@ -36,6 +38,7 @@ module DraftNotes draft_note.review = review create_note_from_draft( draft_note, + executing_user, skip_capture_diff_note_position: true, skip_keep_around_commits: true, skip_merge_status_trigger: true @@ -51,7 +54,7 @@ module DraftNotes after_publish(review) end - def create_note_from_draft(draft, skip_capture_diff_note_position: false, skip_keep_around_commits: false, skip_merge_status_trigger: false) + def create_note_from_draft(draft, executing_user, skip_capture_diff_note_position: false, skip_keep_around_commits: false, skip_merge_status_trigger: false) # Make sure the diff file is unfolded in order to find the correct line # codes. draft.diff_file&.unfold_diff_lines(draft.original_position) @@ -59,7 +62,8 @@ module DraftNotes note_params = draft.publish_params.merge(skip_keep_around_commits: skip_keep_around_commits) note = Notes::CreateService.new(project, current_user, note_params).execute( skip_capture_diff_note_position: skip_capture_diff_note_position, - skip_merge_status_trigger: skip_merge_status_trigger + skip_merge_status_trigger: skip_merge_status_trigger, + executing_user: executing_user ) set_discussion_resolve_status(note, draft) diff --git a/app/services/notes/build_service.rb b/app/services/notes/build_service.rb index 96df14aa574..58b38820fe3 100644 --- a/app/services/notes/build_service.rb +++ b/app/services/notes/build_service.rb @@ -2,9 +2,10 @@ module Notes class BuildService < ::BaseService - def execute + def execute(executing_user: nil) in_reply_to_discussion_id = params.delete(:in_reply_to_discussion_id) external_author = params.delete(:external_author) + executing_user ||= current_user discussion = nil @@ -16,7 +17,7 @@ module Notes if in_reply_to_discussion_id.present? discussion = find_discussion(in_reply_to_discussion_id) - return discussion_not_found unless discussion && can?(current_user, :create_note, discussion.noteable) + return discussion_not_found unless discussion && can?(executing_user, :create_note, discussion.noteable) discussion = discussion.convert_to_discussion! if discussion.can_convert_to_discussion? diff --git a/app/services/notes/create_service.rb b/app/services/notes/create_service.rb index b555b990249..37ba0b57330 100644 --- a/app/services/notes/create_service.rb +++ b/app/services/notes/create_service.rb @@ -4,8 +4,11 @@ module Notes class CreateService < ::Notes::BaseService include IncidentManagement::UsageData - def execute(skip_capture_diff_note_position: false, skip_merge_status_trigger: false) - note = Notes::BuildService.new(project, current_user, params.except(:merge_request_diff_head_sha)).execute + def execute(skip_capture_diff_note_position: false, skip_merge_status_trigger: false, executing_user: nil) + note = + Notes::BuildService + .new(project, current_user, params.except(:merge_request_diff_head_sha)) + .execute(executing_user: executing_user) # n+1: https://gitlab.com/gitlab-org/gitlab-foss/issues/37440 note_valid = Gitlab::GitalyClient.allow_n_plus_1_calls do diff --git a/app/views/shared/issuable/_sidebar_assignees.html.haml b/app/views/shared/issuable/_sidebar_assignees.html.haml index 8a7e5984288..166fdc92b6b 100644 --- a/app/views/shared/issuable/_sidebar_assignees.html.haml +++ b/app/views/shared/issuable/_sidebar_assignees.html.haml @@ -7,42 +7,3 @@ .title.hide-collapsed.gl-display-flex.gl-justify-content-space-between.gl-align-items-center{ class: 'gl-mb-0!' } %span.gl-font-bold= s_('Label|Assignee') = gl_loading_icon(inline: true) - -.js-sidebar-assignee-data.selectbox.hide-collapsed - - if assignees.none? - = hidden_field_tag "#{issuable_type}[assignee_ids][]", 0, id: nil - - else - - assignees.each do |assignee| - = hidden_field_tag "#{issuable_type}[assignee_ids][]", assignee.id, id: nil, data: assignee_sidebar_data(assignee, merge_request: @merge_request) - - - options = { toggle_class: 'js-user-search js-author-search', - title: _('Select assignees'), - filter: true, - dropdown_class: 'dropdown-menu-user dropdown-menu-selectable dropdown-menu-author', - placeholder: _('Search users'), - data: { first_user: issuable_sidebar.dig(:current_user, :username), - current_user: true, - iid: issuable_sidebar[:iid], - issuable_type: issuable_type, - project_id: issuable_sidebar[:project_id], - author_id: issuable_sidebar[:author_id], - field_name: "#{issuable_type}[assignee_ids][]", - issue_update: issuable_sidebar[:issuable_json_path], - ability_name: issuable_type, - null_user: true, - display: 'static' } } - - - title = dropdown_options[:title] - - options[:toggle_class] += ' js-multiselect js-save-user-data' - - data = { field_name: "#{issuable_type}[assignee_ids][]" } - - data[:multi_select] = true - - data['dropdown-title'] = title - - data['dropdown-header'] = dropdown_options[:data][:'dropdown-header'] - - data['max-select'] = dropdown_max_select(dropdown_options[:data]) - - options[:data].merge!(data) - - = render 'shared/issuable/sidebar_user_dropdown', - options: options, - wrapper_class: 'js-sidebar-assignee-dropdown', - track_label: 'edit_assignee', - trigger_source: "#{issuable_type}_assignee_dropdown" diff --git a/config/events/click_view_all_link_in_pipeline_analytics.yml b/config/events/click_view_all_link_in_pipeline_analytics.yml new file mode 100644 index 00000000000..e0e5c0f533e --- /dev/null +++ b/config/events/click_view_all_link_in_pipeline_analytics.yml @@ -0,0 +1,18 @@ +--- +description: View all link on Pipeline Analytics failure rate stat +internal_events: true +action: click_view_all_link_in_pipeline_analytics +identifiers: +- project +- namespace +- user +product_group: runner +milestone: '17.2' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/158957 +distributions: +- ce +- ee +tiers: +- free +- premium +- ultimate diff --git a/config/feature_flags/beta/store_first_reviewer_assignment_timestamp_in_metrics.yml b/config/feature_flags/beta/store_first_reviewer_assignment_timestamp_in_metrics.yml index fd39f165376..4583f1d9b77 100644 --- a/config/feature_flags/beta/store_first_reviewer_assignment_timestamp_in_metrics.yml +++ b/config/feature_flags/beta/store_first_reviewer_assignment_timestamp_in_metrics.yml @@ -6,4 +6,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/470926 milestone: '17.2' group: group::optimize type: beta -default_enabled: false +default_enabled: true diff --git a/config/feature_flags/development/wiki_front_matter.yml b/config/feature_flags/development/wiki_front_matter.yml index 6e4cf0db88b..94618daede7 100644 --- a/config/feature_flags/development/wiki_front_matter.yml +++ b/config/feature_flags/development/wiki_front_matter.yml @@ -1,8 +1,8 @@ --- name: wiki_front_matter introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/27706 -rollout_issue_url: +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/435056 milestone: '12.10' type: development group: group::knowledge -default_enabled: false +default_enabled: true diff --git a/config/feature_flags/development/wiki_front_matter_title.yml b/config/feature_flags/development/wiki_front_matter_title.yml index 381bb2e1154..647e6113047 100644 --- a/config/feature_flags/development/wiki_front_matter_title.yml +++ b/config/feature_flags/development/wiki_front_matter_title.yml @@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/428259 milestone: '16.6' type: development group: group::knowledge -default_enabled: false +default_enabled: true diff --git a/config/metrics/counts_28d/count_distinct_user_id_from_click_view_all_link_in_pipeline_analytics_monthly.yml b/config/metrics/counts_28d/count_distinct_user_id_from_click_view_all_link_in_pipeline_analytics_monthly.yml new file mode 100644 index 00000000000..4232ac6a09e --- /dev/null +++ b/config/metrics/counts_28d/count_distinct_user_id_from_click_view_all_link_in_pipeline_analytics_monthly.yml @@ -0,0 +1,22 @@ +--- +key_path: redis_hll_counters.count_distinct_user_id_from_click_view_all_link_in_pipeline_analytics_monthly +description: Monthly count of unique users who clicked on the View All link on the Pipeline Analytics page +product_group: runner +performance_indicator_type: [] +value_type: number +status: active +milestone: '17.2' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/158957 +time_frame: 28d +data_source: internal_events +data_category: optional +distribution: +- ce +- ee +tier: +- free +- premium +- ultimate +events: +- name: click_view_all_link_in_pipeline_analytics + unique: user.id diff --git a/config/metrics/counts_7d/count_distinct_user_id_from_click_view_all_link_in_pipeline_analytics_weekly.yml b/config/metrics/counts_7d/count_distinct_user_id_from_click_view_all_link_in_pipeline_analytics_weekly.yml new file mode 100644 index 00000000000..424948ef927 --- /dev/null +++ b/config/metrics/counts_7d/count_distinct_user_id_from_click_view_all_link_in_pipeline_analytics_weekly.yml @@ -0,0 +1,22 @@ +--- +key_path: redis_hll_counters.count_distinct_user_id_from_click_view_all_link_in_pipeline_analytics_weekly +description: Weekly count of unique users who clicked on the View All link on the Pipeline Analytics page +product_group: runner +performance_indicator_type: [] +value_type: number +status: active +milestone: '17.2' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/158957 +time_frame: 7d +data_source: internal_events +data_category: optional +distribution: +- ce +- ee +tier: +- free +- premium +- ultimate +events: +- name: click_view_all_link_in_pipeline_analytics + unique: user.id diff --git a/db/docs/namespace_import_users.yml b/db/docs/namespace_import_users.yml new file mode 100644 index 00000000000..16090359363 --- /dev/null +++ b/db/docs/namespace_import_users.yml @@ -0,0 +1,12 @@ +--- +table_name: namespace_import_users +classes: +- Import::NamespaceImportUser +feature_categories: +- importers +description: Represents a list of import users for namespaces +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/157979 +milestone: '17.2' +gitlab_schema: gitlab_main_cell +sharding_key: + namespace_id: namespaces diff --git a/db/migrate/20240701145111_add_namespace_import_users_table.rb b/db/migrate/20240701145111_add_namespace_import_users_table.rb new file mode 100644 index 00000000000..bd9b3ba83f7 --- /dev/null +++ b/db/migrate/20240701145111_add_namespace_import_users_table.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +class AddNamespaceImportUsersTable < Gitlab::Database::Migration[2.2] + milestone '17.2' + + def up + create_table :namespace_import_users do |t| # rubocop:disable Migration/EnsureFactoryForTable -- False Positive + t.bigint :user_id, null: false + t.bigint :namespace_id, null: false + + t.index :namespace_id, unique: true, name: :index_namespace_import_users_on_namespace_id + t.index :user_id, unique: true, name: :index_namespace_import_users_on_user_id + end + end + + def down + drop_table :namespace_import_users + end +end diff --git a/db/migrate/20240701151120_add_foreign_key_on_user_to_namespace_import_users.rb b/db/migrate/20240701151120_add_foreign_key_on_user_to_namespace_import_users.rb new file mode 100644 index 00000000000..edc44c7c560 --- /dev/null +++ b/db/migrate/20240701151120_add_foreign_key_on_user_to_namespace_import_users.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +class AddForeignKeyOnUserToNamespaceImportUsers < Gitlab::Database::Migration[2.2] + milestone '17.2' + + disable_ddl_transaction! + + def up + add_concurrent_foreign_key :namespace_import_users, :users, column: :user_id, on_delete: :cascade + end + + def down + with_lock_retries do + remove_foreign_key :namespace_import_users, column: :user_id + end + end +end diff --git a/db/migrate/20240701151442_add_foreign_key_on_namespace_to_namespace_import_users.rb b/db/migrate/20240701151442_add_foreign_key_on_namespace_to_namespace_import_users.rb new file mode 100644 index 00000000000..ad484f13e0a --- /dev/null +++ b/db/migrate/20240701151442_add_foreign_key_on_namespace_to_namespace_import_users.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +class AddForeignKeyOnNamespaceToNamespaceImportUsers < Gitlab::Database::Migration[2.2] + milestone '17.2' + + disable_ddl_transaction! + + def up + add_concurrent_foreign_key :namespace_import_users, :namespaces, column: :namespace_id, on_delete: :cascade + end + + def down + with_lock_retries do + remove_foreign_key :namespace_import_users, column: :namespace_id + end + end +end diff --git a/db/migrate/20240709173654_add_seat_control_to_namespace_settings.rb b/db/migrate/20240709173654_add_seat_control_to_namespace_settings.rb new file mode 100644 index 00000000000..c8ca9e89998 --- /dev/null +++ b/db/migrate/20240709173654_add_seat_control_to_namespace_settings.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class AddSeatControlToNamespaceSettings < Gitlab::Database::Migration[2.2] + milestone '17.2' + + def change + add_column :namespace_settings, :seat_control, :smallint, null: false, default: 0 + end +end diff --git a/db/schema_migrations/20240701145111 b/db/schema_migrations/20240701145111 new file mode 100644 index 00000000000..1b910d88e17 --- /dev/null +++ b/db/schema_migrations/20240701145111 @@ -0,0 +1 @@ +584a0b88e726e9bcb8a270b3f670de7aaa01df882fe5a80b739e7b30ca6bb28f \ No newline at end of file diff --git a/db/schema_migrations/20240701151120 b/db/schema_migrations/20240701151120 new file mode 100644 index 00000000000..a5740de5305 --- /dev/null +++ b/db/schema_migrations/20240701151120 @@ -0,0 +1 @@ +43829404f3e2b2b9fc5780da09ef3f73fa91fd5d690c4a1387181b9cb765f9c4 \ No newline at end of file diff --git a/db/schema_migrations/20240701151442 b/db/schema_migrations/20240701151442 new file mode 100644 index 00000000000..9518ebf9b19 --- /dev/null +++ b/db/schema_migrations/20240701151442 @@ -0,0 +1 @@ +181a63a57456d990e65c2b7e712d06da4a0d3a336a3d1b9cfcec47b355f3889e \ No newline at end of file diff --git a/db/schema_migrations/20240709173654 b/db/schema_migrations/20240709173654 new file mode 100644 index 00000000000..e9e17ba3ff8 --- /dev/null +++ b/db/schema_migrations/20240709173654 @@ -0,0 +1 @@ +635b0c8cd5d82026f915d924c0923c21799f48e0e0f452bbfd6c92c5b3d1f168 \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 63a1cc8209e..3019a204997 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -13227,6 +13227,21 @@ CREATE TABLE namespace_details ( pending_delete boolean DEFAULT false NOT NULL ); +CREATE TABLE namespace_import_users ( + id bigint NOT NULL, + user_id bigint NOT NULL, + namespace_id bigint NOT NULL +); + +CREATE SEQUENCE namespace_import_users_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE namespace_import_users_id_seq OWNED BY namespace_import_users.id; + CREATE TABLE namespace_ldap_settings ( namespace_id bigint NOT NULL, created_at timestamp with time zone NOT NULL, @@ -13343,6 +13358,7 @@ CREATE TABLE namespace_settings ( remove_dormant_members boolean DEFAULT false NOT NULL, remove_dormant_members_period integer DEFAULT 90 NOT NULL, early_access_program_joined_by_id bigint, + seat_control smallint DEFAULT 0 NOT NULL, CONSTRAINT check_0ba93c78c7 CHECK ((char_length(default_branch_name) <= 255)), CONSTRAINT namespace_settings_unique_project_download_limit_alertlist_size CHECK ((cardinality(unique_project_download_limit_alertlist) <= 100)), CONSTRAINT namespace_settings_unique_project_download_limit_allowlist_size CHECK ((cardinality(unique_project_download_limit_allowlist) <= 100)) @@ -21094,6 +21110,8 @@ ALTER TABLE ONLY namespace_bans ALTER COLUMN id SET DEFAULT nextval('namespace_b ALTER TABLE ONLY namespace_commit_emails ALTER COLUMN id SET DEFAULT nextval('namespace_commit_emails_id_seq'::regclass); +ALTER TABLE ONLY namespace_import_users ALTER COLUMN id SET DEFAULT nextval('namespace_import_users_id_seq'::regclass); + ALTER TABLE ONLY namespace_statistics ALTER COLUMN id SET DEFAULT nextval('namespace_statistics_id_seq'::regclass); ALTER TABLE ONLY namespaces ALTER COLUMN id SET DEFAULT nextval('namespaces_id_seq'::regclass); @@ -23414,6 +23432,9 @@ ALTER TABLE ONLY namespace_commit_emails ALTER TABLE ONLY namespace_details ADD CONSTRAINT namespace_details_pkey PRIMARY KEY (namespace_id); +ALTER TABLE ONLY namespace_import_users + ADD CONSTRAINT namespace_import_users_pkey PRIMARY KEY (id); + ALTER TABLE ONLY namespace_ldap_settings ADD CONSTRAINT namespace_ldap_settings_pkey PRIMARY KEY (namespace_id); @@ -28036,6 +28057,10 @@ CREATE UNIQUE INDEX index_namespace_commit_emails_on_user_id_and_namespace_id ON CREATE INDEX index_namespace_details_on_creator_id ON namespace_details USING btree (creator_id); +CREATE UNIQUE INDEX index_namespace_import_users_on_namespace_id ON namespace_import_users USING btree (namespace_id); + +CREATE UNIQUE INDEX index_namespace_import_users_on_user_id ON namespace_import_users USING btree (user_id); + CREATE UNIQUE INDEX index_namespace_root_storage_statistics_on_namespace_id ON namespace_root_storage_statistics USING btree (namespace_id); CREATE UNIQUE INDEX index_namespace_statistics_on_namespace_id ON namespace_statistics USING btree (namespace_id); @@ -32724,6 +32749,9 @@ ALTER TABLE ONLY merge_request_assignment_events ALTER TABLE ONLY bulk_import_entities ADD CONSTRAINT fk_a44ff95be5 FOREIGN KEY (parent_id) REFERENCES bulk_import_entities(id) ON DELETE CASCADE; +ALTER TABLE ONLY namespace_import_users + ADD CONSTRAINT fk_a49233ca5d FOREIGN KEY (namespace_id) REFERENCES namespaces(id) ON DELETE CASCADE; + ALTER TABLE ONLY abuse_report_user_mentions ADD CONSTRAINT fk_a4bd02b7df FOREIGN KEY (note_id) REFERENCES notes(id) ON DELETE CASCADE; @@ -32853,6 +32881,9 @@ ALTER TABLE ONLY issue_assignees ALTER TABLE ONLY agent_project_authorizations ADD CONSTRAINT fk_b7fe9b4777 FOREIGN KEY (agent_id) REFERENCES cluster_agents(id) ON DELETE CASCADE; +ALTER TABLE ONLY namespace_import_users + ADD CONSTRAINT fk_b82be3e1f3 FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE; + ALTER TABLE ONLY namespace_commit_emails ADD CONSTRAINT fk_b8d89d555e FOREIGN KEY (email_id) REFERENCES emails(id) ON DELETE CASCADE; diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index 2e887c69238..acab81083cc 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -36424,6 +36424,7 @@ Stage event identifiers. | `MERGE_REQUEST_LAST_BUILD_STARTED` | Merge request last build started event. | | `MERGE_REQUEST_LAST_EDITED` | Merge request last edited event. | | `MERGE_REQUEST_MERGED` | Merge request merged event. | +| `MERGE_REQUEST_REVIEWER_FIRST_ASSIGNED` | Merge request reviewer first assigned event. | | `PLAN_STAGE_START` | Plan stage start event. | ### `VerificationStateEnum` diff --git a/doc/architecture/blueprints/ai_context_management/decisions/001_policy_on_the_client.md b/doc/architecture/blueprints/ai_context_management/decisions/001_policy_on_the_client.md index 09ee071bc3f..c14c1ce3572 100644 --- a/doc/architecture/blueprints/ai_context_management/decisions/001_policy_on_the_client.md +++ b/doc/architecture/blueprints/ai_context_management/decisions/001_policy_on_the_client.md @@ -1,45 +1,11 @@ --- -owning-stage: "~devops::ai-powered" -description: 'AI Context Management ADR 001: Keeping AI Context Policy Management close to AI Context Retriever' +redirect_to: 'https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/ai_context_management/decisions/001_policy_on_the_client/' +remove_date: '2025-07-08' --- -# AI Context Management ADR 001: Keeping AI Context Policy Management close to AI Context Retriever +This document was moved to [another location](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/ai_context_management/decisions/001_policy_on_the_client/). -## Summary - -To manage AI Context effectively and ensure flexible and scalable solutions, AI Context Policy Management will reside in the -same environment, as the AI Context Retriever, and, as a result, as close to the context fetching mechanism as possible. This -approach aims to reduce latency and improve user control over the contextual information sent to AI systems. - -## Context - -The original blueprint outlined the necessity of a flexible AI Context Management system to provide accurate and relevant -AI responses while addressing security and trust concerns. It suggested that AI Context Policy Management should act as -a filtering solution between the context resolver and the context fetcher in the AI Context Retriever. However, the -blueprint did not specify the exact location for the AI Context Policy Management within the system. - -During [a sync discussion](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/155707#note_1978675445), it was determined -that placing the AI Context Policy Management close to AI Context Retriever would provide significant benefits. This decision -aligns with our approach of having shared components, like the AI Gateway and the Duo Chat UI, to ensure consistency and reduce -redundancy across different environments. - -## Decision - -AI Context Management will happen as close to the user's interaction with Duo features as possible. As a result, the [AI Gateway](https://gitlab.com/gitlab-org/modelops/applied-ml/code-suggestions/ai-assist) will only receive context that is policy-compliant. - -Users interact with Duo features in many different environments, including their IDE and the GitLab Web UI. Rather than retrieving the context from this environment and sending it to the AI Gateway for filtering based on the AI Context Policy, this decision states that the AI Context Retriever will filter this content *before* it reaches the AI Gateway. - -This decision allows for better security, flexibility and scalability, enabling dynamic user interactions and immediate feedback on context validation. - -## Consequences - -- *Implementation Complexity*: Users must create, modify, and remove context policies in each environment where they are -interacting with Duo features. This requires multiple implementations to support different environments. -- *Flexibility and Scalability*: Storing AI Context Policy Management close the AI Context Retriever allows for more flexible -and scalable policy implementations tailored to specific environments, such as IDEs and the Web. -- *Reduced Latency*: Filtering out unwanted context at the earliest possible stage reduces latency and ensures that only -the necessary information is sent to the AI models. -- *User Experience*: This approach facilitates dynamic UX, providing instant feedback to users in case of failed context -validation. Users can manage their supplementary context more effectively through a user-friendly interface. -- *Security*: By managing policies closer to the content retrieving mechanism, sensitive information can be filtered out -locally, enhancing security and user trust. + + + + diff --git a/doc/architecture/blueprints/ai_context_management/img/architecture.jpg b/doc/architecture/blueprints/ai_context_management/img/architecture.jpg deleted file mode 100644 index a0ec711c439ba82f0a516dc7b392cc8b7f3fa1b6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 255392 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%0A^v21#aBp&~ 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

8M=hB6nIrjWdoBy1hr2PhH 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: Request Code Suggestion - CR->>CR: Retrieve Supplementary User Context list - CR->>CR: Retrieve Automatic AI Context list - CR->>PM: Check AI Context against Policy - PM-->>CR: Return valid AI Context list - CR->>CR: Fetch valid AI Context - CR->>LLM: Send prompt with final AI Context - LLM->>LLM: Generate code suggestions - LLM-->>CS: Return code suggestions - CS->>CS: Present code suggestions to the user -``` - -In case the _AI Context Retriever_ fails to fetch any content from the _AI Context_, the prompt is sent with -_AI Context_, which was successfully fetched. In a low-probability case, when _AI Context Retriever_ cannot fetch any content, the prompt should be sent out as-is. - -## Alternative solutions - -### JSON Configuration Files - -- **Pros**: Widely used, easier integration with web technologies. -- **Cons**: Less readable compared to YAML for complex configurations. - -### Database-Backed Configuration - -- **Pros**: Centralized management, dynamic updates. -- **Cons**: Not version controlled. - -### Environment Variables - -- **Pros**: Simplifies configuration for deployment and scaling. -- **Cons**: Less suitable for complex configurations. - -### Policy as Code (without YAML) - -- **Pros**: Better control and auditing with versioned code. -- **Cons**: It requires users to write code and us to invent a language for it. - -### Policy in `.ai_ignore` and other Git-like files - -- **Pros**: Provides a straightforward approach, identical to the `allow` policy with the list of `exclude` suggested in this blueprint -- **Cons**: Supports only the `allow` policy; the processing of this file type still has to be implemented - -Based on these alternatives, the YAML file was chosen as a format for this blueprint because of versioning -in Git, and more versatility compared to the `.ai_ignore` alternative. - -## Suggested iterative implementation plan - -Please refer to the [Proposal](#proposal) for a detailed explanation of the items in every iteration. - -### Iteration 1 - -- Introduce the global `.ai-context-policy.yaml` YAML configuration file format and schema for this file type -as part of _AI Context Policy Management_. -- _AI Context Retrievers_ introduce support for _Supplementary User Context_. -- Optional: validation mechanism (like CI job and pre-push static analysis) for `.ai-context-policy.yaml` - -**Success criteria for the iteration:** Prompts sent from the Code Suggestions feature in IDEs contain -_AI Context_ only with the open IDE tabs, which adhere to the global _AI Context Policy_ in the root of a repository. - -### Iteration 2 - -- In _AI Context Retrievers_ introduce support for _Automatic AI Context_. -- Connect more features to the _AI Context Management_ system. - -**Success criteria for the iteration:** Prompts sent from the Code Suggestions feature in IDEs contain _AI Context_ -with items of _Automatic AI Context_, which adhere to the global _AI Context Policy_ in the root of a repository. - -### Iteration 3 - -- Connect all Duo features on the Web and in IDEs to _AI Context Retrievers_ and adhere to the global -_AI Context Policy_. - -**Success criteria for the iteration:** All Duo features in all environments send _AI Context_ which adheres to the -global _AI Context Policy_ - -### Iteration 4 - -- Support nested `.ai-context-policy.yaml` YAML configuration files. - -**Success criteria for the iteration:** _AI Context Policy_ placed into the sub-folders of a repository, override -higher-level policies when sending prompts. - -### Iteration 5 - -- User-level UI for _Supplementary User Context_. - -**Success criteria for the iteration:** Users can see and edit the contents of the _Supplementary User Context_ and -the context is shared between all Duo features within the environment (Web, IDEs, etc.) - -### Iteration 6 - -- Optional: UI for configuring the global _AI Context Policy_. - -**Success criteria for the iteration:** Users can see and edit the contents of the _AI Context Policies_ in a UI -editor. + + + + diff --git a/doc/architecture/blueprints/autoflow/index.md b/doc/architecture/blueprints/autoflow/index.md index 4bd486ae3d7..44d8a1faebe 100644 --- a/doc/architecture/blueprints/autoflow/index.md +++ b/doc/architecture/blueprints/autoflow/index.md @@ -1,210 +1,11 @@ --- -status: proposed -creation-date: "2024-02-16" -authors: [ "@ash2k", "@ntepluhina" ] -coach: "@grzesiek" -approvers: [ "@nagyv-gitlab", "@nmezzopera" ] -owning-stage: "~devops::deploy" -participating-stages: [ "~devops::plan" ] +redirect_to: 'https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/autoflow/' +remove_date: '2025-07-08' --- - - +This document was moved to [another location](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/autoflow/). -# AutoFlow - workflows for automation - -Automation + Workflow = AutoFlow. - -## Summary - -GitLab offers a single application for the whole DevSecOps cycle, and we aim to become the AllOps platform. -Being a platform means to provide users with tools to solve various problems in the corresponding domain. -There is a huge number of use cases that boil down to letting users automate interactions between the DevSecOps domain -objects. -Automation is key to increasing productivity and reducing cost in most businesses so those use cases are a big deal for -our customers. -We don't provide a comprehensive way to automate processes across the domain at the moment. - -GitLab AutoFlow allows users to encode workflows of interactions between DevSecOps domain objects and external systems. -Users are able to share, reuse, and collaborate on workflow blocks. - -## Motivation - -### Goals - -- Let users build workflows that automate tasks in the DevSecOps domain. -- Workflows should be able to interact with GitLab, users (via UI), and with external systems. Interactions are via API - calls and emitting/receiving events. -- Users should be able to share and reuse parts of a workflow on a self-serve basis. -- Workflow definitions should be testable without having to run them in a real environment. -- Security of customer data and GitLab platform should be kept in mind at all times. -- Workflows have to be executed in a durable way. Put differently, the automated show must go on even if a server - crashes or network connectivity is disrupted. - -### Example use cases - -#### Trivial - -- When milestone on an issue is `Backlog`, set label `workflow::backlog`. When it's set to a milestone, set label to - `workflow::ready for dev`. -- When label on an issue is `group::environments`, set labels `devops::deploy` and `section::cd`. -- All what [GitLab Triage](https://gitlab.com/gitlab-org/ruby/gems/gitlab-triage) can do. - -#### Interesting - -- **Retro bot**: when a milestone expires or is closed, wait for the next Monday. Get a list of members of a group. - For each member open an issue in a project. - In the issue add a form with fields (a new UI component that we don't have now) to enter what went well in this - milestone, what didn't, and praise for the team. If an issue stays open for more than two days, ping the assigned team - member. - Once all opened issues have been closed or a week later, whatever happens first, collect form data from them - and open a new issue with that data aggregated. - Assign to the group's manager, mention all group members. -- **Project compliance**: when project settings change, trigger a "pre-commit" flow that allows for programmatic - validation of the intended changes. Restricting project settings is a common compliance requirement. Today, GitLab - role model does not allow for much customization, and users work around this functionality with code-based automations - like Terraform. An alternative, often requested approach is to restrict project settings at higher levels. Given the - wide variety of project settings, this would likely either have only partial support or would require re-implementing - all the project settings in the compliance settings. Overall, most single use-case solutions will likely have serious - maintenance and scalability issues. Implementing validation as code could provide a simple interface. - -#### Sophisticated - -- **Deployments**: when a commit is merged into the main branch: - - A build should run. - - On success, certain artifacts should be combined into a release. - - The release then should be rolled out to pre-production environment using a certain deployment method. - - The deployment should be soaked there under synthetic load for 1 day. - - Then promoted to staging. - - After 1 more day in staging, the release should be gradually rolled out to production. - - Production rollout should start with a canary deployment. - - Then it should be scaled up in 10% increments each hour. - - For that deployment, anomaly detection system should be monitoring certain metrics. - - It should stop the rollout if something unusual is detected (we don't have this mechanism yet, but it'd be - great), notify the SRE team. - - If things are "really bad" (i.e. certain metrics breach certain thresholds), create an incident issue and start - rolling the deployment back. - - Keep the incident issue up to date with what's happening with the deployment. - - Get information about the Deployment object (let's assume we are deploying to Kubernetes), events in - the namespace, and Pod logs from the GitLab agent for Kubernetes. - - Feed that into GitLab Duo to get advice on what the problem might be and how to fix it. Post the reply as a comment. -- **Compliance in workflows**: any of the automated workflows, e.g. the one above, can have one or more steps where - a manual interaction from a user is awaited. - - If we let workflows generate UI elements, they could wait for those forms - to be filled, for buttons pushed, etc and take further actions based on user input (or lack of - timeouts). - - We could have a workflow request an approval from the risk management team if the deployment is happening during - a [PCL](https://handbook.gitlab.com/handbook/engineering/infrastructure/change-management/#production-change-lock-pcl). - - Because the process is automated, automation is code that is version-controlled, passing an audit becomes easier. No - chance to forget to follow the process if it's automated and there is no way around it. -- **Access requests**: most (?) of - our [access requests](https://gitlab.com/gitlab-com/team-member-epics/access-requests/) - can probably be automated. - - Team member creates an issue, fills in a form, assigns to their manager, they approve by setting a - label or pressing a special button, automation takes it from there - most systems have APIs that can be used to make - the requested changes. - - Consider how much time is wasted here - people have to wait, people have to do repetitive work. - - Manual actions mean there is a chance of making a mistake while making an important change. - - It's not only us, most of the businesses have a need to automate such processes. - -### Related issues - -Over the years we've accumulated many issues, epics, ideas, use cases on the topic of automation. Here are some of the -more interesting ones. - ---- - -[Improved Work Item lifecycle management & native automations](https://gitlab.com/groups/gitlab-org/-/epics/364), -[GitLab Automations](https://gitlab.com/groups/gitlab-org/-/epics/218), [Workflows Solution Validation](https://gitlab.com/gitlab-org/gitlab/-/issues/344136). -These look at the problem from the `devops::plan` point of view: - -> Customers and prospects frequently lament that there is no way to easily manage end-to-end workflows (Epic, Issue, -> MR...) within GitLab. -> -> Officially requested by 14 distinct accounts and is the third most requested / highest value capability from the Plan -> stage. - -See the linked issues from the epics too. - -[Configure label to be removed when issue is closed](https://gitlab.com/gitlab-org/gitlab/-/issues/17461) is yet -another example. 283 upvotes. - ---- - -[Automatable DevOps](https://gitlab.com/gitlab-org/gitlab/-/issues/330084) is Mikhail's previous attempt to provide the -automation capability. It inspired lots of thinking and lead to this proposal. - ---- - -[Add the ability to define an issue/MR event and an action to take as a result of that event](https://gitlab.com/gitlab-org/gitlab/-/issues/242194). -Customer [quote](https://gitlab.com/gitlab-org/gitlab/-/issues/242194#note_1785436689): - -> I'm in agreement. I'm having a hard enough time bringing a development team on board to GitLab, adding manual label -> management to the process when parts of it should be done via automation adds to the challenge. -> -> We don't want to auto-close issues on merge and have defined a QA role to perform that step. The problem I'm working -> on figuring out now is how to automate label management on an issue when the associated MR is closed, while leaving -> the -> Issue open but updating the workflow labels on it automatically. -> -> We're a smallish team and I need to be focused on product development, not how to build GitLab automation scripts. -> -> Having the ability to trigger some events as a part of an MR merging to manage other aspects of the system would be -> extremely helpful. - ---- - -Some use cases from `group::delivery` (from -[this comment](https://gitlab.com/gitlab-org/ci-cd/section-showcases/-/issues/54#note_1663194580)): - -- If we have events from when certain files are added/changed in Git for a project, we could use this to automate the - Provisioner in the Runway platform (and deprovision when people want to). -- Automating certain tasks when a new backport request issue is created. -- Automated tasks when we want to start a new monthly release. -- Moving to a "GitLab deployment Engine" that is more powerful than GitLab CI alone. This is perhaps the most - interesting use case to me, but I do wonder how complicated it would be to manage these workflows. - ---- - -Some use cases from the Remote Development team (from -[this comment](https://gitlab.com/gitlab-org/ci-cd/section-showcases/-/issues/54#note_1658464245)): - -> A real world example of this is the Remote Development Teams work to implement -> [a standard XP/Scrum style velocity-based process and workflow](https://handbook.gitlab.com/handbook/engineering/development/dev/create/ide/#planning-process-overview) -> in GitLab. -> -> There's -> [multiple limitations in GitLab the product itself](https://gitlab.com/cwoolley-gitlab/gl-velocity-board-extension#why-doesnt-standard-gitlab-support-this) -> which make it difficult to use this common process, and we have to work around them. -> -> To avoid the manual toil of making this process work in GitLab, we would like to automate it. However our efforts to -> set up the -> [several desired automations](https://handbook.gitlab.com/handbook/engineering/development/dev/create/ide/#automations) -> have been limited because of the barriers to implementing and testing them in -> Triage Bot, especially for ones that contain more complex logic, or can't be implemented solely via quick actions. -> -> I believe a tool like GitLab Flow would make it much easier for us and our customers to implement common but -> non-supported processes and workflows such as this on top of GitLab, without having to wait months or years for a -> given feature to be shipped which unblocks the effort. - -## Proof of concept, demos - -- [Implementation issue](https://gitlab.com/gitlab-org/cluster-integration/gitlab-agent/-/issues/473) -- Video with [conceptual and technical details, fist demo](https://www.youtube.com/watch?v=g9HSPV3GKas). It's a long - video, **watch on 1.5x**. [Skip right to the demo](https://youtu.be/g9HSPV3GKas?t=1325) at 22:05. -- [Slides](https://docs.google.com/presentation/d/1doMdiyusAjzHq-hlqHqHr0y4WZN2EiJZHFS_PVrTfJ8/edit?usp=sharing). Please - see speaker notes for links and code. -- Demo project: N/A See speaker notes for code (as text, not video) -- Implementation MRs: - [kas part](https://gitlab.com/gitlab-org/cluster-integration/gitlab-agent/-/merge_requests/1173), [Rails part](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/136696). - -Since the above demo went well, [GitLab AutoFlow for internal use](https://gitlab.com/groups/gitlab-org/-/epics/12120) -epic was opened. Then we tried to address a concrete use case -in [AutoFlow PoC: configurable automation for issue status updates](https://gitlab.com/groups/gitlab-org/-/epics/12571). -We recorded two more demos as part of that (see the epic for more details): - -- [GitLab AutoFlow PoC, iteration 1](https://www.youtube.com/watch?v=2Ntdnv2LY6I) -- [AutoFlow UI for issues triaging (Iteration 3 demo)](https://www.youtube.com/watch?v=bIBWxcJ1YTg&list=PL05JrBw4t0Kqgx_Pzuum5GeyNkMMcf2Bp&index=6) - -## Links to related documents - -- [Relation of GitLab AutoFlow to GitLab CI](relation_to_ci.md) + + + + diff --git a/doc/architecture/blueprints/autoflow/relation_to_ci.md b/doc/architecture/blueprints/autoflow/relation_to_ci.md index 0b2be9741c3..ef682d252fa 100644 --- a/doc/architecture/blueprints/autoflow/relation_to_ci.md +++ b/doc/architecture/blueprints/autoflow/relation_to_ci.md @@ -1,65 +1,11 @@ --- -stage: Deploy -group: Environments -info: Relation of GitLab AutoFlow to GitLab CI +redirect_to: 'https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/autoflow/relation_to_ci/' +remove_date: '2025-07-08' --- -# Relation of GitLab AutoFlow to GitLab CI +This document was moved to [another location](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/autoflow/relation_to_ci/). -GitLab CI and GitLab AutoFlow are different tools for solving different sets of problems. Here are the differences based -on the -[PoC](https://gitlab.com/groups/gitlab-org/-/epics/12571#note_1759648935) / [demo implementation](https://gitlab.com/gitlab-org/ci-cd/section-showcases/-/issues/54) -of the idea that is based on [Temporal](https://temporal.io/). Technical details and decisions on what technology to use -will be part of separate documents. But, since the question of relation to GitLab CI came up a few times, the following -is documented here to pre-emptively answer the question. - -## Conceptual differentiation - -- GitLab CI solves the problem of Continuous Integration. Use it to build and test your software. -- GitLab AutoFlow solves the problem of automation in the DevSecOps domain, but not CI. - Use it to automate business processes. - -## Task-based differentiation - -Use GitLab CI if: - -- Need to execute a program/binary/tool, including a (shell) script. -- Need to execute a container. -- Need to perform heavy computations. -- Need lots of RAM to perform an operation. - -Use GitLab AutoFlow: - -- Orchestrating complex, cross-project CI pipelines as part of the DevSecOps domain. -- Manipulating DevOps domain object(s) when something happens (or on a schedule) by calling APIs. -- Need to wait for an unspecified amount of time (possibly days or even weeks) for async events to take place - before proceeding. - -## Implementation differences - -Temporal-based GitLab AutoFlow implementation: - -- Designed for durable execution. I.e. can safely resume workflow execution after failure. -- Designed to run for an arbitrary long time (literary years). I.e. can wait for events and/or timers to "wake up" a - workflow, only occupying disk space in the DB for state storage. No CPU/RAM resources are reserved/used for a - non-executing workflow. -- Not designed to run heavy execution tasks. This is not a limitation of Temporal (as it does not run any code), it's - just this PoC doesn't give user a way to run something computationally expensive. Well, you could do computations in - Starlark, but you cannot run an external program. -- Not designed to run containers. -- Activities (executable unit of a workflow) have near-zero execution overhead. Think "function invocation" in an - already - running program. No startup cost at all. Activities are literally functions in kas that kas calls when it's told to. -- Not designed (at least not in this PoC) to run untrusted code BUT Starlark interpreter is not doing code generation - and is built in Go, not C, so most of typical "interpreter VM" vulnerabilities are simply impossible. This means it's - quite safe to execute untrusted Starlark code. Such code can only interact with the host program/machine via objects - explicitly injected into the script, which we control, it cannot do anything else. - -GitLab CI: - -- Is not designed for durable execution. If a job fails, it can be manually restarted. It will run from the start, - not from a particular point where it failed. It may not be safe to restart a failed job because it depends on what the - user is doing there. It's by far not a 1:1 comparison, but unlike CI jobs, Temporal activities are/must be idempotent - so are safe to retry automatically. -- Designed as a perfect solution for Continuous Integration. -- Designed to run arbitrary containers and untrusted code. + + + + diff --git a/doc/architecture/blueprints/cdot_plan_managment/index.md b/doc/architecture/blueprints/cdot_plan_managment/index.md index 9029e9e4a98..b2ab10351bb 100644 --- a/doc/architecture/blueprints/cdot_plan_managment/index.md +++ b/doc/architecture/blueprints/cdot_plan_managment/index.md @@ -1,175 +1,11 @@ --- -status: proposed -creation-date: "2024-02-07" -authors: [ "@vshumilo" ] -coach: "@vitallium" -approvers: [ "@tgolubeva", "@jameslopez" ] -owning-stage: "~devops::fulfillment" -participating-stages: [] +redirect_to: 'https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/cdot_plan_managment/' +remove_date: '2025-07-08' --- -# Automate CustomersDot Plan management +This document was moved to [another location](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/cdot_plan_managment/). -## Summary - -The [GitLab Customers Portal](https://customers.gitlab.com/) is an independent application, distinct from the GitLab product, designed to empower GitLab customers in managing their accounts, subscriptions, and conducting tasks such as renewing and purchasing additional seats. More information about the Customers Portal can be found in [the GitLab docs](../../../subscriptions/customers_portal.md). Internally, the application is known as [CustomersDot](https://gitlab.com/gitlab-org/customers-gitlab-com) (also known as CDot). - -GitLab uses [Zuora's platform](https://handbook.gitlab.com/handbook/business-technology/enterprise-applications/guides/zuora/) as the SSoT for all product-related information. The [Zuora Product Catalog](https://knowledgecenter.zuora.com/Get_Started/Zuora_quick_start_tutorials/B_Billing/A_The_Zuora_Product_Catalog) represents the full list of revenue-making products and services that are sellable, or have been sold by GitLab, which is core knowledge for CustomersDot decision making. CustomersDot currently has a local cache of the Zuora Product Catalog via the [IronBank](https://github.com/zendesk/iron_bank) gem and [its LocalRecord extension](https://gitlab.com/gitlab-org/customers-gitlab-com/blob/45f5dedbb4fa803d19827472214ea0b5b0ce1861/lib/gem_extensions/iron_bank/local_records.rb#L1). - -CustomersDot uses `Plan` as a wrapper class for easy access to all the details about a Plan in the Product Catalog. Given the name, price, minimum quantity and other details of the Plan are spread around the `Zuora::ProductRatePlan`, `Zuora::ProductRatePlanCharge` and `Zuora::ProductRatePlanChargeTier` objects, traditional access of these details can be cumbersome. This class is very useful because it saves us the need to query for all these details. On the other hand this class helps with the classification of `Zuora::ProductRatePlan`s based on their tier, deployment type, and other criteria to be used across the app. - -CustomersDot's cached Product Catalog is currently synced manually and requires a restart of CustomersDot to be fully refreshed due to limitations in the `Plan` class. Every time a new Product, Product Rate Plan or Product Rate Plan Charge are updated or added to the Zuora Product Catalog, an additional manual effort is required to add it to the `Plan` class and configure it. - -The main goal for this design document is to improve the architecture and maintainability of the `Plan` model within CustomersDot. When the Product Catalog is updated in Zuora, it automatically reflects in CustomersDot without requiring app restarts, code changes, or manual intervention. - -## Motivation - -Current Zuora Product Catalog updates are not automatically picked up by CustomersDot for a couple of reasons: - -- CustomersDot's cached Product Catalog sync requires a manual intervention via Rails console and a full refresh requires a server restart due to `Plan` heavily relying on constants and class variables for its `Zuora::ProductRatePlan` classification. -- Every time a new Product / SKU is added to the Zuora Product Catalog, even if the cache previously described is refreshed, it requires code changes in CustomersDot to make it available. This is due to the current strategy the `Plan` class uses for classification consisting of assigning the `Zuora::ProductRatePlan` ids to constants and then manually forming groups of ids to represent different categories like all plans in the Ultimate tier or all the add-ons available for self procurement for GitLab.com and then uses those categories for decision-making during execution. - -As the codebase and number of product grows this manual intervention becomes more expensive. - -### Goals - -The main goals are: - -- Make sure the CustomersDot cached Product Catalog is in sync with Zuora at any point in time. -- Automate the Plan management in CustomersDot so it will require no manual intervention for basic Product Catalog updates in Zuora. For example, if a new Product / SKU is added, if a RatePlanCharge is updated, or if a Product is discontinued. For this we need to step away from hardcoding product rate plan ids within CustomersDot and transfer the classification knowledge to the ProductCatalog (by adding CustomersDot metadata to it in the form of custom fields) to be able to resolve these sets dynamically from the `LocalRecord`s on demand. - -## Proposal - -CustomersDot currently [has a local cache](https://gitlab.com/gitlab-org/customers-gitlab-com/-/merge_requests/1762) of the Zuora's Product Catalog via the [IronBank](https://github.com/zendesk/iron_bank) gem and [its LocalRecord extension](https://gitlab.com/gitlab-org/customers-gitlab-com/blob/45f5dedbb4fa803d19827472214ea0b5b0ce1861/lib/gem_extensions/iron_bank/local_records.rb#L1). - -At the moment we refresh this cache manually when we are notified that a new change exists in Zuora that is of interest for CustomersDot: - -```mermaid -sequenceDiagram - participant CustomersDot - participant Zuora - Note left of CustomersDot: ProductCatalog refresh is triggered
via Rails console - CustomersDot->>Zuora: GET Product Catalog - Zuora->>CustomersDot: Respond with Product Catalog - CustomersDot->>CustomersDot: Cache copy of Product Catalog in LocalRecord database - Note left of CustomersDot: For future Product Catalog queries
LocalRecords are used. - CustomersDot->>CustomersDot: GET Zuora::Product - CustomersDot->>CustomersDot: GET Zuora::ProductRatePlan - Note right of Zuora: Product information was updated - CustomersDot->>CustomersDot: GET Zuora::ProductRatePlanCharge - CustomersDot->>CustomersDot: GET Zuora::ProductRatePlanChargeTier - Note left of CustomersDot: CustomersDot is unaware of Zuora changes
until next deployment -``` - -### Iteration 1 - -Keep Product Catalog in sync with Zuora so at any point in time: - -```mermaid -sequenceDiagram - participant CustomersDot - participant Zuora - Note right of Zuora: Product information was updated - Zuora->>CustomersDot: Notification on Product update - CustomersDot->>Zuora: GET Product Catalog - Zuora->>CustomersDot: Respond with Product Catalog - CustomersDot->>CustomersDot: Refresh Product Catalog cache (LocalRecord database) - Note left of CustomersDot: CustomersDot Product Catalog
cache is up to date with Zuora -``` - -### Iteration 2 - -Transfer CustomersDot's classification knowledge to the Zuora Product Catalog (by adding CustomersDot metadata to it in the form of custom fields) to be able to resolve `ProductRatePlan`s directly from the `LocalRecord`s on demand. - -We are proposing to add these custom fields to the Product Catalog: - -```mermaid ---- -title: Zuora Product Catalog Proposed Additions ---- -erDiagram - "Product" ||--|{ "ProductRatePlan" : "has many" - "ProductRatePlan" ||--|{ "ProductRatePlanCharge" : "has many" - "ProductRatePlanCharge" ||--|{ "ProductRatePlanChargeTier" : "has many" - - "Product" { - enum Tier__c - enum DeploymentType__c - } - - "ProductRatePlan" { - boolean WebDirect__c - } -``` - -### Iteration 3 - -Use Zuora custom metadata (introduced in iteration 2) to resolve `ProductRatePlan`s directly from the Zuora Product Catalog, and remove the `Plan` constants that are preventing the full cache refresh. - -## Design and implementation details - -### Iteration 1 - -**(Iteration 1) Product Catalog is in sync with Zuora** - -- Cron job to refresh the Product Catalog every day as a first iteration and add immediate value. -- Create a Finance Systems issue to request: - - New custom event for when custom fields are updated for records from the Zuora Product Catalog. - - | Base object | Custom event name | - | ------------------------- | -------------------------------------- | - | Product | CatalogProductUpdate | - | ProductRatePlan | CatalogProductRatePlanUpdate | - | ProductRatePlanCharge | CatalogProductRatePlanChargeUpdate | - | ProductRatePlanChargeTier | CatalogProductRatePlanChargeTierUpdate | - - - New callout under the `Billing` component for when records from the Zuora Product Catalog are added, deleted or updated. -- Create a new controller in CustomersDot based on `ActionController::Metal` to not include redundant middlewares, callbacks, additional Rails stuff and make this controller as fast as possible. - - ```ruby - module Zuora - class WebHooksController < ActionController::Metal - feature_category :platform - - def create - # Step 1. Validate and save an incoming webhook payload into the database - # Step 2. Kick of the SyncProductCatalogJob - head :ok - end - end - end - ``` - -Ensure a debouncing strategy for `SyncProductCatalogJob` in case we get too many Product Catalog update notifications in a short period of time. Initially we can delay its execution for 5 minutes and ensure no new job is enqueued if one is already in the queue. - -**(Iteration 2) Transfer CustomersDot's classification knowledge to the Zuora Product Catalog** - -_All these changes require a Finance Systems issue._ - -- Review existing field `Zuora::Product#category` to make sure it is properly set for all Products. Possible values: `[null, "Base Products", "Add On Services", "Miscellaneous Products"]`. -- Add new custom field `Zuora::ProductRatePlan#web_direct` to be a `boolean` - - true: the plan is available for self service - - false: the plan is not available for self service -- Add new custom field `Product#tier` to be an `enum` (multiselect). Possible values: `[premium, ultimate, starter, bronze, silver, gold, free, null]` -- Add new custom field `Product#deployment_type` to be an `enum` (multiselect). Possible values: `[self_managed, dedicated, gitlab_dot_com]` - -For each added field: the value in Zuora has to be aligned with CustomersDot classification given by `Zuora::ProductRatePlan` ids current grouping in the `Plan` class. - -NOTE: -There is a [current effort](https://gitlab.com/gitlab-com/business-technology/enterprise-apps/intake/-/issues/44) to add some of these fields to Zuora so we might be able to reuse these. If we are reusing these we need to double check that the value in Zuora and CustomersDot classification are aligned -for each. - -**(Iteration 3) Use this Zuora custom metadata to resolve `ProductRatePlan`s directly from the Zuora Catalog** - -- Create scopes to fetch `Zuora::Product`s and `Zuora::ProductRatePlan`s based on the metadata introduced in Iteration 2. Possible scopes: - - `self_managed` - - `dedicated` - - `gitlab_dot_com` - - `base_products` - - `add_ons` - - `web_direct` - - `sales_assisted` - - `ultimate` - - `premium` - - `active` (based on the effective start / end dates) -- Replace the usage of `Plan` constants that represent a collection of records that meet a given classification by a call to a method that loads the same collection from LocalRecords using the implemented scopes e.g. `ALL_ULTIMATE_SM_PLANS` can be replaced with `Zuora::Product.self_managed.ultimate.flat_map(&:product_rate_plans).map(&:id)`. This step can be done in iteration until all constants are replaced. Depending on how complex each iteration is we can decide if a feature flag is required or not. + + + + diff --git a/doc/architecture/blueprints/cells/topology_service.md b/doc/architecture/blueprints/cells/topology_service.md index 4586078e7ac..3d587aa4ed8 100644 --- a/doc/architecture/blueprints/cells/topology_service.md +++ b/doc/architecture/blueprints/cells/topology_service.md @@ -432,7 +432,7 @@ sequenceDiagram [Spanner](https://cloud.google.com/spanner) will be a new data store introduced into the GitLab Stack, the reasons we are going with Spanner are: -1. It supports Multi-Regional read-write access with a lot less operations when compared to PostgreSQL helping with out [regional DR](../disaster_recovery/index.md) +1. It supports Multi-Regional read-write access with a lot less operations when compared to PostgreSQL helping with out [regional DR](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/disaster_recovery/) 1. The data is read heavy not write heavy. 1. Spanner provides [99.999%](https://cloud.google.com/spanner/sla) SLA when using Multi-Regional deployments. 1. Provides consistency whilst still being globally distributed. @@ -547,7 +547,7 @@ However looking at the [performance documentation](https://cloud.google.com/span ## Disaster Recovery -We must stay in our [Disaster Recovery targets](../disaster_recovery/index.md#dr-implementation-targets) for the Topology Service. +We must stay in our [Disaster Recovery targets](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/disaster_recovery/#dr-implementation-targets) for the Topology Service. Ideally, we need smaller windows for recovery because this service is in the critical path. The service is stateless, which should be much easier to deploy to multiple regions using [runway](https://gitlab.com/groups/gitlab-com/gl-infra/-/epics/1206). diff --git a/doc/architecture/blueprints/ci_build_speed/benchmark.md b/doc/architecture/blueprints/ci_build_speed/benchmark.md index 830db84ead8..c175be1b34e 100644 --- a/doc/architecture/blueprints/ci_build_speed/benchmark.md +++ b/doc/architecture/blueprints/ci_build_speed/benchmark.md @@ -1,84 +1,11 @@ --- -status: ongoing -creation-date: "2024-01-12" -authors: [ "@grzesiek" ] -coach: "@grzesiek" -approvers: [ "@gabrielengel_gl"] +redirect_to: 'https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/ci_build_speed/benchmark/' +remove_date: '2025-07-08' --- -# CI Build Speed Benchmarking Framework +This document was moved to [another location](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/ci_build_speed/benchmark/). -In order to understand how GitLab CI performs in terms of CI build speed, we -plan to build CI Build Speed Benchmarking Framework. - -## Benchmark - -In order to run the benchmark, we will: - -1. Install the benchmarking tool. -1. Start the tool. -1. Runs scenarios. -1. Report results back to GitLab data warehouse. - -In the first iteration, we will focus on measuring the speed of GitLab CI, GitHub Actions, and CircleCI. - -## Principles - -There are a few design principles we should abide by: - -1. Make it CI-platform agnostic. Can run on any Continuous Integration platform. -1. Do not depend on any specific technology that might not be available on some platforms. -1. Easy installation setup, not requiring many dependencies. Zero-dependency would be ideal. -1. Send results back to GitLab through an HTTP request, unless there is a better way. -1. Read as much data about the environment running a build and send details in the telemetry. - -## Benchmarking: Client Side - -The benchmarking tool should be able to measure every step of CI build -execution: - -1. Time from build requested to scenario execution started. -1. Monotonic time to execute each of the steps of the scenario. -1. Thread time to execute each of the steps of the scenario. -1. Time required to report results back to GitLab. - -Ideally the tool could collect this data in the -[Open Telemetry Tracing](https://opentelemetry.io/docs/specs/otel/trace/api/) -format. - -### Go-based tool - -One of the solutions that could meet the requirements / principles listed -above, could be a Go-based binary, which would be installed on different CI -platform using `wget` / `curl` or in a different convinient way. The benefits -of using the binary are: - -1. Easy installation method, without the need to use containers. -1. Few external dependencies for a statically-linked binary. -1. Many libraries available, for tracing or HTTP / API integrations. -1. Multi-threaded execution mode that broadens benchmarking scope. -1. Expressive language that can make it easier to maintain the scenarios. - -### Benchmarking: Server Side - -## Pipelines scheduler - -In order to run the benchmark a new build / pipeline / job will have to be -started on a continuous integration platform under test. Some platforms support -scheduled pipelines, but this could make it difficult to measure the build -start-up time. On alternative to consider during the implementation is to start -pipelines using API trigger endpoints. Most of the CI platforms support this -way of running pipelines, and we could pass the start-up time / pipeline -creation request time in an argument, that then will be consumed by the -benchmarking tool, and forwarded to the data warehouse along with the build -benchmark telemetry. - -## Data warehouse - -The server side, that will receive benchmarking telemetry, will eventually need -to forward the data to a data warehouse, in which we will be able to visualize -results, like Kibana or our Observability / Tracing tooling. - -Before doing that, it could be advisable to persist the payload in object -storage, just in case we need to migrate historical entries to a different data -warehouse later on. + + + + diff --git a/doc/architecture/blueprints/ci_build_speed/index.md b/doc/architecture/blueprints/ci_build_speed/index.md index bd94cca430f..e819ae2c0b9 100644 --- a/doc/architecture/blueprints/ci_build_speed/index.md +++ b/doc/architecture/blueprints/ci_build_speed/index.md @@ -1,73 +1,11 @@ --- -status: ongoing -creation-date: "2024-01-12" -authors: [ "@grzesiek" ] -coach: "@grzesiek" -approvers: [ "@gabrielengel_gl"] +redirect_to: 'https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/ci_build_speed/' +remove_date: '2025-07-08' --- - +This document was moved to [another location](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/ci_build_speed/). -# CI/CD Build Speed - -## Summary - -GitLab CI is a Continuous Integration platform which is widely used to run a -variety of jobs, builds, pipelines. It was [integrated into GitLab in September 2015](https://about.gitlab.com/releases/2015/09/22/gitlab-8-0-released/) -and has become [one of the most beloved CI/CD solutions](https://about.gitlab.com/blog/2017/09/27/gitlab-leader-continuous-integration-forrester-wave/). - -With years we've added a lot of new features and code to the GitLab CI -platform. In order to retain the "one of the most beloved solutions" status, we -also need keep attention to making it fast, reliable and secure. This design -doc is describing the path towards the former: making GitLab CI fast by -improving CI build speed. - -## Goals - -1. Establish a CI Speed Benchmark, used to compare GitLab CI to other platforms. -1. Build CI Benchmark Framework to measure the GitLab CI speed over the long term. -1. Describe next steps for improving GitLab CI Build Speed. - -## Proposal - -### CI Speed Benchmark - -First, we plan to build a [CI Speed Benchmark](benchmark.md) solution, that -will allow us to run specific scenarios on various CI/CD platform and ingest -results into our data warehouse. - -This will make it possible to define a baseline of the CI Build Speed for many -different scenarios and track the progress we, and other providers, are making -over time. - -The core part of this goal is to define a set of scenarios that will allow us -to build a proxy metrics for build speed. For example, we could run following -scenarios: - -1. Time to first byte of build log for `echo "Hello World"` build. -1. Time to result to perform a CPU-intensive cryptographic operation. -1. Time to result to perform a memory-intensive for a given amount of bytes. -1. Time to result to build a Linux kernel. - -The scenarios should be idempotent and deterministic. - -In the first iteration, we will only focus on the total job execution time, and not go into detail e.g. comparing specific startup times. - -### CI Benchmark Framework - -Once we define scenarios that we want to implement, we should build a -[CI Benchmark Framework](benchmark.md). The framework will be used to run -scenarios in a Continuous Integration environment, and to send the results back -to our data warehouse, for analysis and comparison. - -The main principles behind design choices for the framework, are: - -1. Make it CI-platform agnostic. Can run on any Continuous Integration platform. -1. Do not depend on any specific technology that might not be available on some platforms. -1. Easy installation setup, not requiring many dependencies. Zero-dependency would be ideal. -1. Send results back to GitLab through an HTTP request, unless there is a better way. - -#### Improve CI Build Speed - -Once we can measure CI Build Speed, improving it can be possible. We will -define the next steps for improving the speed once we have initial results. + + + + diff --git a/doc/architecture/blueprints/custom_models/index.md b/doc/architecture/blueprints/custom_models/index.md index 9a0f12bbe9a..c088e845615 100644 --- a/doc/architecture/blueprints/custom_models/index.md +++ b/doc/architecture/blueprints/custom_models/index.md @@ -1,249 +1,11 @@ --- -status: proposed -creation-date: "2024-03-29" -authors: [ "@sean_carroll", "@eduardobonet" ] -coach: "@jessieay" -approvers: [ "@susie.bee", "@m_gill" ] -owning-stage: "~devops::ai-powered" -participating-stages: [] +redirect_to: 'https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/custom_models/' +remove_date: '2025-07-08' --- - - +This document was moved to [another location](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/custom_models/). -# Self-Hosted Model Deployment - -This Blueprint describes support for customer self-deployments of Mistral LLMs as a backend for GitLab Duo features, as an alternative to the default Vertex or Anthropic models offered on GitLab Dedicated and .com. This initiative supports both internet connected and air-gapped GitLab deployments. - -## Motivation - -Self-hosted LLM models allow customers to manage the end-to-end transmission of requests to enterprise-hosted LLM backends for [GitLab Duo features](../../../user/ai_features.md), and keep all requests within their enterprise network. GitLab provides as a default LLM backends of Google Vertex and Anthropic, hosted externally to GitLab. GitLab Duo feature developers are able to access other LLM choices via the AI Gateway. More details on model and region information can be [found here](https://gitlab.com/groups/gitlab-org/-/epics/13024#current-feature-outline). - -### Goals - -Self-Managed models serve sophisticated customers capable of managing their own LLM infrastructure. GitLab provides the option to connect supported models to LLM features. Model-specific prompts and GitLab Duo feature support is provided by the self-hosted models feature. - -- Choice of LLM models -- Ability to keep all data and request/response logs within their own domain -- Ability to select specific GitLab Duo Features for their users -- Non-reliance on the .com AI Gateway - -### Non-Goals - -Other features that are goals of the Custom Models group and which may have some future overlap are explicitly out of scope for the current iteration of this blueprint. These include: - -- Local Models -- RAG -- Fine Tuning -- GitLab managed hosting of open source models, other than the current supported third party models. -- Bring Your Own API Key (BYOK) - -## Proposal - -GitLab will provide support for specific LLMs hosted in a customer's infrastructure. The customer will self-host the AI Gateway, and self-host one or more LLMs from a predefined list. Customers will then configure their GitLab instance for specific models by LLM feature. A different model can be chosen for each GitLab Duo feature. - -This feature is accessible at the instance-level and is intended for use in GitLab Self-Managed instances. - -Self-hosted model deployment is a [GitLab Duo Enterprise Add-on](https://about.gitlab.com/pricing/). - -## Design and implementation details - -### Component Architecture - -```mermaid -graph LR - a1 --> c1 - a2 --> b1 - b1 --> c1 - b3 --> b1 - b4 --> b1 - c1 --> c2 - c2 --> c3 - c3 --> d1 - d1 --> d2 - - subgraph "User" - a1[IDE Request] - a2[Web / CLI Request] - end - - subgraph "Self-Managed GitLab" - b1[GitLab Duo Feature] <--> b2[Model & Feature-specific
Prompt Retrieval] - b3[GitLab Duo Feature
Configuration] - b4[LLM Serving Config] - end - - subgraph "Self-Hosted AI Gateway" - c1[Inbound API interface] - c2[Model routing] - c3[Model API interface] - end - - subgraph "Self-Hosted LLM" - d1[LoadBalancer] - d2[GPU-based backend] - end -``` - -#### Diagram Notes - -- **User request**: A GitLab Duo Feature is accessed from one of three possible starting points (Web UI, IDE or Git CLI). The IDE communicates directly with the AI Gateway. -- **LLM Serving Config**: The existence of a customer-hosted model along with its connectivity information is declared in GitLab Rails and exposed to the AI Gateway with an API. -- **GitLab Duo Feature Configuration**: For each supported GitLab Duo feature, a user may select a supported model and the associated prompts are automatically loaded. -- **Prompt Retrieval**: GitLab Rails chooses and processes the correct prompt(s) based on the GitLab Duo Feature and model being used -- **Model Routing**: The AI Gateway routes the request to the correct external AI endpoint. The current default for GitLab Duo features is either Vertex or Anthropic. If a Self-Managed model is used, the AI Gateway must route to the correct customer-hosted model's endpoint. The customer-hosted model server details are the `LLM Serving Config` and retrieved from GitLab Rails as an API call. They may be cached in the AI Gateway. -- **Model API interface**: Each model serving has its own endpoint signature. The AI Gateway needs to be able to communicate using the right signature. We will support commonly supported model serving formats such as the OpenAI API spec. - -### Configuration - -Configuration is set at the GitLab instance-level; for each GitLab Duo feature a drop-down list of options will be presented. The following options will be available: - -- Self-hosted model 1 -- Self-hosted model n -- Feature Inactive - -In the initial implementation a single self-hosted Model will be supported, but this will be expanded to a number of GitLab-defined models. - -### AI Gateway Deployment - -Customers will be required to deploy a local instance of the AI Gateway in their own infrastructure. The initial means to do this is via Docker container, as described [in this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/452489). - -Self-hosted Runway will be the preferred delivery mechanism for deploying the AI Gateway. Future options, in order of preference are: - -- Runway [discussion](https://gitlab.com/gitlab-com/gl-security/security-assurance/fedramp/fedramp-certification/-/issues/452#note_1832261170) -- Kubernetes deployment [issue](https://gitlab.com/gitlab-org/gitlab/-/issues/452490) -- Omnibus packaging [issue](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/8467) - -It should be noted that deployment by Docker container is a temporary measure only, and will be superceeded by the three options listed above. - -### Prompt Support - -For each supported model and supported GitLab Duo feature, prompts will be developed and evaluated by GitLab. They will be baked into the Rails Monolith source code. - -When the standard prompts are migrated into either the AI Gateway or a prompt template repository (direction is to be determined), the prompts supporting self-hosted models will also be migrated. - -### Supported LLMs - -- [Mistral-7B-v0.1](https://huggingface.co/mistralai/Mistral-7B-v0.1) -- [Mixtral-8x7B-instruct](https://huggingface.co/mistralai/Mixtral-8x7B-Instruct-v0.1) -- [Mixtral 8x22B](https://huggingface.co/mistral-community/Mixtral-8x22B-v0.1) -- [CodeGemma 7B IT](https://huggingface.co/google/codegemma-7b-it) -- [CodeGemma 2B](https://huggingface.co/google/codegemma-2b) - -Installation instructions will be added to the Developer documentation. [issue](https://gitlab.com/gitlab-org/gitlab/-/issues/452509) - -### GitLab Duo Feature Support - -| Feature | Default Model | Mistral 7B | Mixtral-8x7B | Mixtral 8x22B | CodeGemma 7B | CodeGemma 2B | -|---------------------|------------------------------|------------|--------------|----------------|--------------|--------------| -| GitLab Duo Chat | Anthropic Claude-3 | 🔎 | 🔎 | 🔎 | 🔎 | 🔎 | -| Code Completion | Vertex AI Codey code-gecko | 🚫 | 🚫 | 🚫 | ✅ | ✅ | -| Code Generation | Anthropic Claude-3 | ✅ | ✅ | ✅ | 🔎 | 🔎 | -| Git Suggestions | Vertex AI Codey codechat-bison | 🔎 | 🔎 | 🔎 | 🔎 | 🔎 | -| Discussion Summary | Vertex AI Codey text-bison | 🔎 | 🔎 | 🔎 | 🔎 | 🔎 | -| Issue Description Generation | Anthropic Claude-2 | 🔎 | 🔎 | 🔎 | 🔎 | 🔎 | -| Test Generation | Anthropic Claude-2 | 🔎 | 🔎 | 🔎 | 🔎 | 🔎 | -| MR template population | Vertex AI Codey text-bison | 🔎 | 🔎 | 🔎 | 🔎 | 🔎 | -| Suggested Reviewers | GitLab In-House Model | 🔎 | 🔎 | 🔎 | 🔎 | 🔎 | -| Merge request summary | Vertex AI Codey text-bison | 🔎 | 🔎 | 🔎 | 🔎 | 🔎 | -| Code review summary | Vertex AI Codey text-bison | 🔎 | 🔎 | 🔎 | 🔎 | 🔎 | -| Vulnerability explanation | Vertex AI Codey text-bison Anthropic | 🚫 | 🔎 | 🔎 | 🔎 | 🔎 | -| Vulnerability resolution | Vertex AI Codey code-bison | 🔎 | 🔎 | 🔎 | 🔎 | 🔎 | -| Code explanation | Vertex AI Codey codechat-bison | 🔎 | 🔎 | 🔎 | 🔎 | 🔎 | -| Root cause analysis | Vertex AI Codey text-bison | 🔎 | 🔎 | 🔎 | 🔎 | 🔎 | -| Value stream forecasting | GitLab In-House Model | 🔎 | 🔎 | 🔎 | 🔎 | 🔎 | - -- The `Suggested Reviewers` and `Value stream forecasting` models are Convolutional Neural Networks (CNNs) developed in-house by GitLab. -- ✅ `Supported` : means the GitLab Duo feature is supported for this model -- 🚫 `Not Supported`: the GitLab Duo feature is not supported for this model -- 🔎 `To be Evaluated`: research is needed to determine if this feature will be supported -- GitLab Duo Chat can also use Vertex AI Codey textembedding-gecko -- Vulnerability explanation can fall back to Claude-2 if degraded performance - -#### RAG / Duo Chat tools - -Most of the tools available to Duo Chat behave the same for self-hosted models as -they do in the GitLab-hosted AI Gateway architecture. Below are the expections: - -##### Duo Documentation search - -Duo documentation search performed through the GitLab-managed AI Gateway (`cloud.gitlab.com`) relies on [VertexAI Search](../gitlab_rag/index.md), -which is not available for air-gapped customers. As a replacement, only within the scope of -self-hosted, air-gapped customers, an index of GitLab documentation has been provided -within the self-hosted AI Gateway. - -This index is an SQLite database that allows for full-text search. An index is -generated for each GitLab version and saved into a generic package registry. The index that matches the customer's GitLab version is then downloaded by the self-hosted AI Gateway. - -Using a local index does bring some limitations: - -- [BM25 search](https://en.wikipedia.org/wiki/Okapi_BM25) performs worse in the presence of typos, and the performance also depends on how the index was built -- Given that the indexed tokens depend on how the corpus was cleaned (stemming, tokenisation, punctuation), the same text cleaning steps need to be applied to the user query for it to properly match the indexes -- Local search diverges from other already implemented solutions, and creates a split between self-managed and GitLab-hosted instances of the AI Gateway. - -Over time, we intend to replace this solution with a self-hosted Elasticsearch/OpenSearch -alternative, but as of now, the percentage of self-hosted customers that have -[Elasticsearch enabled is low](https://gitlab.com/gitlab-org/gitlab/-/issues/438178#current-adoption). - -For further discussion, refer to the [proof of concept](https://gitlab.com/gitlab-org/modelops/applied-ml/code-suggestions/ai-assist/-/merge_requests/974). - -**Index creation** - -Index creation and implementation is being worked on as part of [this epic](https://gitlab.com/groups/gitlab-org/-/epics/14282) - -**Evaluation** - -Evaluation of the local-search is being worked on as part of [this epic](https://gitlab.com/gitlab-org/gitlab/-/issues/468666). - -#### LLM-hosting - -Customers will self-manage LLM hosting. We provided limited documentation on how -customers can host their own [LLMs](../../../administration/self_hosted_models/install_infrastructure.md) - -#### GitLab Duo License Management - -The Self-Managed GitLab Rails will self-issue a token (same process as for .com) that the local AI Gateway can verify, to guarantee that cross-service communication is secure. [Details](https://gitlab.com/gitlab-org/gitlab/-/issues/444216) - -### System Architectures - -At this time a single system architecture only is supported. See the Out of Scope section for discussion on alternatives. - -#### Self-Managed GitLab with self-hosted AI Gateway - -This system architecture supports both a internet-connected GitLab and AI Gateway, or can be run in an air-gapped environment. Customers install a self-managed AI Gateway within their own infrastructure. The long-term vision for such installations is via Runway, but until that is available a Docker-based install will be supported. - -Self-Managed customers who deploy a self-managed AI Gateway will only be able to access self-hosted models at this time. Future work around [Bring Your Own Key](https://gitlab.com/groups/gitlab-org/-/epics/12973) may change that in the future. - -### Development Environment - -Engineering documentation will be produced on how to develop this feature, with work in progress on: - -- [Include AI Gateway in GDK](https://gitlab.com/gitlab-org/gitlab-development-kit/-/issues/2025) -- [Developer setup for self-hosted models](https://gitlab.com/gitlab-org/gitlab/-/issues/452509) -- [Centralized Evaluation Framework](https://gitlab.com/gitlab-org/modelops/ai-model-validation-and-research/ai-evaluation/prompt-library/-/tree/main) - -### Out of scope - -- It would be possible to support customer self-hosted models within a customer's infrastructure for dedicated or .com customers, but this is not within scope at this time. -- Support for models other than those listed in the Supported LLMs section above. -- Support for modified models. - -#### Out of scope System Architectures - -There are no plans to support these system architectures at this time, this could change if there was sufficient customer demand. - -##### Self-Managed GitLab with .com AI Gateway - -In this out-of-scope architecture a self-managed customer continues to use the .com hosted AI gateway, but points back to self-managed models. - -##### .com GitLab with .com AI Gateway - -In this out-of-scope architecture .com customers point to self-managed models. This topology might be desired if there were better quality of results for a given feature by a specific model, or if customers could improve response latency by using their own model-serving infrastructure. - -##### GitLab Dedicated - -Support will not be provided for Dedicated customers to use a self-hosted AI Gateway and self-hosted models. Dedicated customers who use GitLab Duo features can access them via the .com AI Gateway. If there is customer demand for self-managed models for Dedicated customers, this can be considered in the future. - -##### Externally hosted models - -It is expected that customers will self-host models. + + + + diff --git a/doc/architecture/blueprints/disaster_recovery/index.md b/doc/architecture/blueprints/disaster_recovery/index.md index 42ddc5ffbe5..0d4faad2d5d 100644 --- a/doc/architecture/blueprints/disaster_recovery/index.md +++ b/doc/architecture/blueprints/disaster_recovery/index.md @@ -1,87 +1,11 @@ --- -status: ongoing -creation-date: "2024-01-29" -authors: [ "@jarv" ] -coach: -approvers: [ ] +redirect_to: 'https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/disaster_recovery/' +remove_date: '2025-07-08' --- -# Disaster Recovery +This document was moved to [another location](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/disaster_recovery/). -This document is a work-in-progress and proposes architecture changes for the GitLab.com SaaS. -The goal of these changes are to maintain GitLab.com service continuity in the case a regional or zonal outage. - -- A **zonal recovery** is required when all resources are unavailable in one of the three availability zones in `us-east1` or `us-central1`. -- A **regional recovery** is required when all resources become unavailable in one of the regions critical to operation of GitLab.com, either `us-east1` or `us-central1`. - -## Services not included in the current DR strategy for FY24 and FY25 - -We have limited the scope of DR to services that support primary services (Web, API, Git, Pages, Sidekiq, CI, and Registry). -These services tie directly into our overall [availability score](https://dashboards.gitlab.net/d/general-slas/general3a-slas?orgId=1) (internal link) for GitLab.com. - -For example, DR does not include the following: - -- AI services including code suggestions -- Error tracking and other observability services like tracing -- CustomersDot, responsible for billing and new subscriptions -- Advanced Search - -## DR Implementation Targets - -The FY24 targets were: - -| | Recovery Time Objective (RTO) | Recovery Point Objective (RPO) | -|--------------|-------------------------------|--------------------------------| -| **Zonal** | 2 hours | 1 hour | -| **Regional** | 96 hours | 2 hours | - -The FY25 targets before cell architecture are: - -| | Recovery Time Objective (RTO) | Recovery Point Objective (RPO) | -|--------------|-------------------------------|--------------------------------| -| **Zonal** | 0 minutes | 0 minutes | -| **Regional** | 48 hours | 0 minutes | - -**Note**: While the RPO values are targets, they cannot be met exactly due to the limitations of regional bucket replication and replication lag of Gitaly and PostgreSQL. - -## Current Recovery Time Objective (RTO) and Recovery Point Objective (RPO) for Zonal Recovery - -We have not yet simulated a full zonal outage on GitLab.com. -The following are RTO/RPO estimates based on what we have been able to test using the [disaster recovery runbook](https://gitlab.com/gitlab-com/runbooks/-/tree/master/docs/disaster-recovery?ref_type=heads). -It is assumed that each service can be restored in parallel. -A parallel restore is the only way we are able to meet the FY24 RTO target of 2 hours for a zonal recovery. - -| Service | RTO | RPO | -| --- | --- | --- | -| PostgreSQL | 1.5 hr | <=5 min | -| Redis [^1] | 0 | 0 | -| Gitaly | 30 min | <=1 hr | -| CI | 30 min | not applicable | -| Load balancing (HAProxy) | 30 min | not applicable | -| Frontend services (Web, API, Git, Pages, Registry) [^2] | 15 min | 0 | -| Monitoring (Prometheus, Thanos, Grafana, Alerting) | 0 | not applicable | -| Operations (Deployments, runbooks, operational tooling, Chef) [^3] | 30 min | 4 hr | -| PackageCloud (distribution of packages for self-managed) | 0 | 0 | - -## Current Recovery Time Objective (RTO) and Recovery Point Objective (RPO) for Regional Recovery - -Regional recovery requires a complete rebuild of GitLab.com using backups that are stored in multi-region buckets. -The recovery has not yet been validated end-to-end, so we don't know how long the RTO is for a regional failure. -Our target RTO for FY25 is to have a procedure to recover from a regional outage in under 48 hours. - -The following are considerations for choosing multi-region buckets over dual-region buckets: - -- We operate out of a single region so multi-region storage is only used for disaster recovery. -- Although Google recommends dual-region for disaster recovery, dual-region is [not an available storage type for disk snapshots](https://cloud.google.com/compute/docs/disks/snapshots#selecting_a_storage_location). -- To mitigate the bandwidth limitation of multi-region buckets, we spread Gitaly VMs infra across multiple projects. - -## Proposals for Regional and Zonal Recovery - -- [Regional](regional.md) -- [Zonal](zonal.md) - ---- - - [^1]: Most of the Redis load is on the primary node, so losing replicas should not cause any service interruption - [^2]: We setup maximum replicas in our Kubernetes clusters servicing front-end traffic, this is done to avoid saturating downstream dependencies. For a zonal failure, a cluster reconfiguration is necessary to increase these maximums. - [^3]: There is a 4 hr RPO for Operations because Chef is an single point of failure in a single availability zone and our restore method uses disk snapshots, taken every 4 hours. While most of our Chef configuration is also stored in Git, some data (like node registrations) are only stored on the server. + + + + diff --git a/doc/architecture/blueprints/disaster_recovery/regional.md b/doc/architecture/blueprints/disaster_recovery/regional.md index eeada087bbc..ce680cfbd96 100644 --- a/doc/architecture/blueprints/disaster_recovery/regional.md +++ b/doc/architecture/blueprints/disaster_recovery/regional.md @@ -1,163 +1,11 @@ --- -status: ongoing -creation-date: "2024-01-29" -authors: [ "@jarv" ] -coach: -approvers: [ ] +redirect_to: 'https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/disaster_recovery/regional/' +remove_date: '2025-07-08' --- -# Regional Recovery +This document was moved to [another location](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/disaster_recovery/regional/). -## Improving the Recovery Time Objective (RTO) and Recovery Point Objective (RPO) for Regional Recovery - -The following list the top challenges that limit our ability to drive `RTO` to 48 hours for a regional recovery. - -1. We have a large amount of legacy infrastructure managed using Chef. This configuration has been difficult for us to manage and would require a large a mount of manual copying and duplication to create new infrastructure in an alternate region. -1. Operational infrastructure is located in a single region, `us-central1`. For a regional failure in this region, it requires rebuilding the ops infrastructure with only local copies of runbooks and tooling scripts. -1. Observability is hosted in a single region. -1. The infrastructure (`dev.gitlab.org`) that builds Docker images and packages is located in a single region, and is a single point of failure. -1. There is no launch-pad that would allow us to get a head-start on a regional recovery. Our IaC (Infrastructure-as-Code) does not allow us to switch regions for provisioning. -1. We don't have confidence that Google can provide us with the capacity we need in a new region, specifically the large amount of SSD necessary to restore all of our customer Git data. -1. We use [Global DNS](https://cloud.google.com/compute/docs/internal-dns) for internal DNS making it difficult to use multiple instances with the same name across multiple regions, we also don't incorporate regions into DNS names for our internal endpoints (for example dashboards, logs, etc). -1. If we deploy replicas in another region to reduce RPO we are not yet sure of the latency or cloud spend impacts. -1. We have special/negotiated Quota increases for Compute, Network, and API with the Google Cloud Platform only for a single region, we have to match these quotas in a new region, and keep them in sync. -1. We have not standardized a way to divert traffic at the edge from 1 region to another. -1. In monitoring, and configuration we have places where we hardcode the region to `us-east1`. - -## Regional recovery work-streams - -The first step of our regional recovery plan creates new infrastructure in the recovery region that involves a large number of manual steps. -To give us a head-start on recovery, we propose a "regional bulkhead" deployment in a new GCP region. - -A "regional bulkhead" meets the following requirements: - -1. A specific region is allocated. -1. Quotas are set and synced so that we can duplicate all of us-east1 in the new region. -1. Subnets are allocated or reserved in the same VPC for us-east1. -1. Some infrastructure is deployed where it makes sense to lower RTO, while keeping cloud-spend low. - -The following are work-streams that can be done mostly in parallel. -The end-goal of the regional recovery is to have a bulkhead that has the basic scaffolding for deployment in the alternate region. -This bulkhead can be used as a launching pad for a full data restore from `us-east1` to the alternate region. - -### Select an alternate region - -We are going with **`us-central1`**. Discussion for this was done in - -- Dependencies: none -- Teams: Ops - -The following are considerations that need to be made when selecting an alternate region for DR: - -1. Ensure there is enough capacity to meet compute usage. -1. Network and network latency requirements, if any. -1. Feature parity between regions. - -### Deploy Kubernetes clusters supporting front-end services in a new region with deployments - -- Dependencies: [External front-end load balancing](#external-front-end-load-balancing) -- Teams: Ops, Foundations, Delivery - -GitLab.com has Web, API, Git, Git HTTPs, Git SSH, Pages, and Registry as front-end services. -All of these services are run in 4 Kubernetes clusters deployed in `us-east1`. -These services are either stateless or use multi-region storage buckets for data. -In the case of a failure in `us-east1`, we would need to rebuild these clusters in the alternate region and set them up for deployments. - -### Switch from Global to Zonal DNS - -- Dependencies: None -- Teams: Gitaly - -Gitaly VMs are single points of failure that are deployed in `us-east1`. -The internal DNS naming of the nodes have the following convention: - -```plaintext -gitaly-01-stor-gprd.c.gitlab-gitaly-gprd-ccb0.internal - ^ name ^ project -``` - -By switching to zonal DNS, we can change the internal DNS entries so they have the zone in the DNS name: - -```plaintext -gitaly-01-stor-gprd.c.us-east1-b.gitlab-gitaly-gprd-ccb0.internal - ^ name ^ zone ^ project -``` - -Allowing us to keep the same name when recovering into a new region or zone. - -```plaintext -gitaly-01-stor-gprd.c.us-east1-b.gitlab-gitaly-gprd-ccb0.internal -gitaly-01-stor-gprd.c.us-east4-a.gitlab-gitaly-gprd-ccb0.internal -``` - -For fleets of VMs outside of Kubernetes, these names allow us to have the same node names in the recovery region. - -### Gitaly - -- Dependencies: [Switch from Global to Zonal DNS](#switch-from-global-to-zonal-dns) (optional, but desired) -- Teams: Gitaly, Ops, Foundations - -Restoring the entire Gitaly fleet requires a large number of VMs deployed in the alternate region. -It also requires a lot of bandwidth because restore is based on disk snapshots. -To ensure a successful Gitaly restore, quotas need to be synced with us-east1 and there needs to be end-to-end validation. - -### PostgreSQL - -- Dependencies: [Improve Chef provisioning time by using preconfigured golden OS images](zonal.md#improve-chef-provisioning-time-by-using-preconfigured-golden-os-images) (optional, but desired), local backups in the standby region (data disk snapshot and `WAL` archiving). -- Teams: Database Reliability, Ops - -The configuration for Patroni provisioning only allows a single region per cluster. -There is networking infrastructure, Consul, and load balancers that need to be setup in the alternate region. -We may consider setting up a "cascaded cluster" for the databases to improve recovery time for replication. - -### Redis - -- Dependencies: [Improve Chef provisioning time by using preconfigured golden OS images](zonal.md#improve-chef-provisioning-time-by-using-preconfigured-golden-os-images) (optional, but desired) -- Teams: Ops - -To provision Redis subnets need to be allocated in the alternate region with and end-to-end validation of the new deployments. - -### External front-end load balancing - -- Dependencies: HAProxy replacement, mostly likely [GKE Gateway and Istio](https://gitlab.com/groups/gitlab-com/gl-infra/-/epics/1157) -- Teams: Ops, Foundations - -External front-end load balancing is necessary to validate the deployment in the alternate region. -This requires both external and internal LBs for all front-end-services. - -### Monitoring - -- Dependencies: [Eliminate X% Chef dependencies in Infra by moving infra away from Chef](zonal.md#eliminate-x-chef-dependencies-in-infra-by-moving-infra-away-from-chef) (migrate Prometheus infra to Kubernetes) -- Teams: Scalability:Observability, Ops, Foundations - -Setup an alternate ops Kubernetes cluster in a different region that is scaled down to zero replicas. - -### Runners - -Dependencies: [Improve Chef provisioning time by using preconfigured golden OS images](zonal.md#improve-chef-provisioning-time-by-using-preconfigured-golden-os-images) (optional, but desired) -Teams: Scalability:Practices, Ops, Foundations - -Ensure quotas are set and align with us-east1 in the alternate region for both runner managers and ephemeral VMs. -Setup and validate networking configuration with peering configuration. - -### Ops and Packaging - -- Dependencies: [Create an HA Chef server configuration to avoid an outage for a single zone failure](zonal.md#create-an-ha-chef-server-configuration-to-avoid-an-outage-for-a-single-zone-failure) -- Teams: Scalability:Practices, Ops, Foundations, Distribution - -All image creation and packaging is done on a single VM, our operation tooling is also on a single VM. -Both of these are single points of failures that have data stored locally. -In the case of a regional outage, we would need to rebuild them from snapshot and lose about 4 hours of data. - -The following are options to mitigate this risk: - -- Move our packaging jobs to `ops.gitlab.net` so we eliminate `dev.gitlab.org` as a single point of failure. -- Use the Geo feature for `ops.gitlab.net`. - -### Regional Recovery Gameday - -- Dependencies: Recovery improvements -- Teams: Ops - -Following the improvements for regional recovery, a Gameday needs to be executed for end-to-end testing of the procedure. -Once validated, it can be added to our existing [disaster recovery runbook](https://gitlab.com/gitlab-com/runbooks/-/tree/master/docs/disaster-recovery?ref_type=heads). + + + + diff --git a/doc/architecture/blueprints/disaster_recovery/zonal.md b/doc/architecture/blueprints/disaster_recovery/zonal.md index abf179162a8..166b67d16c9 100644 --- a/doc/architecture/blueprints/disaster_recovery/zonal.md +++ b/doc/architecture/blueprints/disaster_recovery/zonal.md @@ -1,121 +1,11 @@ --- -status: ongoing -creation-date: "2024-01-29" -authors: [ "@jarv" ] -coach: -approvers: [ ] +redirect_to: 'https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/disaster_recovery/zonal/' +remove_date: '2025-07-08' --- -# Zonal Recovery +This document was moved to [another location](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/disaster_recovery/zonal/). -## Improving the Recovery Time Objective (RTO) and Recovery Point Objective (RPO) for Zonal Recovery - -The following represents our current DR challenges and are candidates for problems that we should address in this architecture blueprint. - -1. Postgres replicas run close to capacity and are scaled manually. New - instances must go through Terraform CI pipelines and Chef configuration. - Over-provisioning to absorb a zone failure would add significant cloud-spend - (see proposal section at the end of the document for details). -1. HAProxy (load balancing) is scaled manually and must go through Terraform CI - pipelines and Chef configuration. -1. CI runner managers are present in 2 availability zones and scaled close to - capacity. New instances must go through Terraform CI pipelines and Chef - configuration. -1. In a zone there are saturation limits, like the number of replicas that need - to be manually adjusted if load is shifted away from a failed availability - zone. -1. Gitaly `RPO` is limited by the frequency of disk snapshots, `RTO` is limited - by the time it takes to provision and configure through Terraform CI - pipelines and Chef configuration. -1. Monitoring infrastructure that collects metrics from Chef managed VMs is - redundant across 2 availability zones and scaled manually. New instances must - go through Terraform CI pipelines and Chef configuration. -1. The Chef server which is responsible for all configuration of Chef managed - VMs is a single point of failure located in `us-central1`. It has a local - Postgres database and files on local disk. -1. The infrastructure (`dev.gitlab.org`) that builds Docker images and packages - is located in a single region, and is a single point of failure. - -## Zonal recovery work-streams - -Improvements around zonal recovery revolve around improving the time it takes to provision for fleets that do not automatically scale. -There is already work in-progress to completely eliminate statically allocated VMs like HAProxy. -Additionally efforts can be made to shorten launch and configuration times for fleets that are not able to automatically scale like Gitaly, PostgreSQL and Redis. - -### Over-provision to absorb a single zone failure - -- Dependencies: None -- Teams: Ops, Scalability:Practices, Database Reliability - -All of our Chef managed VM fleets run close to capacity and require manual scaling and provisioning using Terraform/Chef. -In the case of a zonal outage, it is necessary to provision more servers through Terraform which adds to our recovery time objective. -One way to avoid this is to over-provision so we have a full zone's worth of extra capacity. - -1. Patroni Main (`n2-highmem-128` 6.5k/month): 3 additional nodes for +20k/month -1. Patroni CI (`n2-highmem-96` 5k/month): 3 additional nodes for +15k/month -1. HAProxy (`t2d-standard-8` 285/month): 20 additional nodes for +5k/month -1. CI Runner managers (`c2-standard-30` 1.3k/month) 60 additional nodes for +78k/month - -The Kubernetes horizontal auto-scaler (`HPA`) has a maximum number of pods configured on front-end services. -It is configured to protect downstream dependencies like the database from saturation due to scaling events. -If we allow a zone to scale up rapidly, these limits need to be adjusted or re-evaluated in the context of disaster recovery. - -### Remove HAProxy as a load balancing layer - -- Dependencies: None -- Teams: Foundations - -HAProxy is a fleet of Chef managed VMs that are statically allocated across 3 AZs in `us-east1`. -In the case of a zonal outage we would need to rapidly scale this fleet, adding to our RTO. - -In FY24Q4 the Foundations team started working on a proof-of-concept to use -[Istio in non-prod environments](https://gitlab.com/groups/gitlab-com/gl-infra/-/epics/1157). -We anticipate in FY25 to have a replacement for HAProxy using Istio and -[GKE Gateway](https://cloud.google.com/kubernetes-engine/docs/concepts/gateway-api). -Completing this work reduces the impact to our LoadBalancing layer for zonal outages, -as it eliminates the need to manually scale the HAProxy fleet. -Additionally, we spend around 17k/month on HAProxy nodes, so there may be a -cloud-spend reduction if we are able to reduce this footprint. - -### Create an HA Chef server configuration to avoid an outage for a single zone failure - -- Dependencies: None -- Teams: Ops - -Chef is responsible for configuring VMs that have workloads outside of Kubernetes. -It is a single point of failure that resides in `us-central1-b`. -Data is persisted locally on disk, and we have not yet investigated moving it to a highly available setup. -In the case of a zonal outage of `us-central1-b` the server would need to be rebuilt from snapshot, losing up to 4 hours of data. - -### Create an HA Packaging server configuration to avoid an outage for a single zone failure - -- Dependencies: None -- Teams: Ops - -In the case of a zonal outage of `us-east1-c` the server (like `dev.gitlab.org`) would need to be rebuilt from snapshot, losing up to 4 hours of data. -The additional challenge of this host is that it is a GitLab-CE instance so we would be limited in features. -The best approach here would likely be to move packaging CI pipelines to `ops.gitlab.net`. - -### Improve Chef provisioning time by using preconfigured golden OS images - -- Dependencies: None -- Teams: Ops - -For the [Gitaly fleet upgrade in 2022](https://gitlab.com/groups/gitlab-com/gl-infra/-/epics/601) a scheduled CI pipeline was created to build a golden OS images. -We can revive this work and start generating images for Gitaly and other VMs to shorten configuration time. -We estimate that using an image can reduce our recovery time by about 15 minutes to improve RTO for zonal failures. - -### Eliminate X% Chef dependencies in Infra by moving infra away from Chef - -- Dependencies: None -- Teams: Ops, Scalability:Observability, Scalability:Practices - -Gitaly, Postgres, CI runner managers, HAProxy, Bastion, CustomersDot, Deploy, DB Lab, Prometheus, Redis, SD Exporter, and Console servers are managed by Chef. -To help improve the speed of recoveries, we can move this infrastructure into Kubernetes or Ansible for configuration management. - -### Write-ahead-log for Gitaly snapshot restores - -- Dependencies: None -- Teams: Gitaly - -There is [work planned in FY25Q1](https://gitlab.com/gitlab-com/gitlab-OKRs/-/work_items/5710) that adds a transaction log for Gitaly to reduce RPO. + + + + diff --git a/doc/architecture/blueprints/duo_workflow/diagrams/duo-workflow-architecture-gitlab-com.png b/doc/architecture/blueprints/duo_workflow/diagrams/duo-workflow-architecture-gitlab-com.png deleted file mode 100644 index 76863474b5972800b52e13c79358fdd686cb9efe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 96843 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 diff --git a/doc/architecture/blueprints/duo_workflow/diagrams/duo-workflow-architecture-self-managed-full.png b/doc/architecture/blueprints/duo_workflow/diagrams/duo-workflow-architecture-self-managed-full.png deleted file mode 100644 index 53ee7f15834c205c08e9e10f70d89d3a6248ab68..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 100628 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 z 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
a0W8$|tWq9HB<^5_c%$&V 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≀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


yXPkoNDv{ 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 F{{_@0M+5)> diff --git a/doc/architecture/blueprints/duo_workflow/diagrams/duo_workflow_auth.png b/doc/architecture/blueprints/duo_workflow/diagrams/duo_workflow_auth.png deleted file mode 100644 index 1a12ec1388434a0291721a6edf5ff9d2360fa788..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 54849 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

- -Dataset - -- Input Bigquery table: `dev-ai-research-0e2f8974.duo_chat_external.documentation__input_v1` -- Output Bigquery table: - - `dev-ai-research-0e2f8974.duo_chat_external_results.sm_doc_tool_vertex_ai_search` - - `dev-ai-research-0e2f8974.duo_chat_external_results.sm_doc_tool_legacy` -- Command: `promptlib duo-chat eval --config-file /eval/data/config/duochat_eval_config.json` - -
- -### Estimated Time of Completion - -- Milestone N: - - Setup in Vertex AI Search with CI/CD automation. - - Introduce `/v1/search/docs` endpoint in AI Gateway. - - Updates the retrieval logic in GitLab-Rails. - - Feature flag clean up. - -Total milestones: 1 + + + + diff --git a/doc/architecture/blueprints/gitlab_rag/elasticsearch.md b/doc/architecture/blueprints/gitlab_rag/elasticsearch.md index 3d721fd35d4..6e3a4a501b2 100644 --- a/doc/architecture/blueprints/gitlab_rag/elasticsearch.md +++ b/doc/architecture/blueprints/gitlab_rag/elasticsearch.md @@ -1,221 +1,11 @@ --- -status: proposed -creation-date: "2024-02-20" -authors: [ "@bvenker", "@mikolaj_wawrzyniak" ] -coach: [ "@stanhu" ] -approvers: [ "@pwietchner", "@oregand", "@shinya.meda", "@mikolaj_wawrzyniak" ] -owning-stage: "~devops::data stores" -participating-stages: ["~devops::ai-powered", "~devops::create"] +redirect_to: 'https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/gitlab_rag/elasticsearch/' +remove_date: '2025-07-08' --- -# Elasticsearch +This document was moved to [another location](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/gitlab_rag/elasticsearch/). -Elasticsearch is a search engine and data store which allows generating, storing and querying vectors and performing keyword and semantic search at scale. - -Elasticsearch employs a distributed architecture, where data is stored across multiple nodes. This allows for parallel processing of queries, ensuring fast results even with massive datasets. - -## Using Elasticsearch as a vector store - -Elasticsearch can be used to store embedding vectors up to 4096 dimensions and find the closest neighbours for a given embedding. - -![Elasticsearch as vector store](img/elasticsearch_vector_store.png) - -### Licensing - -Does not require a paid license. - -### Indexing embeddings - -For every document type (e.g. `gitlab_documentation`), an index is created and stores the original source, embeddings and optional metadata such as URL. An initial backfill is required to index all current documents and a process to upsert or delete documents as the source changes. - -For GitLab Duo Documentation, the current async process for generating and storing embeddings in the embeddings database can be altered to index into Elasticsearch. - -Using the Advanced Search framework, database records are automatically kept up to date in Elasticsearch. [Issue 442197](https://gitlab.com/gitlab-org/gitlab/-/issues/442197) proposes changing the Elasticsearch framework to allow for other datasets to be indexed. - -For documents with large sources that need to be split into chunks, [nested kNN search](https://www.elastic.co/guide/en/elasticsearch/reference/8.12/knn-search.html#nested-knn-search) can be used whereby a single top-level document contains nested objects each with a source and embedding. This enables searching for the top K documents with the most relevant chunks. It is not suited for cases where the top k chunks need to be searched within a single document. In such cases, every chunk should be stored as a separate document. - -### Querying context-relevant information - -A given question is passed to a model to generate embeddings. The vector is then sent to Elasticsearch to find the most relevant documents. - -### Generation - -The N most relevant documents are added to a prompt which is sent to an LLM to generate an answer for the original question. - -## RAG in Elasticsearch using hosted models - -Similar to the above but the question's embeddings are generated from within Elasticsearch. - -![RAG overview](img/elasticsearch_rag_hosted_models.png) - -### Licensing - -Requires a paid license on every cluster. - -### Model hosting - -Requires model(s) used to be hosted on every cluster which adds effort and cost. - -Elasticsearch supports the following models: - -- ELSER (Elastic Learned Sparse Encoder): Built-in model provided by Elasticsearch used to generate text embeddings for semantic search. -- TensorFlow Models: Custom TensorFlow models can be deployed for semantic search using the ML APIs. -- Third-Party Models: Elasticsearch supports deploying models from Hugging Face and other providers. This provides access to a wider range of pre-trained models, but deployment and maintenance requires additional work. - -## Hybrid Search - -Hybrid search combines text and semantic search to return the most revelant sources. A reranker could be used to combine the results from both methods. - -![Hybdid search](img/elasticsearch_hybrid_search.png) - -### Advanced text search features of Elasticsearch - -1. Inverted Indexing: At its core, Elasticsearch relies on a powerful data structure called an inverted index. This index essentially flips the traditional approach, where each document contains a list of words. Instead, the inverted index catalogues every unique word across all documents and tracks where it appears in each one. This enables lightning-fast searches by finding relevant documents based on matching words instantly. - -1. Advanced Text Analysis: Elasticsearch doesn't simply match whole words. It leverages text analyzers to break down and understand text intricacies. This includes handling: - - - Stemming and lemmatization: Reducing words to their root form (e.g., "running" and "ran" both matching "run"). - - Synonyms and related terms: Recognizing synonyms and similar words to expand search results. - - Stop words: Ignoring common words like "the" and "a" that don't contribute much to meaning. - - Custom analysis: Defining your own rules for specific domains or languages. - -1. Powerful Query Capabilities: Elasticsearch goes beyond basic keyword searches. It supports complex queries using Boolean operators (AND, OR, NOT), proximity searches (finding words close together), fuzzy searches (handling typos), and more. You can also filter results based on other criteria alongside text matching. - -### Reranking - -Elasticsearch currently supports [Reciprocal rank fusion (RRF)](https://www.elastic.co/guide/en/elasticsearch/reference/current/rrf.html) which works out-the-box. They also released [Learning to Rank](https://elasticsearch-learning-to-rank.readthedocs.io/en/latest/) which uses ML to improve ranking. - -## Running Elasticsearch - -Elasticsearch is available on GitLab.com and can be integrated on Dedicated and Self-Managed instances. To use as a vector store only: - -- [Install Elasticsearch version `8.12`](../../../integration/advanced_search/elasticsearch.md#install-elasticsearch-or-aws-opensearch-cluster) or upgrade to at least version `8.12`. -- Add URL, Username and Password on the Advanced Search settings page: `admin/application_settings/advanced_search` - -After the integration is configured, instance admins don't need to do further work to use it as a vector store since the GitLab Elasticsearch framework handles setting mappings, settings and indexing data. - -## Supported dimensions - -Elasticsearch can store up to 4096 dimensions and OpenSearch up to 16000 dimensions, compared to `pg_vector` which can store up to 2000. - -## Limitations - -### Licensing - -In order to use the ML capabilities offered by Elastic, every cluster has to have a valid license. - -If Elastic is used only as a vector store and all embeddings generated outside of Elastic, a license is not required. - -### Adoption - -The Elastic integration is available to all GitLab instances to unlock Advanced Search but not all instances have chosen to run the integration. There is also an additional cost for every instance to host the integration. - -## Performance and scalability - -Elasticsearch is horizontally scalable and handles storing and querying at scale. An Elasticsearch cluster consists of multiple nodes each contributing resources. - -## Cost - -Elastic Cloud pricing for GitLab Documentation vector storage is about $38 per month and the price scales with storage requirements. - -## Elasticseach vs. OpenSearch - -### Features - -Both offer storing vector embeddings and similarity search (kNN). - -Elasticsearch supports custom TensorFlow models which OpenSearch does not offer. Both offer pre-trained models. - -The APIs for kNN searching differ slightly between the two platforms but work in the same way. - -### Supported platforms - -Currently GitLab offers Advanced Search for both Elasticsearch and OpenSearch due to parity between the text search APIs. If both are supported for AI features, there would be a need to adapt to two different AI APIs. - -## PoC: Repository X Ray - -To test the viability of Elasticsearch for generating embeddings, a PoC was done with Repository X Ray project. - -Repository X Ray hasn't yet implemented any semantic seach and this section is based soely on a [prototype implementation](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/144715) - -- Statistics (as of February 2024): - - Data type: JSON document with source code libraries descriptions in natural language - - Data access level: Red (each JSON document belongs to specific project, and data access rules should adhere to data access rules configure for that project) - - Data source: Repository X Ray report CI artifact - - Data size: N/A - - Example of user input: "# generate function that fetches sales report for vendor from App Store" - - Example of expected AI-generated response: - - ```python - def sales_reports(vendor_id)\n app_store_connect.sales_reports(\n filter: {\n report_type: 'SALES',\n report_sub_type: 'SUMMARY',\n frequency: 'DAILY', - vendor_number: '123456'\n }\n)\nend - ``` - -### Synchronizing embeddings with data source - -In a similar manner as with the [documentation example](../gitlab_duo_rag/elasticsearch.md#retrieve-gitlab-documentation) Repository X Ray report data is a derivative. It uses an underlying repository source code as a base, and it must be synchronised with it, whenever any changes to the source code occurs. - -Right now there is no synchronisation mechanism that includes embeddings and vector storage. However there is an existing pipeline that generates and stores Repository X Ray reports. - -The ingestion pipeline is performed in following steps: - -1. A CI X Ray scanner job is triggered - a documentation [page](../../../user/project/repository/code_suggestions/repository_xray.md#enable-repository-x-ray) suggest limiting this job to be executed only when changes occur to the main repository branch. However repository maintainers may configure trigger rules differently. - - An X Ray [scanner](https://gitlab.com/gitlab-org/code-creation/repository-x-ray) locates and process one of the supported [dependencies files](../../../user/project/repository/code_suggestions/repository_xray.md#supported-languages-and-package-managers), producing JSON report files. -1. After the X Ray scanner job finishes successfully, a [background job](https://gitlab.com/gitlab-org/gitlab/-/blob/c6b2f18eaf0b78a4e0012e88f28d643eb0dfb1c2/ee/app/workers/ai/store_repository_xray_worker.rb#L18) is triggered in GitLab Rails monolith that imports JSON report into [`Projects::XrayReport`](https://gitlab.com/gitlab-org/gitlab/-/blob/bc2ad40b4b026dd359e289cf2dc232de1a2d3227/ee/app/models/projects/xray_report.rb#L22). - - There can be only one Repository X Ray report per project in the scope of programming language, duplicated records are being upserted during import process - -As of today, there are 84 rows on `xray_reports` table on GitLab.com. - -### Retrieval - -After Repository X Ray report gets imported, when IDE extension sends request for a [code generation](../../../user/project/repository/code_suggestions/index.md), -Repository X Ray report is retrieved in the following steps: - -1. GitLab Rails monotlith fetches corresponding `xray_reports` record from main database. `xray_reports` records are filiterd based on `project_id` foreign key, and `lang` columns. -1. From retrieved record first 50 dependencies are being added into a prompt that is forwarded to AI Gateway - -### Current state overview - -```mermaid -sequenceDiagram - actor USR as User - participant IDE - participant GLR as GitLabRails - participant RN as GitLabRunner - participant PG as GitLabPsqlMainDB - participant AIGW as AIGateway - USR->>+GLR: commits changes to Gemfile.lock - GLR->>RN: triggers Repository X Ray CI scanner job - RN->>GLR: Repository X Ray report - GLR->>GLR: triggers Repository X Ray ingestion job - GLR->>-PG: upserts xray_reports record - - USR->>+IDE: types: "#35; generate function that fetches sales report for vendor from App Store" - IDE->>+GLR: trigger code generation for line ` "#35; generate function ` - GLR->>PG: fetch X Ray report for project and language - PG->>GLR: xray_reports record - GLR->>GLR: include first 50 entities from xray report into code generation prompt - GLR->>-AIGW: trigger code generation ` "#35; generate function ` -``` - -### Embeddings prospect application - -As described in retrieval section above, currently Repository X Ray reports follow very naive approach, that does not include any metric for assessing relevance between Repository X Ray report content and -user instruction. Therefore applying embeddings and semantic search to X Ray report has a high potential of improving results by selecting limited set of related entries from Repository X Ray report based on user instruction. - -To achieve that embeddings should be generated during Repository X Ray ingestion. Additionally an user instruction should be turned into embeddings vector to perform semantic search over stored Repository X Ray report data during retrieval process. - -### Elasticsearch and PGVector comparison - -Following paragraph is a result of [PoC](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/144715) work. - -From a product feature implementation point of view both solutions seems as viable ones, offering in the current state all necessary tools to support the product features requirements. -Given the Elasticsearch built in capabilities it is acknowledged that it might bring better long term support, enabling more powerful RAG solution in the future than `pg_vector` based ones. - -The current Elasticsearch integration only indexes `ActiveRecord` models and -source code from Git repositories. Further work required to build more -generic abstractions to index other data (eg. X-Ray reports) -has been defined by [issue 442197](https://gitlab.com/gitlab-org/gitlab/-/issues/442197). -To prevent suboptimal workarounds of existing limitation -which will create technical debt, it is advised that [issue 442197](https://gitlab.com/gitlab-org/gitlab/-/issues/442197) -is completed before Elasticsearch is selected as main vector storage for RAG. + + + + diff --git a/doc/architecture/blueprints/gitlab_rag/img/blog_figure-1.jpg b/doc/architecture/blueprints/gitlab_rag/img/blog_figure-1.jpg deleted file mode 100644 index ee5aad6375ee5b22543a3746364d9f74bbb9f7e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24155 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 F{|6lj(Cz>L diff --git a/doc/architecture/blueprints/gitlab_rag/img/elasticsearch_rag_hosted_models.png b/doc/architecture/blueprints/gitlab_rag/img/elasticsearch_rag_hosted_models.png deleted file mode 100644 index 16da0f224d1fcda07e9224509a2e09272ba016c7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27082 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 B#FYR5 diff --git a/doc/architecture/blueprints/gitlab_rag/index.md b/doc/architecture/blueprints/gitlab_rag/index.md index 2c1b6e5baef..06ea83f2ef1 100644 --- a/doc/architecture/blueprints/gitlab_rag/index.md +++ b/doc/architecture/blueprints/gitlab_rag/index.md @@ -1,305 +1,11 @@ --- -status: ongoing -creation-date: "2024-02-20" -authors: [ "@maddievn", "@mikolaj_wawrzyniak", "@dgruzd" ] -coach: [ "@stanhu" ] -approvers: [ "@pwietchner", "@oregand", "@shinya.meda", "@mikolaj_wawrzyniak" ] -owning-stage: "~devops::data stores" -participating-stages: ["~devops::ai-powered", "~devops::create"] +redirect_to: 'https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/gitlab_rag/' +remove_date: '2025-07-08' --- -# Retrieval Augmented Generation (RAG) for GitLab +This document was moved to [another location](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/gitlab_rag/). -## Goals - -The goal of this blueprint is to describe viable options for RAG at GitLab -across deployment types. The aim is to describe RAG implementations that provide -our AI features–and by extension our customers–with best-in-class user -experiences. - -## Overview of RAG - -RAG, or Retrieval Augmented Generation, involves several key process blocks: - -- **Input Transformation**: This step involves processing the user's input, - which can vary from natural language text to JSON or keywords. For effective - query construction, we might utilize Large Language Models (LLMs) to format - the input into a standard expected format or to extract specific keywords. -- **Retrieval**: Here, we fetch relevant data from specified data sources, which - may include diverse storage engines like vector, graph, or relational - databases. It's crucial to conduct [data access checks](#data-access-policy) - during this phase. After retrieval, the data should be optimized for LLMs - through post-processing to enhance the quality of the generated responses. -- **Generation**: This phase involves crafting a prompt with the retrieved data - and submitting it to an LLM, which then generates an AI-powered response. - -![Current page](img/blog_figure-1.jpg) - -(Image from [Deconstructing RAG](https://blog.langchain.dev/deconstructing-rag/)) - -## Challenges of RAG - -### Data for LLMs - -Ensuring data is optimized for LLMs is crucial for consistently generating -high-quality AI responses. Several challenges exist when providing context to -LLMs: - -- **Long Contexts:** Extensive contexts can degrade LLM performance, a - phenomenon known as the Lost in the Middle problem. Employing Rerankers can - enhance performance but may also increase computational costs due to longer - processing times. -- **Duplicate Contents:** Repetitive content can reduce the diversity of search - results. For instance, if a semantic search yields ten results indicating - "Tom is a president" but the eleventh reveals "Tom lives in the United States," - solely using the top ten would omit critical information. Filtering out - duplicate content, for example, through Maximal Marginal Relevance (MMR), can - mitigate this issue. -- **Conflicting Information:** Retrieving conflicting data from multiple sources - can lead to LLM "hallucinations." For example, mixing sources that define - "RAG" differently can confuse the LLM. Careful source selection and content - curation are essential. -- **Irrelevant Content:** Including irrelevant data can negatively impact LLM - performance. Setting a threshold for relevance scores or considering that - certain irrelevant contents might actually enhance output quality are - strategies to address this challenge. - -It's highly recommended to evaluate the optimal data format and size for -maximizing LLM performance, as the effects on performance and result quality can -vary significantly based on the data's structure. - -References: - -- [Benchmarking Methods for Semi-Structured RAG](https://youtu.be/KMZZh7Z5mno?si=-Gr-acXcjg7QXmBU) -- [Edge cases of semantic search](https://youtu.be/DY3sT4yIezs?feature=shared&t=1382) - -#### Regenerating Embeddings - -The AI field is evolving rapidly and new models and approaches seem to appear -daily that could improve our users' experience, we want to conscious of model -switching costs. If we decide to swap models or change our chunking strategy (as two examples), -we will need to wipe our existing embeddings and do a full -replacement with embeddings from the new model or with the new text chunks, etc. -Factors to consider which could trigger the need for a full regeneration of -embeddings for the affected data include: - -- A change in the optimal text chunk size -- A change in a preprocessing step which perhaps adds new fields to a text chunk -- Exclusion of content, such as the removal of a field that was previously embedded -- Addition of new metadata that needs to be embedded - -### Multi-source Retrieval - -Addressing complex queries may require data from multiple sources. For instance, -queries linking issues to merge requests necessitate fetching details from both. -GitLab Duo Chat, utilizing the -[ReACT framework](https://arxiv.org/abs/2210.03629), sequentially retrieves data from -PostgreSQL tables, which can prolong the retrieval process due to the sequential -execution of multiple tools and LLM inferences. - -## Searching for Data - -Choosing the appropriate search method is pivotal for feature design and UX optimization. Here are common search techniques: - -### Semantic Search using embeddings - -Semantic search shines when handling complex queries that demand an -understanding of the context or intent behind the words, not just the words -themselves. It's particularly effective for queries expressed in natural -language, such as full sentences or questions, where the overall meaning -outweighs the importance of specific keywords. Semantic search excels at -providing thorough coverage of a topic, capturing related concepts that may not -be directly mentioned in the query, thus uncovering more nuanced or indirectly -related information. - -In the realm of semantic search, the K-Nearest Neighbors (KNN) method is -commonly employed to identify data segments that are semantically closer to the -user's input by using embeddings. To measure the semantic proximity, various methods are used: - -- **Cosine Similarity:** Focuses solely on the direction of vectors. -- **L2 Distance (Euclidean Distance):** Takes into account both the direction - and magnitude of vectors. These vectors, known as "embeddings," are created by - processing the data source through an embedding model. Currently, in GitLab - production, we utilize the `textembedding-gecko` model provided by Vertex AI. - However, there might be scenarios where you consider using alternative embedding - models, such as those available on HuggingFace, to reduce costs. Opting for - different models requires comprehensive evaluation and consultation, - particularly with the legal team, to ensure the chosen model's usage complies - with GitLab policies. See the - [Security, Legal, and Compliance](https://gitlab.com/gitlab-org/gitlab/-/blob/52f4fcb033d13f3d909a777728ba8f3fa2c93256/doc/architecture/blueprints/gitlab_duo_rag/index.md#security-legal-and-compliance) - section for more details. It's also important to note that multilingual support - can vary significantly across different embedding models, and switching models - may lead to regressions. - -For large datasets, it's advisable to implement indexes to enhance query -performance. The HNSW (Hierarchical Navigable Small World) method, combined with -approximate nearest neighbors (ANN) search, is a popular strategy for this -purpose. For insights into HNSW's effectiveness, consider reviewing -[benchmarks on its performance in large-scale applications](https://supabase.com/blog/increase-performance-pgvector-hnsw). - -Due to the existing framework and scalability of Elasticsearch, embeddings will -be stored on Elasticsearch for large datasets such as -[issues](https://gitlab.com/gitlab-org/gitlab/-/issues/451431), merge requests, -etc. This will be used to perform [Hybrid Search](https://gitlab.com/gitlab-org/gitlab/-/issues/440424) -but will also be useful for other features such as finding duplicates, similar results or -categorizing documents. - -### Keyword Search - -Keyword search is the go-to method for straightforward, specific queries where -users are clear about their search intent and can provide precise terms or -phrases. This method is highly effective for retrieving exact matches, making it -suitable for searches within structured databases or when looking for specific -documents, terms, or phrases. - -Keyword search operates on the principle of matching the query terms directly -with the content in the database or document collection, prioritizing results -that have a high frequency of the query terms. Its efficiency and directness -make it particularly useful for situations where users expect quick and precise -results based on specific keywords or phrases. - -Elasicsearch uses a BM25 algorigthm to perform keyword search. -If one of the existing [indexed document types](../../../integration/advanced_search/elasticsearch.md#advanced-search-index-scopes) -is not covered, a [new document type](../../../development/advanced_search.md#add-a-new-document-type-to-elasticsearch) can be added. - -### Hybrid Search - -Hybrid search combines the depth of semantic search with the precision of -keyword search, offering a comprehensive search solution that caters to both -context-rich and specific queries. By running both semantic and keyword searches -simultaneously, it integrates the strengths of both methods—semantic search's -ability to understand the context and keyword search's precision in identifying -exact matches. - -The results from both searches are then combined, with their relevance scores -normalized to provide a unified set of results. This approach is particularly -effective in scenarios where queries may not be fully served by either method -alone, offering a balanced and nuanced response to complex search needs. The -computational demands of kNN searches, which are part of semantic search, are -contrasted with the relative efficiency of [BM25](https://pub.aimind.so/understanding-the-bm25-ranking-algorithm-19f6d45c6ce) -keyword searches, making hybrid search a strategic choice for optimizing -performance across diverse datasets. - -The first hybrid search scope is for issues which combines keyword search with kNN matches using embeddings. - -### Code Search - -Like the other data types above, a source code search task can use different -search types, each more suited to address different queries. - -Two code searches are available: `Elasticsearch` and `Zoekt`. - -Elasticsearch provides blob search which supports [Advanced Search Syntax](../../../user/search/advanced_search.md#syntax). - -[Zoekt](../code_search_with_zoekt/index.md) is employed on GitLab.com to provide -exact match keyword search and regular expression search capabilities for source -code. - -Semantic search and hybrid search functionalities are yet to be -implemented for code. - -### ID Search - -Facilitates data retrieval using specific resource IDs, such as issue links. For -example retrieving data from the specified resource ID, such as an Issue link or -a shortcut. See [ID search](postgresql.md#id-search) for more information. - -### Knowledge Graph - -Knowledge Graph search transcends the limitations of traditional search methods -by leveraging the interconnected nature of data represented in graph form. -Unlike semantic search, which focuses on content similarity, Knowledge Graph -search understands and utilizes the relationships between different data points, -providing a rich, contextual exploration of data. - -This approach is ideal for queries that benefit from understanding the broader -context or the interconnectedness of data entities. Graph databases store -relationships alongside the data, enabling complex queries that can navigate -these connections to retrieve highly contextual and nuanced information. - -Knowledge Graphs are particularly useful in scenarios requiring deep insight -into the relationships between entities, such as recommendation systems, complex -data analysis, and semantic querying, offering a dynamic way to explore and -understand large, interconnected datasets. - -## Security, Legal and Compliance - -### Data access policy - -The retrieval process must comply with the -[GitLab Data Classification Standard](https://handbook.gitlab.com/handbook/security/data-classification-standard/). -If the user doesn't have access to the data, GitLab will not fetch the data for -building a prompt. - -For example: - -- When the data is GitLab Documentation (GREEN level), the data can be fetched - without authorizations. -- When the data is customer data such as issues, merge requests, etc (RED level), - the data must be fetched with proper authorizations based on permissions and roles. - -If you're proposing to fetch data from an external public database -(e.g. fetching data from `arxiv.org` so the LLM can answer questions about -quantitative biology), please conduct a thorough review to ensure the external -data isn't inappropriate for GitLab to process. - -### Data usage - -Using a new embedding model or persisting data into a new storage would require -[legal reviews](https://handbook.gitlab.com/handbook/legal/). See the following -links for more information: - -- [Data privacy](../../../user/gitlab_duo/data_usage.md#data-privacy) -- [Data retention](../../../user/gitlab_duo/data_usage.md#data-retention) -- [Training data](../../../user/gitlab_duo/data_usage.md#training-data) - -## Evaluation - -Evaluation is a crucial step in objectively determining the quality of the -retrieval process. Tailoring the retrieval process based on specific user -feedback can lead to biased optimizations, potentially causing regressions for -other users. It's essential to have a dedicated test dataset and tools for a -comprehensive quality assessment. For assistance with AI evaluation, please -reach out to the [AI Model Validation Group](https://handbook.gitlab.com/handbook/engineering/development/data-science/model-validation/). - -## Before Implementing RAG - -Before integrating Retrieval Augmented Generation (RAG) into your system, it's -important to evaluate whether it enhances the quality of AI-generated responses. -Consider these essential questions: - -- **What does typical user input look like?** - - For instance, "Which class should we use to make an external HTTP request in this repository?" -- **What is the desired AI-generated response?** - - Example: "Within this repository, Class-A is commonly utilized for..." -- **What are the current responses from LLMs?** (This helps determine if the necessary knowledge is already covered by the LLM.) - - Example: Receiving a "Sorry, I don't have an answer for that." from the Anthropic Claude 2.1 model. -- **What data is required in the LLM's context window?** - - Example: The code for Class-A. -- **Consider the current search method used for similar tasks**. (Ask yourself: How would I currently search for this data with the tools at my disposal?) - - Example: Navigate to the code search page and look for occurrences of "http." -- **Have you successfully generated the desired AI response with sample data?** Experiment in a third-party prompt playground or Google Colab to test. -- **If contemplating semantic search**, it's **highly recommended** that you - develop a prototype first to ensure it meets your specific retrieval needs. - Semantic search may interpret queries differently than expected, especially - when the data source lacks natural language context, such as uncommented code. - In such cases, semantic search might not perform as well as traditional - keyword search methods. Here's [an example prototype](https://colab.research.google.com/drive/1K1gf6FibV-cjlXvTJPboQJtjYcSsyYi2?usp=sharing) - that demonstrates semantic search for CI job configurations. - -## Evaluated Solutions - -The following solutions have been validated with PoCs to ensure they meet the -basic requirements of vector storage and retrieval for GitLab Duo Chat with -GitLab documentation. Click the links to learn more about each solutions -attributes that relate to RAG: - -- [PostgreSQL with PGVector](postgresql.md) -- [Elasticsearch](elasticsearch.md) -- [Google Vertex](vertex_ai_search.md) - -To read more about the [GitLab Duo Chat PoCs](../gitlab_duo_rag/index.md) conducted, see: - -- [PGVector PoC](../gitlab_duo_rag/postgresql.md) -- [Elasticsearch PoC](../gitlab_duo_rag/elasticsearch.md) -- [Google Vertex PoC](../gitlab_duo_rag/vertex_ai_search.md) + + + + diff --git a/doc/architecture/blueprints/gitlab_rag/postgresql.md b/doc/architecture/blueprints/gitlab_rag/postgresql.md index bf04903f11b..a31151cfd6a 100644 --- a/doc/architecture/blueprints/gitlab_rag/postgresql.md +++ b/doc/architecture/blueprints/gitlab_rag/postgresql.md @@ -1,140 +1,11 @@ --- -status: proposed -creation-date: "2024-02-20" -authors: [ "@bvenker", "@mikolaj_wawrzyniak" ] -coach: [ "@stanhu" ] -approvers: [ "@pwietchner", "@oregand", "@shinya.meda", "@mikolaj_wawrzyniak" ] -owning-stage: "~devops::data stores" -participating-stages: ["~devops::ai-powered", "~devops::create"] +redirect_to: 'https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/gitlab_rag/postgresql/' +remove_date: '2025-07-08' --- -# PostgreSQL +This document was moved to [another location](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/gitlab_rag/postgresql/). -This page explains how to retrieve data from PostgreSQL for [RAG](index.md). - -## Semantic search - -### Overview - -1. Install [PgVector extension](#vector-store-with-pgvector) to the PostgreSQL database. -1. Add a `vector` column to a new or existing table. -1. Data <=> Embedding synchronization - 1. Load data which you want to search from. - 1. Pass the data to an embedding model and get an vector. - 1. Set the vector to the `vector` column. -1. Retrieval - 1. Pass the user input to an embedding model and get an vector. - 1. Get the nearest neighbors to the user input vector e.g. `SELECT * FROM a_table ORDER BY vector_column <-> '' LIMIT 5;` - -### Vector store with PgVector - -To store the embeddings for semantic search, we need to add a vector store in GitLab PostgreSQL. -This vector store can be added by installing [PgVector extension](https://github.com/pgvector/pgvector) (Postgres 12+ is required). -A vector store is currently running on GitLab.com and it's separately hosted from the main/CI databases. - -Our current architecture of having a separate database for embeddings is probably ideal. We don't gain much by combining them and, as PGVector is all new and will likely require a lot of experimenting to get performance at scale (today we only have a tiny amount of data in it), we'll have a lot more options to experiment with without impacting overall GitLab.com stability (if PGVector is on a separate database). Having a separate database is recommended because it allows for experimentation without impacting performance of the main database. - -### Limitations - -- It could be locked down to a specific embedding model, because you must specify the dimensions of the vector column. -- Vectors with up to 2,000 dimensions can be indexed. - -### Performance and scalability implications - -- Is there any guidance on how much data we can add to the PostgreSQL (regardless of the vector data or normal data)? - - Not really, as we do not usually just add data to the database, but rather it's a result of the instance being used. I don't see any specific [storage requirements](../../../install/requirements.md#storage). If the existing `vertex_gitlab_docs` table size is a good indicator, we probably can add this without causing much trouble, though having an option to opt-in or opt-out is preferable. - -### Availability - -- PostgreSQL is availble in all GitLab installations (both CNG and Omnibus). -- Most major cloud providers have added PgVector to their offerings by now: Google Cloud SQL and Alloy DB, DigitalOcean, AWS RDS and Aurora, Azure Flexible and Cosmos, etc. There might be a case where customers would need to upgrade to versions that support PGVector. - -## ID search - -### Overview - -1. Execute a few-shot prompts to extract a resource identifier from the user input. - - e.g. When user asks `Can you summarize #12312312?`, ResourceIdentifier is `12312312` as a GitLab-Issue. -1. Retrieve the record from the PostgreSQL. e.g. `Issue.find(12312312)` -1. Check if the user can read the resource. -1. Build a prompt with the retrieved data and passing it to an LLM to get a AI-generated response. - -## PoC: Repository X Ray - -Repository X Ray hasn't yet implemented any semantic seach and this section is based soely on a [prototype implementation](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/142912) - -- Statistics (as of February 2024): - - Date type: JSON document with source code libraries desctiptions in natural language - - Date access level: Red (each JSON document belongs to specific project, and data access rules should adhere to data access rules configure for that project) - - Data source: Repository X Ray report CI artifact - - Data size: N/A - - Example of user input: "# generate function that fetches sales report for vendor from App Store" - - Example of expected AI-generated response: - - ```python - def sales_reports(vendor_id)\n app_store_connect.sales_reports(\n filter: {\n report_type: 'SALES',\n report_sub_type: 'SUMMARY',\n frequency: 'DAILY', - vendor_number: '123456'\n }\n)\nend - ``` - -### Synchronizing embeddings with data source - -In symilar manner as with the [documentation example](../gitlab_duo_rag/postgresql.md#retrieve-gitlab-documentation) Repository X Ray report data is a derivative. It uses an underlaying repository source code as a base, -and it must be synchronised with it, whenever any changes to the source code occurs. - -Right now there is no synchronisation mechanism that includes embeddings and vector storage. However there is an existing pipeline that generates and stores Repository X Ray reports - -The ingestion pipeline is performed in following steps: - -1. A CI X Ray scanner job is triggered - a documentation [page](../../../user/project/repository/code_suggestions/repository_xray.md#enable-repository-x-ray) suggest limiting this job to be executed only when changes occur to the main repository branch. However repository maintainers may configure trigger rules differently. - 1. An X Ray [scanner](https://gitlab.com/gitlab-org/code-creation/repository-x-ray) locates and process one of the supported [dependencies files](../../../user/project/repository/code_suggestions/repository_xray.md#supported-languages-and-package-managers), producing JSON report files -1. After the X Ray scanner job finishes successfully, a [background job](https://gitlab.com/gitlab-org/gitlab/-/blob/c6b2f18eaf0b78a4e0012e88f28d643eb0dfb1c2/ee/app/workers/ai/store_repository_xray_worker.rb#L18) is triggered in GitLab Rails monolith that imports JSON report into [`Projects::XrayReport`](https://gitlab.com/gitlab-org/gitlab/-/blob/bc2ad40b4b026dd359e289cf2dc232de1a2d3227/ee/app/models/projects/xray_report.rb#L22) - 1. There can be only one Repository X Ray report per project in the scope of programming language, duplicated records are being upserted during import process - -As of today, there are 84 rows on `xray_reports` table on GitLab.com. - -### Retrieval - -After Repository X Ray report gets imported, when IDE extension sends request for a [code generation](../../../user/project/repository/code_suggestions/index.md), Repository X Ray report is retrieved, in following steps - -1. Fetch embedding of the user input from `textembedding-gecko` model (768 dimensions). -1. Query to `vertex_gitlab_docs` table for finding the nearest neighbors. For example: - - ```sql - SELECT * - FROM vertex_gitlab_docs - ORDER BY vertex_gitlab_docs.embedding <=> '[vectors of user input]' -- nearest neighbors by cosine distance - LIMIT 10 - ``` - -1. GitLab Rails monotlith fetches corresponding `xray_reports` record from main database. `xray_reports` records are filiterd based on `project_id` foreign key, and `lang` columns. -1. From retrieved record first 50 dependencies are being added into a prompt that is forwarded to AI Gateway - -### Current state overview - -```mermaid -sequenceDiagram - actor USR as User - participant IDE - participant GLR as GitLabRails - participant RN as GitLabRunner - participant PG as GitLabPsqlMainDB - participant AIGW as AIGateway - USR->>+GLR: commits changes to Gemfile.lock - GLR->>RN: triggers Repository X Ray CI scanner job - RN->>GLR: Repository X Ray report - GLR->>GLR: triggers Repository X Ray ingestion job - GLR->>-PG: upserts xray_reports record - - USR->>+IDE: types: "#35; generate function that fetches sales report for vendor from App Store" - IDE->>+GLR: trigger code generation for line ` "#35; generate function ` - GLR->>PG: fetch X Ray report for project and language - PG->>GLR: xray_reports record - GLR->>GLR: include first 50 entities from xray report into code generation prompt - GLR->>-AIGW: trigger code generation ` "#35; generate function ` -``` - -### Embeddings prospect application - -As described in retrieval section above, currently Repository X Ray reports follows very naive approach, that does not iclude any metric for assesing relevance between Repository X Ray report content and user instruction. Therefore applying embeddings and semantic search to X Ray report has a high potential of improving results by selecting limited set of related entries from Repository X Ray report based on user instruction. - -To achieve that embeddings should be generated during Repository X Ray ingestion. Additionaly an user instruction should be turned into embeddings vector to perform semantic search over stored Repository X Ray report data during retrieval process. + + + + diff --git a/doc/architecture/blueprints/gitlab_rag/vertex_ai_search.md b/doc/architecture/blueprints/gitlab_rag/vertex_ai_search.md index e3c012b1418..62932f89a8a 100644 --- a/doc/architecture/blueprints/gitlab_rag/vertex_ai_search.md +++ b/doc/architecture/blueprints/gitlab_rag/vertex_ai_search.md @@ -1,87 +1,11 @@ --- -status: proposed -creation-date: "2024-02-20" -authors: [ "@bvenker", "@mikolaj_wawrzyniak" ] -coach: [ "@stanhu" ] -approvers: [ "@pwietchner", "@oregand", "@shinya.meda", "@mikolaj_wawrzyniak" ] -owning-stage: "~devops::data stores" -participating-stages: ["~devops::ai-powered", "~devops::create"] +redirect_to: 'https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/gitlab_rag/vertex_ai_search/' +remove_date: '2025-07-08' --- -# Vertex AI Search +This document was moved to [another location](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/gitlab_rag/vertex_ai_search/). -This page explains how to retrieve data from Google Vertex AI Search for [RAG](index.md). - -## Overview - -Some of our data are public resources that don't require [data access check](index.md#data-access-policy) when retrieving. -These data are often identical across GitLab instances so it's redundant to ingest the same data into every single database. -It'd be more efficient to serve the data from the single service. - -We can use [Vertex AI Search](https://cloud.google.com/products/agent-builder?hl=en) in this case. -It can search at scale, with high queries per second (QPS), high recall, low latency, and cost efficiency. - -This approach allows us to minimize code that we can't update on a customer's behalf, which means avoiding hard-coding AI-related logic in the GitLab monolith codebase. We can retain the flexibility to make changes in our product without asking customers to upgrade their GitLab version. -This is same with the [AI Gateway](../ai_gateway/index.md)'s design principle. - -```mermaid -flowchart LR - subgraph GitLab managed - subgraph AIGateway - VertexAIClient["VertexAIClient"] - end - - subgraph Vertex AI Search["Vertex AI Search"] - subgraph SearchApp1["App"] - direction LR - App1DataStore(["BigQuery"]) - end - subgraph SearchApp2["App"] - direction LR - App2DataStore(["Cloud Storage / Website URLs"]) - end - end - end - - subgraph SM or SaaS GitLab - DuoFeatureA["Duo feature A"] - DuoFeatureB["Duo feature B"] - end - - DuoFeatureA -- Semantic search --- VertexAIClient - DuoFeatureB -- Semantic search --- VertexAIClient - VertexAIClient -- Search from Gitlab Docs --- SearchApp1 - VertexAIClient -- Search from other data store --- SearchApp2 -``` - -## Limitations - -- Data **must be** [GREEN level](index.md#data-access-policy) and publicly shareable. - - Examples: - - GitLab documentations (`gitlab-org/gitlab/doc`, `gitlab-org/gitlab-runner/docs`, `gitlab-org/omnibus-gitlab/doc`, etc) - - Dynamically construct few-shot prompt templates with [Example selectors](https://python.langchain.com/v0.1/docs/modules/model_io/prompts/example_selectors/). - -**IMPORTANT: We do NOT persist customer data into Vertex AI Search. See the other solutions for persisting customer data.** - -## Performance and scalability implications - -- GitLab-side: Vertex AI Search can [search at scale, with high queries per second (QPS), high recall, low latency, and cost efficiency](https://cloud.google.com/vertex-ai/docs/vector-search/overview). -- GitLab-side: Vertex AI Search supports [global and multi-region deployments](https://cloud.google.com/generative-ai-app-builder/docs/locations). -- Customer-side: The outbound requests from their GitLab Self-managed instances could cause more network latency than retrieving from a local vector store. - This latency issue is addressable by multi-region deployments. - -## Availability - -- Customer-side: Air-gapped solutions can't be supported due to the required access to AI Gateway (`cloud.gitlab.com`). - This concern would be negligible since GitLab Duo already requires the access. -- Customer-side: Since the service is the single point of failure, retrievers stop working when the service is down. - -## Cost implications - -- GitLab-side: See [Vertex AI Search pricing](https://cloud.google.com/generative-ai-app-builder/pricing). -- Customer-side: No additional cost required. - -## Maintenance - -- GitLab-side: GitLab needs to maintain the data store (e.g. Structured data in Bigquery or unstructured data in Cloud Storage). Google automatically detects the schema and indexes the stored data. -- Customer-side: No maintenance required. + + + + diff --git a/doc/architecture/blueprints/gitlab_steps/data.drawio.png b/doc/architecture/blueprints/gitlab_steps/data.drawio.png deleted file mode 100644 index 5ffe29641346e7a1f891e8da7bc16e59e238a1b5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19270 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. --> + + + diff --git a/doc/architecture/blueprints/gitlab_steps/gitlab-ci.md b/doc/architecture/blueprints/gitlab_steps/gitlab-ci.md index 8f97c307b37..caeda878944 100644 --- a/doc/architecture/blueprints/gitlab_steps/gitlab-ci.md +++ b/doc/architecture/blueprints/gitlab_steps/gitlab-ci.md @@ -1,247 +1,11 @@ --- -owning-stage: "~devops::verify" -description: Usage of the [GitLab Steps](index.md) with [`.gitlab-ci.yml`](../../../ci/yaml/index.md). +redirect_to: 'https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/gitlab_steps/gitlab-ci/' +remove_date: '2025-07-08' --- -# Usage of the [GitLab Steps](index.md) with [`.gitlab-ci.yml`](../../../ci/yaml/index.md) +This document was moved to [another location](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/gitlab_steps/gitlab-ci/). -This document describes how [GitLab Steps](index.md) are integrated into the `.gitlab-ci.yml`. - -GitLab Steps will be integrated using a three-stage execution cycle -and replace `before_script:`, `script:` and `after_script:`. - -- `setup:`: Execution stage responsible for provisioning the environment, - including cloning the repository, restoring artifacts, or installing all dependencies. - This stage will replace implicitly cloning, restoring artifacts, and cache download. -- `run:`: Execution stage responsible for running a test, build, - or any other main command required by that job. -- `teardown:`: Execution stage responsible for cleaning the environment, - uploading artifacts, or storing cache. This stage will replace implicit - artifacts and cache uploads. - -Before we can achieve three-stage execution we will ship minimal initial support -that does not require any prior GitLab integration. - -## Phase 1: Initial support - -Initially the Step Runner will be used externally, without any prior dependencies -to GitLab: - -- The `step-runner` will be provided as part of a container image. -- The `step-runner` will be explicitly run in the `script:` section. -- The `$STEPS` environment variable will be executed as [`type: steps`](step-definition.md#the-steps-step-type). - -```yaml -hello-world: - image: registry.gitlab.com/gitlab-org/step-runner - variables: - STEPS: | - - step: gitlab.com/josephburnett/component-hello-steppy@master - inputs: - greeting: "hello world" - script: - - /step-runner ci -``` - -## Phase 2: The addition of `run:` to `.gitlab-ci.yml` - -In Phase 2 we will add `run:` as a first class way to use GitLab Steps: - -- `run:` will use a [`type: steps`](step-definition.md#the-steps-step-type) syntax. -- `run:` will replace usage of `before_script`, `script` and `after_script`. -- All existing functions to support Git cloning, artifacts, and cache would continue to be supported. -- It is yet to be defined how we would support `after_script`, which is executed unconditionally - or when the job is canceled. -- `run:` will not be allowed to be combined with `before_script:`, `script:` or `after_script:`. -- GitLab Rails would not parse `run:`, instead it would only perform static validation - with a JSON schema provided by the Step Runner. - -```yaml -hello-world: - image: registry.gitlab.com/gitlab-org/step-runner - run: - - step: gitlab.com/josephburnett/component-hello-steppy@master - inputs: - greeting: "hello world" -``` - -The following example would **fail** syntax validation: - -```yaml -hello-world: - image: registry.gitlab.com/gitlab-org/step-runner - run: - - step: gitlab.com/josephburnett/component-hello-steppy@master - inputs: - greeting: "hello world" - script: echo "This is ambiguous and invalid example" -``` - -### Transitioning from `before_script:`, `script:` and `after_script:` - -GitLab Rails would automatically convert the `*script:` syntax into relevant `run:` specification: - -- Today `before_script:` and `script:` are joined together as a single script for execution. -- The `after_script:` section is always executed in a separate context, representing a separate step to be executed. -- It is yet to be defined how we would retain the existing behavior of `after_script`, which is always executed - regardless of the job status or timeout, and uses a separate timeout. -- We would retain all implicit behavior which defines all environment variables when translating `script:` - into step-based execution. - -For example, this CI/CD configuration: - -```yaml -hello-world: - before_script: - - echo "Run before_script" - script: - - echo "Run script" - after_script: - - echo "Run after_script" -``` - -Could be translated into this equivalent specification: - -```yaml -hello-world: - run: - - step: gitlab.com/gitlab-org/components/steps/legacy/script@v1.0 - inputs: - script: - - echo "Run before_script" - - echo "Run script" - - step: gitlab.com/gitlab-org/components/steps/legacy/script@v1.0 - inputs: - script: - - echo "Run after_script" - when: always -``` - -## Phase 3: The addition of `setup:` and `teardown:` to `.gitlab-ci.yml` - -The addition of `setup:` and `teardown:` will replace the implicit functions -provided by GitLab Runner: Git clone, artifacts and cache handling: - -- The usage of `setup:` would stop GitLab Runner from implicitly cloning the repository. -- `artifacts:` and `cache:`, when specified, would be translated and appended to `setup:` and `teardown:` - to provide backward compatibility for the old syntax. -- `release:`, when specified, would be translated and appended to `teardown:` - to provide backward compatibility for the old syntax. -- `setup:` and `teardown:` could be used in `default:` to simplify support - of common workflows like where the repository is cloned, or how the artifacts are handled. -- The split into 3-stage execution additionally improves composability of steps with `extends:`. -- The `hooks:pre_get_sources_script` would be implemented similar to [`script:`](#transitioning-from-before_script-script-and-after_script) - and be prepended to `setup:`. - -For example, this CI/CD configuration: - -```yaml -rspec: - script: - - echo "This job uses a cache." - artifacts: - paths: [binaries/, .config] - cache: - key: binaries-cache - paths: [binaries/*.apk, .config] -``` - -Could be translated into this equivalent specification executed by a step runner: - -```yaml -rspec: - setup: - - step: gitlab.com/gitlab-org/components/git/clone@v1.0 - - step: gitlab.com/gitlab-org/components/artifacts/download@v1.0 - - step: gitlab.com/gitlab-org/components/cache/restore@v1.0 - inputs: - key: binaries-cache - run: - - step: gitlab.com/gitlab-org/components/steps/legacy/script@v1.0 - inputs: - script: - - echo "This job uses a cache." - teardown: - - step: gitlab.com/gitlab-org/components/artifacts/upload@v1.0 - inputs: - paths: [binaries/, .config] - - step: gitlab.com/gitlab-org/components/cache/restore@v1.0 - inputs: - key: binaries-cache - paths: [binaries/*.apk, .config] -``` - -### Inheriting common operations with `default:` - -`setup:` and `teardown:` are likely to become very verbose over time. One way to simplify them -is to allow inheriting the common `setup:` and `teardown:` operations -with `default:`. - -The previous example could be simplified to: - -```yaml -default: - setup: - - step: gitlab.com/gitlab-org/components/git/clone@v1.0 - - step: gitlab.com/gitlab-org/components/artifacts/download@v1.0 - - step: gitlab.com/gitlab-org/components/cache/restore@v1.0 - inputs: - key: binaries-cache - teardown: - - step: gitlab.com/gitlab-org/components/artifacts/upload@v1.0 - inputs: - paths: [binaries/, .config] - - step: gitlab.com/gitlab-org/components/cache/restore@v1.0 - inputs: - key: binaries-cache - paths: [binaries/*.apk, .config] - -rspec: - run: - - step: gitlab.com/gitlab-org/components/steps/legacy/script@v1.0 - inputs: - script: - - echo "This job uses a cache." - -linter: - run: - - step: gitlab.com/gitlab-org/components/steps/legacy/script@v1.0 - inputs: - script: - - echo "Run linting" -``` - -### Parallel jobs and `setup:` - -With the introduction of `setup:` at some point in the future we will introduce -an efficient way to parallelize the jobs: - -- `setup:` would define all steps required to provision the environment. -- The result of `setup:` would be snapshot and distributed as the base - for all parallel jobs, if `parallel: N` is used. -- The `run:` and `teardown:` would be run on top of cloned job, and all its services. -- The runner would control and intelligently distribute all parallel - jobs, significantly cutting the resource requirements for fixed - parts of the job (Git clone, artifacts, installing dependencies.) - -```yaml -rspec-parallel: - image: ruby:3.2 - services: [postgres, redis] - parallel: 10 - setup: - - step: gitlab.com/gitlab-org/components/git/clone@v1.0 - - step: gitlab.com/gitlab-org/components/artifacts/download@v1.0 - inputs: - jobs: [setup-all] - - script: bundle install --without production - run: - - script: bundle exec knapsack -``` - -Potential GitLab Runner flow: - -1. Runner receives the `rspec-parallel` job with `setup:` and `parallel:` configured. -1. Runner executes a job on top of Kubernetes cluster using block volumes up to the `setup`. -1. Runner then runs 10 parallel jobs in Kubernetes, overlaying the block volume from 2 - and continue execution of `run:` and `teardown:`. + + + + diff --git a/doc/architecture/blueprints/gitlab_steps/implementation.md b/doc/architecture/blueprints/gitlab_steps/implementation.md index d8480cfb2be..48400a0f622 100644 --- a/doc/architecture/blueprints/gitlab_steps/implementation.md +++ b/doc/architecture/blueprints/gitlab_steps/implementation.md @@ -1,339 +1,11 @@ --- -owning-stage: "~devops::verify" -description: Implementation details for [CI Steps](index.md). +redirect_to: 'https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/gitlab_steps/implementation/' +remove_date: '2025-07-08' --- -# Design and implementation details +This document was moved to [another location](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/gitlab_steps/implementation/). -## Baseline Step Proto - -The internals of Step Runner operate on the baseline step definition -which is defined in Protocol Buffer. All GitLab CI steps (and other -supported formats such as GitHub Actions) compile / fold to baseline steps. -Both step invocations in `.gitlab-ci.yml` and step definitions -in `step.yml` files will be compiled to baseline structures. -The term "step" means "baseline step" for the remainder of this document. - -Each step includes a reference `ref` in the form of a URI. The method of -retrieval is determined by the protocol of the URI. - -Steps and step traces have fields for inputs, outputs, -environment variables and environment exports. -After steps are downloaded and the `step.yml` is parsed -a step definition `def` will be added. -If a step defines multiple additional steps then the -trace will include sub-traces for each sub-step. - -```protobuf -message Step { - string name = 1; - string step = 2; - map env = 3; - map inputs = 4; -} - -message Definition { - DefinitionType type = 1; - Exec exec = 2; - repeated Step steps = 3; - message Exec { - repeated string command = 1; - string work_dir = 2; - } -} - -enum DefinitionType { - definition_type_unspecified = 0; - exec = 1; - steps = 2; -} - -message Spec { - Content spec = 1; - message Content { - map inputs = 1; - message Input { - InputType type = 1; - google.protobuf.Value default = 2; - } - } -} - -enum InputType { - spec_type_unspecified = 0; - string = 1; - number = 2; - bool = 3; - struct = 4; - list = 5; -} - -message StepResult { - Step step = 1; - Spec spec = 2; - Definition def = 3; - enum Status { - unspecified = 0; - running = 1; - success = 2; - failure = 3; - } - Status status = 4; - map outputs = 5; - message Output { - string key = 1; - string value = 2; - bool masked = 3; - } - map exports = 6; - int32 exit_code = 7; - repeated StepResult children_step_results = 8; -} -``` - -## Step Caching - -Steps are cached locally by a key comprised of `location` -(URL), `version` and `hash`. This prevents the exact same component -from being downloaded multiple times. The first time a step is -referenced it will be downloaded (unless local) and the cache will -return the path to the folder containing `step.yml` and the other -step files. If the same step is referenced again, the same folder -will be returned without downloading. - -If a step is referenced which differs by version or hash from another -cached step, it will be re-downloaded into a different folder and -cached separately. - -## Execution Context - -State is kept by Step Runner across all steps in the form of -an execution context. The context contains the output of each step, -environment variables and overall job and environment metadata. -The execution context can be referenced by expressions in -GitLab CI steps provided by the workflow author. - -Example of context available to expressions in `.gitlab-ci.yml`: - -```yaml -steps: - previous_step: - outputs: - name: "hello world" -env: - EXAMPLE_VAR: "bar" -job: - id: 1234 -``` - -Expressions in step definitions can also reference execution -context. However they can only access overall -job and environment metadata and the inputs defined in `step.yml`. -They cannot access the outputs of previous steps. In order to -provide the output of one step to the next, the step input -values should include an expression which references another -step's output. - -Example of context available to expressions in `step.yml`: - -```yaml -inputs: - name: "foo" -env: - EXAMPLE_VAR: "bar" -job: - id: 1234 -``` - -E.g. this is not allowed in a `step.yml file` because steps -should not couple to one another. - -```yaml -spec: - inputs: - name: ---- -type: exec -exec: - command: [echo, hello, ${{ steps.previous_step.outputs.name }}] -``` - -This is allowed because the GitLab CI steps syntax passes data -from one step to another: - -```yaml -spec: - inputs: - name: ---- -type: exec -exec: - command: [echo, hello, ${{ inputs.name }}] -``` - -```yaml -steps: -- name: previous_step - ... -- name: greeting - inputs: - name: ${{ steps.previous_step.outputs.name }} -``` - -Therefore evaluation of expressions will done in two different kinds -of context. One as a GitLab CI Step and one as a step definition. - -### Step Inputs - -Step inputs can be given in several ways. They can be embeded -directly into expressions in an `exec` command (as above). Or they -can be embedded in expressions for environment variables set during -exec: - -```yaml -spec: - inputs: - name: ---- -type: exec -exec: - command: [greeting.sh] -env: - NAME: ${{ inputs.name }} -``` - -### Input Types - -Input values are stored as strings. But they can also have a type -associated with them. Supported types are: - -- `string` -- `bool` -- `number` -- `object` - -String type values can be any string. Bool type values must be either `true` -or `false` when parsed as JSON. Number type values must a valid float64 -when parsed as JSON. Object types will be a JSON serialization of -the YAML input structure. - -For example, these would be valid inputs: - -```yaml -steps: -- name: my_step - inputs: - foo: bar - baz: true - bam: 1 -``` - -Given this step definition: - -```yaml -spec: - inputs: - foo: - type: string - baz: - type: bool - bam: - type: number ---- -type: exec -exec: - command: [echo, ${{ inputs.foo }}, ${{ inputs.baz }}, ${{ inputs.bam }}] -``` - -And it would output `bar true 1` - -For an object type, these would be valid inputs: - -```yaml -steps: - name: my_step - inputs: - foo: - steps: - - name: my_inner_step - inputs: - name: steppy -``` - -Given this step definition: - -```yaml -spec: - inputs: - foo: - type: object ---- -type: exec -exec: - command: [echo, ${{ inputs.foo }}] -``` - -And it would output `{"steps":[{"name":"my_inner_step","inputs":{"name":"steppy"}}]}` - -### Outputs - -Output files are created into which steps can write their -outputs and environment variable exports. The file locations are -provided in `OUTPUT_FILE` and `ENV_FILE` environment variables. - -After execution Step Runner will read the output and environment -variable files and populate the trace with their values. The -outputs will be stored under the context for the executed step. -And the exported environment variables will be merged with environment -provided to the next step. - -Some steps can be of type `steps` and be composed of a sequence -of GitLab CI steps. These will be compiled and executed in sequence. -Any environment variables exported by nested steps will be available -to subsequent steps. And will be available to high level steps -when the nested steps are complete. E.g. entering nested steps does -not create a new "scope" or context object. Environment variables -are global. - -## Containers - -We've tried a couple approaches to running steps in containers. -In end we've decided to delegate steps entirely to a step runner -in the container. - -Here are the options considered: - -### Delegation (chosen option) - -A provision is made for passing complex structures to steps, which -is to serialize them as JSON (see Inputs above). In this way the actual -step to be run can be merely a parameter to step running in container. -So the outer step is a `docker/run` step with a command that executes -`step-runner` with a `steps` input parameter. The `docker/run` step will -run the container and then extract the output files from the container -and re-emit them to the outer steps. - -This same technique will work for running steps in VMs or whatever. -Step Runner doesn't have to know anything about containerizing or -isolation steps. - -### Special Compilation (rejected option) - -When we see the `image` keyword in a GitLab CI step we would download -and compile the "target" step. Then manufacture a `docker/run` step -and pass the complied `exec` command as an input. Then we would compile -the `docker/run` step and execute it. - -However this requires Step Runner to know how to construct a `docker/run` -step. Which couples Step Runner with the method of isolation, making -isolation in VMs and other methods more complicated. - -### Native Docker (rejected option) - -The baseline step can include provisions for running a step in a -Docker container. For example the step could include a `ref` "target" -field and an `image` field. - -However this also couples Step Runner with Docker and expands the role -of Step Runner. It is preferable to make Docker an external step -that Step Runner execs in the same way as any other step. + + + + diff --git a/doc/architecture/blueprints/gitlab_steps/index.md b/doc/architecture/blueprints/gitlab_steps/index.md index 19dca8f46e1..74bd5097627 100644 --- a/doc/architecture/blueprints/gitlab_steps/index.md +++ b/doc/architecture/blueprints/gitlab_steps/index.md @@ -1,239 +1,11 @@ --- -status: proposed -creation-date: "2023-08-23" -authors: [ "@ayufan", "@josephburnett" ] -coach: "@grzegorz" -approvers: [ "@dhershkovitch", "@DarrenEastman", "@cheryl.li" ] -owning-stage: "~devops::verify" -participating-stages: [ ] +redirect_to: 'https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/gitlab_steps/' +remove_date: '2025-07-08' --- -# Step Runner for executing GitLab Steps +This document was moved to [another location](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/gitlab_steps/). -## Summary - -This document describes architecture of a new component called Step Runner, the GitLab Steps syntax it uses, -and how the GitHub Actions support will be achieved. - -The competitive CI products [drone.io](https://www.drone.io/), -[GitHub Actions](https://docs.github.com/en/actions/creating-actions) -have a composable CI jobs execution in form of steps, or actions. - -Their usage and our prior evaluation of [GitLab Runner Plugins](https://gitlab.com/gitlab-org/gitlab/-/issues/15067) -shows a need for a better way to define CI job execution. - -## Glossary - -- GitLab Steps: a name of GitLab CI feature to define and use reusable components - within a single job execution context. -- Step Runner: a RFC implementation for GitLab Steps that provides compatibility with the GitHub Actions. -- GitHub Actions: similar to GitLab Steps, a reusable execution component used on GitHub. -- CI Catalog: a public or private component catalog that could be used to discover and use shared components. -- GitLab Rails: a main application responsible for pipeline execution, running on GitLab.com or on-premise installation. - -## Motivation - -Even though the current `.gitlab-ci.yml` file is reasonably flexible, it easily becomes very -complex when trying to support complex workflows. This complexity is represented -with repetitive patterns, a purpose-specific syntax, or a complex sequence of commands -to execute. - -This is particularly challenging, because the `.gitlab-ci.yml` file -is inflexible on more complex workflows that require fine-tuning or special behavior -for the CI job execution. Its prescriptive approach how to handle Git cloning, -when artifacts are downloaded, or how the shell script is being executed quite often -results in the need to work around the system for pipelines that are not "standard" -or when new features are requested. - -This proves especially challenging when trying to add a new syntax to the -`.gitlab-ci.yml` file -to support a specific feature, like [`secure files`](../../../ci/secure_files/index.md) -or `release:` keyword. Adding these special features on a syntax level -results in a more complex config, which is harder to maintain, and more complex -to deal with technical debt when requirements change. - -An example of the `drone.io` and the `GitHub Actions` shows that a lot of workflows do not -have to be part of CI syntax. Instead, they can be provided in the form of reusable components -that are configured in a generic way in the CI config, and later downloaded and executed according -to inputs and parameters. - -GitLab Steps is meant to fill that product-gap by following similar model to competitors -and to some extent staying compatible with them. The GitLab Steps is meant to replace all -purpose-specific syntax to handle specific features. By providing and using reusable components, -that are build outside of `.gitlab-ci.yml`, that are versioned, and requested when needed -this allows the customer much more flexibility, and allows us to iterate on a catalog much faster. - -The reusable components that are part of a CI job execution could be used from a publicily hosted -repository on GitLab.com, from on-premise repository of steps, or be fetched from local project. - -Each CI job would define a list of `steps:` to execute, that would reference GitLab Steps -or GitHub Actions. Those steps would be executed by the step runner directly in the context of -the target environment. GitLab Runner would be responsible to be connection between GitLab.com -(or on-premise installation) and Step Runner. - -### Goals - -GitLab Steps: - -- GitLab Steps defines a syntax and structure for GitLab specific Steps implementation. -- GitLab Steps are published in CI Catalog. -- GitLab Steps can be used across instances (federation). -- GitLab Steps do define `inputs` and `outputs`. -- GitLab Steps needs to explicitly request sensitive informations with expected permissions. - For example: secrets, variables, tokens. - -GitLab Inc. managed repository of GitLab Steps: - -- GitLab Inc. provides a repository of GitLab Steps that are a drop-in replacement - for all current purpose-specific syntax: `artifacts:`, `cache:`, `release:`, etc. -- GitLab Inc. will provide a generic step to execute `shell` steps supporting various - shells (`bash`, `powershell`). -- The usage of purpose-specific syntax might be eventually deprecated in favor of steps. - -Step Runner: - -- Step Runner is hosted in a separate project in `https://gitlab.com/gitlab-org`. -- Step Runner can be used to execute most of GitHub Actions. -- Step Runner is run as a process in a target environment. -- Step Runner can be used by user on their local machine to run steps of a specific CI job - from locally stored `.gitlab-ci.yml`. -- Step Runner is external component to GitLab Runner, the GitLab Runner does provision - environment, construct payload and pass execution to Step Runner. -- Step Runner is to replace all custom handling in GitLab Runner for `clone`, `artifacts`, - `caches`, `script` and `after_script`, and custom handling for all different shells (`bash`, `powershell`). -- Step Runner is responsible for parsing and compiling GitLab Steps and GitHub Actions. -- Step Runner is responsible for downloading, and managing repositories required by GitLab Steps and GitHub Actions. -- Step Runner does control and monitor execution flow of individual steps of execution. -- Step Runner is required to be executable from the command-line interface (CLI). It means that it can be configured either via config file, - or environment file, or be able to read `.gitlab-ci.yml`. -- Step Runner can expose gRPC or other programmable interface to run config or get trace from. - -Steps Execution: - -- Each Step is defined by a single published or locally defined GitLab Step, or GitHub Action. -- Each Step is executed depending on conditions that are defined by that step. -- Each Step is executed with least amount of information exposed. Exposed informations to step - are requested explicitly by the step. For example: only environment variables explicitly - requested by the step will be passed to the step. -- Each Step is considered untrusted. It means that even though some steps are trusted, the whole - CI job should be considered untrusted, since system cannot guarantee trust. -- Each Step describes its execution in a form of preconditions, versions used, and output produced. - This is meant to allow to sign steps execution for the purpose of creating reproducible builds. - -Backward compatibility: - -- All currently executable syntax (for example: `before_script:`, `script:`, `artifacts:`, `cache:`, etc.) - should be convertible by GitLab (Rails) - -## Non-Goals - -TBD - -## Proposal - -Step Runner will be a new go binary which lives at `https://gitlab.com/gitlab-org/step-runner`. -It will be able to accept a number of input formats which are compiled to a standard proto format. -Output will be a standard proto trace which will include details for debugging and reproducing the build. - -### Capabilities - -- Read steps - - from environment variable - - from `.gitlab-ci.yml` file - - from gRPC server in step-runner - - from commandline JSON input -- Compile GitLab Steps and GitHub Actions to a baseline step definition - - explicit inputs and outputs - - explicit environment and exports - - baseline steps can be type `exec` or more steps -- Download and run steps from: - - Git repos - - zip files - - locally provided -- A job can be composed of different kinds of steps - - steps can come from different sources and be run in different ways - - steps can access environment exports and output of previous steps -- Produce a step-by-step trace of execution - - including final inputs and outputs - - including final environment and exports - - including logs of each step - - each step specifies the exact runtime and component used (hash) - - (optional) masking sensitive inputs, outputs, environment and exports -- Replaying a trace - - reuses the exact runtimes and components from trace - - output of trace will be the same trace if build is deterministic - -### Example invocations - -#### Command line - -- `STEPS=$(cat steps.yml) step-runner ci` -- `step-runner local .gitlab-ci.yml --format gitlab-ci --job-name hello-world --output-file trace.json` -- `step-runner replay trace.json` -- `step-runner ci --port 8080` - -#### GitLab CI - -```yaml -hello-world: - image: registry.gitlab.com/gitlab-org/step-runner - variables: - STEPS: | - - step: gitlab.com/josephburnett/component-hello-steppy@master - inputs: - greeting: "hello ${{ env.name }}" - env: - name: world - script: - - /step-runner ci - artifacts: - paths: - - trace.json -``` - -### Basic compilation and execution process - -Steps as expressed in GitLab CI are complied to the baseline step definition. -Referenced steps are loaded and compiled to produce an `exec` command, -or to produce an additional list of GitLab CI steps which are compiled recursively. -Each steps is executed immediately after compilation so its output will be available for subsequent compilations. - -![diagram of data during compilation](data.drawio.png) - -Steps return outputs and exports via files which are collected by Step Runner after each step. -Finally all the compiled inputs and outputs for each step are collected in a step trace. - -![sequenced diagram of step runner compilation and execution](step-runner-sequence.drawio.png) - -### GitLab Steps definition and syntax - -- [Step Definition](step-definition.md). -- [Syntactic Sugar extensions](steps-syntactic-sugar.md). - -### Integration of GitLab Steps - -- [Usage of the GitLab Steps with `.gitlab-ci.yml`](gitlab-ci.md). -- [Runner Integration](runner-integration.md). - -## Design and implementation details - -### 2023-11-28 - GitLab Steps ADR 001: Bootstrap Step Runner - -- See the [GitLab Steps ADR 001: Bootstrap Step Runner](decisions/001_initial_support.md). -- See the [Baseline Step Proto](implementation.md). - -## References - -- [GitLab Issue #215511](https://gitlab.com/gitlab-org/gitlab/-/issues/215511) -- [Step Runner Code](https://gitlab.com/josephburnett/step-runner/-/tree/blueprint2). - This is the exploratory code created during the writing of this blueprint. - It shows the structure of the Step Runner binary and how the pieces fit together. - It runs but doesn't quite do the right thing (see all the TODOs). -- [CI Steps / CI Events / Executors / Taskonaut (video)](https://youtu.be/nZoO547IISM). - Some high-level discussion about how these 4 blueprints relate to each other. - And a good prequel to the video about this MR. -- [Steps in Runner (video)](https://youtu.be/82WLQ4zHYts). - A walk through of the Step Runner details from the code perspective. -- [CI YAML keywords](https://gitlab.com/gitlab-org/gitlab/-/issues/398129#note_1324467337). - An inventory of affected keywords. -- [GitLab Epic 11535](https://gitlab.com/groups/gitlab-org/-/epics/11535) + + + + diff --git a/doc/architecture/blueprints/gitlab_steps/runner-integration.md b/doc/architecture/blueprints/gitlab_steps/runner-integration.md index e0ce8cc83b7..1a6ecd5fa47 100644 --- a/doc/architecture/blueprints/gitlab_steps/runner-integration.md +++ b/doc/architecture/blueprints/gitlab_steps/runner-integration.md @@ -1,293 +1,11 @@ --- -owning-stage: "~devops::verify" -description: Runner integration for [CI Steps](index.md). +redirect_to: 'https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/gitlab_steps/runner-integration/' +remove_date: '2025-07-08' --- -# Runner Integration +This document was moved to [another location](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/gitlab_steps/runner-integration/). -## Non goals - -This proposal does not address deployment of the Step Runner binary into -target environments, nor of starting the Step Runner gRPC service -described below. The rest of the proposal assumes both that the Step -Runner binary exists in the target environment and that the gRPC service -is running and listening on a local socket. Similarly this proposal does -not address the life-cycle of the `Step Runner` service, and how to handle -things like restarting the service if it dies, or upgrades. - -See [Deployment and Lifecycle Management](service-deployment.md) for relevant blueprint. - -## Steps Service gRPC Definition - -The Step Runner service gRPC definition is as follows: - -```proto -service StepRunner { - rpc Run(RunRequest) returns (RunResponse); - rpc FollowSteps(FollowStepsRequest) returns (stream FollowStepsResponse); - rpc FollowLogs(FollowLogsRequest) returns (stream FollowLogsResponse); - rpc Finish(FinishRequest) returns (FinishResponse); - rpc Status(StatusRequest) returns (StatusResponse); -} - -message Variable { - string key = 1; - string value = 2; - bool file = 3; - bool masked = 4; -} - -message Job { - repeated Variable variables = 1; - string job_id = 2; - string pipeline_id = 3; - string build_dir = 4; - repeated string token_prefixes = 5; -} - -message Masking { - repeated string phrases = 1; - repeated string token_prefixes = 2; -} - -message RunRequest { - string id = 1; - string work_dir = 2; - map env = 3; - Masking masking = 4; - Job job = 5; - string steps = 6; -} - -message RunResponse { -} - -message FollowStepsRequest { - string id = 1; -} - -message FollowStepsResponse { - StepResult result = 1; -} - -message FollowLogsRequest { - string id = 1; - int32 offset = 2; -} - -message FollowLogsResponse { - bytes data = 1; -} - -message FinishRequest { - string id = 1; -} - -message FinishResponse { -} - -message Status { - string id = 1; - bool finished = 2; - int32 exit_code = 3; - google.protobuf.Timestamp start_time = 4; - google.protobuf.Timestamp end_time = 5; -} - -message StatusRequest { - string id = 1; -} - -message StatusResponse { - repeated Status jobs = 1; -} -``` - -Runner interacts with Step Runner over the above gRPC service which is -started on a local socket in the execution environment. Runner accesses -the local socket by first connecting to the target environment via -executor-specific protocols, then use a provided `proxy` command to -connect to the `gRPC` service, and transparently tunnel `gRPC` requests -from the Runner to Step Runner (see[Proxy Command](#proxy-command)). This -is the same way that Nesting serves a gRPC service in a dedicated Mac -instance. The service has five RPCs, `Run`, `FollowSteps`, `FollowLogs`, -`Finish` and `Status`. - -`Run` is the initial delivery of the steps. `FollowSteps` requests a -streaming response of step-result traces. `FollowLogs` similarly requests -a streaming response of output (`stdout`/`stderr`) written by processes -executed as part of running the steps, and logs produced by Step Runner -itself. `Finish` stops execution of the request (if still running) and -cleans up resources as soon as possible. `Status` lists the status of the -specified job, or if no job was specified, of all active jobs in the Step -Runner service (including completed but not `Finish`ed jobs). `Status` can -for example be used by a runner to recover after a crash. - -The Step Runner gRPC service will be able to execute multiple `Run` -payloads at once. That is, each call to `Run` will start a new goroutine -and execute the steps until completion. Multiple calls to `Run` may be -made simultaneously. - -As steps are executed, step-result traces and sub-process logs can be -streamed back to GitLab Runner. This allows Runner (or any caller) to -follow execution, at the step level for step-result traces -(`FollowSteps`), and as written for sub-process and Step Runner logs -(`FollowLogs`). Logs will be written in a [specific format](#log-format), -and sensitive tokens will be [masked](#masking) by Step Runner before -being streamed to Runner. - -All APIs excluding `Status` are idempotent, meaning that multiple calls to -the same API with the same parameters should return the same result. For -example, If `Run` is called multiple times with the same `id`, only the -first invocation should begin processing of the job request, and -subsequent invocations return a success status but otherwise do noting. -Similarly, multiple calls to `Finish` with the same `id` should finish and -remove the relevant job on the first call, and do nothing on subsequent -calls. - -The service should not assume clients will be well-behaved, and should be -able to handle clients that never call or prematurely disconnect from -either of the `Follow` APIs, and also clients that never call `Finish` on -a corresponding `Run` request. To this end the `Step Runner` process -should periodically perform a scan to identify and prune stale or -runaway/stuck jobs. A stale job could be a job that has finished some -specified time ago (and has not been `Finish`ed). A runaway job is a job -that has been running some (long) specified amount of time, possibly -without producing output. - -Finally, to facilitate integrating steps into the below runner executors, -it is recommended that steps provide a client library to coordinate -execution of the `Run`/`Follow*`/`Finish` APIs, and to handle reconnecting -to the step-runner service in the event that the `Follow*` calls loose -connectivity. - -## RunRequest Parameters - -Steps are delivered to Step Runner in the `RunRequest.Steps` field as a -JSON-serialized version of -[step.go](https://gitlab.com/gitlab-org/step-runner/-/blob/main/schema/v1/step.go), -with no processing of the step definition required by runner itself. The -`id` field uniquely identifies each request running on the `Step Runner` -service. The `RunRequest.Env` field holds environment variable that are to -be injected into the environment when each step is executed. - -The optional `Job` parameter will include select parameters from the -corresponding CI job. `Job` will include the corresponding CI job's build -directory; `Job.BuildDir` should be copied to `RunRequest.WorkDir`, and -all steps in a request should be invoked in that directory to preserve -existing job script behavior. The `RunRequest` will also include the CI -job's environment variables (i.e. the `variables` defined at the job and -global levels in the CI configuration). When a `RunRequest` is made by -Runner, variables must be included in `Job.Variables`, and -`RunRequest.Env` should be left empty. When the run request is processed, -file-type variables will be written to file, variables will be expanded, -copied into `RunRequest.Env`, and the `Job` field will be discarded from -the remainder of the request. Variables should be expanded by the Step -Runner service since they may reference object in the execution -environment (like other environment variables or paths). This includes -file-type variables, which should be written to the same path as they -are be in traditional runner job execution. Similarly, from -`Job.Variables`, phrases to be masked should be extracted and used to -populate `Masking.Phrases`, and `Job.TokenPrefixes` should be copied into -`Masking.TokenPrefixes`. - -Clients other than Runner wishing to run steps can omit the `Job` field, -and in this case the `Masking` and `Env` fields should be populated -directly by the caller. - -## Log Format - -Log lines emitted buy the `FollowLogs` API should have the format - -```plaintext - -``` - -This is the same log format introduce into runner in [this merge request](https://gitlab.com/gitlab-org/gitlab-runner/-/merge_requests/4591). -The logging library used to produce this format should be shared between -`GitLab Runner` and `Step Runner`. - -## Masking - -`Step Runner` will be responsible for masking sensitive variables or -tokens. This should be done before the raw log message is formatted into -the above log format. The libraries used to mask variables should shared -between `GitLab Runner` and `Step Runner`. (See -[relevant](https://gitlab.com/gitlab-org/gitlab-runner/-/blob/main/helpers/trace/internal/tokensanitizer/token_masker.go) -[modules](https://gitlab.com/gitlab-org/gitlab-runner/-/blob/main/helpers/trace/internal/masker/masker.go)). - -## Proxy Command - -The `Step Runner` binary will include a command to proxy data from -(typically text-based) `stdin`/`stdout`/`stderr`-based protocols to the -gRPC service. This command will run in the same host as the gRPC service, -and will read input from `stdin`, forward it to the gRPC service over a -local socket, receive output from the gRPC service over same socket, and -forward it to the client via `stdout`/`stderr`. This command will enable -clients (like Runner) to transparently tunnel to the `gRPC` service via -`stdin`/`stderr`/`stdout`-based protocols like SSH or `docker exec`, which -will eliminate the need to expose the Step Runner service's gRPC port on -Docker images, or set up SSH port forwarding on VMs, and will allow runner -to interact with `Step Runner` using established protocols (i.e. SSH and -`docker exec`). `stdout` should be reserved for writing responses from the -`Step Runner` service, and `stderr` should be reserved for errors -originating in the `proxy` command itself. - -## Executors - -Here is how GitLab Runner will connect to Step Runner in each runner -executor: - -### Instance - -The Instance executor is accessed via SSH, the same as today. However -instead of starting a bash shell and piping in commands, it invokes the -[proxy command](#proxy-command), which in turn connects to the Step -Runner socket in a known location. Runner can then make `gRPC` calls -directly, and transparently tunnel through the `SSH` connection to the -`gRPC` service. This is the same as how Runner calls the Nesting server in -dedicated Mac instances to make VMs. - -This requires that Step Runner is present and started in the job -execution environment. - -### Docker - -The same requirement that Step Runner is present and the gRPC service is -running is true for the Docker executor (and `docker-autoscaler`). However -in order to connect to the gRPC service inside the container, Runner will -`docker exec` to the container and execute the proxy command to connect to -the gRPC service in the container. The client can then write to the -`docker exec`'s `stdin`, which will transparently be proxied to the gRPC -service, and read from its `stdout/stderr`, which will contain responses -from the gRPC service. - -### Kubernetes - -The Kubelet on Kubernetes Nodes exposes an exec API which will start a -process in a container of a running Pod. We will use this to `exec create` -a bridge process that will allow the caller to make `gRPC` calls inside -the Pod, similar to the Docker executor. - -In order to access to this protected Kubelet API we must use the -Kubernetes API which provides an exec sub-resource on Pod. A caller -can POST to the URL of a pod suffixed with `/exec` and then negotiate -the connection up to a SPDY protocol for bidirectional byte -streaming. So GitLab Runner can use the Kubernetes API to connect to -the Step Runner service and deliver job payloads. - -This is the same way that `kubectl exec` works. In fact most of the -internals such as SPDY negotiation are provided as `client-go` -libraries. So Runner can call the Kubernetes API directly by -importing the necessary libraries rather than shelling out to -Kubectl. - -Historically one of the weaknesses of the Kubernetes Executor was -running a whole job through a single exec. To mitigate this Runner -uses the attach command instead, which can "re-attach" to an existing -shell process and pick up where it left off. - -This is not necessary for Step Runner however, because the exec is -just establishing a bridge to the long-running gRPC process. If the -connection drops, Runner will just "re-attach" by exec'ing another -connection and continuing to make RPC calls like `follow`. + + + + diff --git a/doc/architecture/blueprints/gitlab_steps/service-deployment.md b/doc/architecture/blueprints/gitlab_steps/service-deployment.md index 0dc42b558a7..094ae013e5c 100644 --- a/doc/architecture/blueprints/gitlab_steps/service-deployment.md +++ b/doc/architecture/blueprints/gitlab_steps/service-deployment.md @@ -1,18 +1,11 @@ --- -owning-stage: "~devops::verify" -description: Steps Runner Deployment and Lifecycle Management for [Runner Integration](runner-integration.md). - +redirect_to: 'https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/gitlab_steps/service-deployment/' +remove_date: '2025-07-08' --- -# Steps Runner Deployment and Lifecycle Management +This document was moved to [another location](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/gitlab_steps/service-deployment/). -This Blueprint is concerned with: - -- The deployment or injection of the Step Runner binary into target - environments. This includes build containers for Docker, Kubernetes and - Instance executors. -- Startup of the Step Runner gRPC service in said environments. -- Any required install-time configuration. -- Service restart in the event of a crash. -- Step Runner binary upgrade for environments where the Step Runner service is long lived. -- Management of any resources used by the Step Runner service + + + + diff --git a/doc/architecture/blueprints/gitlab_steps/step-definition.md b/doc/architecture/blueprints/gitlab_steps/step-definition.md index 08ca1ab7c31..7d0a937a2c8 100644 --- a/doc/architecture/blueprints/gitlab_steps/step-definition.md +++ b/doc/architecture/blueprints/gitlab_steps/step-definition.md @@ -1,368 +1,11 @@ --- -owning-stage: "~devops::verify" -description: The Step Definition for [GitLab Steps](index.md). +redirect_to: 'https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/gitlab_steps/step-definition/' +remove_date: '2025-07-08' --- -# The Step definition +This document was moved to [another location](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/gitlab_steps/step-definition/). -A step is the minimum executable unit that user can provide and is defined in a `step.yml` file. - -The following step definition describes the minimal syntax supported. -The syntax is extended with [syntactic sugar](steps-syntactic-sugar.md). - -A step definition consists of two documents. The purpose of the document split is -to distinguish between the declaration and implementation: - -1. [Specification / Declaration](#step-specification): - - Provides the specification which describes step inputs and outputs, - as well any other metadata that might be needed by the step in the future (license, author, etc.). - In programming language terms, this is similar to a function declaration with arguments and return values. - -1. [Implementation](#step-implementation): - - The implementation part of the document describes how to execute the step, including how the environment - has to be configured, or how actions can be configured. - -## Example step that prints a message to stdout - -In the following step example: - -1. The declaration specifies that the step accepts a single input named `message`. - The `message` is a required argument that needs to be provided when running the step - because it does not define `default:`. -1. The implementation section specifies that the step is of type `exec`. When run, the step - will execute an `echo` command with a single argument (the `message` value). - -```yaml -# .gitlab/ci/steps/exec-echo.yaml -spec: - inputs: - message: ---- -type: exec -exec: - command: [echo, "${{inputs.message}}"] -``` - -## Step specification - -The step specification currently only defines inputs and outputs: - -- Inputs: - - Can be required or optional. - - Have a name and can have a description. - - Can contain a list of accepted options. Options limit what value can be provided for the input. - - Can define matching regexp. The matching regexp limits what value can be provided for the input. - - Can be expanded with the usage of syntax `${{ inputs.input_name }}`. -- All **input values** can be accessed when `type: exec` is used, - by decoding the `$STEP_JSON` file that does provide information about the context of the execution. -- Outputs: - - Have a name and can have a description. - - Can be set by writing to a special [dotenv](https://github.com/bkeepers/dotenv) file named: - `$OUTPUT_FILE` with a format of `output_name=VALUE` per output. - -For example: - -```yaml -spec: - inputs: - message_with_default: - default: "Hello World" - message_that_is_required: - description: "This description explains that the input is required, because it does not specify a default:" - type_with_limited_options: - options: [bash, powershell, detect] - type_with_default_and_limited_options: - default: bash - options: [bash, powershell, detect] - description: "Since the options are provided, the default: needs to be one of the options" - version_with_matching_regexp: - match: ^v\d+\.\d+$ - description: "The match pattern only allows values similar to `v1.2`" - outputs: - code_coverage: - description: "Measured code coverage that was calculated as part of the step" ---- -type: steps -steps: - - step: ./bash-script.yaml - inputs: - script: "echo Code Coverage = 95.4% >> $OUTPUT_FILE" -``` - -## Step Implementation - -The step definition can use the following types to implement the step: - -- `type: exec`: Run a binary command, using STDOUT/STDERR for tracing the executed process. -- `type: steps`: Run a sequence of steps. -- `type: parallel` (Planned): Run all steps in parallel, waiting for all of them to finish. -- `type: grpc` (Planned): Run a binary command but use gRPC for intra-process communication. -- `type: container` (Planned): Run a nested Step Runner in a container image of choice, - transferring all execution flow. - -### The `exec` step type - -The ability to run binary commands is one of the primitive functions: - -- The command to execute is defined by the `exec:` section. -- The result of the execution is the exit code of the command to be executed, unless the default behavior is overwritten. -- The default working directory in which the command is executed is the directory in which the - step is located. -- By default, the command is not time-limited, but can be time-limited during job execution with `timeout:`. - -For example, an `exec` step with no inputs: - -```yaml -spec: ---- -type: exec -exec: - command: [/bin/bash, ./my-script.sh] - timeout: 30m - workdir: /tmp -``` - -#### Example step that executes user-defined command - -The following example is a minimal step definition that executes a user-provided command: - -- The declaration section specifies that the step accepts a single input named `script`. -- The `script` input is a required argument that needs to be provided when running the step - because no `default:` is defined. -- The implementation section specifies that the step is of type `exec`. When run, the step - will execute in `bash` passing the user command with `-c` argument. -- The command to be executed will be prefixed with `set -veo pipefail` to print the execution - to the job log and exit on the first failure. - -```yaml -# .gitlab/ci/steps/exec-script.yaml - -spec: - inputs: - script: - description: 'Run user script.' ---- -type: exec -exec: - command: [/usr/bin/env, bash, -c, "set -veo pipefail; ${{inputs.script}}"] -``` - -### The `steps` step type - -The ability to run multiple steps in sequence is one of the primitive functions: - -- A sequence of steps is defined by an array of step references: `steps: []`. -- The next step is run only if previous step succeeded, unless the default behavior is overwritten. -- The result of the execution is either: - - A failure at the first failed step. - - Success if all steps in sequence succeed. - -#### Steps that use other steps - -The `steps` type depends extensively on being able to use other steps. -Each item in a sequence can reference other external steps, for example: - -```yaml -spec: ---- -type: steps -steps: - - step: ./.gitlab/ci/steps/ruby/install.yml - inputs: - version: 3.1 - env: - HTTP_TIMEOUT: 10s - - step: gitlab.com/gitlab-org/components/bash/script@v1.0 - inputs: - script: echo Hello World -``` - -The `step:` value is a string that describes where the step definition is located: - -- **Local**: The definition can be retrieved from a local source with `step: ./path/to/local/step.yml`. - A local reference is used when the path starts with `./` or `../`. - The resolved path to another local step is always **relative** to the location of the current step. - There is no limitation where the step is located in the repository. -- **Remote**: The definition can also be retrieved from a remote source with `step: gitlab.com/gitlab-org/components/bash/script@v1.0`. - Using a FQDN makes the Step Runner pull the repository or archive containing - the step, using the version provided after the `@`. - -The `inputs:` section is a list of key-value pairs. The `inputs:` specify values -that are passed and matched against the [step specification](#step-specification). - -The `env:` section is a list of key-value pairs. `env:` exposes the given environment -variables to all children steps, including [`type: exec`](#the-exec-step-type) or [`type: steps`](#the-steps-step-type). - -#### Remote Steps - -To use remote steps with `step: gitlab.com/gitlab-org/components/bash/script@v1.0` -the step definitions must be stored in a structured-way. The step definitions: - -- Must be stored in the `steps/` folder. -- Can be nested in sub-directories. -- Can be referenced by the directory name alone if the step definition - is stored in a `step.yml` file. - -For example, the file structure for a repository hosted in `git clone https://gitlab.com/gitlab-org/components.git`: - -```plaintext -├── steps/ -├── ├── secret_detection.yml -| ├── sast/ -│ | └── step.yml -│ └── dast -│ ├── java.yml -│ └── ruby.yml -``` - -This structure exposes the following steps: - -- `step: gitlab.com/gitlab-org/components/secret_detection@v1.0`: From the definition stored at `steps/secret_detection.yml`. -- `step: gitlab.com/gitlab-org/components/sast@v1.0`: From the definition stored at `steps/sast/step.yml`. -- `step: gitlab.com/gitlab-org/components/dast/java@v1.0`: From the definition stored at `steps/dast/java.yml`. -- `step: gitlab.com/gitlab-org/components/dast/ruby@v1.0`: From the definition stored at `steps/dast/ruby.yml`. - -#### Example step that runs other steps - -The following example is a minimal step definition that -runs other steps that are local to the current step. - -- The declaration specifies that the step accepts two inputs, each with - a default value. -- The implementation section specifies that the step is of type `steps`, meaning - the step will execute the listed steps in sequence. The usage of a top-level - `env:` makes the `HTTP_TIMEOUT` variable available in all executed steps. - -```yaml -spec: - inputs: - ruby_version: - default: 3.1 - http_timeout: - default: 10s ---- -type: steps -env: - HTTP_TIMEOUT: ${{inputs.http_timeout}} -steps: - - step: ./.gitlab/ci/steps/exec-echo.yaml - inputs: - message: "Installing Ruby ${{inputs.ruby_version}}..." - - step: ./.gitlab/ci/ruby/install.yaml - inputs: - version: ${{inputs.ruby_version}} -``` - -## Context and interpolation - -Every step definition is executed in a context object which -stores the following information that can be used by the step definition: - -- `inputs`: The list of inputs, including user-provided or default. -- `outputs`: The list of expected outputs. -- `env`: The current environment variable values. -- `job`: The metadata about the current job being executed. - - `job.project`: Information about the project, for example ID, name, or full path. - - `job.variables`: All [CI/CD Variables](../../../ci/variables/predefined_variables.md) as provided by the CI/CD execution, - including project variables, predefined variables, etc. - - `job.pipeline`: Information about the current executed pipeline, like the ID, name, full path -- `step`: Information about the current executed step, like the location of the step, the version used, or the [specification](#step-specification). -- `steps` (only for `type: exec`): - Information about each step in sequence to be run, containing information about the - result of the step execution, like status or trace log. - - `steps..status`: The status of the step, like `success` or `failed`. - - `steps..outputs.`: To fetch the output provided by the step - -The context object is used to enable support for the interpolation in the form of `${{ }}`. - -Interpolation: - -- Is forbidden in the [step specification](#step-specification) section. - The specification is static configuration that should not affected by the runtime environment. -- Can be used in the [step implementation](#step-implementation) section. The implementation - describes the runtime set of instructions for how step should be executed. -- Is applied to every value of the hash of each data structure. -- Of the *values* of each hash is possible (for now). The interpolation of *keys* is forbidden. -- Is done when executing and passing control to a given step, instead of running - it once when the configuration is loaded. This enables chaining outputs to inputs, or making steps depend on the execution - of earlier steps. - -For example: - -```yaml -# .gitlab/ci/steps/exec-echo.yaml -spec: - inputs: - timeout: - default: 10s - bash_support_version: ---- -type: steps -env: - HTTP_TIMEOUT: ${{inputs.timeout}} - PROJECT_ID: ${{job.project.id}} -steps: - - step: ./my/local/step/to/echo.yml - inputs: - message: "I'm currently building a project: ${{job.project.full_path}}" - - step: gitlab.com/gitlab-org/components/bash/script@v${{inputs.bash_support_version}} -``` - -## Reference data structures describing YAML document - -```go -package main - -type StepEnvironment map[string]string - -type StepSpecInput struct { - Default *string `yaml:"default"` - Description string `yaml:"description"` - Options *[]string `yaml:"options"` - Match *string `yaml:"match"` -} - -type StepSpecOutput struct { -} - -type StepSpecInputs map[string]StepSpecInput -type StepSpecOutputs map[string]StepSpecOutput - -type StepSpec struct { - Inputs StepSpecInput `yaml:"inputs"` - Outputs StepSpecOutputs `yaml:"outputs"` -} - -type StepSpecDoc struct { - Spec StepSpec `yaml:"spec"` -} - -type StepType string - -const StepTypeExec StepType = "exec" -const StepTypeSteps StepType = "steps" - -type StepDefinition struct { - Def StepSpecDoc `yaml:"-"` - Env StepEnvironment `yaml:"env"` - Steps *StepDefinitionSequence `yaml:"steps"` - Exec *StepDefinitionExec `yaml:"exec"` -} - -type StepDefinitionExec struct { - Command []string `yaml:"command"` - WorkingDir *string `yaml:"working_dir"` - Timeout *time.Duration `yaml:"timeout"` -} - -type StepDefinitionSequence []StepReference - -type StepReferenceInputs map[string]string - -type StepReference struct { - Step string `yaml:"step"` - Inputs StepReferenceInputs `yaml:"inputs"` - Env StepEnvironment `yaml:"env"` -} -``` + + + + diff --git a/doc/architecture/blueprints/gitlab_steps/step-runner-sequence.drawio.png b/doc/architecture/blueprints/gitlab_steps/step-runner-sequence.drawio.png deleted file mode 100644 index 57029733b3cd49534b10c0a98545a4e668fac14a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32938 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 L?gede4ZZO<4S%>n diff --git a/doc/architecture/blueprints/gitlab_steps/steps-syntactic-sugar.md b/doc/architecture/blueprints/gitlab_steps/steps-syntactic-sugar.md index 3ca54a45477..f03d626ca45 100644 --- a/doc/architecture/blueprints/gitlab_steps/steps-syntactic-sugar.md +++ b/doc/architecture/blueprints/gitlab_steps/steps-syntactic-sugar.md @@ -1,66 +1,11 @@ --- -owning-stage: "~devops::verify" -description: The Syntactic Sugar extensions to the Step Definition +redirect_to: 'https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/gitlab_steps/steps-syntactic-sugar/' +remove_date: '2025-07-08' --- -# The Syntactic Sugar extensions to the Step Definition +This document was moved to [another location](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/gitlab_steps/steps-syntactic-sugar/). -[The Step Definition](step-definition.md) describes a minimal required syntax -to be supported. To aid common workflows the following syntactic sugar is used -to extend different parts of that document. - -## Syntactic Sugar for Step Reference - -Each of syntactic sugar extensions is converted into the simple -[step reference](step-definition.md#steps-that-use-other-steps). - -### Easily execute scripts in a target environment - -`script:` is a shorthand syntax to aid execution of simple scripts, which cannot be used with `step:` -and is run by an externally stored step component provided by GitLab. - -The GitLab-provided step component performs shell auto-detection unless overwritten, -similar to how GitLab Runner does that now: based on a running system. - -`inputs:` and `env:` can be used for additional control of some aspects of that step component. - -For example: - -```yaml -spec: ---- -type: steps -steps: - - script: bundle exec rspec - - script: bundle exec rspec - inputs: - shell: sh # Force runner to use `sh` shell, instead of performing auto-detection -``` - -This syntax example translates into the following equivalent syntax for -execution by the Step Runner: - -```yaml -spec: ---- -type: steps -steps: - - step: gitlab.com/gitlab-org/components/steps/script@v1.0 - inputs: - script: bundle exec rspec - - step: gitlab.com/gitlab-org/components/steps/script@v1.0 - inputs: - script: bundle exec rspec - shell: sh # Force runner to use `sh` shell, instead of performing auto-detection -``` - -This syntax example is **invalid** (and ambiguous) because the `script:` and `step:` cannot be used together: - -```yaml -spec: ---- -type: steps -steps: - - step: gitlab.com/my-component/ruby/install@v1.0 - script: bundle exec rspec -``` + + + + diff --git a/doc/architecture/blueprints/gitlab_xray_rag/index.md b/doc/architecture/blueprints/gitlab_xray_rag/index.md index b3c902dfa65..bd24720339a 100644 --- a/doc/architecture/blueprints/gitlab_xray_rag/index.md +++ b/doc/architecture/blueprints/gitlab_xray_rag/index.md @@ -1,286 +1,11 @@ --- -status: ongoing -creation-date: "2024-04-23" -authors: [ "@mikolaj_wawrzyniak" ] -coach: "N/A" -approvers: [ "@jprovaznik", "@maddievn", "@mkaeppler" ] -owning-stage: "~devops::create" -participating-stages: ["~devops::data stores"] +redirect_to: 'https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/gitlab_xray_rag/' +remove_date: '2025-07-08' --- -# Repository X-Ray RAG +This document was moved to [another location](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/gitlab_xray_rag/). -Group ~"group::global search" is leading effort to build [RAG at GitLab](../gitlab_rag/index.md). Because this is a global effort, it is in the spirit of efficiency and collaboration values for ~"group::code creation" to join that effort and integrate [Repository X-Ray](https://gitlab.com/gitlab-org/code-creation/repository-x-ray#repository-x-ray) data into GitLab RAG. Doing so should not only result in more efficient resource allocation, but also enable other AI features to integrate and reuse Repository X-Ray data, for example users could ask questions to GitLab Duo Chat that could be answered with X-Ray report data. - -## Goal - -Integrate the existing Repository X-Ray scan flow with the RAG platform. - -## Proof of Concept - -Proof of concept has been built at [merge request 144715](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/144715). This MR contains a large amount of information that can be helpful during implementation. - -## Implementation - -### Current state - -Currently, Repository X-Ray does not use semantic search. The MVC approach -naively selects first 50 entities from the X-Ray report and includes them into a code generation request. -For more details, see the following diagrams. - -
Current state of Repository X-Ray diagrams - -The Repository X-Ray scan is processed as shown on the following diagram: - -```mermaid -sequenceDiagram - actor USR as User - participant RN as GitLab Runner - - - participant GLR as GitLab Rails - participant PG as GitLab PostgreSQL DB - participant AIGW as AI Gateway - - - USR->>GLR: commits changes
to a package manager file
eg. Gemfile.lock - GLR->>+RN: triggers Repository X Ray CI scanner job - loop for each batch of packages - RN->>GLR: Request packages description by AI - GLR->>AIGW: Forward request for packages description - AIGW->>GLR: Packages description - GLR->>RN: Forwards packages description - end - RN->>-GLR: Repository X Ray report - GLR->>+GLR: triggers Repository X Ray ingestion background job - GLR->>-PG: upserts xray_reports record -``` - -The report is later used as shown in the following diagram: - -```mermaid -sequenceDiagram - actor USR as User - participant IDE - participant PG as GitLab PostgreSQL DB - participant GLR as GitLab Rails - participant AIGW as AI Gateway - - USR->>+IDE: types: "#35; generate a function that transposes a matrix" - IDE->>+GLR: trigger code generation for line ` "#35; generate function ` - GLR->>PG: fetch X Ray report for project and language - PG->>GLR: xray_reports record - GLR->>GLR: include first 50 entities from xray report into code generation prompt - GLR->>-AIGW: trigger code generation ` "#35; generate function ` - -``` - -
- -### Desired outcome - -After this effort is completed, the Repository X-Ray scan is processed in the following manner: - -```mermaid -sequenceDiagram - actor USR as User - participant RN as GitLab Runner - participant GLR as GitLab Rails - participant ES as Elasticsearch - participant PG as GitLab PostgreSQL DB - participant AIGW as AI Gateway - - - USR->>GLR: commits changes
to a package manager file
eg. Gemfile.lock - GLR->>+RN: triggers Repository X Ray CI scanner job - loop for each batch of packages - RN->>GLR: Request packages description by AI - GLR->>AIGW: Forward request for packages description - AIGW-->>GLR: Packages description - GLR-->>RN: Forwards packages description - end - RN->>-GLR: Repository X Ray report - GLR->>+GLR: triggers Repository X Ray ingestion background job - GLR->>PG: Fetch currently stored packages for given repository and language - PG-->>GLR: List of repository packages - GLR->>GLR: Group packages into lists#58; "skip", "add", "remove" - GLR->>PG: Insert new packages from "add" list for given repository - GLR->>PG: Delete packages from "remove" list for given repository - rect rgb(0, 223, 0, .1) - note right of RN: Embeddings flow - opt with Elasticsearch available on an instance - GLR->>ES: Check if packages from "add" list already exists in ES index - ES-->>GLR: List of existing packages from "add" list - GLR->>GLR: Remove already indexed packages from "add" list - loop for each remaining package in "add" list - GLR->>AIGW: Request embeddings for package description - AIGW-->>GLR: Embeddings for package description - end - GLR->>ES: Create documents for packages in "add" list in ES index - - GLR->>PG: Check if packages form "remove" list exists in any repository - PG-->>GLR: List of packages from "remove" group still in use - GLR->>GLR: Filter packages that are still in use from "remove" list - GLR->>ES: Delete all remaining packages in "remove" list from ES index - end - end -``` - -Later on, the Repository X-Ray report is used as follows: - -```mermaid -sequenceDiagram - actor USR as User - participant IDE - participant GLR as GitLabRails - participant ES as Elasticsearch - participant PG as PostgreSQL - participant AIGW as AI Gateway - - USR->>+IDE: types: "#35; generate method that fetches
top charts from Spotify" - IDE->>+GLR: trigger code generation for "#35; generate method
that fetches top charts from Spotify" - alt with Elasticsearch available on an instance - rect rgb(0, 223, 0, .1) - note left of GLR: new embeddings flow - GLR->>+AIGW: fetch embedding for instruction "in utils.js generate method that ... - AIGW-->>-GLR: embeddings vector for instruction - GLR->>PG: fetch packages for given repository and programming language - PG-->>GLR: List of repository package names for given language - GLR->>ES: fetches packages documents using KNN for instruction embeddings vector
from filtered list using repository package names - ES-->>GLR: spotify/web-api-ts-sdk - A package that wraps ... - GLR->>AIGW: code generation request with prompt including
spotify/web-api-ts-sdk - A package that wraps... - end - else - rect rgb(128, 128, 128, .1) - note left of GLR: current flow as fallback - GLR->>PG: fetch X Ray report for repository and language - PG-->>GLR: xray_report_packages records - GLR->>AIGW: code generation request with prompt first 50
entities from xray report - end - end -``` - -### Required changes - -#### On X Ray write path - -##### 1. Create new AI Gateway endpoint that generate embeddings - -Embeddings for GitLab documentation are requested directly from GitLab Rails. That approach limits embeddings availability to GitLab.com only, and this is also misaligned with the architecture blueprint that outlines AI Gateway as a provider of AI services for GitLab features (for more information, see the related [AI Gateway epic 13024](https://gitlab.com/groups/gitlab-org/-/epics/13024)). To avoid similar problems for Repository X-Ray, a new endpoint should be added to AI Gateway API that accepts batches of strings, and responds with embeddings vectors. - -In the following iterations, it is expected that the AI Gateway endpoint (as the central point which has complete overview of all connected instances traffic) will enforce rate limiting and manage token consumption. However, from the very start clients will be responsible for correctly handling rate limiting and token exhaustion type errors. - -**Open question:** - -[Embeddings](../gitlab_rag/postgresql.md) are generated with the `textembedding-gecko` model (768 dimensions). When adding a new API endpoint, we might be able to change model if needed, if so we should decide which one. -Because Repository X Ray report data has small volume and size (at this moment there are 379 reports on GitLab.com), the decision to switch model and rebuild embeddings data is low cost and can be deferred in order to unblock iteration. - -##### 2. Store embeddings vectors for Repository X-Ray report - -The current Elasticsearch framework uses ActiveRecord to keep the index up to date. It uses callbacks on create/update/delete to action the corresponding record in Elasticsearch. -Because `xray_reports` table in PostgreSQL main database stores whole reports as JSON blobs in order to persist generated embeddings vectors for each of report item (representing library used in given repository) -we would need to either: - -- Modify the current Elastic framework as defined in [issue 442197](https://gitlab.com/gitlab-org/gitlab/-/issues/442197). -- If, due to higher urgency, migrate `xray_reports` table to a new structure, where each record represents a single entity (package/library) in the Repository X-Ray report, which would be compatible with the current Elasticsearch upload pipeline. - -The index in Elasticsearch that stores Repository X-Ray packages has the following format: - -```json -index: xray-packages -document: { - id: - name: - language: - description: - embedding: -} -``` - -This index would store: - -- Description. -- Name. -- The embeddings generated for the concatenated package name and description. -- Programming language. - -This index will be shared between all repositories in a given GitLab instance. However, upon search the list of relevant packages will be filtered using prefetched lists of names of packages that belong to the given repository in a given programming language. That approach should: - -- Reduce required storage capacity. -- Improve search performance. -- Reduce AI consumption used to generate embeddings. - -###### Open question - -We need to check if descriptions vary significantly depending on version, and decide whether to store records per package and its version, or just one per package. - -##### 3. Modify storage on GitLab Rails layer - -Right now Repository X-Ray packages are stored in `xray_reports` table at the main PostgreSQL DB. - -```sql -CREATE TABLE xray_reports ( - id bigint NOT NULL, - project_id bigint NOT NULL, - created_at timestamp with time zone NOT NULL, - updated_at timestamp with time zone NOT NULL, - lang text NOT NULL, - payload jsonb NOT NULL, - file_checksum bytea NOT NULL, -); -``` - -In order to support sharing embeddings between repositories whilst providing way to remove stale data from Elasticsearch, we must create a new table, `xray_report_packages`. - -```sql -CREATE TABLE xray_report_packages ( - id bigint NOT NULL, - project_id bigint NOT NULL, - created_at timestamp with time zone NOT NULL, - updated_at timestamp with time zone NOT NULL, - lang text NOT NULL, - name text NOT NULL, - version text NOT NULL, - description text, --nullable filed as with Elasticsearch available this file will not be in use -); -``` - -After the new table is created, all reports from `xray_reports` should be migrated there, and `xray_reports` should be removed. - -##### 4. Modify Repository X-Ray import pipeline - -The Repository X-Ray report, after being generated during the CI job, is imported using a background job. -That job uses [`Ai::StoreRepositoryXrayService`](https://gitlab.com/gitlab-org/gitlab/blob/c6b2f18eaf0b78a4e0012e88f28d643eb0dfb1c2/ee/app/services/ai/store_repository_xray_service.rb#L4) -to parse and save the report file into the `xray_reports` table at the main PostgreSQL DB. - -In order to support semantic search for Repository X-Ray, we must apply the following modifications: - -1. Load the new Repository X-Ray report from scanner into the list of `current_report_packages`. -1. Load the list of packages reported in previous report for the given project stored in `xray_report_packages` into the list of `previous_report_packages` - (For example, `SELECT * FROM xray_report_packages WHERE lang = 'ruby' AND project_id = 123`). -1. Filter packages from the new X-Ray report into three groups: - 1. `skip` - a collection of unmodified packages that do not require any action. - 1. `add` - a collection of new packages that should be added to `xray_report_packages`. - 1. `remove` - a collection of packages that were present in the old report but missing in the new one (`previous_report_packages` - `current_report_packages`). -1. Insert the new records into the PostgreSQL `xray_report_packages` for the `add` list of packages. -1. From the `add` list, filter out packages that already exist in Elasticsearch. Those are packages that are being used by other repositories, and in this case embeddings and - descriptions will be shared. -1. Update and insert all packages that are left in the `add` list of packages in Elasticsearch. -1. Delete the `remove` list of packages from PostgreSQL `xray_report_packages` for a given project. -1. Create an `orphaned` list of packages from the `remove` list by selecting ones of the same `name` and `lang` that are not being used by any other project - (For example, `SELECT 1 FROM xray_report_packages WHERE name = 'kaminari' AND lang = 'ruby' LIMIT 1`). -1. Remove the `orphaned` packages from Elasticsearch. - -#### On X-Ray read path - -1. Retrieve code generation instruction either from: - 1. IDE: - 1. IDE / LS upon detecting generation should send a code generation instruction (for example, a content of a comment that triggered code generation). - 1. GitLab Rails Code Suggestions API needs to add optional string parameter `instruction`. - 1. GitLab Rails if due to different priorities the IDE will not be able to deliver instruction on time to unblock this effort, for the sake of the initial iteration, the code generation instruction can be also retrieved by GitLab Rails. -1. GitLab Rails needs to detect if Elasticsearch is available and: - 1. When Elasticsearch is available: - 1. Fetch list of `names` of packages that are stored in `xray_report_packages` PostgreSQL database for the given project and language. - 1. Use k-nearest neighbor (kNN) search of Elasticsearch filtered with `names` list of projects packages to retrieve the most relevant context. - 1. When Elasticsearch is not available: - 1. Select the first 50 records from `xray_report_packages` in PostgreSQL database aligned with the [current state](#current-state) diagram. + + + + diff --git a/doc/architecture/blueprints/modular_monolith/bounded_contexts.md b/doc/architecture/blueprints/modular_monolith/bounded_contexts.md index 9e09c4ea254..edd2c0f0639 100644 --- a/doc/architecture/blueprints/modular_monolith/bounded_contexts.md +++ b/doc/architecture/blueprints/modular_monolith/bounded_contexts.md @@ -1,52 +1,11 @@ --- -status: proposed -creation-date: "2023-06-21" -authors: [ "@fabiopitino" ] -coach: [ ] -approvers: [ ] -owning-stage: "" +redirect_to: 'https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/modular_monolith/bounded_contexts/' +remove_date: '2025-07-08' --- -# Defining bounded contexts +This document was moved to [another location](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/modular_monolith/bounded_contexts/). -## Historical context - -Until May 2024 the GitLab codebase didn't have a clear domain structure. -We have [forced the creation of some modules](https://gitlab.com/gitlab-org/gitlab/-/issues/212156) -as a first step but we didn't have a well defined strategy for doing it consistently. - -The majority of the code was not properly namespaced and organized: - -- Ruby namespaces used didn't always represent the SSoT. We had overlapping concepts spread across multiple - namespaces. For example: `Abuse::` and `Spam::` or `Security::Orchestration::` and `Security::SecurityOrchestration`. -- Domain code related to the same bounded context was scattered across multiple directories. -- Domain code was present in `lib/` directory under namespaces that differed from the same domain under `app/`. -- Some namespaces were very shallow, containing a few classes while other namespaces were very deep and large. -- A lot of the old code was not namespaced, making it difficult to understand the context where it was used. - -In May 2024 we [defined and enforced bounded contexts](decisions/002_bounded_contexts_definition.md). - -## Goal - -1. Define a list of characteristics that bounded contexts should have. For example: must relate to at least 1 product category. -1. Have a list of top-level bounded contexts where all domain code is broken down into. -1. Engineers can clearly see the list of available bounded contexts and can make an easy decision where to add - new classes and modules. -1. Define a process for adding a new bounded context to the application. This should occur quite infrequently - and new bounded contexts need to adhere to the characteristics defined previously. -1. Enforce the list of bounded contexts so that no new top-level namespaces can be used aside from the authorized ones. - -## Iterations - -1. [Extract libraries out of the `lib/` directory](https://gitlab.com/gitlab-org/gitlab/-/blob/4c6e120069abe751d3128c05ade45ea749a033df/doc/development/gems.md). - - This step is non blocking to modularization but the less generic code exists in `lib/` the - easier will be to identify and separate bounded context. - - Isolate code that could live in a separate project, to prevent it from depending on domain code. - -1. [ADR-001: Modularize application domain](decisions/001_modular_application_domain.md)? Start with modularizing -1. [ADR-002: Define bounded context around feature categories](decisions/002_bounded_contexts_definition.md) as a SSoT in the code. -1. [ADR-003: Assign stewards to all modules and libraries](decisions/003_stewardship.md). -1. [Publish the list of bounded contexts](../../../development/software_design.md#use-namespaces-to-define-bounded-contexts). - - Define a SSoT list of bounded contexts. - - Enforce enforce it using RuboCop static analyzer. - - Autoload non-standard Rails directories based on the given list. + + + + diff --git a/doc/architecture/blueprints/modular_monolith/decisions/001_modular_application_domain.md b/doc/architecture/blueprints/modular_monolith/decisions/001_modular_application_domain.md index dc5a65f4235..a6c606bb657 100644 --- a/doc/architecture/blueprints/modular_monolith/decisions/001_modular_application_domain.md +++ b/doc/architecture/blueprints/modular_monolith/decisions/001_modular_application_domain.md @@ -1,53 +1,11 @@ --- -creation-date: "2024-05-07" -authors: [ "@fabiopitino" ] +redirect_to: 'https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/modular_monolith/decisions/001_modular_application_domain/' +remove_date: '2025-07-08' --- -# Modular Monolith ADR 001: Modularize the application domain +This document was moved to [another location](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/modular_monolith/decisions/001_modular_application_domain/). -## Context - -Before we modularize a codebase we first needed to define how we are going to divide it. - -## Decision - -We start by focusing on the application domain (backend business logic) leaving the -application adapters (Web controllers and views, REST/GraphQL endpoints) outside the -scope of the modularization initially. - -The reasons for this are: - -1. Code in application adapters may not always align with a specific - domain. For example: a project settings endpoint or a merge request page contain - references to many domains. -1. There was a need to run separate Rails nodes for the SaaS architecture using different - profiles in order to save on memory. - For example: on SaaS we wanted to be able to spin up more Sidekiq nodes without the need - to load the whole Rails application. The assumption is that for running Sidekiq we don't - need ActionCable, REST endpoints, GraphQL mutations or Rails views. - We only need the application domain and infrastructure code. - This could still be true even with the introduction of [Cells](../../cells/index.md) but - we need to re-evaluate this assumption. -1. Keep the scope and effort smaller. Tackling only domain code is easier to understand than - the complexity of how to breakdown the application adapters and all their edge cases. - -The decision to scope out application adapters is not final and we decided to defer -it to later. - -Finally, the infrastructure code containing technical concerns (typically the `lib/`) will -be part of a common "platform" module that every domain module will depend on in order to function. - -The "platform" module can be broken down into independent libraries extracted as gems. - -## Consequences - -We focus on modularizing business logic primarily we simplify the rules and guidelines for -engineers. We can apply the same set of patterns across modules. - -## Alternatives - -We looked into including application adapters to the modularization effort but noticed that: - -1. Modularizing adapters is more delicate as we need to preserve user-facing dependencies like - routes. -1. The size of the adapters code is much smaller than the whole application domain. + + + + diff --git a/doc/architecture/blueprints/modular_monolith/decisions/002_bounded_contexts_definition.md b/doc/architecture/blueprints/modular_monolith/decisions/002_bounded_contexts_definition.md index d8b6fde193d..ff6d4279e43 100644 --- a/doc/architecture/blueprints/modular_monolith/decisions/002_bounded_contexts_definition.md +++ b/doc/architecture/blueprints/modular_monolith/decisions/002_bounded_contexts_definition.md @@ -1,61 +1,11 @@ --- -creation-date: "2024-05-07" -authors: [ "@fabiopitino" ] +redirect_to: 'https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/modular_monolith/decisions/002_bounded_contexts_definition/' +remove_date: '2025-07-08' --- -# Modular Monolith ADR 002: Define bounded contexts +This document was moved to [another location](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/modular_monolith/decisions/002_bounded_contexts_definition/). -## Context - -With the focus primarily on the application domain we needed to define how to -modularize it. - -## Decision - -The application domain is divided into bounded contexts which define the top-level -modules of GitLab application. The term bounded context is widely used in -Domain-Driven Design. - -Defining bounded contexts means to organize the code around product structure rather than -organizational structure. - -From the research in [Proposal: split GitLab monolith into components](https://gitlab.com/gitlab-org/gitlab/-/issues/365293) -it seems that following [product categories](https://handbook.gitlab.com/handbook/product/categories/#hierarchy), as a guideline, -would be much better than translating organization structure into folder structure (for example, `app/modules/verify/pipeline-execution/...`). - -However, this guideline alone is not sufficient and we need a more specific strategy: - -- Bounded contexts (top level modules) should be [sufficiently deep](../../../../development/software_design.md#use-namespaces-to-define-bounded-contexts) - to encapsulate implementation details and provide a smaller interface. -- Some product categories, such as Browser Performance Testing, are just too small to represent - a bounded context on their own. - We should have a strategy for grouping product categories together when makes sense. -- Product categories don't necessarily translate into clean boundaries. - `Category:Pipeline Composition` and `Category:Continuous Integration` are some examples - where Pipeline Authoring team and Pipeline Execution team share a lot of code. -- Some parts of the code might not have a clear product category associated to it. - -Despite the above, product categories provide a rough view of the bounded contexts at play in the application. -For that we use product categories to sketch the initial set of bounded contexts. -Then, group related or strongly coupled categories under the same bounded context and create new bounded contexts if missing. - -## Consequences - -In May 2024 we completed the [Bounded Contexts working group](https://handbook.gitlab.com/handbook/company/working-groups/bounded-contexts/) -which completed the first phase of modularization, described in this page. - -We defined a list of [bounded contexts in code](../../../../development/software_design.md#use-namespaces-to-define-bounded-contexts) -and started enforcing them with RuboCop, in order to move towards a fully namespaced monolith. -Team members can edit this list by creating and deleting bounded contexts explicitly and the decision is reviewed -by Staff+ engineers. - -## Alternatives - -We evaluated whether to align the code to the organizational structure but we decided it wasn't viable: - -- Product categories can change ownership and we have seen some pretty frequent changes, even back and forth. - Moving code every time a product category changes ownership adds too much maintenance overhead. -- Teams and organization changes should just mean relabelling the ownership of specific modules. -- Coupling and complexity are directly correlated to business logic and product structure. - A code organization that aligns to organizational structure could generate unnecessary complexity and - much more coupling. + + + + diff --git a/doc/architecture/blueprints/modular_monolith/decisions/003_stewardship.md b/doc/architecture/blueprints/modular_monolith/decisions/003_stewardship.md index aefb55da825..246a2074381 100644 --- a/doc/architecture/blueprints/modular_monolith/decisions/003_stewardship.md +++ b/doc/architecture/blueprints/modular_monolith/decisions/003_stewardship.md @@ -1,60 +1,11 @@ --- -creation-date: "2024-05-08" -authors: [ "@fabiopitino" ] +redirect_to: 'https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/modular_monolith/decisions/003_stewardship/' +remove_date: '2025-07-08' --- -# Modular Monolith ADR 003: Module stewardship +This document was moved to [another location](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/modular_monolith/decisions/003_stewardship/). -## Context - -How do we assign stewardship to domain and platform modules? We have a large amount of shared code -that does not have explicit stewards who can provide a vision and direction on that part of code. - -## Decision - -We use the term **stewards** instead of **owners** to be more in line with GitLab principle of -**everyone can contribute**. Stewards are care takers of the code. They know how a specific -functionality is designed and why. They know the architectural characteristics and constraints. -However, they welcome changes and guide contributors towards success. - -A module, whether is from a domain bounded context or platform module, must have at least 1 group of stewards. -This group can be a team name (or GitLab group handle). Optionally, the list of stewards can include -single IC entries. - -When we will use a Packwerk package to extract a module we will be able to indicate stewardship directly -in the `package.yml`: - -```yaml -metadata: - stewards: - - group::pipeline execution # team name - - group::pipeline authoring # team name - - @grzesiek # IC - - @ayufan # IC -``` - -For platform modules (e.g. `Gitlab::Redis`) we might not have a whole team dedicated as stewards since -all platform code is classified as "shared". However, team members can add themselves as experts of a -particular functionality. - -## Consequences - -Stewardship defined in code can be very powerful: - -- Sections of CODEOWNERS could be automatically generated from packages' metadata. -- Review Roulette or Suggested Reviews features can use this list as first preference. -- Engineers can easily identify stewards and have design conversations early. -- Gems living in the monolith (`gems/`), which should be wrapped into a Packwerk package, - can benefit of having explicit stewards. - -## Alternatives - -In the initial phase of modularization, before adopting Packwerk, we don't have an explicit concept -of ownership. We are initially relying on each team to know what bounded contexts they are responsible -for. For the "shared code" in the platform modules we initially expect maintainers to fill the role of -stewards. - -- Pros: we give trainee maintainer a clear development path and goals. Today it feels unclear what they must - learn in order to become successful maintainers. -- Cons: The amount of "shared" code is very large and still hard to understand who knows best about - a particular functionality. Even extracting code into gems doesn't solve the lack of explicit ownership. + + + + diff --git a/doc/architecture/blueprints/modular_monolith/hexagonal_monolith/hexagonal_architecture.png b/doc/architecture/blueprints/modular_monolith/hexagonal_monolith/hexagonal_architecture.png deleted file mode 100644 index a8d79e276a2cf39bbebdaaa4b067b294ac87b0b0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33135 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
_}#NWSGLcFSK3Mu@%zRg?Q? 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 G|NjA=l!qh$ diff --git a/doc/architecture/blueprints/modular_monolith/hexagonal_monolith/index.md b/doc/architecture/blueprints/modular_monolith/hexagonal_monolith/index.md index 13e4bdd73a1..b99e2fe4e75 100644 --- a/doc/architecture/blueprints/modular_monolith/hexagonal_monolith/index.md +++ b/doc/architecture/blueprints/modular_monolith/hexagonal_monolith/index.md @@ -1,218 +1,11 @@ --- -status: proposed -creation-date: "2023-05-22" -authors: [ "@fabiopitino" ] -coach: [ ] -approvers: [ ] -owning-stage: "" +redirect_to: 'https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/modular_monolith/hexagonal_monolith/' +remove_date: '2025-07-08' --- -# Hexagonal Rails Monolith +This document was moved to [another location](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/modular_monolith/hexagonal_monolith/). -## Background - -This design document supersedes the previous [Composable GitLab Codebase](../../composable_codebase_using_rails_engines/index.md) -where we explored the idea of separating the codebase into technical runtime profiles: -for example, run the monolith solely as a Sidekiq node. -With a modular monolith and the use of an Hexagonal Architecture, we can achieve both -separation of domains as well as separation of application adapters, which may include the usage of engines and/or different runtime profiles. - -## Summary - -**TL;DR:** Change the Rails monolith from a [big ball of mud](https://en.wikipedia.org/wiki/Big_ball_of_mud) state to -a [modular monolith](https://www.thereformedprogrammer.net/my-experience-of-using-modular-monolith-and-ddd-architectures/) -that uses an [Hexagonal architecture](https://en.wikipedia.org/wiki/Hexagonal_architecture_(software)) (or ports and adapters architecture). -Extract cohesive functional domains into separate directory structure using Domain-Driven Design practices. -Extract infrastructure code (logging, database tools, instrumentation, etc.) into gems, essentially remove the need for `lib/` directory. -Define what parts of the functional domains (for example application services) are of public use for integration (the ports) -and what parts are instead private encapsulated details. -Define Web, Sidekiq, REST, GraphQL, and Action Cable as the adapters in the external layer of the architecture. -Use [Packwerk](https://github.com/Shopify/packwerk) to enforce privacy and dependency between modules of the monolith. - -![Hexagonal Architecture for GitLab monolith](hexagonal_architecture.png) - -## Details - -```mermaid -flowchart TD - u([User]) -- interacts directly with --> AA[Application Adapter: WebUI, REST, GraphQL, git, ...] - AA --uses abstractions from--> D[Application Domain] - AA -- depends on --> Platform - D -- depends on --> Platform[Platform: gems, configs, framework, ...] -``` - -### Application domain - -The application core (functional domains) is composed of all the code that describes the business logic, policies and data -that is unique to GitLab product. -It is divided into separate top-level [bounded contexts](../bounded_contexts.md). -A bounded-context is represented in the form of a Ruby module. -This follows the existing [guideline on naming namespaces](../../../../development/software_design.md#use-namespaces-to-define-bounded-contexts) -but puts more structure to it. - -Modules should: - -- Be deep enough to encapsulate a lot of the internal logic, state and data. -- Have a public interface that is as small as possible, safe to use by other bounded contexts and well documented. -- Be cohesive and represent the SSoT (single source of truth) of the feature it describes. - -Feature categories represent a product area that is large enough for the module to be deep, so we don't have a proliferation -of small top-level modules. It also helps the codebase to follow the -[ubiquitous language](../../../../development/software_design.md#use-ubiquitous-language-instead-of-crud-terminology). -A team can be responsible for multiple feature categories, hence owning the vision for multiple bounded contexts. -While feature categories can sometimes change ownership, this change of mapping the bounded context to new owners -is very cheap. -Using feature categories also helps new contributors, either as GitLab team members of members of the wider community, -to navigate the codebase. - -If multiple feature categories are strongly related, they may be grouped under a single bounded context. -If a feature category is only relevant in the context of a parent feature category, it may be included in the -parent's bounded context. For example: Build artifacts existing in the context of Continuous Integration feature category -and they may be merged under a single bounded context. - -The application domain has no knowledge of outer layers like the application adapters and only depends on the -platform code. This makes the domain code to be the SSoT of the business logic, be reusable and testable regardless -whether the request came from the WebUI or REST API. - -If a dependency between an outer layer and an inner layer is required (domain code depending on the interface of an adapter), this can be solved using inversion of control techniques, especially dependency injection. - -### Application adapters - ->>> -_Adapters are the glue between components and the outside world._ -_They tailor the exchanges between the external world and the ports that represent the requirements of the inside_ -_of the application component. There can be several adapters for one port, for example, data can be provided by_ -_a user through a GUI or a command-line interface, by an automated data source, or by test scripts._ - -[Wikipedia](https://en.wikipedia.org/wiki/Hexagonal_architecture_(software)#Principle) ->>> - -Application adapters would be: - -- Web UI (Rails controllers, view, JS and Vue client) -- REST API endpoints -- GraphQL Endpoints - -They are responsible for the interaction with the user. Each adapter should interpret the request, parse parameters -and invoke the right abstraction from the application domain, then present the result back to the user. - -Presentation logic, and possibly authentication, would be specific to the adapters layer. - -The application adapters layer depends on the platform code to run: the Rails framework, the gems that power the adapter, -the configurations and utilities. - -### Platform code - -For platform code we consider any classes and modules that are required by the application domain and/or application -adapters to work. - -The Rails' `lib/` directory today contains multiple categories of code that could live somewhere else, -most of which is platform code: - -- REST API endpoints could be part of the [application adapters](#application-adapters). -- domain code (both large domain code such as `Gitlab::Ci` and small such as `Gitlab::JiraImport`) should be - moved inside the [application domain](#application-domain). -- The rest could be extracted as separate single-purpose gems under the `gems/` directory inside the monolith. - This can include utilities such as logging, error reporting and metrics, rate limiters, - infrastructure code like `Gitlab::ApplicationRateLimiter`, `Gitlab::Redis`, `Gitlab::Database` - and generic subdomains like `Banzai`. - -Base classes to extend Rails framework such as `ApplicationRecord` or `ApplicationWorker` as well as GitLab base classes -such as `BaseService` could be implemented as gem extensions. - -This means that aside from the Rails framework code, the rest of the platform code resides in `gems/`. - -Eventually all code inside `gems/` could potentially be extracted in a separate repository or open sourced. -Placing platform code inside `gems/` makes it clear that its purpose is to serve the application code. - -### Enforcing boundaries - -Ruby does not have the concept of privacy of constants in a given module. Unlike other programming languages, even extracting -well documented gems doesn't prevent other developers from coupling code to implementation details because all constants -are public in Ruby. - -We can have a codebase perfectly organized in an hexagonal architecture but still having the application domain, the biggest -part of the codebase, being a non modularized [big ball of mud](https://en.wikipedia.org/wiki/Big_ball_of_mud). - -Enforcing boundaries is also vital to maintaining the structure long term. We don't want that after a big modularization -effort we slowly fall back into a big ball of mud gain by violating the boundaries. - -We explored the idea of [using Packwerk in a proof of concept](../proof_of_concepts.md#use-packwerk-to-enforce-module-boundaries) -to enforce module boundaries. - -[Packwerk](https://github.com/Shopify/packwerk) is a static analyzer that allows to gradually introduce packages in the -codebase and enforce privacy and explicit dependencies. Packwerk can detect if some Ruby code is using private implementation -details of another package or if it's using a package that wasn't declared explicitly as a dependency. - -Being a static analyzer it does not affect code execution, meaning that introducing Packwerk is safe and can be done -gradually. - -Companies like Gusto have been developing and maintaining a list of [development and engineering tools](https://github.com/rubyatscale) -for organizations that want to move to using a Rails modular monolith around Packwerk. - -### EE and JH extensions - -One of the unique challenges of modularizing the GitLab codebase is the presence of EE extensions (managed by GitLab) -and JH extensions (managed by JiHu). - -By moving related domain code (e.g. `Ci::`) under the same bounded context and Packwerk package, we would also need to -move `ee/` extensions in it. - -To have top-level bounded contexts to also match Packwerk packages it means that all code related to a specific domain -needs to be placed under the same package directory, including EE extensions, for example. - -The following is just an example of a possible directory structure: - -```shell -domains -├── ci -│ ├── package.yml # package definition. -│ ├── packwerk.yml # tool configurations for this package. -│ ├── package_todo.yml # existing violations. -│ ├── core # Core features available in Community Edition and always autoloaded. -│ │ ├── app -│ │ │ ├── models/... -│ │ │ ├── services/... -│ │ │ └── lib/... # domain-specific `lib` moved inside `app` together with other classes. -│ │ └── spec -│ │ └── models/... -│ ├── ee # EE extensions specific to the bounded context, conditionally autoloaded. -│ │ ├── models/... -│ │ └── spec -│ │ └── models/... -│ └── public # Public constants are placed here so they can be referenced by other packages. -│ ├── core -│ │ ├── app -│ │ │ └── models/... -│ │ └── spec -│ │ └── models/... -│ └── ee -│ ├── app -│ │ └── models/... -│ └── spec -│ └── models/... -├── merge_requests/ -├── repositories/ -└── ... -``` - -## Challenges - -- Such changes require a shift in the development mindset to understand the benefits of the modular - architecture and not fallback into legacy practices. -- Changing the application architecture is a challenging task. It takes time, resources and commitment - but most importantly it requires buy-in from engineers. -- This may require us to have a medium-long term team of engineers or a Working Group that makes progresses - on the architecture evolution plan, foster discussions in various engineering channels and resolve adoption challenges. -- We need to ensure we build standards and guidelines and not silos. -- We need to ensure we have clear guidelines on where new code should be placed. We must not recreate junk drawer folders like `lib/`. - -## Opportunities - -The move to a modular monolith architecture enables a lot of opportunities that we could explore in the future: - -- We could align the concept of domain expert with explicitly owning specific modules of the monolith. -- The use of static analysis tool (such as Packwerk, RuboCop) can catch design violations in development and CI, ensuring - that best practices are honored. -- By defining dependencies between modules explicitly we could speed up CI by testing only the parts that are affected by - the changes. -- Such modular architecture could help to further decompose modules into separate services if needed. + + + + diff --git a/doc/architecture/blueprints/modular_monolith/index.md b/doc/architecture/blueprints/modular_monolith/index.md index 643fb113826..f4ee9083bde 100644 --- a/doc/architecture/blueprints/modular_monolith/index.md +++ b/doc/architecture/blueprints/modular_monolith/index.md @@ -1,136 +1,11 @@ --- -status: proposed -creation-date: "2023-05-22" -authors: [ "@grzesiek", "@fabiopitino" ] -coach: [ ] -approvers: [ ] -owning-stage: "" -participating-stages: [] +redirect_to: 'https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/modular_monolith/' +remove_date: '2025-07-08' --- - +This document was moved to [another location](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/modular_monolith/). -# GitLab Modular Monolith - -## Summary - -The main [GitLab Rails](https://gitlab.com/gitlab-org/gitlab) -project has been implemented as a large monolithic application, using -[Ruby on Rails](https://rubyonrails.org/) framework. It has over 2.2 million -lines of Ruby code and hundreds of engineers contributing to it every day. - -The application has been growing in complexity for more than a decade. The -monolithic architecture has served us well during this time, making it possible -to keep high development velocity and great engineering productivity. - -Even though we strive for having [an approachable open-core architecture](https://about.gitlab.com/blog/2022/07/14/open-core-is-worse-than-plugins/) -we need to strengthen the boundaries between domains to retain velocity and -increase development predictability. - -As we grow as an engineering organization, we want to explore a slightly -different, but related, architectural paradigm: -[a modular monolith design](https://en.wikipedia.org/wiki/Modular_programming), -while still using a [monolithic architecture](https://en.wikipedia.org/wiki/Monolithic_application) -with satellite services. - -This should allow us to increase engineering efficiency, reduce the cognitive -load, and eventually decouple internal components to the extend that will allow -us to deploy and run them separately if needed. - -## Motivation - -Working with a large and tightly coupled monolithic application is challenging: - -Engineering: - -- Onboarding engineers takes time. It takes a while before engineers feel - productive due to the size of the context and the amount of coupling. -- We need to use `CODEOWNERS` file feature for several domains but - [these rules are complex](https://gitlab.com/gitlab-org/gitlab/-/blob/409228f064a950af8ff2cecdd138fc9da41c8e63/.gitlab/CODEOWNERS#L1396-1457). -- It is difficult for engineers to build a mental map of the application due to its size. - Even apparently isolated changes can have [far-reaching repercussions](https://handbook.gitlab.com/handbook/engineering/core-development/#reducing-the-impact-of-far-reaching-work) - on other parts of the monolith. -- Attrition/retention of engineering talent. It is fatiguing and demoralizing for - engineers to constantly deal with the obstacles to productivity. - -Architecture: - -- There is little structure inside the monolith. We have attempted to enforce - the creation [of some modules](https://gitlab.com/gitlab-org/gitlab/-/issues/212156) - but have no company-wide strategy on what the functional parts of the - monolith should be, and how code should be organized. -- There is no isolation between existing modules. Ruby does not provide - out-of-the-box tools to effectively enforce boundaries. Everything lives - under the same memory space. -- We rarely build abstractions that can boost our efficiency. -- Moving stable parts of the application into separate services is impossible - due to high coupling. -- We are unable to deploy changes to specific domains separately and isolate - failures that are happening inside them. - -Productivity: - -- High median-time-to-production for complex changes. -- It can be overwhelming for the wider-community members to contribute. -- Reducing testing times requires diligent and persistent efforts. - -## Goals - -- Increase the development velocity and predicability through separation of concerns. -- Improve code quality by reducing coupling and introducing useful abstractions. -- Build abstractions required to deploy and run GitLab components separately. - -## How do we get there? - -While we do recognize that modularization is a significant technical endeavor, -we believe that the main challenge is organizational, rather than technical. We -not only need to design separation in a way that modules are decoupled in a -pragmatic way which works well on GitLab.com but also on self-managed -instances, but we need to align modularization with the way in which we want to -work at GitLab. - -There are many aspects and details required to make modularization of our -monolith successful. We will work on the aspects listed below, refine them, and -add more important details as we move forward towards the goal: - -1. [Deliver modularization proof-of-concepts that will deliver key insights](proof_of_concepts.md). -1. Align modularization plans to the product structure by [defining bounded contexts](bounded_contexts.md). -1. [Separate domains into modules](packages_extraction.md) that will reflect product structure. -1. Start a training program for team members on how to work with decoupled domains (TODO) -1. Build tools that will make it easier to build decoupled domains through inversion of control (TODO) -1. [Introduce hexagonal architecture within the monolith](hexagonal_monolith/index.md) -1. Introduce clean architecture with one-way-dependencies and host application (TODO) -1. Build abstractions that will make it possible to run and deploy domains separately (TODO) - -## Status - -In progress. - -- A working group [Bounded Contexts](https://handbook.gitlab.com/handbook/company/working-groups/bounded-contexts/) - was concluded in April 2024 which defined a list of bounded contexts to be enforced for GitLab Rails domain and - infrastructure layer. - -## Decisions - -1. [ADR-001: Modularize application domain](decisions/001_modular_application_domain.md)? Start with modularizing - the application domain and infrastructure code. -1. [ADR-002: Define bounded context around feature categories](decisions/002_bounded_contexts_definition.md) as a SSoT in the code. -1. [ADR-003: Assign stewards to all modules and libraries](decisions/003_stewardship.md). - -## Glossary - -- `modules` are Ruby modules and can be used to nest code hierarchically. -- `namespaces` are unique hierarchies of Ruby constants. For example, `Ci::` but also `Ci::JobArtifacts::` or `Ci::Pipeline::Chain::`. -- `packages` are Packwerk packages to group together related functionalities. These packages can be big or small depending on the design and architecture. Inside a package all constants (classes and modules) have the same namespace. For example: - - In a package `ci`, all the classes would be nested under `Ci::` namespace. There can be also nested namespaces like `Ci::PipelineProcessing::`. - - In a package `ci-pipeline_creation` all classes are nested under `Ci::PipelineCreation`, like `Ci::PipelineCreation::Chain::Command`. - - In a package `ci` a class named `MergeRequests::UpdateHeadPipelineService` would not be allowed because it would not match the package's namespace. - - This can be enforced easily with [Packwerk's based RuboCop Cops](https://github.com/rubyatscale/rubocop-packs/blob/main/lib/rubocop/cop/packs/root_namespace_is_pack_name.rb). -- `bounded context` is a top-level Packwerk package that represents a macro aspect of the domain. For example: `Ci::`, `MergeRequests::`, `Packages::`, etc. - - A bounded context is represented by a single Ruby module/namespace. For example, `Ci::` and not `Ci::JobArtifacts::`. - - A bounded context can be made of 1 or multiple Packwerk packages. Nested packages would be recommended if the domain is quite complex and we want to enforce privacy among all the implementation details. For example: `Ci::PipelineProcessing::` and `Ci::PipelineCreation::` could be separate packages of the same bounded context and expose their public API while keeping implementation details private. - - A new bounded context like `RemoteDevelopment::` can be represented a single package while large and complex bounded contexts like `Ci::` would need to be organized into smaller/nested packages. - -## References - -[List of references](references.md) + + + + diff --git a/doc/architecture/blueprints/modular_monolith/packages_extraction.md b/doc/architecture/blueprints/modular_monolith/packages_extraction.md index 75640dc2e66..cf7490b1f3c 100644 --- a/doc/architecture/blueprints/modular_monolith/packages_extraction.md +++ b/doc/architecture/blueprints/modular_monolith/packages_extraction.md @@ -1,52 +1,11 @@ --- -status: proposed -creation-date: "2023-09-29" -authors: [ "@fabiopitino" ] -coach: [ ] -approvers: [ ] -owning-stage: "" +redirect_to: 'https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/modular_monolith/packages_extraction/' +remove_date: '2025-07-08' --- -# Convert domain module into packages +This document was moved to [another location](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/modular_monolith/packages_extraction/). -The general steps of refactoring existing code to modularization could be: - -1. Use the same namespace for all classes and modules related to the same [bounded context](bounded_contexts.md). - - - **Why?** Without even a rough understanding of the domains at play in the codebase it is difficult to draw a plan. - Having well namespaced code that everyone else can follow is also the pre-requisite for modularization. - - If a domain is already well namespaced and no similar or related namespaces exist, we can move directly to the - next step. -1. Prepare Rails development for Packwerk packages. This is a **once off step** with maybe some improvements - added over time. - - - We will have the Rails autoloader to work with Packwerk's directory structure, as demonstrated in - [this PoC](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/129254/diffs#note_1512982957). - - We will have [Danger-Packwerk](https://github.com/rubyatscale/danger-packwerk) running in CI for merge requests. - - We will possibly have Packer check running in Lefthook on pre-commit or pre-push. -1. Move file into a Packwerk package. - - - This should consist in creating a Packwerk package and iteratively move files into the package. - - Constants are auto-loaded correctly whether they are in `app/` or `lib/` inside a Packwerk package. - - This is a phase where the domain code will be split between the package directory and the Rails directory structure. - **We must move quickly here**. -1. Enforce namespace boundaries by requiring packages declare their [dependencies explicitly](https://github.com/Shopify/packwerk/blob/main/USAGE.md#enforcing-dependency-boundary) - and only depend on other packages' [public interface](https://github.com/rubyatscale/packwerk-extensions#privacy-checker). - - - **Why?** Up until now all constants would be public since we have not enforced privacy. By moving existing files - into packages without enforcing boundaries we can focus on wrapping a namespace in a package without being distracted - by Packwer privacy violations. By enforcing privacy afterwards we gain an understanding of coupling between various - constants and domains. - - This way we know what constants need to be made public (as they are used by other packages) and what can - remain private (taking the benefit of encapsulation). We will use Packwerk's recorded violations (like RuboCop TODOs) - to refactor the code over time. - - We can update the dependency graph to see where it fit in the overall architecture. -1. Work off Packwerk's recorded violations to make refactorings. **This is a long term phase** that the DRIs of the - domain need to nurture over time. We will use Packwerk failures and the dependency diagram to influence the modular design. - - - Revisit wheteher a class should be private instead of public, and crate a better interface. - - Move constants to different package if too coupled with that. - - Join packages if they are too coupled to each other. - -Once we have Packwerk configured for the Rails application (step 2 above), emerging domains could be directly implemented -as Packwerk packages, benefiting from isolation and clear interface immediately. + + + + diff --git a/doc/architecture/blueprints/modular_monolith/proof_of_concepts.md b/doc/architecture/blueprints/modular_monolith/proof_of_concepts.md index b962551194b..97bda7b335a 100644 --- a/doc/architecture/blueprints/modular_monolith/proof_of_concepts.md +++ b/doc/architecture/blueprints/modular_monolith/proof_of_concepts.md @@ -1,134 +1,11 @@ --- -status: proposed -creation-date: "2023-07-05" -authors: [ "@grzesiek", "@fabiopitino" ] -coach: [ ] -owners: [ ] +redirect_to: 'https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/modular_monolith/proof_of_concepts/' +remove_date: '2025-07-08' --- -# Modular Monolith: PoCs +This document was moved to [another location](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/modular_monolith/proof_of_concepts/). -Modularization of our monolith is a complex project. There will be many -unknowns. One thing that can help us mitigate the risks and deliver key -insights are Proof-of-Concepts that we could deliver early on, to better -understand what will need to be done. - -## Inter-module communicaton - -A PoC that we plan to deliver is a PoC of inter-module communication. We do -recognize the need to separate modules, but still allow them to communicate -together using a well defined interface. Modules can communicate through a -facade classes (like libraries usually do), or through eventing system. Both -ways are important. - -The main question is: how do we want to define the interface and how to design -the communication channels? - -It is one of our goals to make it possible to plug modules out, and operate -some of them as separate services. This will make it easier deploy GitLab.com -in the future and scale key domains. One possible way to achieve this goal -would be to design the inter-module communication using a protobuf as an -interface and gRPC as a communication channel. When modules are plugged-in, we -would bypass gRPC and serialization and use in-process communication primitives -(while still using protobuf as an interface). When a module gets plugged-out, -gRPC would carry messages between modules. - -## Use Packwerk to enforce module boundaries - -Packwerk is a static analyzer that helps defining and enforcing module boundaries -in Ruby. - -[In this PoC merge request](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/98801) -we demonstrate a possible directory structure of the monolith broken down into separate -modules. - -The PoC also aims to solve the problem of EE extensions (and JH too) allowing the -Rails autoloader to be tweaked depending on whether to load only the Core codebase or -any extensions. - -The PoC also attempted to only move a small part of the `Ci::` namespace into a -`components/ci` Packwerk package. This seems to be the most iterative approach -explored so far. - -There are different approaches we could use to adopt Packwerk. Other PoC's also -explored are the [large extraction of CI package](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/88899) -and [moving the 2 main CI classes into a package](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/90595). - -All 3 PoC's have a lot in common, from the introduction of Packwerk packages and configurations -to setting paths for the autoloader to work with any packages. What changes between the -various merge requests is the approach on choosing which files to move first. - -The main goals of the PoC were: - -- understand if Packwerk can be used on the GitLab codebase. -- understand the learning curve for developers. -- verify support for EE and JH extensions. -- allow gradual modularization. - -### Positive results - -- Using Packwerk would be pretty simple on GitLab since it's designed primarily to work - on Rails codebases. -- We can change the organization of the domain code to be module-oriented instead of following - the MVC pattern. It requires small initial changes to allow the Rails autoloading - to support the new directory structure, which is by the way not imposed by Packwerk. - After that, registering a new top-level package/bounded-context would be a 1 LOC change. -- Using the correct directory structure indicated in the [PoC](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/98801) - allows packages to contain all the code, including EE and JH extensions. -- Gradual modularization is possible and we can have any degree of modularization as we want, - from initial no enforcement down to complete isolation simulating an in-memory micro-service environment. -- Moving files into a Packwerk package doesn't necessarily mean renaming constants. - While this is not advisable long term, its an extra flexibility that the tool provides. - - For example: If we are extracting the `Ci::` module into a Packwerk package there can be - constants that belong to the CI domain but are not namespaced, like `CommitStatus` or - that have a different namespace, like `Gitlab::Ci::`. - Packwerk allows such constants to be moved inside the `ci` package and correctly flags - boundary violations. - - Packwerk enhancements from RubyAtScale tooling allow to enforce that all constants inside - a package share the same Ruby namespace. We eventually would want to leverage that. -- RubyAtScale provides also tools to track metrics about modularization and adoption which we - would need to monitor and drive as an engineering organization. -- Packwerk has IDE extensions (e.g. for VS Code) to provide realtime feedback on violations - (like RuboCop). It can also be run via CLI during the development workflow against a single - package. It could be integrated into pre-push Git hooks or Danger during code reviews. - -### Challenges - -Some of these challenges are not specific to Packwerk as tool/approach. They were observed -during the PoC and are more generically related to the process of modularization: - -- There is no right or wrong approach when introducing Packwerk packages. We need to define - clear guidelines to give developers the tools to make the best decision: - - Sometimes it could be creating an empty package and move files in it gradually. - - Sometimes it could be wrapping an already well designed and isolated part of the codebase. - - Sometimes it could be creating a new package from scratch. -- As we move code to a different directory structure we need to involve JiHu as they manage - extensions following the current directory structure. - We may have modules that are partially migrated and we need to ensure JiHu is up-to-date - with the current progresses. -- After privacy/dependency checks are enabled, Packwerk will log a lot of violations - (like RuboCop TODOs) since constant references in a Rails codebase are very entangled. - - The team owning the package needs to define a vision for the package. - What would the package look like once all violations have been fixed? - This may mean specifying where the package fits in the - [context map](https://www.oreilly.com/library/view/what-is-domain-driven/9781492057802/ch04.html) - of the system. How the current package should be used by another package `A` and how - it should use other packages. - - The vision above should tell developers how they should fix these violations over time. - Should they make a specific constant public? Should the package list another package as its - dependencies? Should events be used in some scenarios? - - Teams will likely need guidance in doing that. We may need to have a team of engineers, like - maintainers with a very broad understanding of the domains, that will support engineering - teams in this effort. -- Changes to CI configurations on tuning Knapsack and selective testing were ignored durign the - PoC. - -## Frontend sorting hat - -Frontend sorting-hat is a PoC for combining multiple domains to render a full -page of GitLab (with menus, and items that come from multiple separate -domains). - -## Frontend assets aggregation - -Frontend assets aggregation is a PoC for a possible separation of micro-frontends. + + + + diff --git a/doc/architecture/blueprints/modular_monolith/references.md b/doc/architecture/blueprints/modular_monolith/references.md index 2c7d3dc972d..3842447adcd 100644 --- a/doc/architecture/blueprints/modular_monolith/references.md +++ b/doc/architecture/blueprints/modular_monolith/references.md @@ -1,70 +1,11 @@ --- -status: proposed -creation-date: "2023-06-21" -authors: [ "@fabiopitino" ] -coach: [ ] -approvers: [ ] -owning-stage: "" +redirect_to: 'https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/modular_monolith/references/' +remove_date: '2025-07-08' --- -# References +This document was moved to [another location](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/modular_monolith/references/). -## Related design docs - -- [Composable codebase design doc](../composable_codebase_using_rails_engines/index.md) - -## Related Issues - -- [Split GitLab monolith into components](https://gitlab.com/gitlab-org/gitlab/-/issues/365293) -- [Make it simple to build and use "Decoupled Services"](https://gitlab.com/gitlab-org/gitlab/-/issues/31121) -- [Use nested structure to organize CI classes](https://gitlab.com/gitlab-org/gitlab/-/issues/209745) -- [Create new models / classes within a module / namespace](https://gitlab.com/gitlab-org/gitlab/-/issues/212156) -- [Make teams to be maintainers of their code](https://gitlab.com/gitlab-org/gitlab/-/issues/25872) -- [Add backend guide for Dependency Injection](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/73644) - -## Internal Slack Channels - -- [`#modular_monolith`](https://gitlab.slack.com/archives/C03NTK6HZBM) -- [`#architecture`](https://gitlab.slack.com/archives/CJ4DB7517) - -## Reference Implementations / Guides - -Gusto / RubyAtScale: - -- [RubyAtScale toolchain for modularization](https://github.com/rubyatscale) -- [Gusto's engineering blog](https://engineering.gusto.com/laying-the-cultural-and-technical-foundation-for-big-rails/) -- [Gradual modularization](https://gradualmodularization.com/) (successor to CBRA) -- [Component-Based Rails Applications](https://cbra.info) ("deprecated") - -Shopify: - -- [Packwerk](https://github.com/Shopify/packwerk) -- [Shopify's jurney to modularization](https://shopify.engineering/shopify-monolith) -- [Internal GitLab doc transcript of an AMA with a Shopify engineer](https://docs.google.com/document/d/1uZbcaK8Aqs-D_n7_uQ5XE295r5UWDJEBwA6g5bTjcwc/edit#heading=h.d1tml5rlzrpa) - -Domain-Driven Rails / Rails Event Store: - -Rails Event Store is relevant because it is a mechanism to achieve many -of the goals discussed here, and is based upon patterns used by Arkency -to build production applications. - -This doesn't mean we need to use this specific framework or approach. - -However, the general concepts of DDD/ES/CQRS are important and in some -cases maybe necessary to achieve the goals of this blueprint, so it's -useful to have concrete production-proven implementations of those -concepts to look at as an example. - -- [Arkency's domain-driven Rails](https://products.arkency.com/domain-driven-rails/) -- [Arkency's Rails Event Store](https://railseventstore.org) - -App Continuum: - -An illustration of how an application can evolve from a small, unstructured app, through various -stages including a modular well-structured monolith, all the way to a microservices architecture. - -Includes discussion of why you might want to stop at various stages, and specifically the -challenges/concerns with making the jump to microservices, and why sticking with a -well-structured monolith may be preferable in many cases. - -- [App Continuum](https://www.appcontinuum.io) + + + + diff --git a/doc/architecture/blueprints/pipeline_mini_graph/img/pipeline_mini_graph.png b/doc/architecture/blueprints/pipeline_mini_graph/img/pipeline_mini_graph.png deleted file mode 100644 index fccea4ed5695dfe71e753f5910bd4123c2159df9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4285 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 - +This document was moved to [another location](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/pipeline_mini_graph/). -# Pipeline Mini Graph - -This blueprint serves as living documentation for the Pipeline Mini Graph. The Pipeline Mini Graph is used in various places throughout the platform to communicate to users the status of the relevant pipeline. Users are able to re-run jobs directly from the component or drilldown into said jobs and linked pipelines for further investigation. - -![Pipeline Mini Graph](img/pipeline_mini_graph.png) - -## Motivation - -While the Pipeline Mini Graph primarily functions via REST, we are updating the component to support GraphQL and subsequently migrating all instances of this component to GraphQL. This documentation will serve as the SSOT for this refactor. Developers have expressed difficulty contributing to this component as the two APIs co-exist, so we need to make this code easier to update while also supporting both REST and GraphQL. This refactor lives behind a feature flag called `ci_graphql_pipeline_mini_graph`. - -### Goals - -- Improved maintainability -- Backwards compatibility -- Improved query performance -- Deprecation of REST support - -### Non-Goals - -- Redesign of the Pipeline Mini Graph UI - -## Proposal - -To break down implementation, we are taking the following steps: - -1. Separate the REST version and the GraphQL version of the component into 2 directories called `pipeline_mini_graph` and `legacy_pipeline_mini_graph`. This way, developers can contribute with more ease and we can easily remove the REST version once all apps are using GraphQL. -1. Finish updating the newer component to fully support GraphQL -1. Optimize GraphQL query structure to be more performant. -1. Roll out `ci_graphql_pipeline_mini_graph` to globally enable GraphQL instances of the component. - -## Implementation Details - -| Issue | Milestone | MR | Status | -| ----- | --------- | -- | ------ | -| [Move legacy files to new directory](https://gitlab.com/gitlab-org/gitlab/-/work_items/464375) | [17.1](https://gitlab.com/groups/gitlab-org/-/milestones/99#tab-issues) | [154625](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/154625) | ✅ | -| [Move remaining legacy code](https://gitlab.com/gitlab-org/gitlab/-/work_items/464379) | [17.1](https://gitlab.com/groups/gitlab-org/-/milestones/99#tab-issues) |[154818](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/154818) | ✅ | -| [Create README for PMG](https://gitlab.com/gitlab-org/gitlab/-/work_items/464632) | [17.1](https://gitlab.com/groups/gitlab-org/-/milestones/99#tab-issues) | [154964](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/154964) | ✅ | -| [GraphQL Query Optimization](https://gitlab.com/gitlab-org/gitlab/-/issues/465309) | [17.1](https://gitlab.com/groups/gitlab-org/-/milestones/99#tab-issues) | [465309](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/155129) | ✅ | -| [Dedicated component for downstream pipelines](https://gitlab.com/gitlab-org/gitlab/-/issues/466238) | [17.1](https://gitlab.com/groups/gitlab-org/-/milestones/99#tab-issues) | [155382](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/155382) | ✅ | -| [Fetch Stage by ID](https://gitlab.com/gitlab-org/gitlab/-/issues/464100) | [17.2](https://gitlab.com/groups/gitlab-org/-/milestones/100#tab-issues) | [157506](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/157506) | ✅ | -| [Job Item](https://gitlab.com/gitlab-org/gitlab/-/issues/467278) | [17.2](https://gitlab.com/groups/gitlab-org/-/milestones/100#tab-issues) | [157798](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/157798) | In Review | -| [Job Actions](https://gitlab.com/gitlab-org/gitlab/-/issues/467279) | [17.2](https://gitlab.com/groups/gitlab-org/-/milestones/100#tab-issues) | TBD | In Dev | -| [Rollout `ci_graphql_pipeline_mini_graph`](https://gitlab.com/gitlab-org/gitlab/-/issues/407818) | [17.2](https://gitlab.com/groups/gitlab-org/-/milestones/100#tab-issues) | TBD | Blocked | -| [Migrate MR PMG to GraphQL instance](https://gitlab.com/gitlab-org/gitlab/-/issues/419725) | [17.2](https://gitlab.com/groups/gitlab-org/-/milestones/100#tab-issues) | TBD | Blocked | -| [Migrate pipeline editor PMG to GraphQL instance](https://gitlab.com/gitlab-org/gitlab/-/issues/466275) | TBD | TBD | Blocked | -| [Migrate commit page PMG to GraphQL instance](https://gitlab.com/gitlab-org/gitlab/-/issues/466274) | TBD | TBD | Blocked | -| [Remove dead logic from PMG codebase](https://gitlab.com/gitlab-org/gitlab/-/issues/466277) | TBD | TBD | Blocked | - -## Design Details - -### REST Structure - -#### File Structure - -```plaintext -├── pipeline_mini_graph/ -├── └── legacy_pipeline_mini_graph/ -│ ├── legacy_job_item.vue -│ ├── legacy_linked_pipelines_mini_list.vue -│ ├── legacy_pipeline_mini_graph.vue -│ ├── legacy_pipeline_stage.vue -│ └── legacy_pipeline_stages.yml -``` - -All data for the legacy pipeline mini graph is passed into the REST instance of the component. This data comes from various API calls throughout different apps which use the component. - -#### Properties - -| Name | Type | Required | Description | -| ---- | ---- | -------- | ----------- | -|`downstreamPipelines` | Array | false | pipelines triggered by current pipeline | -|`isMergeTrain` | Boolean | false | whether the pipeline is part of a merge train | -|`pipelinePath` | String | false | pipeline URL | -|`stages` | Array | true | stages of current pipeline | -|`updateDropdown` | Boolean | false | whether to fetch jobs when the dropdown is open | -|`upstreamPipeline` | Object | false | upstream pipeline which triggered current pipeline | - -### GraphQL Structure - -The GraphQL instance of the pipeline mini graph has self-managed data. - -#### Current File Structure - -```plaintext -├── pipeline_mini_graph/ -| ├── job_item.vue -│ ├── linked_pipelines_mini_list.vue -│ ├── pipeline_mini_graph.vue -│ ├── pipeline_stage.vue -│ └── pipeline_stages.yml -├── ├── graphql/ -│ ├── get_pipeline_stage_query.query.graphql -│ └── get_pipeline_stages_query.query.graphql -``` - -#### Current Properties - -| Name | Type | Required | Description | -| --- | --- | --- | --- | -|`fullPath` | String | true | full path for the queries | -|`iid` | String | true | pipeline iid for the queries | -|`isMergeTrain` | Boolean | false | whether the pipeline is part of a merge train (under consideration) | -|`pipelineEtag` | String | true | etag for caching (under consideration) | -|`pollInterval` | Number | false | interval for polling updates | - -#### Considerations - -##### Properties - -- `isMergeTrain`: This property is specific to the MR page and is used to display a message in the job dropdown to warn users that merge train jobs cannot be retried. This is an odd flow. Perhaps we should consider either having this data come from somewhere else within the pipeline mini graph, or living in the merge train widget itself. It is worth noting here that this boolean is not used for any other logic outside of displaying this message. - -- `pipelineEtag`: Consider whether this data must be passed into the pipeline mini graph, or whether we can generate this within the component through a query. - -##### Query Structure - -Currently the approach is to use 2 queries in the parent file - -- [getPipelineStages](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/assets/javascripts/ci/pipeline_mini_graph/graphql/queries/get_pipeline_stages.query.graphql?ref_type=heads): populates the stages of the current pipeline -- [getLinkedPipelines](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/assets/javascripts/ci/pipeline_details/graphql/queries/get_linked_pipelines.query.graphql?ref_type=heads): if there is an upstream pipeline or any downstream pipelines, this query will populate those - -Another query called [getPipelineStage](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/assets/javascripts/ci/pipeline_mini_graph/graphql/queries/get_pipeline_stage.query.graphql?ref_type=heads) will be used to populate the dropdown when a stage icon is clicked. - -We need to consider whether this is the most performant way to query for the mini graph. - -Should pipeline stages and linked pipelines populate in 2 separate queries or is it okay to just have a single query for all icons and statuses in the mini graph to be displayed? Considerations below: - -- The reason this is currently split is because the upstream/downstream sections of the mini graph were added as a feature enhancement after the initial component already existed and they used to be a premium feature. Since we have restructured this to all exist as free, we should reconsider. -- The pmg exists in several pipelines lists, so there will be up to 15 on a page rendering at a time. -- Since we have not implemented subscriptions, we are still using polling to update this component. -- If we merge two queries, do we hit maximum complexity or not? -- Loading time and the UI: What information do we want to render the fastest? Can we defer some information to load with the second query? - -##### Directory Structure - -There has been an [interest](https://gitlab.com/gitlab-org/gitlab/-/issues/345571) in more information from downstream pipelines. As such, we may want to consider rethinking `linked_pipelines_mini_list.vue` and having the downstream pipelines into their own file called `downstream_pipelines.vue` instead. The upstream pipeline could then be rendered with a simple `ci-icon`. - -## Future Improvements - -- [GraphQL Subscriptions](https://gitlab.com/gitlab-org/gitlab/-/issues/406652) -- [Show downstream pipeline jobs](https://gitlab.com/gitlab-org/gitlab/-/issues/345571) -- Possible redesign + + + + diff --git a/doc/development/documentation/testing/index.md b/doc/development/documentation/testing/index.md index 185fea7cd81..e1bab525d00 100644 --- a/doc/development/documentation/testing/index.md +++ b/doc/development/documentation/testing/index.md @@ -210,12 +210,49 @@ and all updates should first be made there. On a regular basis, the changes made in `gitlab` project to the Vale and markdownlint configuration should be synchronized to the other projects. In each of the [supported projects](#supported-projects): -1. Create a new branch. -1. Copy the configuration files from the `gitlab` project into this branch, overwriting - the project's old configuration. Make sure no project-specific changes from the `gitlab` - project are included. For example, [`RelativeLinks.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/.vale/gitlab/RelativeLinks.yml) - is hard coded for specific projects. -1. Create a merge request and submit it to a technical writer for review and merge. +1. Create a new branch. Add `docs-` to the beginning or `-docs` to the end of the branch name. Some projects use this + convention to limit the jobs that run. +1. Copy the configuration files from the `gitlab` project. For example, in the root directory of the project, run: + + ```shell + # Copy markdownlint configuration file + cp ../gitlab/.markdownlint-cli2.yaml . + # Copy Vale configuration files for a project with documentation stored in 'docs' directory + cp -r ../gitlab/doc/.vale docs + ``` + +1. Review the diff created for `.markdownlint-cli2.yaml`. For example, run: + + ```shell + git diff .markdownlint-cli2.yaml + ``` + +1. Remove any changes that aren't required. For example, `customRules` is only used in the `gitlab` project. +1. Review the diffs created for the Vale configuration. For example, run: + + ```shell + git diff docs + ``` + +1. Remove unneeded changes to `RelativeLinks.yml`. This rule is specific to each project. +1. Remove any `.tmpl` files. These files are only used in the `gitlab` project. +1. Run `markdownlint-cli2` to check for any violations of the new rules. For example: + + ```shell + markdownlint-cli2 docs/**/*.md + ``` + +1. Run Vale to check for any violations of the new rules. For example: + + ```shell + vale --minAlertLevel error docs + ``` + +1. Commit the changes to the new branch. Some projects require + [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) so check the contributing information for the + project before committing. + +1. Submit a merge request for review. ## Update linting images diff --git a/doc/user/gitlab_duo/index.md b/doc/user/gitlab_duo/index.md index 8e01ec26f96..387d8bf770e 100644 --- a/doc/user/gitlab_duo/index.md +++ b/doc/user/gitlab_duo/index.md @@ -56,26 +56,6 @@ DETAILS: - [Watch overview](https://youtu.be/ds7SG1wgcVM) - [View documentation](../project/repository/code_suggestions/index.md). -### Vulnerability resolution - -DETAILS: -**Tier:** Ultimate with [GitLab Duo Enterprise](../../subscriptions/subscription-add-ons.md) add-on -**Offering:** GitLab.com, Self-managed, GitLab Dedicated - -- Help resolve a vulnerability by generating a merge request that addresses it. -- LLM: Anthropic's [`claude-3-haiku`](https://docs.anthropic.com/en/docs/about-claude/models#claude-3-a-new-generation-of-ai) -- [View documentation](../application_security/vulnerabilities/index.md#vulnerability-resolution). - -### Vulnerability explanation - -DETAILS: -**Tier:** Ultimate with [GitLab Duo Enterprise](../../subscriptions/subscription-add-ons.md) -**Offering:** GitLab.com, Self-managed, GitLab Dedicated - -- Helps you understand vulnerabilities, how they can be exploited, and how to fix them. -- LLM: Anthropic's [`claude-3-haiku`](https://docs.anthropic.com/en/docs/about-claude/models#claude-3-a-new-generation-of-ai) -- [View documentation](../application_security/vulnerabilities/index.md#explaining-a-vulnerability). - ### Code explanation in the IDE DETAILS: @@ -118,6 +98,26 @@ DETAILS: - LLM: Vertex AI Codey [`text-bison`](https://console.cloud.google.com/vertex-ai/publishers/google/model-garden/text-bison). - [View documentation](../project/merge_requests/duo_in_merge_requests.md#generate-a-merge-commit-message). +### Vulnerability explanation + +DETAILS: +**Tier:** Ultimate with [GitLab Duo Enterprise](../../subscriptions/subscription-add-ons.md) +**Offering:** GitLab.com, Self-managed, GitLab Dedicated + +- Helps you understand vulnerabilities, how they can be exploited, and how to fix them. +- LLM: Anthropic's [`claude-3-haiku`](https://docs.anthropic.com/en/docs/about-claude/models#claude-3-a-new-generation-of-ai). +- [View documentation](../application_security/vulnerabilities/index.md#explaining-a-vulnerability). + +### Vulnerability resolution + +DETAILS: +**Tier:** Ultimate with [GitLab Duo Enterprise](../../subscriptions/subscription-add-ons.md) add-on +**Offering:** GitLab.com, Self-managed, GitLab Dedicated + +- Help resolve a vulnerability by generating a merge request that addresses it. +- LLM: Anthropic's [`claude-3-haiku`](https://docs.anthropic.com/en/docs/about-claude/models#claude-3-a-new-generation-of-ai). +- [View documentation](../application_security/vulnerabilities/index.md#vulnerability-resolution). + ## Beta features ### Merge request template population diff --git a/doc/user/group/epics/epic_work_items.md b/doc/user/group/epics/epic_work_items.md index f331f9b02c5..5355c14ae4d 100644 --- a/doc/user/group/epics/epic_work_items.md +++ b/doc/user/group/epics/epic_work_items.md @@ -54,6 +54,11 @@ Because this is an experimental feature, | `work_items_rolledup_dates` | Calculates the start and due dates in a hierarchy for work items. | Group | **Required** | | `epic_and_work_item_associations_unification` | Delegates other epic and work item associations. | Group | **Required** | +## Feedback + +If you run into any issue while trying out this change, you can use +[feedback issue](https://gitlab.com/gitlab-org/gitlab/-/issues/463598) to provide more details. + ## Related topics - [Work items development](../../../development/work_items.md) diff --git a/doc/user/group/value_stream_analytics/index.md b/doc/user/group/value_stream_analytics/index.md index e11581f2ee2..90e6cc68a2c 100644 --- a/doc/user/group/value_stream_analytics/index.md +++ b/doc/user/group/value_stream_analytics/index.md @@ -77,6 +77,8 @@ Value streams are container objects for the stages. You can have multiple value ### Value stream stage events +> - Merge request first reviewer assigned event [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/466383) in GitLab 17.2. Reviewer assignment events in merge requests created or updated prior to GitLab 17.2 are not available for reporting. + Events are the smallest building blocks of the value stream analytics feature. A stage consists of a start event and an end event. The following stage events are available: @@ -95,6 +97,7 @@ The following stage events are available: - MR created - MR first commit time - MR first assigned +- MR first reviewer assigned - MR first deployed - MR label added - MR label removed diff --git a/doc/user/profile/achievements.md b/doc/user/profile/achievements.md index 6f4a46c2343..8cc6942a9ea 100644 --- a/doc/user/profile/achievements.md +++ b/doc/user/profile/achievements.md @@ -42,6 +42,14 @@ Practically, you can differentiate between achievements that are awarded: - Once and revocable. For example, a "Core team member" achievement. - Multiple times. For example, a "Contributor of the month" achievement. +## View group achievements + +To view all available and awarded achievements for a group: + +- Go to `https://gitlab.com/groups//-/achievements`. + +The page displays a list of achievements and the members who were awarded the achievement. + ## View a user's achievements You can view a user's achievements on their profile page. @@ -92,44 +100,52 @@ Prerequisites: - You must have the Maintainer or Owner role for the namespace. -To create an achievement, call the [`achievementsCreate` GraphQL mutation](../../api/graphql/reference/index.md#mutationachievementscreate). +To create an achievement: -```graphql -mutation achievementsCreate($file: Upload!) { - achievementsCreate( - input: { - namespaceId: "gid://gitlab/Namespace/", - name: "", - description: "", - avatar: $file} - ) { - errors - achievement { - id - name - description - avatarUrl +- In the UI: + 1. On the [Achievements page](#view-group-achievements), select **New achievement**. + 1. Enter a name for the achievement. + 1. Optional. Enter a description and upload an avatar for the achievement. + 1. Select **Save changes**. + +- With the GraphQL API, call the [`achievementsCreate` GraphQL mutation](../../api/graphql/reference/index.md#mutationachievementscreate): + + ```graphql + mutation achievementsCreate($file: Upload!) { + achievementsCreate( + input: { + namespaceId: "gid://gitlab/Namespace/", + name: "", + description: "", + avatar: $file} + ) { + errors + achievement { + id + name + description + avatarUrl + } } } -} -``` + ``` -To supply the avatar file, call the mutation using `curl`: + To supply the avatar file, call the mutation using `curl`: -```shell -curl "https://gitlab.com/api/graphql" \ - -H "Authorization: Bearer " \ - -H "Content-Type: multipart/form-data" \ - -F operations='{ "query": "mutation ($file: Upload!) { achievementsCreate(input: { namespaceId: \"gid://gitlab/Namespace/\", name: \"\", description: \"\", avatar: $file }) { achievement { id name description avatarUrl } } }", "variables": { "file": null } }' \ - -F map='{ "0": ["variables.file"] }' \ - -F 0='@/path/to/your/file.jpg' -``` + ```shell + curl "https://gitlab.com/api/graphql" \ + -H "Authorization: Bearer " \ + -H "Content-Type: multipart/form-data" \ + -F operations='{ "query": "mutation ($file: Upload!) { achievementsCreate(input: { namespaceId: \"gid://gitlab/Namespace/\", name: \"\", description: \"\", avatar: $file }) { achievement { id name description avatarUrl } } }", "variables": { "file": null } }' \ + -F map='{ "0": ["variables.file"] }' \ + -F 0='@/path/to/your/file.jpg' + ``` -When successful, the response returns the achievement ID: + When successful, the response returns the achievement ID: -```shell -{"data":{"achievementsCreate":{"achievement":{"id":"gid://gitlab/Achievements::Achievement/1","name":"","description":"","avatarUrl":"https://gitlab.com/uploads/-/system/achievements/achievement/avatar/1/file.jpg"}}}} -``` + ```shell + {"data":{"achievementsCreate":{"achievement":{"id":"gid://gitlab/Achievements::Achievement/1","name":"","description":"","avatarUrl":"https://gitlab.com/uploads/-/system/achievements/achievement/avatar/1/file.jpg"}}}} + ``` ## Update an achievement diff --git a/doc/user/profile/index.md b/doc/user/profile/index.md index ac45fb56248..b5fe52a43f9 100644 --- a/doc/user/profile/index.md +++ b/doc/user/profile/index.md @@ -45,7 +45,7 @@ Prerequisites: - Must be between 2 and 255 characters in length. - Must only include non-accented letters, digits, `_`, `-`, and `.`. - Must not: - - Start with `-`. + - Start with `_`, `-`, or `.`. - Contain emoji. - End with `.` or `.`, for example `jon.png`, `jon.git` or `jon.atom`. However, `jonpng` is valid. diff --git a/lib/api/draft_notes.rb b/lib/api/draft_notes.rb index 6a20979269e..571bea7c54e 100644 --- a/lib/api/draft_notes.rb +++ b/lib/api/draft_notes.rb @@ -30,7 +30,7 @@ module API def publish_draft_note(params:) ::DraftNotes::PublishService .new(merge_request(params: params), current_user) - .execute(get_draft_note(params: params)) + .execute(draft: get_draft_note(params: params)) end def publish_draft_notes(params:) diff --git a/lib/gitlab/import/import_user_creator.rb b/lib/gitlab/import/import_user_creator.rb new file mode 100644 index 00000000000..62b4620b7d2 --- /dev/null +++ b/lib/gitlab/import/import_user_creator.rb @@ -0,0 +1,77 @@ +# frozen_string_literal: true + +module Gitlab + module Import + class ImportUserCreator + include Gitlab::Utils::StrongMemoize + + NoRootGroupError = Class.new(StandardError) + + attr_reader :portable + + def initialize(portable:) + @portable = portable + end + + def execute + raise NoRootGroupError, 'Portable has no root group' unless root_ancestor + + return import_user if import_user + + User.transaction do + user = create_user + create_namespace_import_user(user) + user + end + rescue ActiveRecord::RecordNotUnique => e + ::Import::Framework::Logger.warn( + message: 'Failed to create namespace_import_user', + error: e.message + ) + + # Import user already exists, try finding it again + import_user + end + + private + + def create_user + User.create!( + user_type: :import_user, + name: 'Import User', + username: username_and_email_generator.username, + email: username_and_email_generator.email + ) do |u| + u.assign_personal_namespace(Organizations::Organization.default_organization) + end + end + + def create_namespace_import_user(user) + ::Import::NamespaceImportUser.create!( + user_id: user.id, + namespace_id: root_ancestor.id + ) + end + + def username_and_email_generator + Gitlab::Utils::UsernameAndEmailGenerator.new( + username_prefix: username_prefix, + email_domain: "noreply.#{Gitlab.config.gitlab.host}" + ) + end + strong_memoize_attr :username_and_email_generator + + def import_user + root_ancestor.import_user + end + + def root_ancestor + portable.root_ancestor + end + + def username_prefix + "import_user_namespace_#{root_ancestor.id}" + end + end + end +end diff --git a/lib/tasks/gitlab/populate_job_traces.rake b/lib/tasks/gitlab/populate_job_traces.rake new file mode 100644 index 00000000000..2f071995cb1 --- /dev/null +++ b/lib/tasks/gitlab/populate_job_traces.rake @@ -0,0 +1,180 @@ +# frozen_string_literal: true + +NUMBER_OF_GITLAB_TRACES = 1000 +DEFAULT_NUMBER_OF_TRACES = 250 + +namespace :gitlab do + namespace :populate_job_traces do + desc "GitLab | Populates projects builds with real job traces, requires existing builds" + task :populate, + [:target_project_id, :access_token, :custom_project_id, :custom_number_to_populate] => + [:environment] do |_t, args| + # These projects are chosen as they're open to the public + @projects = [ + ["gitlab-org/gitlab", 278964, NUMBER_OF_GITLAB_TRACES], + ["gitlab-org/modelops/ai-model-validation-and-research/ai-evaluation/prompt-library", + 46678122, DEFAULT_NUMBER_OF_TRACES], + ["gitlab-org/gitlab-runner", 250833, DEFAULT_NUMBER_OF_TRACES], + ["gitlab-org/gitlab-services/design.gitlab.com", 4456656, DEFAULT_NUMBER_OF_TRACES], + ["gitlab-com/www-gitlab-com", 7764, DEFAULT_NUMBER_OF_TRACES], + ["gitlab-org/gitlab-development-kit", 74823, DEFAULT_NUMBER_OF_TRACES], + ["gitlab-org/omnibus-gitlab", 20699, DEFAULT_NUMBER_OF_TRACES], + ["gitlab-org/cli", 34675721, DEFAULT_NUMBER_OF_TRACES], + ["gitlab-org/gitaly", 2009901, DEFAULT_NUMBER_OF_TRACES], + ["gitlab-org/gitlab-docs", 1794617, DEFAULT_NUMBER_OF_TRACES], + ["gitlab-org/gitlab-ui", 7071551, DEFAULT_NUMBER_OF_TRACES], + ["gitlab-org/gitlab-vscode-extension", 5261717, DEFAULT_NUMBER_OF_TRACES], + ["gitlab-org/gitlab-pages", 734943, DEFAULT_NUMBER_OF_TRACES], + ["gitlab-org/release-tools", 430285, DEFAULT_NUMBER_OF_TRACES] + ].freeze + + if args.custom_project_id.present? && args.custom_number_to_populate.present? + @projects = [["custom_project", args.custom_project_id.to_i, args.custom_number_to_populate.to_i]] + end + + @project_id = args.target_project_id + @access_token = args.access_token + + start_populate + end + + desc "GitLab | Populate existing projects builds with only 1 trace per project" + task :populate_trial, + [:target_project_id, :access_token, :custom_project_id, :custom_number_to_populate] => + [:environment] do |_t, args| + @projects = [ + ["gitlab-org/gitlab", 278964, 1], + ["gitlab-org/modelops/ai-model-validation-and-research/ai-evaluation/prompt-library", 46678122, 1], + ["gitlab-org/gitlab-runner", 250833, 1], + ["gitlab-org/gitlab-services/design.gitlab.com", 4456656, 1], + ["gitlab-com/www-gitlab-com", 7764, 1], + ["gitlab-org/gitlab-development-kit", 74823, 1], + ["gitlab-org/omnibus-gitlab", 20699, 1], + ["gitlab-org/cli", 34675721, 1], + ["gitlab-org/gitaly", 2009901, 1], + ["gitlab-org/gitlab-docs", 1794617, 1], + ["gitlab-org/gitlab-ui", 7071551, 1], + ["gitlab-org/gitlab-vscode-extension", 5261717, 1], + ["gitlab-org/gitlab-pages", 734943, 1], + ["gitlab-org/release-tools", 430285, 1] + ].freeze + + if args.custom_project_id.present? && args.custom_number_to_populate.present? + @projects = [["custom_project", args.custom_project_id.to_i, args.custom_number_to_populate.to_i]] + end + + @project_id = args.target_project_id + @access_token = args.access_token + + start_populate + end + + def start_populate + @num_builds_processed_this_project = 0 + @current_project_index = -1 # This is because process goes to next project at first iteration + @current_project_id = nil + @ids_for_current_project = [] + @current_job_index_for_project = 0 + + project = Project.find(@project_id) + + start_time = Time.zone.now + + project.builds.failed.each_batch(of: 500, order: :desc) do |batch| + puts "Starting batch with id range: #{batch.first.id} - #{batch.last.id}" + break if process_builds(batch) + end + + puts "Time Elapsed: #{Time.zone.now - start_time}" + end + + private + + def process_builds(batch) + batch.each do |job| + # We've done all we should do for the current project, load the next one + if @current_job_index_for_project >= @ids_for_current_project.length + @current_project_index += 1 + + if @current_project_index >= @projects.length + puts "Finished!" + + return true + end + + @current_job_index_for_project = 0 + _, project_id, number_to_load = @projects[@current_project_index] + @current_project_id = project_id + @ids_for_current_project = load_ids_from_pagination(project_id, number_to_load) + next if @ids_for_current_project.empty? + end + + job_id = @ids_for_current_project[@current_job_index_for_project] + job_trace = load_trace_for_job(@current_project_id, job_id) + + begin + job.trace.erase! + job.trace.set(job_trace) + rescue StandardError => e + puts "ERROR: (write log) #{job_id} - #{e}" + end + + @current_job_index_for_project += 1 + end + + false + end + + def load_ids_from_pagination(project_id, number_to_load) + url = "https://gitlab.com/api/v4/projects/#{project_id}/jobs?" \ + "scope[]=failed" \ + "&pagination=keyset" \ + "&per_page=100" \ + "&order_by=id" \ + "&sort=desc" + + headers = { "PRIVATE-TOKEN": @access_token } + + ids = [] + num_loaded = 0 + + (number_to_load / 100.to_f).ceil.times do + puts "Calling JOBs API: #{url}" + project_jobs_response = Gitlab::HTTP.get(url, headers: headers) + + unless project_jobs_response.code == 200 + puts "ERROR: (project api) #{project_jobs_response.code} : #{project_jobs_response.message}" + @current_project_index += 1 + return [] + end + + project_jobs_response.each do |response| + break if num_loaded >= number_to_load + + ids << response["id"] + num_loaded += 1 + end + + url = project_jobs_response.headers["link"]&.match(/<([^>]*)>/)&.[](1) + end + + puts "Loaded ##{ids.length} ids for #{project_id}" + ids + end + + def load_trace_for_job(project_id, job_id) + job_trace_url = "https://gitlab.com/api/v4/projects/#{project_id}/jobs/#{job_id}/trace" + + headers = { "PRIVATE-TOKEN": @access_token } + + job_trace_response = Gitlab::HTTP.get(job_trace_url, headers: headers) + + unless job_trace_response.code == 200 + puts "ERROR: (trace api) #{job_trace_response.code} : #{job_trace_response.message}" + return "Ignore: Failed to fetch actual job trace" + end + + job_trace_response + end + end +end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index eb37d183086..a6b34c54704 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -16242,6 +16242,9 @@ msgstr "" msgid "CycleAnalyticsEvent|Merge request merged" msgstr "" +msgid "CycleAnalyticsEvent|Merge request reviewer first assigned" +msgstr "" + msgid "CycleAnalyticsStage|Code" msgstr "" @@ -20999,9 +21002,6 @@ msgstr "" msgid "Error occurred when fetching sidebar data" msgstr "" -msgid "Error occurred when saving assignees" -msgstr "" - msgid "Error occurred when saving reviewers" msgstr "" @@ -32973,9 +32973,6 @@ msgstr "" msgid "Merges this merge request immediately." msgstr "" -msgid "Merging immediately isn't recommended as it may negatively impact the existing merge train. Read the %{docsLinkStart}documentation%{docsLinkEnd} for more information." -msgstr "" - msgid "Mermaid diagram" msgstr "" @@ -38457,13 +38454,10 @@ msgstr "" msgid "PipelineCharts|CI/CD Analytics" msgstr "" -msgid "PipelineCharts|Failed pipelines" +msgid "PipelineCharts|Failure rate" msgstr "" -msgid "PipelineCharts|Success ratio" -msgstr "" - -msgid "PipelineCharts|Successful pipelines" +msgid "PipelineCharts|Success rate" msgstr "" msgid "PipelineCharts|There was an error parsing the data for the charts." @@ -39312,9 +39306,6 @@ msgstr "" msgid "Pipeline|Running" msgstr "" -msgid "Pipeline|See details" -msgstr "" - msgid "Pipeline|Skipped" msgstr "" @@ -39381,6 +39372,9 @@ msgstr "" msgid "Pipeline|Variables" msgstr "" +msgid "Pipeline|View all" +msgstr "" + msgid "Pipeline|View commit" msgstr "" @@ -51371,6 +51365,9 @@ msgstr "" msgid "Status checks must pass." msgstr "" +msgid "Status not supported" +msgstr "" + msgid "Status was retried." msgstr "" @@ -60586,12 +60583,18 @@ msgstr "" msgid "Workspaces" msgstr "" +msgid "Workspaces|%{workspaceName} has been terminated." +msgstr "" + msgid "Workspaces|A devfile defines the development environment for a GitLab project. A workspace must have a valid devfile in the Git reference you use." msgstr "" msgid "Workspaces|A workspace is a virtual sandbox environment for your code in GitLab." msgstr "" +msgid "Workspaces|Active" +msgstr "" + msgid "Workspaces|Agents connect workspaces to your Kubernetes cluster. To create a workspace with an allowed agent, group members must have at least the Developer role." msgstr "" @@ -60682,6 +60685,12 @@ msgstr "" msgid "Workspaces|New workspace" msgstr "" +msgid "Workspaces|No active workspaces. Create a workspace to get started." +msgstr "" + +msgid "Workspaces|No terminated workspaces to show." +msgstr "" + msgid "Workspaces|Path to devfile" msgstr "" @@ -63506,10 +63515,13 @@ msgstr "" msgid "mrWidget|Merged by" msgstr "" -msgid "mrWidget|Merging immediately is not recommended. The merged changes could cause pipeline failures on the target branch, and the changes will not be validated against the commits being added by the merge requests currently in the merge train. Read the %{linkStart}documentation%{linkEnd} for more information." +msgid "mrWidget|Merging immediately is not recommended because your changes won't be validated by the merge train, and any running merge train pipelines will be restarted. %{docsLinkStart}What are the risks?%{docsLinkEnd}" msgstr "" -msgid "mrWidget|Merging immediately isn't recommended as it may negatively impact the existing merge train. Read the %{linkStart}documentation%{linkEnd} for more information." +msgid "mrWidget|Merging immediately is not recommended because your changes won't be validated by the merge train, and any running merge train pipelines will be restarted. %{docsLinkStart}What are the risks?%{docsLinkEnd}." +msgstr "" + +msgid "mrWidget|Merging immediately is not recommended. The merged changes could cause pipeline failures on the target branch, and the changes will not be validated against the commits being added by the merge requests currently in the merge train. Read the %{linkStart}documentation%{linkEnd} for more information." msgstr "" msgid "mrWidget|Migrate to GitLab CI/CD from Jenkins" diff --git a/qa/qa/specs/features/shared_examples/create_and_terminate_workspace_shared_examples.rb b/qa/qa/specs/features/shared_examples/create_and_terminate_workspace_shared_examples.rb index 7a9a485d611..b4313e3873f 100644 --- a/qa/qa/specs/features/shared_examples/create_and_terminate_workspace_shared_examples.rb +++ b/qa/qa/specs/features/shared_examples/create_and_terminate_workspace_shared_examples.rb @@ -7,11 +7,7 @@ module QA workspace_name = "" QA::EE::Page::Workspace::List.perform do |list| - existing_workspaces = list.get_workspaces_list - list.create_workspace(kubernetes_agent.name, devfile_project.name) - updated_workspaces = list.get_workspaces_list - workspace_name = (updated_workspaces - existing_workspaces).fetch(0, '').to_s - raise "Workspace name is empty" if workspace_name == '' + workspace_name = list.create_workspace(kubernetes_agent.name, devfile_project.name) expect(list).to have_workspace_state(workspace_name, "Creating") list.wait_for_workspaces_creation(workspace_name) @@ -31,6 +27,7 @@ module QA end QA::EE::Page::Workspace::List.perform do |list_item| + list_item.click_terminated_tab expect(list_item).to have_workspace_state(workspace_name, "Terminated") end end diff --git a/spec/factories/import/namespace_import_users.rb b/spec/factories/import/namespace_import_users.rb new file mode 100644 index 00000000000..79c8896a726 --- /dev/null +++ b/spec/factories/import/namespace_import_users.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :namespace_import_user, class: 'Import::NamespaceImportUser' do + import_user factory: :user + namespace + end +end diff --git a/spec/factories/users.rb b/spec/factories/users.rb index f2e2f1933e5..33f929cadf8 100644 --- a/spec/factories/users.rb +++ b/spec/factories/users.rb @@ -133,6 +133,10 @@ FactoryBot.define do user_type { :placeholder } end + trait :import_user do + user_type { :import_user } + end + trait :external do external { true } end diff --git a/spec/features/merge_request/user_views_open_merge_request_spec.rb b/spec/features/merge_request/user_views_open_merge_request_spec.rb index f1b68cd2dc9..7e72727f31e 100644 --- a/spec/features/merge_request/user_views_open_merge_request_spec.rb +++ b/spec/features/merge_request/user_views_open_merge_request_spec.rb @@ -96,22 +96,6 @@ RSpec.describe 'User views an open merge request', feature_category: :code_revie expect(page).not_to have_content(/([0-9]+ commits? behind)/) end end - - context 'when the assignee\'s availability set' do - before do - merge_request.author.create_status(availability: 'busy') - merge_request.assignees << merge_request.author - - visit(merge_request_path(merge_request)) - end - - it 'exposes the availability in the data-availability attribute' do - assignees_data = find_all("input[name='merge_request[assignee_ids][]']", visible: false) - - expect(assignees_data.size).to eq(1) - expect(assignees_data.first['data-availability']).to eq('busy') - end - end end context 'when user preferred language has changed', :use_clean_rails_memory_store_fragment_caching do diff --git a/spec/frontend/fixtures/merge_requests.rb b/spec/frontend/fixtures/merge_requests.rb index e8272a1f93a..38cf58247ea 100644 --- a/spec/frontend/fixtures/merge_requests.rb +++ b/spec/frontend/fixtures/merge_requests.rb @@ -67,7 +67,7 @@ RSpec it 'merge_requests/merge_request_with_single_assignee_feature.html' do stub_licensed_features(multiple_merge_request_assignees: false) - render_merge_request(merge_request) + render_merge_request_edit(merge_request) end it 'merge_requests/merge_request_of_current_user.html' do @@ -239,4 +239,14 @@ RSpec expect(response).to be_successful end + + def render_merge_request_edit(merge_request) + get :edit, params: { + namespace_id: project.namespace.to_param, + project_id: project, + id: merge_request.to_param + }, format: :html + + expect(response).to be_successful + end end diff --git a/spec/frontend/projects/pipelines/charts/components/__snapshots__/statistics_list_spec.js.snap b/spec/frontend/projects/pipelines/charts/components/__snapshots__/statistics_list_spec.js.snap index 435efee6728..3d179ec9b8c 100644 --- a/spec/frontend/projects/pipelines/charts/components/__snapshots__/statistics_list_spec.js.snap +++ b/spec/frontend/projects/pipelines/charts/components/__snapshots__/statistics_list_spec.js.snap @@ -22,45 +22,33 @@ exports[`StatisticsList displays the counts data with labels 1`] = ` animationdecimalplaces="0" id="reference-1" shouldanimate="true" - title="Success ratio" + title="Failure rate" titleiconclass="" unit="" usedelimiters="true" - value="50.00%" + value="50%" variant="muted" /> + + View all +
-
- - - See details - -
`; diff --git a/spec/frontend/projects/pipelines/charts/components/pipeline_charts_spec.js b/spec/frontend/projects/pipelines/charts/components/pipeline_charts_spec.js index 137100be34e..5269d11307c 100644 --- a/spec/frontend/projects/pipelines/charts/components/pipeline_charts_spec.js +++ b/spec/frontend/projects/pipelines/charts/components/pipeline_charts_spec.js @@ -43,10 +43,9 @@ describe('~/projects/pipelines/charts/components/pipeline_charts.vue', () => { expect(list.exists()).toBe(true); expect(list.props('counts')).toEqual({ - total: 34, - success: 23, - failed: 1, - successRatio: (23 / (23 + 1)) * 100, + total: 40, + successRatio: (23 / 40) * 100, + failureRatio: (1 / 40) * 100, }); }); diff --git a/spec/frontend/projects/pipelines/charts/components/statistics_list_spec.js b/spec/frontend/projects/pipelines/charts/components/statistics_list_spec.js index 0119d1ca7c5..47c1817f7c6 100644 --- a/spec/frontend/projects/pipelines/charts/components/statistics_list_spec.js +++ b/spec/frontend/projects/pipelines/charts/components/statistics_list_spec.js @@ -1,16 +1,23 @@ +import { nextTick } from 'vue'; import { GlLink } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; import Component from '~/projects/pipelines/charts/components/statistics_list.vue'; + +import { InternalEvents } from '~/tracking'; +import { mockTracking } from 'helpers/tracking_helper'; + import { counts } from '../mock_data'; describe('StatisticsList', () => { let wrapper; + let trackingSpy; const failedPipelinesLink = '/flightjs/Flight/-/pipelines?page=1&scope=all&status=failed'; const findFailedPipelinesLink = () => wrapper.findComponent(GlLink); beforeEach(() => { + trackingSpy = mockTracking(undefined, window.document, jest.spyOn); wrapper = shallowMount(Component, { provide: { failedPipelinesLink, @@ -29,6 +36,22 @@ describe('StatisticsList', () => { expect(findFailedPipelinesLink().attributes('href')).toBe(failedPipelinesLink); }); + describe('when View All link is clicked', () => { + beforeEach(async () => { + InternalEvents.bindInternalEventDocument(findFailedPipelinesLink().element); + await findFailedPipelinesLink().trigger('click'); + await nextTick(); + }); + + it('should track that the View All link has been clicked', () => { + expect(trackingSpy).toHaveBeenCalledWith( + undefined, + 'click_view_all_link_in_pipeline_analytics', + expect.any(Object), + ); + }); + }); + describe('with no failed pipelines link', () => { beforeEach(() => { wrapper = shallowMount(Component, { diff --git a/spec/frontend/projects/pipelines/charts/mock_data.js b/spec/frontend/projects/pipelines/charts/mock_data.js index 26de7389522..f10ed3ebaf6 100644 --- a/spec/frontend/projects/pipelines/charts/mock_data.js +++ b/spec/frontend/projects/pipelines/charts/mock_data.js @@ -3,6 +3,7 @@ export const counts = { success: 20000, total: 40000, successRatio: 50, + failureRatio: 50, totalDuration: 116158, }; @@ -49,7 +50,7 @@ export const mockPipelineCount = { data: { project: { id: '1', - totalPipelines: { count: 34, __typename: 'PipelineConnection' }, + totalPipelines: { count: 40, __typename: 'PipelineConnection' }, successfulPipelines: { count: 23, __typename: 'PipelineConnection' }, failedPipelines: { count: 1, __typename: 'PipelineConnection' }, totalPipelineDuration: 2471, diff --git a/spec/frontend/sidebar/components/assignees/sidebar_assignees_spec.js b/spec/frontend/sidebar/components/assignees/sidebar_assignees_spec.js deleted file mode 100644 index a8b2db66723..00000000000 --- a/spec/frontend/sidebar/components/assignees/sidebar_assignees_spec.js +++ /dev/null @@ -1,102 +0,0 @@ -import { shallowMount } from '@vue/test-utils'; -import axios from 'axios'; -import AxiosMockAdapter from 'axios-mock-adapter'; -import { nextTick } from 'vue'; -import Assigness from '~/sidebar/components/assignees/assignees.vue'; -import AssigneesRealtime from '~/sidebar/components/assignees/assignees_realtime.vue'; -import SidebarAssignees from '~/sidebar/components/assignees/sidebar_assignees.vue'; -import SidebarService from '~/sidebar/services/sidebar_service'; -import SidebarMediator from '~/sidebar/sidebar_mediator'; -import SidebarStore from '~/sidebar/stores/sidebar_store'; -import eventHub from '~/sidebar/event_hub'; -import Mock from '../../mock_data'; - -describe('sidebar assignees', () => { - let wrapper; - let mediator; - let axiosMock; - const createComponent = (props) => { - wrapper = shallowMount(SidebarAssignees, { - propsData: { - issuableIid: '1', - issuableId: 1, - mediator, - field: '', - projectPath: 'projectPath', - changing: false, - ...props, - }, - // Attaching to document is required because this component emits something from the parent element :/ - attachTo: document.body, - }); - }; - - const findAssigness = () => wrapper.findComponent(Assigness); - const findAssigneesRealtime = () => wrapper.findComponent(AssigneesRealtime); - - beforeEach(() => { - axiosMock = new AxiosMockAdapter(axios); - mediator = new SidebarMediator(Mock.mediator); - - jest.spyOn(mediator, 'saveAssignees'); - jest.spyOn(mediator, 'assignYourself'); - }); - - afterEach(() => { - SidebarService.singleton = null; - SidebarStore.singleton = null; - SidebarMediator.singleton = null; - axiosMock.restore(); - }); - - it('calls the mediator when saves the assignees', () => { - createComponent(); - - expect(mediator.saveAssignees).not.toHaveBeenCalled(); - - eventHub.$emit('sidebar.saveAssignees'); - - expect(mediator.saveAssignees).toHaveBeenCalled(); - }); - - it('calls the mediator when "assignSelf" method is called', async () => { - createComponent(); - mediator.store.isFetching.assignees = false; - await nextTick(); - - expect(mediator.assignYourself).not.toHaveBeenCalled(); - expect(mediator.store.assignees.length).toBe(0); - - await findAssigness().vm.$emit('assign-self'); - - expect(mediator.assignYourself).toHaveBeenCalled(); - expect(mediator.store.assignees.length).toBe(1); - }); - - it('hides assignees until fetched', async () => { - createComponent(); - - expect(findAssigness().exists()).toBe(false); - - mediator.store.isFetching.assignees = false; - - await nextTick(); - expect(findAssigness().exists()).toBe(true); - }); - - describe('when issuableType is issue', () => { - it('finds AssigneesRealtime component', () => { - createComponent(); - - expect(findAssigneesRealtime().exists()).toBe(true); - }); - }); - - describe('when issuableType is MR', () => { - it('does not find AssigneesRealtime component', () => { - createComponent({ issuableType: 'MR' }); - - expect(findAssigneesRealtime().exists()).toBe(false); - }); - }); -}); diff --git a/spec/frontend/users_select/test_helper.js b/spec/frontend/users_select/test_helper.js index 0d8e3275aa5..05b0bf314a0 100644 --- a/spec/frontend/users_select/test_helper.js +++ b/spec/frontend/users_select/test_helper.js @@ -10,7 +10,7 @@ import UsersSelect from '~/users_select'; const getUserSearchHTML = memoize((fixture) => { const parser = new DOMParser(); - const el = parser.parseFromString(fixture, 'text/html').querySelector('.assignee'); + const el = parser.parseFromString(fixture, 'text/html').querySelector('.merge-request-assignee'); return el.outerHTML; }); @@ -88,7 +88,7 @@ export const findDropdownItemsModel = () => export const setAssignees = (...users) => { findAssigneesInputs().forEach((x) => x.remove()); - const container = document.querySelector('.js-sidebar-assignee-data'); + const container = document.querySelector('.selectbox'); container.prepend( ...users.map((user) => { @@ -145,6 +145,7 @@ export const createInputsModelExpectation = (users) => can_update_merge_request: user.can_update_merge_request.toString(), id: user.id.toString(), name: user.name, + meta: user.name, show_status: user.show_status.toString(), state: user.state, locked: user.locked.toString(), diff --git a/spec/helpers/issuables_helper_spec.rb b/spec/helpers/issuables_helper_spec.rb index a92c34043e9..f2e7d041690 100644 --- a/spec/helpers/issuables_helper_spec.rb +++ b/spec/helpers/issuables_helper_spec.rb @@ -481,35 +481,6 @@ RSpec.describe IssuablesHelper, feature_category: :team_planning do end end - describe '#assignee_sidebar_data' do - let(:user) { create(:user) } - let(:merge_request) { nil } - - subject { helper.assignee_sidebar_data(user, merge_request: merge_request) } - - it 'returns hash of assignee data' do - is_expected.to eql({ - avatar_url: user.avatar_url, - name: user.name, - username: user.username - }) - end - - context 'with merge_request' do - let(:merge_request) { build_stubbed(:merge_request) } - - where(can_merge: [true, false]) - - with_them do - before do - allow(merge_request).to receive(:can_be_merged_by?).and_return(can_merge) - end - - it { is_expected.to include({ can_merge: can_merge }) } - end - end - end - describe '#issuable_squash_option?' do using RSpec::Parameterized::TableSyntax diff --git a/spec/lib/gitlab/import/import_user_creator_spec.rb b/spec/lib/gitlab/import/import_user_creator_spec.rb new file mode 100644 index 00000000000..b6f3e807a98 --- /dev/null +++ b/spec/lib/gitlab/import/import_user_creator_spec.rb @@ -0,0 +1,87 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Import::ImportUserCreator, feature_category: :importers do + let(:group) { create(:group) } + + subject(:service) { described_class.new(portable: group) } + + it 'creates import user' do + user = service.execute + + expect(user.user_type).to eq('import_user') + expect(group.reload.import_user).to eq(user) + end + + context 'when import user already exists' do + it 'returns existing import user' do + user = create(:user) + namespace_import_user = create(:namespace_import_user, import_user: user, namespace: group) + + import_user = service.execute + + expect(import_user.id).to eq(namespace_import_user.import_user.id) + end + end + + context 'when provided portable is a subgroup' do + it 'creates import user on root group level' do + subgroup = create(:group, parent: group) + + import_user = described_class.new(portable: subgroup).execute + + expect(import_user.reload.namespace_import_user.namespace).to eq(group) + end + end + + context 'when provided portable is a project' do + it 'creates import user on root group level' do + project = create(:project, group: group) + + import_user = described_class.new(portable: project).execute + + expect(group.reload.import_user).to eq(import_user) + end + end + + context 'when exception occurs' do + it 'returns an error' do + allow(service).to receive(:create_user).and_raise(ActiveRecord::RecordInvalid) + + expect { service.execute }.to raise_error(ActiveRecord::RecordInvalid) + end + end + + context 'when portable is in user personal namespace' do + it 'creates import user' do + user_namespace = create(:user_namespace) + user = create(:user, namespace: user_namespace) + project = create(:project, creator: user, namespace: user_namespace) + + import_user = described_class.new(portable: project).execute + + expect(user.namespace.reload.import_user).to eq(import_user) + end + end + + context 'when namespace import user creation fails due to not unique error' do + it 'logs and returns existing import user' do + import_user = create(:user, :import_user) + create(:namespace_import_user, import_user: import_user, namespace: group) + + allow(service).to receive(:import_user).and_return(nil, import_user) + + expect(::Import::Framework::Logger) + .to receive(:warn) + .with( + message: 'Failed to create namespace_import_user', + error: a_string_matching('PG::UniqueViolation: ERROR: duplicate key value violates unique constraint') + ) + + user = service.execute + + expect(user.id).to eq(import_user.id) + end + end +end diff --git a/spec/models/concerns/has_user_type_spec.rb b/spec/models/concerns/has_user_type_spec.rb index ababb62808c..c59a603e8d5 100644 --- a/spec/models/concerns/has_user_type_spec.rb +++ b/spec/models/concerns/has_user_type_spec.rb @@ -14,7 +14,7 @@ RSpec.describe User, feature_category: :system_access do expect(described_class::USER_TYPES.keys) .to match_array(%w[human ghost alert_bot project_bot support_bot service_user security_bot visual_review_bot migration_bot automation_bot security_policy_bot admin_bot suggested_reviewers_bot - service_account llm_bot placeholder duo_code_review_bot]) + service_account llm_bot placeholder duo_code_review_bot import_user]) expect(described_class::USER_TYPES).to include(*described_class::BOT_USER_TYPES) expect(described_class::USER_TYPES).to include(*described_class::NON_INTERNAL_USER_TYPES) expect(described_class::USER_TYPES).to include(*described_class::INTERNAL_USER_TYPES) diff --git a/spec/models/import/namespace_import_user_spec.rb b/spec/models/import/namespace_import_user_spec.rb new file mode 100644 index 00000000000..d8185e91c42 --- /dev/null +++ b/spec/models/import/namespace_import_user_spec.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Import::NamespaceImportUser, type: :model, feature_category: :importers do + let(:namespace_import_user) { create(:namespace_import_user) } + + describe 'associations' do + it { is_expected.to belong_to(:import_user).class_name('User') } + it { is_expected.to belong_to(:namespace) } + end + + describe 'validations' do + it { is_expected.to validate_presence_of(:namespace_id) } + it { is_expected.to validate_presence_of(:user_id) } + end + + describe 'cascade deletion' do + context 'when user is removed' do + it 'removes namespace import user' do + namespace_import_user.import_user.destroy! + + expect { namespace_import_user.reload }.to raise_error(ActiveRecord::RecordNotFound) + end + end + + context 'when namespace is removed' do + it 'removes namespace import user' do + namespace_import_user.namespace.destroy! + + expect { namespace_import_user.reload }.to raise_error(ActiveRecord::RecordNotFound) + end + end + end +end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 5a0b4f06922..3e973085cab 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -6968,7 +6968,7 @@ RSpec.describe User, feature_category: :user_profile do end context "with bot users" do - %i[project_bot service_account security_policy_bot].each do |user_type| + %i[project_bot service_account security_policy_bot import_user].each do |user_type| context "when user is #{user_type}" do let(:user) { build(:user, user_type) } @@ -8132,6 +8132,14 @@ RSpec.describe User, feature_category: :user_profile do it_behaves_like 'does not require password to be present' end + + context 'when user is an import user' do + before do + user.update!(user_type: 'import_user') + end + + it_behaves_like 'does not require password to be present' + end end describe 'can_trigger_notifications?' do diff --git a/spec/policies/base_policy_spec.rb b/spec/policies/base_policy_spec.rb index b0d98fd9cf7..79ee26e8942 100644 --- a/spec/policies/base_policy_spec.rb +++ b/spec/policies/base_policy_spec.rb @@ -110,4 +110,16 @@ RSpec.describe BasePolicy do it { expect_disallowed(:receive_notifications) } it { expect_disallowed(:use_slash_commands) } end + + describe 'import_user' do + let(:current_user) { build_stubbed(:user, user_type: :import_user) } + + subject { described_class.new(current_user, nil) } + + it { expect_disallowed(:access_git) } + it { expect_disallowed(:log_in) } + it { expect_disallowed(:access_api) } + it { expect_disallowed(:receive_notifications) } + it { expect_disallowed(:use_slash_commands) } + end end diff --git a/spec/policies/group_policy_spec.rb b/spec/policies/group_policy_spec.rb index 184111ca81d..10cfc4e9e14 100644 --- a/spec/policies/group_policy_spec.rb +++ b/spec/policies/group_policy_spec.rb @@ -1227,8 +1227,16 @@ RSpec.describe GroupPolicy, feature_category: :system_access do it_behaves_like 'disallows all dependency proxy access' end + context 'import user' do + let_it_be(:import_user) { create(:user, user_type: :import_user, developer_of: group) } + + subject { described_class.new(import_user, group) } + + it_behaves_like 'disallows all dependency proxy access' + end + context 'all other user types' do - User::USER_TYPES.except(:human, :project_bot, :placeholder).each_value do |user_type| + User::USER_TYPES.except(:human, :project_bot, :placeholder, :import_user).each_value do |user_type| context "with user_type #{user_type}" do before do current_user.update!(user_type: user_type) diff --git a/spec/services/draft_notes/publish_service_spec.rb b/spec/services/draft_notes/publish_service_spec.rb index 43e207bef6b..4209d6e15aa 100644 --- a/spec/services/draft_notes/publish_service_spec.rb +++ b/spec/services/draft_notes/publish_service_spec.rb @@ -9,6 +9,7 @@ RSpec.describe DraftNotes::PublishService, feature_category: :code_review_workfl let(:user) { merge_request.author } let(:commit) { project.commit(sample_commit.id) } let(:internal) { false } + let(:executing_user) { nil } let(:position) do Gitlab::Diff::Position.new( @@ -21,7 +22,7 @@ RSpec.describe DraftNotes::PublishService, feature_category: :code_review_workfl end def publish(draft: nil) - DraftNotes::PublishService.new(merge_request, user).execute(draft) + DraftNotes::PublishService.new(merge_request, user).execute(draft: draft, executing_user: executing_user) end context 'single draft note' do @@ -380,5 +381,35 @@ RSpec.describe DraftNotes::PublishService, feature_category: :code_review_workfl it 'returns an error' do expect(publish[:status]).to eq(:error) end + + context 'when executing_user is specified' do + let(:executing_user) { create(:user) } + + context 'and executing_user can create notes' do + before do + allow(Ability) + .to receive(:allowed?) + .with(executing_user, :create_note, merge_request) + .and_return(true) + end + + it 'returns success' do + expect(publish[:status]).to eq(:success) + end + end + + context 'and executing_user cannot create notes' do + before do + allow(Ability) + .to receive(:allowed?) + .with(executing_user, :create_note, merge_request) + .and_return(false) + end + + it 'returns an error' do + expect(publish[:status]).to eq(:error) + end + end + end end end diff --git a/spec/services/notes/build_service_spec.rb b/spec/services/notes/build_service_spec.rb index b300a1621de..849879eeeed 100644 --- a/spec/services/notes/build_service_spec.rb +++ b/spec/services/notes/build_service_spec.rb @@ -15,8 +15,13 @@ RSpec.describe Notes::BuildService, feature_category: :team_planning do let(:base_params) { { note: 'Test' } } let(:params) { {} } + let(:executing_user) { nil } - subject(:new_note) { described_class.new(project, user, base_params.merge(params)).execute } + subject(:new_note) do + described_class + .new(project, user, base_params.merge(params)) + .execute(executing_user: executing_user) + end describe '#execute' do context 'when in_reply_to_discussion_id is specified' do @@ -63,6 +68,25 @@ RSpec.describe Notes::BuildService, feature_category: :team_planning do it 'sets an error' do expect(new_note.errors[:base]).to include('Discussion to reply to cannot be found') end + + context 'when executing_user is specified' do + context 'and executing_user has access to discussion' do + let(:executing_user) { author } + + it 'sets the note up to be in reply to that note' do + expect(new_note).to be_valid + expect(new_note.in_reply_to?(note)).to be_truthy + end + end + + context 'and executing_user also has no access to discussion' do + let(:executing_user) { other_user } + + it 'sets an error' do + expect(new_note.errors[:base]).to include('Discussion to reply to cannot be found') + end + end + end end context 'personal snippet note' do diff --git a/spec/tasks/gitlab/populate_job_traces_rake_spec.rb b/spec/tasks/gitlab/populate_job_traces_rake_spec.rb new file mode 100644 index 00000000000..af0a28464f3 --- /dev/null +++ b/spec/tasks/gitlab/populate_job_traces_rake_spec.rb @@ -0,0 +1,109 @@ +# frozen_string_literal: true + +require 'spec_helper' + +PROJECT_IDS = [ + 278964, + 46678122, + 250833, + 4456656, + 7764, + 74823, + 20699, + 34675721, + 2009901, + 1794617, + 7071551, + 5261717, + 734943, + 430285 +].freeze + +RSpec.describe 'gitlab:populate_job_traces rake task', :silence_stdout, feature_category: :continuous_integration do + let!(:project) { create(:project) } + + before do + Rake.application.rake_require 'tasks/gitlab/populate_job_traces' + end + + describe "#start_populate" do + before do + create_list(:ci_build, 14, :failed, project: project) + + stub_all_job_requests + stub_all_trace_requests + stub_const("NUMBER_OF_GITLAB_TRACES", 1) + stub_const("DEFAULT_NUMBER_OF_TRACES", 1) + end + + it "populates the job traces" do + run_rake_task('gitlab:populate_job_traces:populate', project.id, "fake-access-token") + + collection_of_all_traces = +"" + + project.builds.each do |job| + collection_of_all_traces << job.trace.raw + end + + trace_project_ids = collection_of_all_traces.scan(/\d+/).map(&:to_i).to_set + project_id_set = PROJECT_IDS.to_set + + expect(project_id_set.subset?(trace_project_ids)).to be_truthy + end + end + + describe "with custom project preferences" do + before do + create(:ci_build, :failed, project: project) + + stub_job_request("https://gitlab.com/api/v4/projects/999/jobs?order_by=id&pagination=keyset&per_page=100&scope[]=failed&sort=desc") + stub_trace_request(999) + end + + it 'populates custom jobs if provided project and number' do + run_rake_task('gitlab:populate_job_traces:populate', project.id, "fake-access-token", 999, 1) + + expect(project.builds.first.trace.raw).to include("999") + end + end + + def stub_all_job_requests + base_url = "https://gitlab.com/api/v4/projects/%s/jobs?order_by=id&pagination=keyset&per_page=100&scope[]=failed&sort=desc" + + PROJECT_IDS.each do |project_id| + stub_job_request(format(base_url, project_id)) + end + end + + def stub_job_request(url) + stub_request(:get, url) + .with( + headers: { + 'Accept' => '*/*', + 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', + 'Private-Token' => 'fake-access-token', + 'User-Agent' => 'Ruby' + }) + .to_return(status: 200, body: [{ id: 101 }].to_json, headers: { 'Content-Type' => 'application/json' }) + end + + def stub_all_trace_requests + PROJECT_IDS.each do |project_id| + stub_trace_request(project_id) + end + end + + def stub_trace_request(project_id) + base_url = "https://gitlab.com/api/v4/projects/%s/jobs/101/trace" + + stub_request(:get, format(base_url, project_id)) + .with( + headers: { + 'Accept' => '*/*', + 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', + 'Private-Token' => 'fake-access-token', + 'User-Agent' => 'Ruby' + }) + .to_return(status: 200, body: "Raw trace body #{project_id}", headers: {}) + end +end

;L=> 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 diff --git a/doc/architecture/blueprints/duo_workflow/index.md b/doc/architecture/blueprints/duo_workflow/index.md index 7fff493676d..1cbee692fd4 100644 --- a/doc/architecture/blueprints/duo_workflow/index.md +++ b/doc/architecture/blueprints/duo_workflow/index.md @@ -1,685 +1,11 @@ --- -status: ongoing -creation-date: "2024-05-17" -authors: [ "@DylanGriffith", "@mikolaj_wawrzyniak"] -coach: -approvers: [ ] -owning-stage: "~devops::create" -participating-stages: [] +redirect_to: 'https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/duo_workflow/' +remove_date: '2025-07-08' --- - +This document was moved to [another location](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/duo_workflow/). -# GitLab Duo Workflow - -## Execution Environment - -### Executive Summary - -The functionality to support Duo Workflow needs to be able to execute arbitrary code -which effectively means "untrusted" code. This means that they cannot just run -like any other service we deploy and specifically they cannot just run inside -the Duo Workflow Service or AI Gateway. - -In order to address this the Duo Workflow functionality will be comprised of 2 -separate components: - -1. The Duo Workflow Service, which is a Python service we run in our - infrastructure. The Workflow Service is built on top of - [LangGraph](https://github.com/langchain-ai/langgraph). -1. The Duo Worklow Executor, which is a Go binary that communicates via long - running gRPC connection to the Duo Workflow Service and executes the arbtitrary - commands. It will be possible for users to run this locally or in CI pipelines. - -In our first release we will support 2 execution modes: - -1. Local Executor: which will run commands and edit files locally in a - sandboxed Docker container on the developer machine. They will be able to - see the files being edited live and it will be interactive -1. CI Executor: All non-local use-cases of Duo Workflow (for example: - issue/epic based workflows) will be triggered by the GitLab UI and will - create a CI Pipeline to run the Duo Workflow Executor - -Our architecture will also support mixed deployments for self-managed such that -some features of Duo Workflow will be available using a cloud-hosted AI -Gateway. - -### Detailed plan - -We plan on building this feature set with 3 independent components that can be -run in multiple runtimes: - -1. The Duo Workflow Web UI. This will be web UI built into GitLab that manages the - creation and interaction of all workflows. There may be many interaction - points in the GitLab application but there should be a central workflow UI - with reusable components (e.g. Vue components) that could be embedded into - our editor extensions -1. The Duo Workflow Service. This is a Python-based service we deploy with - a gRPC API. The only interface to this is the gRPC interface, which is - called from the Duo Workflow Executor. Internally, this will use LangGraph to - execute the workflows. For reasons why LangGraph was chosen, see [this work item](https://gitlab.com/gitlab-org/gitlab/-/work_items/457958). - The Workflow Service will not have any persisted state but the state of - running workflows will be kept in memory and periodically checkpointed in - GitLab. The Workflow Service is built [in its own codebase](https://gitlab.com/gitlab-org/duo-workflow/duo-workflow-service/) - and will have its own deployment but the codebase - [may be merged with the AI Gateway codebase in the future](https://gitlab.com/gitlab-org/modelops/applied-ml/code-suggestions/ai-assist/-/issues/527) -1. The [Duo Workflow Executor](https://gitlab.com/gitlab-org/duo-workflow/duo-workflow-executor). - This is being written in Go for easy installation - in development containers. This component will run in CI jobs or on a user's - local workstation. In the local workstation it will run sandboxed in a - Docker container with the working directory optionally mounted by the - user for a live pairing experience. It will only be responsible for opening - a gRPC connection to Duo Workflow Service and executing the commands it is - told to. - -The following are important constraints of the architecture: - -1. All state management for workflows will be inside GitLab. -1. Duo Workflow Service is expected to periodically checkpoint its state in GitLab -1. Duo Workflow Service in-memory state can be dropped/lost at any time so - checkpointing will be the only guaranteed point that can be returned to -1. If a local Duo Workflow Executor drops the connection, the Duo Workflow - Service will checkpoint and shutdown the state as soon as it runs into - something where it is waiting on the executor -1. In order to avoid multiple Duo Workflow Service instances running on the - same workflow, the Duo Workflow Service will always acquire a lock with - GitLab before it starts running. When it suspends, it will release the lock and - similarly there will be a timeout state if it has not checkpointed in the - last 60 seconds. GitLab will not accept checkpoints from a timed out run of - the Duo Workflow Service. -1. Each time a Duo Workflow Service resumes a workflow it gets a new ID and - this is sent when checkpointing so that GitLab can drop/ignore zombie - services running the workflow and inform the zombie service to shutdown. -1. Code is checkpointed by the executor pushing hidden Git refs to the GitLab - instance. This will be happening on the same frequency as other checkpoints. -1. For local execution Duo Workflows are initiated using the Duo Workflow - Executor directly calling Duo Workflow Service -1. For workflows triggered via the UI that don't require a Duo Workflow - Executor GitLab can call the Duo Workflow Service directly -1. All API calls from Duo Workflow Service to GitLab that access private data - or update data will be authenticated on behalf of the user that created the - worklow. Duo Workflow Service should not need privileged access to GitLab - -CI pipelines have been chosen as the hosted runtime option for Duo Workflow -Executor because it is the only infrastructure we have available today to run -untrusted customer workloads with stability, support, security, abuse -prevention and a billing model. In the short term for early customers we may -rely on the existing compute minutes for CI pipelines but in the long run we -may want to deploy dedicated runners and introduce a billing model specific for -Duo Workflow. - -For many development use cases we expect developers may prefer to run Duo -Workflow Executor locally as it can operate on a locally mounted directory and -allow the user to more easily watch changes as they happen. - -### GitLab.com architecture - - - -1. Initially we focus on running locally and in CI pipelines with all inputs as - environment variables -1. State stored in GitLab so it can be accessed from the web UI and through IDE - extensions - -#### With Local (IDE) execution - -```mermaid -sequenceDiagram - participant user as User - participant ide as IDE - participant executor as Duo Workflow Executor - participant gitlab_rails as GitLab Rails - box AI-gateway service - participant duo_workflow_service as Duo Workflow Service - participant ai_gateway as AI Gateway - end - participant llm_provider as LLM Provider - ide->>gitlab_rails: Request AI Gateway JWT using OAuth token or PAT - ide->>executor: start executor with JWT - user->>ide: trigger workflow from IDE - executor->>+duo_workflow_service: Solve this issue (open grpc connection auth'd with AI Gateway JWT) - duo_workflow_service->>gitlab_rails: Request ai_workflow scoped OAuth token using AI Gateway JWT - duo_workflow_service->>gitlab_rails: Create the workflow (auth'd with ai_workflow OAuth token) - duo_workflow_service->>llm_provider: Ask LLM what to do - llm_provider->>duo_workflow_service: Run rails new my_new_app - duo_workflow_service->>executor: execute `rails new my_new_app` - executor->>duo_workflow_service: result `rails new my_new_app` - duo_workflow_service->>gitlab_rails: Save checkpoint - duo_workflow_service->>llm_provider: What's next? - llm_provider->>duo_workflow_service: You're finished - duo_workflow_service->>gitlab_rails: Save checkpoint and mark completed - duo_workflow_service->>gitlab_rails: Revoke ai_workflow scoped OAuth token - deactivate duo_workflow_service - gitlab_rails->>user: Workflow done! -``` - -#### With Remote (CI pipeline) execution - -```mermaid -sequenceDiagram - participant user as User - participant gitlab_rails as GitLab Rails - box CI-Runner - participant executor as Duo Workflow Executor - end - box AI-gateway service - participant duo_workflow_service as Duo Workflow Service - participant ai_gateway as AI Gateway - end - participant llm_provider as LLM Provider - - user->>gitlab_rails: trigger workflow from Web UI - gitlab_rails->>executor: start executor (sends AI Gateway JWT with request) - executor->>+duo_workflow_service: Solve this issue (open grpc connection auth'd with AI Gateway JWT) - duo_workflow_service->>gitlab_rails: Request ai_workflow scoped OAuth token using AI Gateway JWT - duo_workflow_service->>gitlab_rails: Create the workflow (auth'd with ai_workflow OAuth token) - duo_workflow_service->>llm_provider: Ask LLM what to do - llm_provider->>duo_workflow_service: Run rails new my_new_app - duo_workflow_service->>executor: execute `rails new my_new_app` - executor->>duo_workflow_service: result `rails new my_new_app` - duo_workflow_service->>gitlab_rails: Save checkpoint - duo_workflow_service->>llm_provider: What's next? - llm_provider->>duo_workflow_service: You're finished - duo_workflow_service->>gitlab_rails: Save checkpoint and mark completed - duo_workflow_service->>gitlab_rails: Revoke ai_workflow scoped OAuth token - deactivate duo_workflow_service - gitlab_rails->>user: Workflow done! -``` - -### Self-managed architecture - -#### With local Duo Workflow Service - -When customers are running the Duo Workflow Service locally the architecture will be very -similar to GitLab.com . This will also allow them to use whatever customer -models they configure in their Duo Workflow Service. - - - -#### With cloud Duo Workflow Service - -In order to allow self-managed customers to trial and rapidly adopt Duo -Workflow without running all Duo Workflow Service components this architecture will -supported a mixed deployment mode. In this case we assume that the cloud AI -Gateway will not have access to the customers GitLab instance but we can make -use of the local executor (on the user's machine or in a CI runner) to proxy -all interactions with GitLab. - - - -#### Running without the Executor - -As described above there are 2 reasons we need the Duo Workflow Executor: - -1. We need a sandboxed environment to safely execute arbitrary commands - generated by the AI -1. We need a path to proxy requests to self-managed GitLab instances that may - not be visible to our cloud Duo Workflow Service - -But we will have a subset of use cases for Duo Workflow where these conditions -will not apply. Specifically we expect to have "non-code" workflows (e.g. -review this merge request) where we just need to interact between LLM and -GitLab APIs. And if the customer is using GitLab.com or a self-hosted AI -Gateway that has access to their GitLab instance then we can safely run all of -this inside the Duo Workflow Service making API calls to the GitLab instance. - -This architecture unlocks a considerable advantage as we expect the cost of -running workloads in CI pipelines is quite high (and indeed wasteful) and the -effort to get a local Executor running is also quite high and it may limit the -value for simple non-code workflows if these were a requirement. Additionally -we get quite significant scaling advantages if we never have to create a CI -pipeline to do some of these non-code workflows as starting up a pipeline and -keeping it running for the duration of a workflow is a large overhead. - - - -We may choose to support this architecture later but it will depend on the -following design decisions: - -1. The workflow should know how to call GitLab directly for several API calls - and especially checkpointing -1. Proxying to GitLab via the Executor should be designed as an optional proxy - where the Duo Workflow Service constructs the full HTTP request, but under - certain configurations, will choose to pass the HTTP request to the executor - instead of calling GitLab directly -1. The Duo Workflow Executor is optional for workflows. The workflow should - only suspend when the workflow depends on an executor and one is not - present. When the workflow suspends it should know to checkpoint in GitLab - before shutting down. -1. The Duo Workflow Service will need to acquire some kind of "lease" from the - GitLab instance while it is running to prevent the possibility of 2 - instances of the same workflow running concurrently or to prevent a crashed - workflow from being indefinitely locked up and cannot be resumed. Leases - should timeout and API calls to checkpoint (or perform other operations) may - be rejected if they come from an instance of the workflow with an expired - lease. - -### Data flow - -The below diagram shows what happens when the user is triggering workflows from -their IDE using a local executor. The architecture will be similar when -triggering from the GitLab UI using CI pipelines except that GitLab will start -a CI pipeline to create run the Duo Workflow Executor and create the workflow. - -```mermaid -sequenceDiagram - participant user as User - participant ide as IDE - participant executor as Duo Workflow Executor - participant gitlab_rails as GitLab Rails - participant duo_workflow_service as Duo Workflow Service - participant llm_provider as LLM Provider - user->>ide: trigger workflow from IDE - ide->>executor: start executor - executor->>+duo_workflow_service: Solve this issue - duo_workflow_service->>gitlab_rails: Create the workflow - duo_workflow_service->>llm_provider: Ask LLM what to do - llm_provider->>duo_workflow_service: Need the file list - duo_workflow_service->>executor: execute `ls` - duo_workflow_service->>gitlab_rails: Save checkpoint - executor->>duo_workflow_service: result `ls` - duo_workflow_service->>llm_provider: What's next? - llm_provider->>duo_workflow_service: Here's a patch - duo_workflow_service->>executor: execute `git apply` - duo_workflow_service->>gitlab_rails: Save checkpoint - duo_workflow_service->>executor: execute `poetry run pytest` - duo_workflow_service->>gitlab_rails: Save checkpoint - executor->>duo_workflow_service: result `poetry run pytest` - duo_workflow_service->>llm_provider: fix the tests - llm_provider->>duo_workflow_service: Here's a patch - duo_workflow_service->>executor: execute `git apply` - duo_workflow_service->>gitlab_rails: Save checkpoint - duo_workflow_service->>executor: execute `poetry run pytest` - executor->>duo_workflow_service: result `poetry run pytest` - duo_workflow_service->>executor: Next step? - executor->>gitlab_rails: Check in & Next step? - gitlab_rails->>executor: Last step! - executor->>duo_workflow_service: Done! - deactivate duo_workflow_service - gitlab_rails->>user: Workflow done! -``` - -### CI Pipeline architecture - -We don't want users to have to configure a specific `.gitlab-ci.yml` in order -to support Duo Workflow. In order to avoid this we'll use the same approach as -[that used by DAST site validations](https://gitlab.com/gitlab-org/gitlab/-/blob/19e0669446f55bd29a8df29174d3b0379b8e22c2/ee/app/services/app_sec/dast/site_validations/runner_service.rb#L11) -which dynamically constructs a pipeline configuration in GitLab and triggers -the pipeline without using any `.gitlab-ci.yml`. - -CI Pipelines also must be run inside a project. There will be some usecases of -Duo Workflow where there is no appropriate project in which to run the pipeline -(e.g. bootstrapping a new project). For these workflows we will: - -1. Initially require the user to have a default Workflow project created. It - can just be any empty project and we'll automatically run the pipeline there. -1. If this proves to be too much setup we'll automate the creation of a default - Duo Workflow project for you -1. If the UX is poor over time we might abstract the user away from the - existence of the Project altogether and make this an implementation detail. - This will be considered a last resort because it could be quite a wide - impacting change to GitLab as projects are a central part of GitLab. - -#### Considerations for CI Runners and Infrastructure - -1. Our Duo Workflow rollout may involve substantial increases to our CI runner - usage -1. Duo Workflow will likely involve running long running CI pipelines that use - very little CPU. Mostly what they will be doing is communicating back - and forth with the LLMs and users in a long running gRPC connection. -1. Users will expect very low latency for CI Runner startup - 1. We should determine if there are ways to have preloaded VMs with our - Docker images running ready to start a pipeline when it a - workflow is triggered -1. We likely want a set of CI Runners that are just for Duo Workflow. This may - mean enabling the runners to a subset of customers or just using appropriate - job labeling/runner matching to only use these runners for Duo Workflow -1. It might be possible to roll out some Duo Workflow features on our existing - runner fleets but we believe there will be enough benefits to invest in - segregating these runners. - -### State checkpointing - -The Duo Workflow state will be persisted in GitLab-Rails as the Duo Workflow -Service works. There are 2 components to state: - -1. The State object being managed by Langgraph. This includes all prompt history - between user and agents and any other metadata created by the LangGraph - graph -1. The working directory where the agent is writing code. -1. We will have data retention limits on all state. We will use PostgreSQL - partitioning to drop old workflow data after some time and we will also - drop old Git refs after some time. - -We will be persisting the LangGraph state object using APIs in GitLab to -persist this state to PostgreSQL as it goes. The API will use similar LangGraph -conventions to identify all checkpoints with a `thread_ts` as implemented in -the POC . - -For the current working directory which contains the code the agent has written -so far we will store this by pushing hidden Git refs to GitLab for the checkpoint. Each -checkpoint will have an associated ref and a checkpoint naming convention (or -something stored in PostgreSQL) will allow us to identify the appropriate Git ref -for the state checkpoint. - -Storing in Git has the advantage that we don't need to build any new API for -storing artifacts and it's very easy for the user to access the code by just -checking out that SHA. It also has huge storage savings where a workflow is -working on an existing large project. Ultimately we expect code changes end up -being pushed to Git anyway so this is the simplest solution. - -Some Duo Workflows do not have an existing project (e.g. bootstrapping a -project). Even those workflows will need to be triggered from some project (as -explained in the section about CI piplelines). As such we can use the workflow -project as a temporary repository to store the snapshots of code generated by -the workflow. - -Consideration should also be made to cleanup Git refs over time after some -workflow expiration period. - -### Authentication - -Duo Workflow requires several authentication flows. - -In this section, each connection that requires authentication is listed and the -authentication mechanism is discussed. - - - -#### Local Duo Workflow Executor -> Duo Workflow Service - -When a Duo Workflow starts, the Duo Workflow Executor must connect to the Duo Workflow Service. - -To authenticate this connection: - -1. The IDE will use the OAuth token of Personal Access Token (PAT) that the user - generated while setting up the GitLab editor extension. -1. The IDE uses that token to authenticate a request to a GitLab Rails API - endpoint to obtain a short-lived user- and system-scoped JWT. -1. When the GitLab Rails instance receives this request, it loads its - instance-scoped JWT (synced daily from CustomersDot) and contacts the AI - gateway to swap this instance token for the above-mentioned user-scoped token - (also cryptographically signed) -1. GitLab Rails returns this JWT to the IDE. -1. The IDE passes on this JWT to the local Duo Workflow Executor component. -1. The Duo Workflow Executor uses this JWT to authenticate the Duo Workflow - Service gRPC connection. - -This flow mimics the -[token flow that allows IDEs to connect direct to the AI Gateway](https://gitlab.com/groups/gitlab-org/-/epics/13252). - -#### CI Duo Workflow Executor -> Duo Workflow Service - -When a Duo Workflow is executed by a CI Runner, the Duo Workflow Executor must -connect to the Duo Workflow Service. - -A CI Pipeline is created by GitLab, so there is no need to query a GitLab Rails -API endpoint to obtain a short-lived user- and system-scoped JWT. Instead, in -the process of creating the CI pipeline, GitLab Rails will: - -1. Generate the user-scoped JWT. -1. Inject the JWT as an environment variable (for example: `DUO_WORKFLOW_TOKEN`) - in the CI pipeline. -1. The Duo Workflow Executor running inside the CI job uses this environment - variable value to authenticate the Duo Workflow Service gRPC connection. - -#### Duo Workflow Service -> GitLab Rails API - -Reasons that the Duo Workflow Service must be able to authenticate requests to -the GitLab Rails API: - -1. The Duo Workflow Service will need to periodically make requests to GitLab Rails - to sync workflow state. This means that the Duo Workflow Service must be able - to authenticate these requests. -1. Duo Workflow may need to make other GitLab Rails API queries to gather - context. For example, a Duo Workflow for "solve issue with code" would - require an API request to retrieve the issue content. -1. The end state of a Duo Workflow may take the form of a generated artifact - (for example, Git commit or pull request) on the GitLab platform. To - generate this artifact, the Duo Workflow Service must be able to make API - requests to GitLab Rails. - -Requirements for the token used to authenticate requests from the Duo Workflow Service to -the GitLab Rails API: - -1. Any artifacts created by a Duo Workflow must be auditable in order - to maintain transparency about AI-generated activities on the GitLab platform. -1. The token's access level must match the access level of the user who - initiated the Workflow to ensure that there is no privilege escalation. -1. We must have the ability to block read/write for all resources that belong to - instances/projects/groups with `duo_features_enabled` set to false. -1. Token must be valid for as long as it takes an agent to execute or be - refreshable by the Duo Workflow Service. Workflow execution may take several hours. - -The JWT that the Workflow Executor uses to authenticate to the Duo Workflow -Service could potentially be adapted to also work for this use-case but has some problems: - -1. Need to update GitLab Rails to accept this type of token for API authentication. -1. JWTs are not revocable; what if we need to cut off an agent's access? -1. Need to build token rotation. How would the Duo Workflow Service authenticate an API - request to generate a new token if the old JWT is already expired? - -For these reasons, OAuth is a better protocol for this use-case. OAuth tokens: - -1. Are only valid for 2 hours. -1. Can be revoked. -1. Have a built-in refresh flow. -1. Are an established authentication pattern for federating access between - services. - -To use OAuth, we will: - -1. Create a new token scope called `ai_workflows` - ([related issue](https://gitlab.com/gitlab-org/gitlab/-/issues/467160)) -1. When the IDE requests the Duo Workflow Service User JWT from GitLab Rails, we - will also generate and return an OAuth token with the `ai_workflows` scope. -1. Duo Workflow executor will send that OAuth token, along with the `base_url` - of the GitLab Rails instance, as metadata when the Duo Workflow Service when - the gRPC connection is opened. -1. The Duo Workflow Service will use the OAuth token for any GitLab Rails API - Requests to read or write data for a Workflow. - -### Options we've considered and pros/cons - -#### Delegate only unsafe execution to local/CI pipelines - -This was the option we chose. It attempts to keep as much of the functionality -as possible in services we run while delegating the unsafe execution to Duo -Workflow Executor which can run locally or in CI pipelines. - -**Pros**: - -1. Running the infrastructure ourselves gives us more control over the versions - being rolled out -1. There is less dependencies the user needs to install for local usage -1. It offers a rapid onboarding experience for self-managed customers to try - Duo Workflow without deploying any new GitLab components - -**Cons** - -1. We need to deploy and maintain new infrastructure which has different - scaling characteristics to other services we run duo to long running - execution - -#### Run it locally - -**Pros**: - -1. This keeps developers in their local environment where most of them work -1. Compute is absorbed by the local developer so they don't have to worry about - being billed per minute -1. Low latency for user interaction especially where the user needs to - review/edit code while the agent is working - -**Cons**: - -1. There are more risks running it locally unless you have an isolated - development environment as commands have full access to your computer. This - can be mitigated by UX that limits what commands the agent can run without - user confirmation. -1. This approach will require some local developer setup and may not be suited - to tasks that users are expecting to kick off from the web UI (e.g. - issue/epic planning) - -#### CI pipelines (on CI runners) - -See for a POC and investigation. - -**Pros**: - -1. CI pipelines are the only pre-configured infrastructure we have that can run untrusted workflows -1. We have an established billing model for CI minutes - -**Cons**: - -1. CI pipelines are slow to start up and this might mean that iteration and incremental AI development might be slow if the pipelines need to be restarted while timing out waiting for user input -1. CI minutes will need to be consumed while the agent is awaiting for user input. This will likely require a timeout mechanism and as such if the user returns we'll need to restart a new pipeline when they give input -1. CI pipelines run in a difficult to access environment (ie. you cannot SSH it or introspect it live) and as such it may make it difficult for users to interact with code that is being built out live in front of them without -1. CI pipelines require there to be some project to run in. This is not likely something we can overcome but we may be able to simplify the setup process by automatically creating you a "workflow project" for your workflow pipelines to run in -1. When we implement non-code workflows (e.g. reviewing MRs) there is no need for an isolated compute environment but we'll still be forcing customers to use compute minutes. We've seen this is not a good experience in other cases like X-Ray reports - -#### GitLab workspaces (remote development) - -See for a POC and investigation. - -**Pros**: - -1. This has the fastest iteration cycle as the agent is working locally in your development environment and can interact with you and you can even see and edit the same files live as them -1. Customers can run it on their own infrastructure and this gives them control over efficient resource usage - -**Cons**: - -1. Today we only support customers bringing their own infrastructure (K8s cluster) and this means that the barrier to getting started is to bring your own K8s cluster and this is a fairly significant effort -1. If we wanted to build out infrastructure on GitLab.com to save customers having to bring their own K8s cluster this would be a fairly large effort from a security and infrastructure perspective. It's possible but to deal with all the complexities of security, abuse and billing would require many teams involvement in both initial development and sustained maintenance. - -## Security - -### Threat modeling - -See . - -### Security considerations for local execution - -Local execution presents the highest value opportunity for developers but also -comes with the greatest risk that a bug or mistake from an LLM could lead to -causing significant harm to a user's local development environment or -compromise confidential information. - -Some examples of risks: - -1. An AI that can make honest but significant mistakes -1. An AI that might sometimes be adversarial -1. The AI gateway serving the LLM responses may be compromised which would then - allow shell access to all users of this tool - -### Sandboxing Duo Workflow Executor - -One proposal here to mitigate risks would be to use some form of sandboxing -where the Duo Workflow Executor is only able to run inside of an unprivileged -Docker container. Such a solution would need to: - -1. Mount the local working directory into the container so it is still editing - the files the user is working on in the host -1. Install all development dependencies the user or agent would need to run the - application and tests - -The above option may also make use of Dev Containers. - -### User confirmation for commands - -Another option for limiting the risk is to require the user to confirm every -command the agent executes before it runs the command. We will likely be -implementing this as an option anyway but given the desire for efficient -development of larger workflows it might limit the efficiency of the tool if it -needs to execute a lot of commands to finish a task. - -We may also consider a hybrid approach where there a set of user-defined -allowlisted commands (e.g. `ls` and `cat`) which allow the agent to read and -learn about a project without the user needing to confirm. This approach may -not solve all needs though where the user may want to allowlist commands like -`rspec` which then effectively still allow for arbitrary code execution as the -agent can put whatever they want in the spec file. - -## Duo Workflow UI - -The Duo Workflow UI will need to be available at least in the following places: - -1. In GitLab Rails web UI -1. In our editor extensions - -The fact that we'll need multiple UIs and also as described above we have -multiple execution environments for Duo Workflow Executor have led to the -following decisions. - -### How do we package and run the local web UI - -We will build the majority of data access related to our local IDE UI into the -[GitLab Language Server](https://gitlab.com/gitlab-org/editor-extensions/gitlab-lsp) -to maximize re-use across all our editor extensions. We will also employ a mix -of webviews rendered in the IDE and served by the LSP as well as native IDE UI -elements. Where it doesn't considerably limit our user experience we'll opt to -build the interface into a web page served from the LSP and then rendered in -the IDE as a web view because this again maximises re-use across all our editor -extensions. - -### How does the web UI reflect the current state live - -The Duo Workflow Service will persist it's state frequently to the main GitLab Rails -application. There will be GraphQL subscriptions for streaming updates about a -workflow. The UI will consume these GraphQL apis and update the UI as -updates stream in. - -Given that the user may be running the Duo Workflow Executor locally which may -be seeing some of the state as it happens it might be reasonable to want to -just live render the in-memory state of the running workflow process. We may -choose this optional deliberately for latency reasons but we need to be careful -to architect the frontend and Duo Workflow Executor as completely decoupled -because they will not always be running together. For example users may trigger -a workflow locally which runs in GitLab CI or they may be using the web UI to -interact with and re-run a workflow that was initiated locally. - -As such we will generally prefer not to have direct interaction between the UI -and Executor but instead all communication should be happening via GitLab. Any -exceptions to this might be considered case by case but we'll need clear API -boundaries which allow the functionality to easily be changed to consume from -GitLab for the reasons described. - -## Duo Workflow Agent's tools - -Duo Workflow **agents** are, in a simplified view, a pair of: **prompt** and **LLM**. -By this definition, agents on their own are not able to interact with the outside world, -which significantly limits the scope of work that can be automated. To overcome this limitation, agents are being equipped with **tools**. - -Tools are functions that agents can invoke using the [function calling](https://docs.anthropic.com/en/docs/tool-use) LLM feature. -These functions perform different actions on behalf of the agent. For example, an agent might be equipped with a tool (function) -that executes bash commands like `ls` or `cat` and returns the result of those bash commands back to the agent. - -The breadth of the tool set available to **agents** defines the scope of work that can be automated. Therefore, to -set up the Duo Workflow feature for success, it will be required to deliver a broad and exhaustive tool set. - -Foreseen tools include: - -1. Tools to execute bash commands via the Duo Workflow Executor -1. Tools to manipulate files (including reading and writing to files) -1. Tools to manipulate Git VCS -1. Tools to integrate with the [GitLab HTTP API](../../../api/api_resources.md) - -The fact that the Duo Workflow Service is going to require Git and GitLab API tools entails that the **Duo Workflow Service -must have the ability to establish an SSH connection and make HTTP requests to the GitLab instance.** This ability can be granted directly to the Duo Workflow Service or can be provided via the Duo Workflow Executor if a direct connection between the Duo Workflow Service and a GitLab instance is not possible due to a firewall or network partition. - -## Milestones - -1. All the components implemented and communicating correctly with only a - trivial workflow implemented -1. Checkpointing code as well as LangGraph state -1. Workflow locking in GitLab to ensure only 1 concurrent instance of a - workflow -1. Add more workflows and tools -1. Ability to resume a workflow - -## POC - Demos - -1. [POC: Solve issue (internal only)](https://www.youtube.com/watch?v=n1mpFirme4o) -1. [POC: Duo Workflow in Workspaces (internal only)](https://youtu.be/x7AxYwiQayg) -1. [POC: Autograph using Docker Executor (internal only)](https://www.youtube.com/watch?v=V-Mw6TXOkKI) -1. [POC: Duo Workflows in CI pipelines with timeout and restart (internal only)](https://youtu.be/v8WWZuAGXMU) + + + + diff --git a/doc/architecture/blueprints/epss/decisions/002_use_new_bucket.md b/doc/architecture/blueprints/epss/decisions/002_use_new_bucket.md index 9b8e15231ae..68500d6a91f 100644 --- a/doc/architecture/blueprints/epss/decisions/002_use_new_bucket.md +++ b/doc/architecture/blueprints/epss/decisions/002_use_new_bucket.md @@ -1,28 +1,11 @@ --- -owning-stage: "~devops::secure" -description: 'EPSS Support ADR 002: Use a new bucket for EPSS data' +redirect_to: 'https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/epss/decisions/002_use_new_bucket/' +remove_date: '2025-07-08' --- -# EPSS Support ADR 002: Use a new bucket for EPSS data +This document was moved to [another location](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/epss/decisions/002_use_new_bucket/). -## Context - -PMDB exports data to GCP buckets. The data is later pulled by GitLab instances. Advisory data and license data are stored in different buckets. This is sensible, because advisory and license data are not directly related, and rather provide additional information about packages. Data is updated based on deltas—changes from the previous state of the data. Only those changes are saved with each addition to the database. - -EPSS data is directly associated with advisories, so it feels natural to add it to the existing advisories bucket. However, the current advisories bucket is structured based on `purl_type`. Adding an `epss` data type would couple `epss` with `purl_type` which is a faulty pairing. Due to the tight coupling between `purl_type` and the existing advisories bucket, it would be difficult and convoluted to add `epss` to it. - -Following [extensive discussions on the EPSS epic](https://gitlab.com/groups/gitlab-org/-/epics/11544#note_1952695268) and [discussion](https://gitlab.com/gitlab-org/gitlab/-/issues/468131#note_1961344123) during the refinement of PMDB issues, it was initially decided to use the existing bucket as this feels most intuitive and at the time felt a healthier approach. [Further discussion](https://gitlab.com/gitlab-org/gitlab/-/issues/467672#note_1980715240) during the refinement of the GitLab backend effort led to the decision to use a new bucket, due to the complexity of the coupling of `purl_type` and other, unrelated areas in the monolith. Adding `epss` to `purl_type` would impact other components and we want to avoid having to work around that. We may want to later simplify these areas and reconsider the bucket structure at a later stage. - -## Decision - -Export EPSS data to a new bucket, rather than exporting it into the existing PMDB advisories bucket. - -## Consequences - -The implementation is simpler than adding a directory to the existing advisories bucket, but may feel less intuitive. -This change require the relevant Terraform changes regarding the provisioning of a new bucket. -This should also be addressed in the exporter and the GitLab `package_metadata` sync configuration. - -## Alternatives - -The other option is to add EPSS data to the advisories bucket, since they are directly related. This was the [initial decision](https://gitlab.com/gitlab-org/gitlab/-/issues/468131#note_1980366323). This would allow us to utilize existing mechanisms and keep related data close. However, EPSS data doesn't fit into the current structure of the advisories bucket. An ideal solution would reconstruct the buckets in a manner more fitting for this approach, but this would be a big effort and is not critical enough. + + + + diff --git a/doc/architecture/blueprints/epss/index.md b/doc/architecture/blueprints/epss/index.md index 44cf2f54ec4..7fb9447896c 100644 --- a/doc/architecture/blueprints/epss/index.md +++ b/doc/architecture/blueprints/epss/index.md @@ -1,279 +1,11 @@ --- -status: proposed -creation-date: "2024-06-19" -authors: [ "@YashaRise" ] -coach: [ "@theoretick" ] -approvers: [ "@johncrowley", "@tkopel", "@nilieskou" ] -owning-stage: "~devops::secure" -participating-stages: TBD +redirect_to: 'https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/epss/' +remove_date: '2025-07-08' --- - - - - - -# EPSS Support - - - - - -For important terms, see [glossary](#glossary). - -## Summary - -[EPSS](https://www.first.org/epss/faq) scores specify the likelihood a CVE will be exploited in the next 30 days. This data may be used to improve and simplify prioritization efforts when remediating vulnerabilities in a project. EPSS support requirements are outlined in [the EPSS epic](https://gitlab.com/groups/gitlab-org/-/epics/11544) along with an overview of EPSS. This document focuses on the technical implementation of EPSS support. - -EPSS scores may be populated from the [EPSS Data page](https://www.first.org/epss/data_stats) or through their provided API. Ultimately, EPSS scores should be reachable through the GitLab GraphQL API, as seen on the vulnerability report and details pages, and be filterable and usable when setting policies. - -Package metadata database (PMDB, also known as license-db), an existing advisory pull-and-enrichment method, is for this purpose. The flow is as follows: - -```mermaid -flowchart LR - A[EPSS Source] -->|Pull| B[PMDB] - B -->|Process and export| C[Bucket] - C -->|Pull| D[GitLab Instance] -``` - - - -## Motivation - -The classic approach to vulnerability prioritization is using severity based on [CVSS](https://www.first.org/cvss/). This approach provides some guidance, but is too unrefined—more than half of all published CVEs have a high or critical score. Other metrics need to be employed to reduce remediation fatigue and help developers prioritize their work better. EPSS provides a metric to identify which vulnerabilities are most likely to be exploited in the near future. Combined with existing prioritization methods, EPSS helps to focus remediation efforts better and reduce remediation workload. By adding EPSS to the information presented to users, we deliver these benefits to the GitLab platform. - - - -### Goals - -- Enable users to use EPSS scores on GitLab as another metric for their vulnerability prioritization efforts. -- Provide scalable means of efficiently repopulating recurring EPSS scores to minimize system load. - -#### Phase 1 (MVC) - -- Enable access to EPSS scores through GraphQL API. - -#### Phase 2 - -- Show EPSS scores in vulnerability report and details pages. - -#### Phase 3 - -- Allow filtering vulnerabilities based on EPSS scores. -- Allow creating policies based on EPSS scores. - - - -### Non-Goals - -- Dictate priority to users based on EPSS (or any other metric). - - - -## Proposal - -Support EPSS on the GitLab platform. - -Following the discussions in the [EPSS epic](https://gitlab.com/groups/gitlab-org/-/epics/11544), the proposed flow is: - -1. PMDB database is extended with a new table to store EPSS scores. -1. PMDB infrastructure runs the feeder daily in order to pull and process EPSS data. -1. The advisory-processor receives the EPSS data and stores them to the PMDB DB. -1. PMDB exports EPSS data to a new PMDB EPSS bucket. - - Create a new bucket to store EPSS data. - - Delete former EPSS data once new data is uploaded, as the old data is no longer needed. - - Truncate EPSS scores to two digits after the dot. -1. GitLab instances pull data from the PMDB EPSS bucket. - - Create a new table in rails DB to store EPSS data. -1. GitLab instances expose EPSS data through GraphQL API and present data in vulnerability report and details pages. - -```mermaid -flowchart LR - AF[Feeder] -->|pulls| A[EPSS Source] - AF -->|publishes| AP[Advisory Processor] - AP -->|stores| DD[PMDB database] - E[Exporter] -->|loads|DD - E --> |exports| B[Public Bucket] - GitLab[GitLab instance] --> |syncs| B - GitLab --> |stores| GitLabDB -``` - - - -## Design and implementation details - -### Decisions - -- [002: Use a new bucket for EPSS data](decisions/002_use_new_bucket.md) - -### Important notes - -- All EPSS scores get updated on a daily basis. This is pivotal to this feature's design. -- The [fields retrieved](https://www.first.org/epss/data_stats) from the EPSS source are `cve`, `score`, `percentile`. 9 digits after the dot are maintained. - - To reduce the amount of upserts, based on a [spike to check magnitude of change](https://gitlab.com/gitlab-org/gitlab/-/issues/468286), we will truncate EPSS scores to two digits after the dot. - -### PMDB - -- Create a new EPSS table in [PMDB](https://gitlab.com/gitlab-org/security-products/license-db) with an advisory identifier and the EPSS score. This includes changing the [schema](https://gitlab.com/gitlab-org/security-products/license-db/schema) and any necessary migrations. -- Ingest EPSS data into new PMDB table. We want to keep the EPSS data structure as close as possible to the origin so all of the data may be available to the exporter, and the exporter may choose how to process it. Therefore we will save scores and percentiles with their complete values. -- Export EPSS scores in separate bucket. - - Delete the previous day's export as it is no longer needed after the new one is added. -- Add new pubsub topics to deployment to be used by PMDB components, using existing terraform modules. - -### GitLab Rails backend - -- Create table in rails backend to hold EPSS scores. -- Configure Rails sync to ingest EPSS exports and save to new table. -- Include EPSS data attributes in GraphQL API Occurrence objects. - -### GitLab UI - -- Add EPSS data to vulnerability report page. -- Add EPSS data to vulnerability details page. -- Allow filtering by EPSS score. -- Allow creating policies based on EPSS score. - - - -## Alternative Solutions - - - -## Glossary - -- **PMDB** (Package metadata database, also known as License DB): PMDB is a standalone service (and not solely a database), outside of the Rails application, that gathers, stores and exports packages metadata for GitLab instances to consume. See [complete documentation](https://gitlab.com/gitlab-org/security-products/license-db/deployment/-/blob/main/docs/DESIGN.md?ref_type=heads). PMDB components include: - - **Feeder**: a scheduled job called by the PMDB deployment to publish data from the relevant sources to pub/sub messages consumed by PMDB processors. - - **Advisory processor**: Runs as a Cloud Run instance and consumes messages published by the advisory feeder containing advisory related data and stores them to the PMDB database. - - **PMDB database**: a PostgreSQL instance storing license and advisory data. - - **Exporter**: exports license/advisory data from the PMDB database to public GCP buckets. -- **GitLab database**: the database used by GitLab instances. -- **CVE** (Common Vulnerabilities and Exposures): a list of publicly known information-security vulnerabilities. "A CVE" usually refers to a specific vulnerability and its CVE ID. -- **EPSS** (Exploit prediction scoring system) **score**: a score ranging from 0 to 1 representing the probability of exploitation in the wild in the next 30 days of a given vulnerability. -- **EPSS score percentile**: for a given EPSS score (of some vulnerability), the proportion of all scored vulnerabilities with the same or a lower EPSS score. + + + + diff --git a/doc/architecture/blueprints/gitlab_duo_rag/elasticsearch.md b/doc/architecture/blueprints/gitlab_duo_rag/elasticsearch.md index 51a746a8170..34e10d48065 100644 --- a/doc/architecture/blueprints/gitlab_duo_rag/elasticsearch.md +++ b/doc/architecture/blueprints/gitlab_duo_rag/elasticsearch.md @@ -1,41 +1,11 @@ --- -status: proposed -creation-date: "2024-01-25" -authors: [ "@shinya.maeda", "@mikolaj_wawrzyniak" ] -coach: [ "@stanhu" ] -approvers: [ "@pwietchner", "@oregand", "@tlinz" ] -owning-stage: "~devops::ai-powered" -participating-stages: ["~devops::data stores", "~devops::create"] +redirect_to: 'https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/gitlab_duo_rag/elasticsearch/' +remove_date: '2025-07-08' --- -# Elasticsearch +This document was moved to [another location](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/gitlab_duo_rag/elasticsearch/). -For more information on Elasticsearch and RAG broadly, see the [Elasticsearch article](../gitlab_rag/elasticsearch.md) in [RAG at GitLab](../gitlab_rag/index.md). - -## Retrieve GitLab Documentation - -A [proof of concept](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/145392) was done to switch the documentation embeddings from being stored in the embedding database to being stored on Elasticsearch. - -### Synchronizing embeddings with data source - -The same procedure used by [PostgreSQL](postgresql.md) can be followed to keep the embeddings up to date in Elasticsearch. - -### Retrieval - -To get the nearest neighbours, the following query can be executed an index containing the embeddings: - -```ruby -{ - "knn": { - "field": vector_field_containing_embeddings, - "query_vector": embedding_for_question, - "k": limit, - "num_candidates": number_of_candidates_to_compare - } -} -``` - -### Requirements to get to self-managed - -- Productionalize the PoC [MR](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/145392) -- Get more self-managed instances to install Elasticsearch by [shipping GitLab with Elasticsearch](https://gitlab.com/gitlab-org/gitlab/-/issues/438178). Elastic gave their approval to ship with the free license. The work required for making it easy for customers to host Elasticsearch is more than 2 milestones. + + + + diff --git a/doc/architecture/blueprints/gitlab_duo_rag/index.md b/doc/architecture/blueprints/gitlab_duo_rag/index.md index b4d1492c531..97bf579535b 100644 --- a/doc/architecture/blueprints/gitlab_duo_rag/index.md +++ b/doc/architecture/blueprints/gitlab_duo_rag/index.md @@ -1,37 +1,11 @@ --- -status: proposed -creation-date: "2024-01-25" -authors: [ "@shinya.maeda", "@mikolaj_wawrzyniak" ] -coach: [ "@stanhu" ] -approvers: [ "@pwietchner", "@oregand", "@tlinz" ] -owning-stage: "~devops::ai-powered" -participating-stages: ["~devops::data stores", "~devops::create"] +redirect_to: 'https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/gitlab_duo_rag/' +remove_date: '2025-07-08' --- -# Retrieval Augmented Generation (RAG) for GitLab Duo on self-managed +This document was moved to [another location](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/gitlab_duo_rag/). -RAG is an application architecture used to provide knowledge to a large language model that doesn't exist in its training set, so that it can use that knowledge to answer user questions. To learn more about RAG, see [RAG for GitLab](../gitlab_rag/index.md). - -## Goals of this blueprint - -This blueprint aims to drive a decision for a RAG solution for GitLab Duo on self-managed, specifically for shipping GitLab Duo with access to GitLab documentation. We outline three potential solutions, including PoCs for each to demonstrate feasibility for this use case. - -## Constraints - -- The solution must be viable for self-managed customers to run and maintain -- The solution must be shippable in 1-2 milestones -- The solution should be low-lock-in, since we are still determining our long term technical solution(s) for RAG at GitLab - -## Proposals for GitLab Duo Chat RAG for GitLab documentation - -The following solutions have been proposed and evaluated for the GitLab Duo Chat for GitLab documentation use case: - -- [Vertex AI Search](vertex_ai_search.md) -- [Elasticsearch](elasticsearch.md) -- [PostgreSQL with PGVector extension](postgresql.md) - -You can read more about how each evaluatoin was conducted in the links above. - -## Chosen solution - -[Vertex AI Search](vertex_ai_search.md) is going to be implemented due to the low lock-in and being able to reach customers quickly. It could be moved over to another solution in the future. + + + + diff --git a/doc/architecture/blueprints/gitlab_duo_rag/postgresql.md b/doc/architecture/blueprints/gitlab_duo_rag/postgresql.md index a41e88f7688..961bfca55cd 100644 --- a/doc/architecture/blueprints/gitlab_duo_rag/postgresql.md +++ b/doc/architecture/blueprints/gitlab_duo_rag/postgresql.md @@ -1,95 +1,11 @@ --- -status: proposed -creation-date: "2024-01-25" -authors: [ "@shinya.maeda", "@mikolaj_wawrzyniak" ] -coach: [ "@stanhu" ] -approvers: [ "@pwietchner", "@oregand", "@tlinz" ] -owning-stage: "~devops::ai-powered" -participating-stages: ["~devops::data stores", "~devops::create"] +redirect_to: 'https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/gitlab_duo_rag/postgresql/' +remove_date: '2025-07-08' --- -# PostgreSQL +This document was moved to [another location](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/gitlab_duo_rag/postgresql/). -## Retrieve GitLab Documentation - -PGVector is currently being used for the retrieval of relevant documentation for GitLab Duo chat's RAG. - -A separate `embedding` database runs alongside `geo` and `main` which has the `pg-vector` extension installed and contains embeddings for GitLab documentation. - -- Statistics (as of January 2024): - - Data type: Markdown written in natural language (Unstructured) - - Data access level: Green (No authorization required) - - Data source: `https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc` - - Data size: 147 MB in `vertex_gitlab_docs`. 2194 pages. - - Service: `https://docs.gitlab.com/` ([source repo](https://gitlab.com/gitlab-org/gitlab-docs) - - Example of user input: "How do I create an issue?" - - Example of expected AI-generated response: "To create an issue:\n\nOn the left sidebar, select Search or go to and find your project.\n\nOn the left sidebar, select Plan > Issues, and then, in the upper-right corner, select New issue." - -### Synchronizing embeddings with data source - -Here is the overview of synchronizing process that is currently running in GitLab.com: - -1. Load documentation files of the GitLab instance. i.e. `doc/**/*.md`. -1. Compare the checksum of each file to detect an new, update or deleted documents. -1. If a doc is added or updated: - 1. Split the docs with the following strategy: - - Text splitter: Split by new lines (`\n`). Subsequently split by 100~1500 chars. - 1. Bulk-fetch embeddings of the chunks from `textembedding-gecko` model (768 dimensions). - 1. Bulk-insert the embeddings into the `vertex_gitlab_docs` table. - 1. Cleanup the older embeddings. -1. If a doc is deleted: - 1. Delete embeddings of the page. - -As of today, there are 17345 rows (chunks) on `vertex_gitlab_docs` table on GitLab.com. - -For Self-managed instances, we serve embeddings from AI Gateway and GCP's Cloud Storage, -so the above process can be simpler: - -1. Download an embedding package from Cloud Storage through AI Gateway API. -1. Bulk-insert the embeddings into the `vertex_gitlab_docs` table. -1. Delete older embeddings. - -We generate this embeddings package before GitLab monthly release. -Sidekiq cron worker automatically renews the embeddings by comparing the embedding version and the GitLab version. -If it's outdated, it will download the new embedding package. - -Going further, we can consolidate the business logic between SaaS and Self-managed by generating the package every day (or every grpd deployment). -This is to reduce the point of failure in the business logic and let us easily reproduce an issue that reported by Self-managed users. - -Here is the current table schema: - -```sql -CREATE TABLE vertex_gitlab_docs ( - id bigint NOT NULL, - created_at timestamp with time zone NOT NULL, - updated_at timestamp with time zone NOT NULL, - version integer DEFAULT 0 NOT NULL, -- For replacing the old embeddings by new embeddings (e.g. when doc is updated) - embedding vector(768), -- Vector representation of the chunk - url text NOT NULL, - content text NOT NULL, -- Chunked data - metadata jsonb NOT NULL, -- Additional metadata e.g. page URL, file name - CONSTRAINT check_2e35a254ce CHECK ((char_length(url) <= 2048)), - CONSTRAINT check_93ca52e019 CHECK ((char_length(content) <= 32768)) -); - -CREATE INDEX index_vertex_gitlab_docs_on_version_and_metadata_source_and_id ON vertex_gitlab_docs USING btree (version, ((metadata ->> 'source'::text)), id); -CREATE INDEX index_vertex_gitlab_docs_on_version_where_embedding_is_null ON vertex_gitlab_docs USING btree (version) WHERE (embedding IS NULL); -``` - -### Retrieval - -After the embeddings are ready, GitLab-Rails can retrieve chunks in the following steps: - -1. Fetch embedding of the user input from `textembedding-gecko` model (768 dimensions). -1. Query to `vertex_gitlab_docs` table for finding the nearest neighbors. e.g.: - - ```sql - SELECT * - FROM vertex_gitlab_docs - ORDER BY vertex_gitlab_docs.embedding <=> '[vectors of user input]' -- nearest neighbors by cosine distance - LIMIT 10 - ``` - -### Requirements to get to self-managed - -All instances of GitLab have postgres running but allowing instances to administer a separate database for embeddings or combining the embeddings into the main database would require some effort which spans more than a milestone. + + + + diff --git a/doc/architecture/blueprints/gitlab_duo_rag/vertex_ai_search.md b/doc/architecture/blueprints/gitlab_duo_rag/vertex_ai_search.md index a8d4d53c530..04724d61bb5 100644 --- a/doc/architecture/blueprints/gitlab_duo_rag/vertex_ai_search.md +++ b/doc/architecture/blueprints/gitlab_duo_rag/vertex_ai_search.md @@ -1,134 +1,11 @@ --- -status: proposed -creation-date: "2024-01-25" -authors: [ "@shinya.maeda", "@mikolaj_wawrzyniak" ] -coach: [ "@stanhu" ] -approvers: [ "@pwietchner", "@oregand", "@tlinz" ] -owning-stage: "~devops::ai-powered" -participating-stages: ["~devops::data stores", "~devops::create"] +redirect_to: 'https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/gitlab_duo_rag/vertex_ai_search/' +remove_date: '2025-07-08' --- -# Vertex AI Search +This document was moved to [another location](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/gitlab_duo_rag/vertex_ai_search/). -## Retrieve GitLab Documentation - -- Statistics (as of January 2024): - - Date type: Markdown (Unstructured) written in natural language - - Date access level: Green (No authorization required) - - Data source: `https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc` - - Data size: approx. 56,000,000 bytes. 2194 pages. - - Service: `https://docs.gitlab.com/` ([source repo](https://gitlab.com/gitlab-org/gitlab-docs) - - Example of user input: "How do I create an issue?" - - Example of expected AI-generated response: "To create an issue:\n\nOn the left sidebar, select Search or go to and find your project.\n\nOn the left sidebar, select Plan > Issues, and then, in the upper-right corner, select New issue." - -[The GitLab documentation](https://gitlab.com/gitlab-org/gitlab-docs/-/blob/main/doc/architecture.md) is the SSoT service to serve GitLab documentation for SaaS (both GitLab.com and Dedicated) and Self-managed. -When a user accesses to a documentation link in GitLab instance, -they are [redirected to the service](https://gitlab.com/groups/gitlab-org/-/epics/11600#note_1690083049) since 16.0 (except air-gapped solutions). -In addition, the current search backend of `docs.gitlab.com` needs to transition to [Vertex AI Search](https://cloud.google.com/enterprise-search?hl=en). See [this issue](https://gitlab.com/gitlab-com/legal-and-compliance/-/issues/1876) (GitLab member only) for more information. - -We introduce a new semantic search API powered by Vertex AI Search for the documentation tool of GitLab Duo Chat. - -### Setup in Vertex AI Search - -We [create a search app](https://cloud.google.com/generative-ai-app-builder/docs/create-engine-es) for each GitLab versions. -These processes will likely be automated in the [GitLab Documentation project](https://gitlab.com/gitlab-org/gitlab-docs/-/blob/main/doc/architecture.md) -by CI/CD pipelines. - -1. Create a new Bigquery table e.g. `gitlab-docs-latest` or `gitlab-docs-v16.4` -1. Download documents from repositories (e.g. `gitlab-org/gitlab/doc`, `gitlab-org/gitlab-runner/docs`, `gitlab-org/omnibus-gitlab/doc`). -1. Split them by Markdown headers and generate metadata (e.g. URL and title). -1. Insert rows into the Bigquery table. -1. [Create a search app](https://cloud.google.com/generative-ai-app-builder/docs/create-engine-es) - -See [this notebook](https://colab.research.google.com/drive/1XxYPWkNBnwZ0UG1aJ0Pjb2gfYmLnrHft?usp=sharing) for more implementation details. -The data of the latest version will be refreshed by a nightly build with [Data Store API](https://cloud.google.com/generative-ai-app-builder/docs/reference/rpc). - -### AI Gateway API - -API design is following the existing patterns in [AI Gateway](../ai_gateway/index.md). - -```plaintext -POST /v1/search/docs -``` - -```json -{ - "type": "search", - "metadata": { - "source": "GitLab EE", - "version": "16.3" // Used for switching search apps for older GitLab instances - }, - "payload": { - "query": "How can I create an issue?", - "params": { // Params for Vertex AI Search - "page_size": 10, - "filter": "", - }, - "provider": "vertex-ai" - } -} -``` - -The response will include the search results. For example: - -```json -{ - "response": { - "results": [ - { - "id": "d0454e6098773a4a4ebb613946aadd89", - "content": "\nTo create an issue from a group: \n1. On the left sidebar, ...", - "metadata": { - "Header1": "Create an issue", - "Header2": "From a group", - "url": "https://docs.gitlab.com/ee/user/project/issues/create_issues.html" - } - } - ] - }, - "metadata": { - "provider": "vertex-ai" - } -} -``` - -See [SearchRequest](https://cloud.google.com/python/docs/reference/discoveryengine/latest/google.cloud.discoveryengine_v1.types.SearchRequest) and [SearchResponse](https://cloud.google.com/python/docs/reference/discoveryengine/latest/google.cloud.discoveryengine_v1.types.SearchResponse) for Vertex AI API specs. - -### Proof of Concept - -- [GitLab-Rails MR](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/144719) -- [AI Gateway MR](https://gitlab.com/gitlab-org/modelops/applied-ml/code-suggestions/ai-assist/-/merge_requests/642) -- [Vertex AI Search service](https://console.cloud.google.com/gen-app-builder/engines?referrer=search&project=ai-enablement-dev-69497ba7) -- [Google Colab notebook](https://colab.research.google.com/drive/1XxYPWkNBnwZ0UG1aJ0Pjb2gfYmLnrHft?usp=sharing) -- [Demo video](https://youtu.be/ipEpMt-U6rQ?feature=shared) (Note: In this video, Website URLs are used as data source). - -#### Evaluation score - -Here is the evaluation scores generated by [Prompt Library](https://gitlab.com/gitlab-org/modelops/ai-model-validation-and-research/ai-evaluation/prompt-library). - -|Setup|correctness|comprehensiveness|readability|evaluating_model| -|---|---|---|---|---| -|New (w/ Vertex AI Search)|3.7209302325581382|3.6976744186046511|3.9069767441860455|claude-2| -|Current (w/ Manual embeddings in GitLab-Rails and PgVector)|3.7441860465116279|3.6976744186046511|3.9767441860465116|claude-2| - -