Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2024-09-30 18:13:37 +00:00
parent fa7561fc9b
commit f55d9f40cb
36 changed files with 1072 additions and 112 deletions

View File

@ -1 +1 @@
904445041c98f13f525a69d24965e32da257b58a
aef1b75346b177cdfa96b644912e1be60696b4fd

View File

@ -76,6 +76,10 @@ module Routing
project_release_url(entity.project, entity, *args)
end
def project_wiki_page_url(entity, *args)
project_wiki_url(entity.project, entity.canonical_slug, *args)
end
def edit_milestone_path(entity, *args)
if entity.resource_parent.is_a?(Group)
edit_group_milestone_path(entity.resource_parent, entity, *args)

View File

@ -12,12 +12,12 @@ module Noteable
class_methods do
# `Noteable` class names that support replying to individual notes.
def replyable_types
%w[Issue MergeRequest AbuseReport]
%w[Issue MergeRequest AbuseReport WikiPage::Meta]
end
# `Noteable` class names that support resolvable notes.
def resolvable_types
%w[Issue MergeRequest DesignManagement::Design AbuseReport]
%w[Issue MergeRequest DesignManagement::Design AbuseReport WikiPage::Meta]
end
# `Noteable` class names that support creating/forwarding individual notes.

View File

@ -11,7 +11,7 @@ class DiscussionNote < Note
# Names of all implementers of `Noteable` that support discussions.
def self.noteable_types
%w[MergeRequest Issue Commit Snippet]
%w[MergeRequest Issue Commit Snippet WikiPage::Meta]
end
validates :noteable_type, inclusion: { in: noteable_types }

View File

@ -361,6 +361,10 @@ class Note < ApplicationRecord
noteable.is_a?(PersonalSnippet)
end
def for_wiki_page?
noteable_type == "WikiPage::Meta"
end
def for_project_noteable?
!(for_personal_snippet? || for_abuse_report? || group_level_issue?)
end

View File

@ -3,6 +3,8 @@
class WikiPage
class Meta < ApplicationRecord
include HasWikiPageMetaAttributes
include Mentionable
include Noteable
self.table_name = 'wiki_page_meta'
@ -10,15 +12,14 @@ class WikiPage
belongs_to :namespace, optional: true
has_many :slugs, class_name: 'WikiPage::Slug', foreign_key: 'wiki_page_meta_id', inverse_of: :wiki_page_meta
has_many :notes, as: :noteable
has_many :user_mentions, class_name: 'Wikis::UserMention', foreign_key: 'wiki_page_meta_id',
inverse_of: :wiki_page_meta
validate :project_or_namespace_present?
alias_method :resource_parent, :project
def container_key
namespace.present? ? :namespace_id : :project_id
end
def container
project || namespace
end
@ -28,6 +29,14 @@ class WikiPage
self.namespace = value if value.is_a?(Namespace)
end
def for_group_wiki?
namespace_id.present?
end
def container_key
for_group_wiki? ? :namespace_id : :project_id
end
private
def project_or_namespace_present?

View File

@ -0,0 +1,18 @@
# frozen_string_literal: true
module Wikis
class UserMention < UserMention
self.table_name = 'wiki_page_meta_user_mentions'
belongs_to :wiki_page_meta, class_name: 'WikiPage::Meta', optional: false
belongs_to :note, optional: false
before_validation :set_namespace_id_from_note, on: :create
private
def set_namespace_id_from_note
self.namespace_id ||= note&.namespace_id
end
end
end

View File

@ -11,4 +11,4 @@ GITLAB_PATH = File.expand_path('../', __dir__)
require_relative '../config/boot'
require 'gitlab/backup/cli'
Gitlab::Backup::Cli::Runner.start(ARGV)
Gitlab::Backup::Cli.start(ARGV)

View File

@ -0,0 +1,12 @@
---
table_name: wiki_page_meta_user_mentions
classes:
- Wikis::UserMention
feature_categories:
- wiki
description: User mentions in wiki page notes
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/163305
milestone: '17.5'
gitlab_schema: gitlab_main_cell
sharding_key:
namespace_id: namespaces

View File

@ -0,0 +1,31 @@
# frozen_string_literal: true
# See https://docs.gitlab.com/ee/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class CreateWikiPageUserMentions < Gitlab::Database::Migration[2.2]
enable_lock_retries!
milestone '17.5'
def up
create_table :wiki_page_meta_user_mentions do |t| # rubocop:disable Migration/EnsureFactoryForTable -- No factory needed
t.bigint :wiki_page_meta_id, null: false
t.bigint :note_id, null: false
t.bigint :namespace_id, null: false
t.bigint :mentioned_users_ids, array: true, default: nil
t.bigint :mentioned_projects_ids, array: true, default: nil
t.bigint :mentioned_groups_ids, array: true, default: nil
t.index :note_id
t.index :namespace_id
t.index [:wiki_page_meta_id, :note_id],
unique: true,
name: :index_wiki_meta_user_mentions_on_wiki_page_meta_id_and_note_id
end
end
def down
drop_table :wiki_page_meta_user_mentions, if_exists: true
end
end

View File

@ -0,0 +1,18 @@
# frozen_string_literal: true
class AddWikiPageMetaForeignKeyToWikiPageMetaUserMentions < Gitlab::Database::Migration[2.2]
milestone '17.5'
disable_ddl_transaction!
def up
add_concurrent_foreign_key :wiki_page_meta_user_mentions, :wiki_page_meta, column: :wiki_page_meta_id,
on_delete: :cascade
end
def down
with_lock_retries do
remove_foreign_key :wiki_page_meta_user_mentions, column: :wiki_page_meta_id
end
end
end

View File

@ -0,0 +1,17 @@
# frozen_string_literal: true
class AddNotesForeignKeyToWikiPageMetaUserMentions < Gitlab::Database::Migration[2.2]
milestone '17.5'
disable_ddl_transaction!
def up
add_concurrent_foreign_key :wiki_page_meta_user_mentions, :notes, column: :note_id, on_delete: :cascade
end
def down
with_lock_retries do
remove_foreign_key :wiki_page_meta_user_mentions, column: :note_id
end
end
end

View File

@ -0,0 +1,17 @@
# frozen_string_literal: true
class AddNamespacesForeignKeyToWikiPageMetaUserMentions < Gitlab::Database::Migration[2.2]
milestone '17.5'
disable_ddl_transaction!
def up
add_concurrent_foreign_key :wiki_page_meta_user_mentions, :namespaces, column: :namespace_id, on_delete: :cascade
end
def down
with_lock_retries do
remove_foreign_key :wiki_page_meta_user_mentions, column: :namespace_id
end
end
end

View File

@ -0,0 +1 @@
9ee63c7a0b13f8eabca033494d551c498d8320144f6acec53b2bd6263e1b5152

View File

@ -0,0 +1 @@
ec9255f62950f75064ad2cd65c9fef6a1b4c136d4bb86238b24ed567098fb28e

View File

@ -0,0 +1 @@
caa78be456eebea5d9fc8cbcc0d0daa56e11f3e633f5327014702814919a073c

View File

@ -0,0 +1 @@
b844c5179a55387b9c0f631a9172b7b62fae98e38d8c86532dbcac2365f8a1c7

View File

@ -20647,6 +20647,25 @@ CREATE SEQUENCE wiki_page_meta_id_seq
ALTER SEQUENCE wiki_page_meta_id_seq OWNED BY wiki_page_meta.id;
CREATE TABLE wiki_page_meta_user_mentions (
id bigint NOT NULL,
wiki_page_meta_id bigint NOT NULL,
note_id bigint NOT NULL,
namespace_id bigint NOT NULL,
mentioned_users_ids bigint[],
mentioned_projects_ids bigint[],
mentioned_groups_ids bigint[]
);
CREATE SEQUENCE wiki_page_meta_user_mentions_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE wiki_page_meta_user_mentions_id_seq OWNED BY wiki_page_meta_user_mentions.id;
CREATE TABLE wiki_page_slugs (
id bigint NOT NULL,
canonical boolean DEFAULT false NOT NULL,
@ -22812,6 +22831,8 @@ ALTER TABLE ONLY webauthn_registrations ALTER COLUMN id SET DEFAULT nextval('web
ALTER TABLE ONLY wiki_page_meta ALTER COLUMN id SET DEFAULT nextval('wiki_page_meta_id_seq'::regclass);
ALTER TABLE ONLY wiki_page_meta_user_mentions ALTER COLUMN id SET DEFAULT nextval('wiki_page_meta_user_mentions_id_seq'::regclass);
ALTER TABLE ONLY wiki_page_slugs ALTER COLUMN id SET DEFAULT nextval('wiki_page_slugs_id_seq'::regclass);
ALTER TABLE ONLY wiki_repository_states ALTER COLUMN id SET DEFAULT nextval('wiki_repository_states_id_seq'::regclass);
@ -25622,6 +25643,9 @@ ALTER TABLE ONLY webauthn_registrations
ALTER TABLE ONLY wiki_page_meta
ADD CONSTRAINT wiki_page_meta_pkey PRIMARY KEY (id);
ALTER TABLE ONLY wiki_page_meta_user_mentions
ADD CONSTRAINT wiki_page_meta_user_mentions_pkey PRIMARY KEY (id);
ALTER TABLE ONLY wiki_page_slugs
ADD CONSTRAINT wiki_page_slugs_pkey PRIMARY KEY (id);
@ -31239,10 +31263,16 @@ CREATE UNIQUE INDEX index_webauthn_registrations_on_credential_xid ON webauthn_r
CREATE INDEX index_webauthn_registrations_on_user_id ON webauthn_registrations USING btree (user_id);
CREATE UNIQUE INDEX index_wiki_meta_user_mentions_on_wiki_page_meta_id_and_note_id ON wiki_page_meta_user_mentions USING btree (wiki_page_meta_id, note_id);
CREATE INDEX index_wiki_page_meta_on_namespace_id ON wiki_page_meta USING btree (namespace_id);
CREATE INDEX index_wiki_page_meta_on_project_id ON wiki_page_meta USING btree (project_id);
CREATE INDEX index_wiki_page_meta_user_mentions_on_namespace_id ON wiki_page_meta_user_mentions USING btree (namespace_id);
CREATE INDEX index_wiki_page_meta_user_mentions_on_note_id ON wiki_page_meta_user_mentions USING btree (note_id);
CREATE INDEX index_wiki_page_slugs_on_project_id ON wiki_page_slugs USING btree (project_id);
CREATE UNIQUE INDEX index_wiki_page_slugs_on_slug_and_wiki_page_meta_id ON wiki_page_slugs USING btree (slug, wiki_page_meta_id);
@ -34208,6 +34238,9 @@ ALTER TABLE ONLY users
ALTER TABLE ONLY analytics_devops_adoption_snapshots
ADD CONSTRAINT fk_78c9eac821 FOREIGN KEY (namespace_id) REFERENCES namespaces(id) ON DELETE CASCADE;
ALTER TABLE ONLY wiki_page_meta_user_mentions
ADD CONSTRAINT fk_7954f34107 FOREIGN KEY (namespace_id) REFERENCES namespaces(id) ON DELETE CASCADE;
ALTER TABLE ONLY topics
ADD CONSTRAINT fk_79ae18bd4b FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE;
@ -34601,12 +34634,18 @@ ALTER TABLE ONLY uploads
ALTER TABLE ONLY deployments
ADD CONSTRAINT fk_b9a3851b82 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
ALTER TABLE ONLY wiki_page_meta_user_mentions
ADD CONSTRAINT fk_ba8a9d7f95 FOREIGN KEY (note_id) REFERENCES notes(id) ON DELETE CASCADE;
ALTER TABLE ONLY project_compliance_standards_adherence
ADD CONSTRAINT fk_baf6f6f878 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
ALTER TABLE p_ci_runner_machine_builds
ADD CONSTRAINT fk_bb490f12fe_p FOREIGN KEY (partition_id, build_id) REFERENCES p_ci_builds(partition_id, id) ON UPDATE CASCADE ON DELETE CASCADE;
ALTER TABLE ONLY wiki_page_meta_user_mentions
ADD CONSTRAINT fk_bc155eba89 FOREIGN KEY (wiki_page_meta_id) REFERENCES wiki_page_meta(id) ON DELETE CASCADE;
ALTER TABLE ONLY security_orchestration_policy_rule_schedules
ADD CONSTRAINT fk_bcbb90477f FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;

View File

@ -55,12 +55,10 @@ Install one of the following GitLab-approved LLM models:
### Use a serving architecture
You should use one of the following serving architectures with your
installed LLM:
To host your models, you should use:
- [vLLM](https://docs.vllm.ai/en/stable/)
- [TensorRT-LLM](https://docs.mistral.ai/deployment/self-deployment/overview/)
- [Ollama and litellm](litellm_proxy_setup.md)
- For non-cloud on-premise deployments, [vLLM](https://docs.vllm.ai/en/stable/).
- For cloud deployments, AWS Bedrock as a cloud provider.
## Configure your GitLab instance
@ -128,15 +126,7 @@ Find the GitLab official Docker image at:
- [Release process for self-hosted AI Gateway](https://gitlab.com/gitlab-org/modelops/applied-ml/code-suggestions/ai-assist/-/blob/main/docs/release.md).
Use the image tag that corresponds to your GitLab version. For example, if the
GitLab version is `v17.4.0`, use `self-hosted-v17.4.0-ee` tag.
For version `v17.3.0-ee`, use image tag `gitlab-v17.3.0`.
WARNING:
Docker for Windows is not officially supported. There are known issues with volume
permissions, and potentially other unknown issues. If you are trying to run on Docker
for Windows, see the [getting help page](https://about.gitlab.com/get-help/) for links
to community resources (such as IRC or forums) to seek help from other users.
GitLab version is `v17.5.0`, use `self-hosted-v17.5.0-ee` tag.
#### Set up the volumes location
@ -151,77 +141,19 @@ sudo mkdir -p /srv/gitlab-agw
If you're running Docker with a user other than `root`, ensure appropriate
permissions have been granted to that directory.
#### Optional: Download documentation index
NOTE:
This only applies to AI Gateway image tag `gitlab-17.3.0-ee` and earlier. For images with tag `self-hosted-v17.4.0-ee` and later, documentation search is embedded into the Docker image.
To improve results when asking GitLab Duo Chat questions about GitLab, you can
index GitLab documentation and provide it as a file to the AI Gateway.
To index the documentation in your local installation, run:
```shell
pip install requests langchain langchain_text_splitters
python3 scripts/custom_models/create_index.py -o <path_to_created_index/docs.db>
```
This creates a file `docs.db` at the specified path.
You can also create an index for a specific GitLab version:
```shell
python3 scripts/custom_models/create_index.py --version_tag="{gitlab-version}"
```
#### Start a container from the image
For Docker images with version `self-hosted-17.4.0-ee` and later, run the following:
```shell
docker run -e AIGW_GITLAB_URL=<your_gitlab_instance> <image>
docker run -e AIGW_GITLAB_URL=<your_gitlab_instance> \
-e AIGW_GITLAB_API_URL=https://<your_gitlab_domain>/api/v4/ \
<image>
```
For Docker images with version `gitlab-17.3.0-ee` and `gitlab-17.2.0`, run:
```shell
docker run -e AIGW_CUSTOM_MODELS__ENABLED=true \
-v path/to/created/index/docs.db:/app/tmp/docs.db \
-e AIGW_FASTAPI__OPENAPI_URL="/openapi.json" \
-e AIGW_AUTH__BYPASS_EXTERNAL=true \
-e AIGW_FASTAPI__DOCS_URL="/docs"\
-e AIGW_FASTAPI__API_PORT=5052 \
<image>
```
The arguments `AIGW_FASTAPI__OPENAPI_URL` and `AIGW_FASTAPI__DOCS_URL` are not
mandatory, but are useful for debugging. From the host, accessing `http://localhost:5052/docs`
From the container host, accessing `http://localhost:5052/docs`
should open the AI Gateway API documentation.
### Install by using Docker Engine
1. For the AI Gateway to access the API, it must know where the GitLab instance
is located. To do this, set the environment variables `AIGW_GITLAB_URL` and
`AIGW_GITLAB_API_URL`:
```shell
AIGW_GITLAB_URL=https://<your_gitlab_domain>
AIGW_GITLAB_API_URL=https://<your_gitlab_domain>/api/v4/
```
1. [Configure the GitLab instance](#configure-your-gitlab-instance).
1. After you've set up the environment variables, [run the image](#start-a-container-from-the-image).
1. Track the initialization process:
```shell
sudo docker logs -f gitlab-aigw
```
After starting the container, visit `gitlab-aigw.example.com`. It might take
a while before the Docker container starts to respond to queries.
### Install by using the AI Gateway Helm chart
Prerequisites:

View File

@ -487,7 +487,7 @@ Requires `current_user` and `group_ids` fields. Query based on the permissions t
}
```
##### `by_confidentiality`
##### `by_project_confidentiality`
Requires `confidential`, `author_id`, `assignee_id`, `project_id` fields. Query with `confidential` in options.

View File

@ -729,6 +729,10 @@ Gitlab::Llm::Anthropic::Client.new(user, unit_primitive: 'your_feature')
In addition to standard logging in the GitLab Rails Monolith instance, specialized logging is available for features based on large language models (LLMs).
### Logged events
Currently logged events are documented [here](logged_events.md).
### Implementation
#### Logger Class
@ -745,7 +749,7 @@ A feature flag named `expanded_ai_logging` controls the logging of sensitive dat
Use the `conditional_info` helper method for conditional logging based on the feature flag status:
- If the feature flag is enabled for the current user, it logs the information on `info` level (logs are accessible in Kibana).
- If the feature flag is disabled for the current user, it logs the information on `debug` level (logs are not accessible in Kibana).
- If the feature flag is disabled for the current user, it logs the information on `info` level, but without optional parameters (logs are accessible in Kibana, but only obligatory fields).
### Best Practices
@ -759,14 +763,14 @@ When implementing logging for LLM features, consider the following:
### Example Usage
```ruby
# Logging non-sensitive information
Gitlab::Llm::Logger.build.info("LLM feature initialized")
# including concern that handles logging
include Gitlab::Llm::Concerns::Logger
# Logging potentially sensitive information
Gitlab::Llm::Logger.build.conditional_info(user, message:"User prompt processed: #{sanitized_prompt}")
log_conditional_info(user, message:"User prompt processed", event_name: 'ai_event', ai_component: 'abstraction_layer', prompt: sanitized_prompt)
# Logging application error information
Gitlab::Llm::Logger.build.error(user, message: "System application error: #{sanitized_error_message}")
log_error(user, message: "System application error", event_name: 'ai_event', ai_component: 'abstraction_layer', error_message: sanitized_error_message)
```
**Important**: Please familiarize yourself with our [Data Retention Policy](../../user/gitlab_duo/data_usage.md#data-retention) and remember

View File

@ -0,0 +1,720 @@
---
stage: AI-powered
group: AI Framework
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
# Logged events
In addition to standard logging in the GitLab Rails Monolith instance, specialized logging is available for features based on large language models (LLMs).
## Events logged
<!-- markdownlint-disable -->
<!-- vale off -->
### Returning from Service due to validation
- Description: user not permitted to perform action
- Class: `Llm::BaseService`
- Ai_event_name: permission_denied
- Level: info
- Arguments:
- none
- Part of the system: abstraction_layer
- Expanded logging?: no
- Rails: yes
- Sidekiq: no
### Enqueuing CompletionWorker
- Description: scheduling completion worker in sidekiq
- Class: `Llm::BaseService`
- Ai_event_name: worker_enqueued
- Level: info
- Arguments:
- user_id: message.user.id
- resource_id: message.resource&.id
- resource_class: message.resource&.class&.name
- request_id: message.request_id
- action_name: message.ai_action
- options: job_options
- Part of the system: abstraction_layer
- Expanded logging?: yes
- Rails: yes
- Sidekiq: no
### aborting: missing resource
- Description: If there is no resource for slash command
- Class: `Llm::ChatService`
- Ai_event_name: missing_resource
- Level: info
- Arguments:
- none
- Part of the system: duo_chat
- Expanded logging?: no
- Rails: yes
- Sidekiq: no
### Performing CompletionService
- Description: performing completion
- Class: `Llm::Internal::CompletionService`
- Ai_event_name: completion_service_performed
- Level: info
- Arguments:
- user_id: prompt_message.user.to_gid
- resource_id: prompt_message.resource&.to_gid
- action_name: prompt_message.ai_action
- request_id: prompt_message.request_id
- client_subscription_id: prompt_message.client_subscription_id
- completion_service_name: completion_class_name
- Part of the system: abstraction_layer
- Expanded logging?: yes
- Rails: no
- Sidekiq: yes
### Answer from LLM response
- Description: Get answer from response
- Class: `Gitlab::Llm::Chain::Answer`
- Ai_event_name: answer_received
- Level: info
- Arguments:
- llm_answer_content: content
- Part of the system: duo_chat
- Expanded logging?: yes
- Rails: no
- Sidekiq: yes
### Final answer
- Description: Get final answer from response
- Class: `Gitlab::Llm::Chain::Answer`
- Ai_event_name: final_answer_received
- Level: info
- Arguments:
- llm_answer_content: content
- Part of the system: duo_chat
- Expanded logging?: yes
- Rails: no
- Sidekiq: yes
### Default final answer
- Description: Default final answer: I'm sorry, I couldn't respond in time. Please try a more specific request or enter /clear to start a new chat.
- Class: `Gitlab::Llm::Chain::Answer`
- Ai_event_name: default_final_answer_received
- Level: info
- Arguments:
- error_code: "A6000"
- Part of the system: duo_chat
- Expanded logging?: yes
- Rails: no
- Sidekiq: yes
### Error message/ "Error"
- Description: when answering with an error
- Class: `Gitlab::Llm::Chain::Answer`
- Ai_event_name: error_returned
- Level: error
- Arguments:
- error: content
- error_code: error_code
- source: source
- Part of the system: duo_chat
- Expanded logging?: no
- Rails: no
- Sidekiq: yes
### Received response from AI Gateway
- Description: when response from AIGW is returned
- Class: `Gitlab::Llm::AiGateway::Client`
- Ai_event_name: response_received
- Level: info
- Arguments:
- response_from_llm: response_body
- Part of the system: abstraction_layer
- Expanded logging?: yes
- Rails: no
- Sidekiq: yes
### Received error from AI gateway
- Description: when error is returned from AIGW for streaming command
- Class: `Gitlab::Llm::AiGateway::Client`
- Ai_event_name: error_response_received
- Level: error
- Arguments:
- response_from_llm: parsed_response.dig('detail'
- 0
- 'msg')
- Part of the system: abstraction_layer
- Expanded logging?: no
- Rails: no
- Sidekiq: yes
### Performing request to AI Gateway
- Description: before performing request to the AI GW
- Class: `Gitlab::Llm::AiGateway::Client`
- Ai_event_name: performing_request
- Level: info
- Arguments:
- url: url
- body: body
- timeout: timeout
- stream: stream
- Part of the system: abstraction_layer
- Expanded logging?: yes
- Rails: no
- Sidekiq: yes
### Creating user access token
- Description: creating short-lived token in AIGW
- Class: `Gitlab::Llm::AiGateway::CodeSuggestionsClient`
- Ai_event_name: user_token_created
- Level: info
- Arguments:
- none
- Part of the system: code suggestions
- Expanded logging?: no
- Rails: yes
- Sidekiq: no
### Received response from Anthropic
- Description: Received response
- Class: `Gitlab::Llm::Anthropic::Client`
- Ai_event_name: response_received
- Level: info
- Arguments:
- ai_request_type: request_type
- unit_primitive: unit_primitive
- Part of the system: abstraction_layer
- Expanded logging?: no
- Rails: no
- Sidekiq: yes
### Response content
- Description: Content of response
- Class: `Gitlab::Llm::Anthropic::Client`
- Ai_event_name: response_received
- Level: info
- Arguments:
- ai_request_type: request_type
- unit_primitive: unit_primitive
- response_from_llm: response_body
- Part of the system: abstraction_layer
- Expanded logging?: yes
- Rails: no
- Sidekiq: yes
### Performing request to Anthropic
- Description: performing completion request
- Class: `Gitlab::Llm::Anthropic::Client`
- Ai_event_name: performing_request
- Level: info
- Arguments:
- options: options
- ai_request_type: request_type
- unit_primitive: unit_primitive
- Part of the system: abstraction_layer
- Expanded logging?: no
- Rails: no
- Sidekiq: yes
### Searching docs from AI Gateway
- Description: performing search docs request
- Class: `Gitlab::Llm::AiGateway::DocsClient`
- Ai_event_name: performing_request
- Level: info
- Arguments:
- options: options
- Part of the system: duo_chat
- Expanded logging?: no
- Rails: no
- Sidekiq: yes
### Searched docs content from AI Gateway
- Description: response from AIGW with docs
- Class: `Gitlab::Llm::AiGateway::DocsClient`
- Ai_event_name: response_received
- Level: info
- Arguments:
- response_from_llm: response
- Part of the system: duo_chat
- Expanded logging?: yes
- Rails: no
- Sidekiq: yes
### Json parsing error during Question Categorization
- Description: logged when json is not parsable
- Class: `Gitlab::Llm::Anthropic::Completions::CategorizeQuestions`
- Ai_event_name: error
- Level: error
- Arguments:
- none
- Part of the system: duo_chat
- Expanded logging?: no
- Rails: no
- Sidekiq: yes
### Response did not contain defined categories
- Description: logged when response is not containing one of the defined categories
- Class: `Gitlab::Llm::Anthropic::Completions::CategorizeQuestions`
- Ai_event_name: error
- Level: error
- Arguments:
- none
- Part of the system: duo_chat
- Expanded logging?: no
- Rails: no
- Sidekiq: yes
### Picked tool
- Description: information about tool picked by chat
- Class: `Gitlab::Llm::Chain::Agents::ZeroShot::Executor`
- Ai_event_name: picked_tool
- Level: info
- Arguments:
- duo_chat_tool: tool_class.to_s
- Part of the system: duo_chat
- Expanded logging?: no
- Rails: no
- Sidekiq: yes
### Made request to AI Client
- Description: making request for chat
- Class: `Gitlab::Llm::Chain::Requests::AiGateway`
- Ai_event_name: response_received
- Level: info
- Arguments:
- prompt: prompt[:prompt]
- response_from_llm: response
- unit_primitive: unit_primitive
- Part of the system: duo_chat
- Expanded logging?: yes
- Rails: no
- Sidekiq: yes
### Streaming error
- Description: Error returned when streaming
- Class: `Gitlab::Llm::Chain::Requests::Anthropic`
- Ai_event_name: error_response_received
- Level: error
- Arguments:
- error: data&.dig("error")
- Part of the system: abstraction_layer
- Expanded logging?: no
- Rails: no
- Sidekiq: yes
### Got Final Result for documentation question content
- Description: got result for documentation question - content
- Class: `Gitlab::Llm::Chain::Tools::EmbeddingsCompletion`
- Ai_event_name: response_received
- Level: info
- Arguments:
- prompt: final_prompt[:prompt]
- response_from_llm: final_prompt_result
- Part of the system: duo_chat
- Expanded logging?: yes
- Rails: no
- Sidekiq: yes
### Streaming error
- Description: when error is returned from AIGW for streaming command in docs question
- Class: `Gitlab::Llm::Chain::Tools::EmbeddingsCompletion`
- Ai_event_name: error_response_received
- Level: error
- Arguments:
- error: error.message
- Part of the system: duo_chat
- Expanded logging?: no
- Rails: no
- Sidekiq: yes
### Answer already received from tool
- Description: when tool was already picked up (content: You already have the answer from #{self.class::NAME} tool, read carefully.)
- Class: `Gitlab::Llm::Chain::Tools::Tool`
- Ai_event_name: incorrect_response_received
- Level: info
- Arguments:
- error_message: content
- Part of the system: duo_chat
- Expanded logging?: yes
- Rails: no
- Sidekiq: yes
### Tool cycling detected
- Description: When tool is picked up again
- Class: `Gitlab::Llm::Chain::Tools::Tool`
- Ai_event_name: incorrect_response_received
- Level: info
- Arguments:
- picked_tool: cls.class.to_s
- Part of the system: duo_chat
- Expanded logging?: no
- Rails: no
- Sidekiq: yes
### Calling TanukiBot
- Description: performing documentation request
- Class: `Gitlab::Llm::Chain::Tools::GitlabDocumentation::Executor`
- Ai_event_name: documentation_question_initial_request
- Level: info
- Arguments:
- none
- Part of the system: duo_chat
- Expanded logging?: no
- Rails: no
- Sidekiq: yes
### Error finding #{resource_name}
- Description: when resource (issue/epic/mr) is not found
- Class: `Gitlab::Llm::Chain::Tools::Identifier`
- Ai_event_name: incorrect_response_received
- Level: error
- Arguments:
- error_message: authorizer.message
- Part of the system: duo_chat
- Expanded logging?: no
- Rails: no
- Sidekiq: yes
### Answer received from LLM
- Description: response from identifier
- Class: `Gitlab::Llm::Chain::Tools::Identifier`
- Ai_event_name: response_received
- Level: info
- Arguments:
- response_from_llm: content
- Part of the system: duo_chat
- Expanded logging?: yes
- Rails: no
- Sidekiq: yes
### Json parsing error
- Description: when json is malformed (Observation: JSON has an invalid format. Please retry)
- Class: `Gitlab::Llm::Chain::Tools::Identifier`
- Ai_event_name: error
- Level: error
- Arguments:
- none
- Part of the system: duo_chat
- Expanded logging?: no
- Rails: no
- Sidekiq: yes
### Resource already identified
- Description: already identified resource (You already have identified the #{resource_name} #{resource.to_global_id}, read carefully.)
- Class: `Gitlab::Llm::Chain::Tools::Identifier`
- Ai_event_name: incorrect_response_received
- Level: info
- Arguments:
- error_message: content
- Part of the system: duo_chat
- Expanded logging?: yes
- Rails: no
- Sidekiq: yes
### Supported Issuable Typees Ability Allowed
- Description: logging the ability (policy.can?) for the issue/epic
- Class: `Gitlab::Llm::Chain::Tools::SummarizeComments::Executor`
- Ai_event_name: permission
- Level: info
- Arguments:
- allowed: ability
- Part of the system: feature
- Expanded logging?: yes
- Rails: no
- Sidekiq: yes
### Supported Issuable Typees Ability Allowed
- Description: logging the ability (policy.can?) for the issue/epic
- Class: `Gitlab::Llm::Chain::Tools::SummarizeComments::ExecutorOld`
- Ai_event_name: permission
- Level: info
- Arguments:
- allowed: ability
- Part of the system: feature
- Expanded logging?: yes
- Rails: no
- Sidekiq: yes
### Answer content for summarize_comments
- Description: Answer for summarize comments feature
- Class: `Gitlab::Llm::Chain::Tools::SummarizeComments::ExecutorOld`
- Ai_event_name: response_received
- Level: info
- Arguments:
- response_from_llm: content
- Part of the system: feature
- Expanded logging?: yes
- Rails: no
- Sidekiq: yes
### Content of the prompt from chat request
- Description: chat-related request
- Class: `Gitlab::Llm::Chain::Concerns::AiDependent`
- Ai_event_name: prompt_content
- Level: info
- Arguments:
- prompt: prompt_text
- Part of the system: duo_chat
- Expanded logging?: yes
- Rails: no
- Sidekiq: yes
### "Too many requests, will retry in #{delay} seconds"
- Description: When entered in exponential backoff loop
- Class: `Gitlab::Llm::Chain::Concerns::ExponentialBackoff`
- Ai_event_name: retrying_request
- Level: info
- Arguments:
- none
- Part of the system: abstraction_layer
- Expanded logging?: no
- Rails: no
- Sidekiq: yes
### Resource not found
- Description: Resource not found/not authorized
- Class: `Gitlab::Llm::Utils::Authorizer`
- Ai_event_name: permission_denied
- Level: info
- Arguments:
- error_code: "M3003"
- Part of the system: abstraction_layer
- Expanded logging?: no
- Rails: no
- Sidekiq: yes
### No access to Duo Chat
- Description: No access to duo chat
- Class: `Gitlab::Llm::Utils::Authorizer`
- Ai_event_name: permission_denied
- Level: info
- Arguments:
- error_code: "M3004"
- Part of the system: abstraction_layer
- Expanded logging?: no
- Rails: no
- Sidekiq: yes
### AI is disabled
- Description: AI is not enabled for container
- Class: `Gitlab::Llm::Utils::Authorizer`
- Ai_event_name: permission_denied
- Level: info
- Arguments:
- error_code: "M3002"
- Part of the system: abstraction_layer
- Expanded logging?: no
- Rails: no
- Sidekiq: yes
### Performing request to Vertex
- Description: performing request
- Class: `Gitlab::Llm::VertexAi::Client`
- Ai_event_name: performing_request
- Level: info
- Arguments:
- unit_primitive: unit_primitive
- options: config
- Part of the system: abstraction_layer
- Expanded logging?: no
- Rails: no
- Sidekiq: yes
### Response content
- Description: response from aigw - vertex -content
- Class: `Gitlab::Llm::VertexAi::Client`
- Ai_event_name: response_received
- Level: info
- Arguments:
- unit_primitive: unit_primitive
- response_from_llm: response.to_json
- Part of the system: abstraction_layer
- Expanded logging?: yes
- Rails: no
- Sidekiq: yes
### Received response from Vertex
- Description: response from aigw - vertex
- Class: `Gitlab::Llm::VertexAi::Client`
- Ai_event_name: response_received
- Level: info
- Arguments:
- unit_primitive: unit_primitive
- Part of the system: abstraction_layer
- Expanded logging?: no
- Rails: no
- Sidekiq: yes
### Empty response from Vertex
- Description: empty response from aigw - vertex
- Class: `Gitlab::Llm::VertexAi::Client`
- Ai_event_name: empty_response_received
- Level: error
- Arguments:
- unit_primitive: unit_primitive
- Part of the system: abstraction_layer
- Expanded logging?: no
- Rails: no
- Sidekiq: yes
### Surface an unknown event as a final answer to the user
- Description: unknown event
- Class: `Gitlab::Llm::Chain::Agents::SingleActionExecutor`
- Ai_event_name: unknown_event
- Level: warn
- Arguments:
- none
- Part of the system: duo_chat
- Expanded logging?: no
- Rails: no
- Sidekiq: yes
### Failed to find a tool in GitLab Rails
- Description: failed to find a tool
- Class: `Gitlab::Llm::Chain::Agents::SingleActionExecutor`
- Ai_event_name: tool_not_find
- Level: error
- Arguments:
- tool_name: tool_name
- Part of the system: duo_chat
- Expanded logging?: no
- Rails: no
- Sidekiq: yes
### Received an event from v2/chat/agent
- Description: Received event
- Class: `Gitlab::Duo::Chat::StepExecutor`
- Ai_event_name: event_received
- Level: info
- Arguments:
- event: event
- Part of the system: duo_chat
- Expanded logging?: yes
- Rails: no
- Sidekiq: yes
### Failed to update observation
- Description: Failed to update observation
- Class: `Gitlab::Duo::Chat::StepExecutor`
- Ai_event_name: agent_steps_empty
- Level: error
- Arguments:
- none
- Part of the system: duo_chat
- Expanded logging?: no
- Rails: no
- Sidekiq: yes
### Request to v2/chat/agent
- Description: request
- Class: `Gitlab::Duo::Chat::StepExecutor`
- Ai_event_name: performing_request
- Level: info
- Arguments:
- params: params
- Part of the system: duo_chat
- Expanded logging?: yes
- Rails: no
- Sidekiq: yes
### Finished streaming from v2/chat/agent
- Description: finished streaming
- Class: `Gitlab::Duo::Chat::StepExecutor`
- Ai_event_name: streaming_finished
- Level: info
- Arguments:
- none
- Part of the system: duo_chat
- Expanded logging?: no
- Rails: no
- Sidekiq: yes
### Received error from Duo Chat Agent
- Description: Error returned when streaming
- Class: `Gitlab::Duo::Chat::StepExecutor`
- Ai_event_name: error_returned
- Level: error
- Arguments:
- status: response.code
- Part of the system: duo_chat
- Expanded logging?: no
- Rails: no
- Sidekiq: yes
### Failed to parse a chunk from Duo Chat Agent
- Description: failed to parse a chunk
- Class: `Gitlab::Duo::Chat::AgentEventParser`
- Ai_event_name: parsing_error
- Level: warn
- Arguments:
- event_json_size: event_json.length
- Part of the system: duo_chat
- Expanded logging?: no
- Rails: no
- Sidekiq: yes
### Failed to find the event class in GitLab-Rails
- Description: no event class
- Class: `Gitlab::Duo::Chat::AgentEventParser`
- Ai_event_name: parsing_error
- Level: error
- Arguments:
- event_type: event['type']
- Part of the system: duo_chat
- Expanded logging?: no
- Rails: no
- Sidekiq: yes
<!-- markdownlint-enable -->
<!-- vale on -->

View File

@ -319,7 +319,7 @@ such as move unresolved threads to an issue or prevent merging until all threads
DETAILS:
**Tier:** For a limited time, Ultimate. On October 17, 2024, Ultimate with [GitLab Duo Enterprise](https://about.gitlab.com/gitlab-duo/#pricing).
**Offering:** GitLab.com, Self-managed
**Offering:** GitLab.com, Self-managed, GitLab Dedicated
> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/10344) in GitLab 16.0 as an [experiment](../../policy/experiment-beta-support.md#experiment).
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/454550) to GitLab Duo and promoted to [beta](../../policy/experiment-beta-support.md#beta) in GitLab 17.3 [with a flag](../../administration/feature_flags.md) named `summarize_notes_with_duo`. Disabled by default.
@ -329,7 +329,6 @@ Generate a summary of discussions on an issue.
Prerequisites:
- You must belong to at least one group with the [experiment and beta features setting](../gitlab_duo/turn_on_off.md#turn-on-beta-and-experimental-features) enabled.
- You must have permission to view the issue.
To generate a summary of issue discussions:
@ -340,7 +339,5 @@ To generate a summary of issue discussions:
The comments in the issue are summarized in as many as 10 list items.
You can ask follow up questions based on the response.
Provide feedback on this beta feature in [issue 407779](https://gitlab.com/gitlab-org/gitlab/-/issues/407779).
**Data usage**: When you use this feature, the text of all comments on the issue are sent to
the large [language model listed on the GitLab Duo page](../gitlab_duo/index.md#discussion-summary).

View File

@ -44,6 +44,17 @@ DETAILS:
- <i class="fa fa-youtube-play youtube" aria-hidden="true"></i> [Watch overview](https://www.youtube.com/watch?v=ZQBAuf-CTAY&list=PLFGfElNsQthYDx0A_FaNNfUm9NHsK6zED)
- [View documentation](../gitlab_duo_chat/index.md).
### Discussion Summary
DETAILS:
**Tier:** For a limited time, Ultimate. On October 17, 2024, Ultimate with [GitLab Duo Enterprise](https://about.gitlab.com/gitlab-duo/#pricing).
**Offering:** GitLab.com, Self-managed, GitLab Dedicated
- Helps everyone get up to speed by summarizing the lengthy conversations in an issue.
- LLM: Anthropic [Claude 3.5 Sonnet](https://console.cloud.google.com/vertex-ai/publishers/anthropic/model-garden/claude-3-5-sonnet)
- <i class="fa fa-youtube-play youtube" aria-hidden="true"></i> [Watch overview](https://www.youtube.com/watch?v=IcdxLfTIUgc)
- [View documentation](../discussions/index.md#summarize-issue-discussions-with-duo-chat).
### Code Suggestions
DETAILS:
@ -137,17 +148,6 @@ DETAILS:
- Track the progress of AI adoption.
- [View documentation](../analytics/ai_impact_analytics.md).
### Discussion summary
DETAILS:
**Tier:** For a limited time, Ultimate. On October 17, 2024, Ultimate with [GitLab Duo Enterprise](https://about.gitlab.com/gitlab-duo/#pricing).
**Offering:** GitLab.com, Self-managed
- Helps everyone get up to speed by summarizing the lengthy conversations in an issue.
- LLM: Anthropic [Claude 3.5 Sonnet](https://console.cloud.google.com/vertex-ai/publishers/anthropic/model-garden/claude-3-5-sonnet)
- <i class="fa fa-youtube-play youtube" aria-hidden="true"></i> [Watch overview](https://www.youtube.com/watch?v=IcdxLfTIUgc)
- [View documentation](../discussions/index.md#summarize-issue-discussions-with-duo-chat).
## Beta features
### Self-Hosted Models

View File

@ -6,8 +6,8 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Namespaces
In GitLab, a namespace provides a place to organize projects. Projects in a namespace are separate
from other namespaces, enabling you to use the same project name in different namespaces.
Namespaces organize projects in GitLab. Because each namespace is separate,
you can use the same project name in multiple namespaces.
## Types of namespaces
@ -26,9 +26,9 @@ GitLab has two types of namespaces:
- You can configure settings specifically for each subgroup and project.
- You can manage the group or subgroup URL independently of the name.
## Determine which type of namespace you're viewing
## Determine which type of namespace you're in
To determine whether you're viewing a group or personal namespace, you can view the URL. For example:
To determine whether you're in a group or personal namespace, you can view the URL. For example:
| Namespace for | URL | Namespace |
| ------------- | --- | --------- |
@ -36,9 +36,9 @@ To determine whether you're viewing a group or personal namespace, you can view
| A group named `alex-team`. | `https://gitlab.example.com/alex-team` | `alex-team` |
| A group named `alex-team` with a subgroup named `marketing`. | `https://gitlab.example.com/alex-team/marketing` | `alex-team/marketing` |
## Naming limitations for namespaces
## Name limitations
When you choose a name for your namespace, keep in mind the [character limitations](../reserved_names.md#limitations-on-usernames-project-and-group-names-and-slugs) and [reserved group names](../reserved_names.md#reserved-group-names).
NOTE:
Namespaces with a period (`.`) cause issues with validating SSL certificates and the source path when [publishing Terraform modules](../packages/terraform_module_registry/index.md#publish-a-terraform-module).
Namespaces with a period (`.`) cause issues with SSL certificate validation and the source path when [publishing Terraform modules](../packages/terraform_module_registry/index.md#publish-a-terraform-module).

View File

@ -53,6 +53,10 @@ Code generation requests take longer than code completion requests, but provide
[View a click-through demo](https://gitlab.navattic.com/code-suggestions).
<!-- Video published on 2023-12-09 --> <!-- Demo published on 2024-02-01 -->
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
[View a code completion vs. code generation comparison demo](https://www.youtube.com/watch?v=9dsyqMt9yg4).
<!-- Video published on 2024-09-26 -->
## Use Code Suggestions
Prerequisites:

View File

@ -29,6 +29,21 @@ module Gitlab
Error = Class.new(StandardError)
# Entrypoint for the application
# Run any initialization logic from here
def self.start(argv)
# Set a custom process name
update_process_title!
Gitlab::Backup::Cli::Runner.start(argv)
end
def self.update_process_title!(status_message = nil)
process_title = status_message ? "gitlab-backup-cli: #{status_message}" : "gitlab-backup-cli"
Process.setproctitle(process_title)
end
def self.rails_environment!
require File.join(GITLAB_PATH, 'config/application')

View File

@ -28,6 +28,8 @@ module Gitlab
desc 'all', 'Creates a backup including repositories, database and local files'
def all
Gitlab::Backup::Cli.update_process_title!('backup all')
duration = measure_duration do
Gitlab::Backup::Cli::Output.info("Initializing environment...")
Gitlab::Backup::Cli.rails_environment!

View File

@ -9,6 +9,8 @@ module Gitlab
desc 'all BACKUP_ID', 'Restores a backup including repositories, database and local files'
def all(backup_id)
Gitlab::Backup::Cli.update_process_title!("restore all from #{backup_id}")
duration = measure_duration do
Gitlab::Backup::Cli::Output.info("Initializing environment...")
Gitlab::Backup::Cli.rails_environment!

View File

@ -1,7 +1,54 @@
# frozen_string_literal: true
RSpec.describe Gitlab::Backup::Cli do
subject(:cli) { described_class }
around do |example|
previous_title = get_process_title
example.run
Process.setproctitle(previous_title)
end
it "has a version number" do
expect(Gitlab::Backup::Cli::VERSION).not_to be nil
end
describe '.start' do
it 'sets the process title', :silence_output do
cli.start([])
expect(get_process_title).to eq('gitlab-backup-cli')
end
it 'delegates to Runner.start' do
argv = ['version']
expect(Gitlab::Backup::Cli::Runner).to receive(:start).with(argv)
cli.start(argv)
end
end
describe '.update_process_title!' do
context 'without any parameters' do
it 'sets a process title to `gitlab-backup-cli`' do
cli.update_process_title!
expect(get_process_title).to eq('gitlab-backup-cli')
end
end
context 'with parameters' do
it 'sets a process title to `gitlab-backup-cli: ` including provided content' do
cli.update_process_title!('context info')
expect(get_process_title).to eq('gitlab-backup-cli: context info')
end
end
end
def get_process_title
ps = `ps -p #{Process.pid} -o command`
ps.split("\n").last.strip
end
end

View File

@ -101,6 +101,8 @@ module Gitlab
instance.gitlab_snippet_url(note.noteable, anchor: dom_id(note), **options)
elsif note.for_abuse_report?
instance.admin_abuse_report_url(note.noteable, anchor: dom_id(note), **options)
elsif note.for_wiki_page?
instance.project_wiki_page_url(note.noteable, anchor: dom_id(note), **options)
end
end

View File

@ -18,6 +18,7 @@ FactoryBot.define do
factory :note_on_personal_snippet, traits: [:on_personal_snippet]
factory :note_on_design, traits: [:on_design]
factory :note_on_alert, traits: [:on_alert]
factory :note_on_wiki_page, traits: [:on_wiki_page]
factory :system_note, traits: [:system]
factory :discussion_note, class: 'DiscussionNote'
@ -39,6 +40,8 @@ FactoryBot.define do
factory :discussion_note_on_project_snippet, traits: [:on_project_snippet], class: 'DiscussionNote'
factory :discussion_note_on_wiki_page, traits: [:on_wiki_page], class: 'DiscussionNote'
factory :legacy_diff_note_on_commit, traits: [:on_commit, :legacy_diff_note], class: 'LegacyDiffNote'
factory :legacy_diff_note_on_merge_request, traits: [:on_merge_request, :legacy_diff_note], class: 'LegacyDiffNote' do
@ -140,6 +143,10 @@ FactoryBot.define do
noteable { association(:work_item, :group_level) }
end
trait :on_project_level_wiki do
noteable { association(:wiki_page_meta, project: project) }
end
trait :on_merge_request do
noteable { association(:merge_request, source_project: project) }
end
@ -171,6 +178,10 @@ FactoryBot.define do
noteable { association(:alert_management_alert, project: project) }
end
trait :on_wiki_page do
noteable { association(:wiki_page_meta, project: project) }
end
trait :resolved do
resolved_at { Time.now }
resolved_by { association(:user) }

View File

@ -80,6 +80,22 @@ RSpec.describe Gitlab::UrlBuilder do
end
end
context 'when passing a wiki note' do
let_it_be(:wiki_page_slug) { create(:wiki_page_slug, canonical: true) }
let(:wiki_page_meta) { wiki_page_slug.reload.wiki_page_meta }
let(:note) { build_stubbed(:note, noteable: wiki_page_meta, project: wiki_page_meta.project) }
let(:path) { "/#{note.project.full_path}/-/wikis/#{note.noteable.canonical_slug}#note_#{note.id}" }
it 'returns the full URL' do
expect(subject.build(note)).to eq("#{Gitlab.config.gitlab.url}#{path}")
end
it 'returns only the path if only_path is given' do
expect(subject.build(note, only_path: true)).to eq(path)
end
end
context 'when passing a compare' do
# NOTE: The Compare requires an actual repository, which isn't available
# with the `build_stubbed` strategy used by the table tests above

View File

@ -105,6 +105,14 @@ RSpec.describe Note, feature_category: :team_planning do
end
end
context 'when noteable is a wiki page' do
subject { build(:note, noteable: build_stubbed(:wiki_page_meta), project: nil, namespace: nil) }
it 'is not valid without project or namespace' do
is_expected.to be_invalid
end
end
describe 'max notes limit' do
let_it_be(:noteable) { create(:issue) }
let_it_be(:existing_note) { create(:note, project: noteable.project, noteable: noteable) }
@ -1396,6 +1404,12 @@ RSpec.describe Note, feature_category: :team_planning do
end
end
describe '#for_wiki_page?' do
it 'returns true for a wiki_page' do
expect(build(:note_on_wiki_page).for_wiki_page?).to be_truthy
end
end
describe '#for_design' do
it 'is true when the noteable is a design' do
note = build(:note, noteable: build(:design))

View File

@ -10,6 +10,15 @@ RSpec.describe WikiPage::Meta, feature_category: :wiki do
it { is_expected.to belong_to(:project) }
it { is_expected.to have_many(:slugs) }
it { is_expected.to have_many(:events) }
it { is_expected.to have_many(:notes) }
it do
is_expected
.to have_many(:user_mentions)
.class_name('Wikis::UserMention')
.with_foreign_key('wiki_page_meta_id')
.inverse_of('wiki_page_meta')
end
it 'can find slugs' do
meta = create(:wiki_page_meta)

View File

@ -0,0 +1,12 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Wikis::UserMention, feature_category: :wiki do
describe 'associations' do
it { is_expected.to belong_to(:wiki_page_meta).optional(false) }
it { is_expected.to belong_to(:note).optional(false) }
end
it_behaves_like 'has user mentions'
end