Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
20955a8e18
commit
bd95731045
|
|
@ -3411,7 +3411,6 @@ Gitlab/BoundedContexts:
|
|||
- 'ee/app/services/llm/chat_service.rb'
|
||||
- 'ee/app/services/llm/execute_method_service.rb'
|
||||
- 'ee/app/services/llm/explain_code_service.rb'
|
||||
- 'ee/app/services/llm/explain_vulnerability_service.rb'
|
||||
- 'ee/app/services/llm/generate_commit_message_service.rb'
|
||||
- 'ee/app/services/llm/generate_description_service.rb'
|
||||
- 'ee/app/services/llm/generate_summary_service.rb'
|
||||
|
|
|
|||
|
|
@ -67,7 +67,6 @@ GraphQL/ExtractType:
|
|||
- 'app/graphql/types/work_items/linked_item_type.rb'
|
||||
- 'app/graphql/types/work_items/widgets/description_type.rb'
|
||||
- 'ee/app/graphql/mutations/projects/product_analytics_project_settings_update.rb'
|
||||
- 'ee/app/graphql/types/ai/prompt/explain_vulnerability_prompt_type.rb'
|
||||
- 'ee/app/graphql/types/analytics/ai_metrics.rb'
|
||||
- 'ee/app/graphql/types/analytics/contribution_analytics/contribution_metadata_type.rb'
|
||||
- 'ee/app/graphql/types/analytics/devops_adoption/snapshot_type.rb'
|
||||
|
|
|
|||
|
|
@ -177,8 +177,6 @@ Layout/LineEndStringConcatenationIndentation:
|
|||
- 'ee/app/graphql/resolvers/security_orchestration/scan_execution_policy_resolver.rb'
|
||||
- 'ee/app/graphql/resolvers/vulnerabilities_resolver.rb'
|
||||
- 'ee/app/graphql/resolvers/vulnerability_severities_count_resolver.rb'
|
||||
- 'ee/app/graphql/types/ai/prompt/explain_vulnerability_prompt_type.rb'
|
||||
- 'ee/app/graphql/types/ai/prompt/explain_vulnerability_prompt_type/presubmission_check_results_type.rb'
|
||||
- 'ee/app/graphql/types/ai/summarize_new_merge_request_input_type.rb'
|
||||
- 'ee/app/graphql/types/analytics/cycle_analytics/aggregation_status_type.rb'
|
||||
- 'ee/app/graphql/types/analytics/value_stream_dashboard/metric_enum.rb'
|
||||
|
|
@ -264,7 +262,6 @@ Layout/LineEndStringConcatenationIndentation:
|
|||
- 'ee/lib/gitlab/llm/chain/tools/refactor_code/executor.rb'
|
||||
- 'ee/lib/gitlab/llm/chain/tools/summarize_comments/executor.rb'
|
||||
- 'ee/lib/gitlab/llm/chain/tools/write_tests/executor.rb'
|
||||
- 'ee/lib/gitlab/llm/completions/explain_vulnerability.rb'
|
||||
- 'ee/lib/gitlab/llm/completions/resolve_vulnerability.rb'
|
||||
- 'ee/lib/gitlab/status_page/storage.rb'
|
||||
- 'ee/lib/google_cloud/base_client.rb'
|
||||
|
|
|
|||
|
|
@ -436,7 +436,6 @@ RSpec/NamedSubject:
|
|||
- 'ee/spec/lib/gitlab/llm/completions/chat_spec.rb'
|
||||
- 'ee/spec/lib/gitlab/llm/concerns/exponential_backoff_spec.rb'
|
||||
- 'ee/spec/lib/gitlab/llm/templates/categorize_question_spec.rb'
|
||||
- 'ee/spec/lib/gitlab/llm/templates/explain_vulnerability_spec.rb'
|
||||
- 'ee/spec/lib/gitlab/llm/templates/generate_commit_message_spec.rb'
|
||||
- 'ee/spec/lib/gitlab/llm/templates/summarize_review_spec.rb'
|
||||
- 'ee/spec/lib/gitlab/llm/vertex_ai/completions/summarize_review_spec.rb'
|
||||
|
|
@ -677,7 +676,6 @@ RSpec/NamedSubject:
|
|||
- 'ee/spec/requests/api/graphql/boards/epic_list_query_spec.rb'
|
||||
- 'ee/spec/requests/api/graphql/ci/minutes/usage_spec.rb'
|
||||
- 'ee/spec/requests/api/graphql/environments/deployments_spec.rb'
|
||||
- 'ee/spec/requests/api/graphql/explain_vulnerability_prompt_spec.rb'
|
||||
- 'ee/spec/requests/api/graphql/member_role/group_member_role_spec.rb'
|
||||
- 'ee/spec/requests/api/graphql/member_role/permissions_list_spec.rb'
|
||||
- 'ee/spec/requests/api/graphql/member_role/project_member_role_spec.rb'
|
||||
|
|
@ -1005,7 +1003,6 @@ RSpec/NamedSubject:
|
|||
- 'ee/spec/services/llm/chat_service_spec.rb'
|
||||
- 'ee/spec/services/llm/execute_method_service_spec.rb'
|
||||
- 'ee/spec/services/llm/explain_code_service_spec.rb'
|
||||
- 'ee/spec/services/llm/explain_vulnerability_service_spec.rb'
|
||||
- 'ee/spec/services/llm/generate_commit_message_service_spec.rb'
|
||||
- 'ee/spec/services/llm/git_command_service_spec.rb'
|
||||
- 'ee/spec/services/llm/resolve_vulnerability_service_spec.rb'
|
||||
|
|
|
|||
|
|
@ -1201,9 +1201,6 @@ Style/InlineDisableAnnotation:
|
|||
- 'ee/app/graphql/types/admin/cloud_licenses/subscription_future_entry_type.rb'
|
||||
- 'ee/app/graphql/types/ai/message_extras_type.rb'
|
||||
- 'ee/app/graphql/types/ai/message_type.rb'
|
||||
- 'ee/app/graphql/types/ai/prompt/ai_prompt_type.rb'
|
||||
- 'ee/app/graphql/types/ai/prompt/explain_vulnerability_prompt_type.rb'
|
||||
- 'ee/app/graphql/types/ai/prompt/explain_vulnerability_prompt_type/presubmission_check_results_type.rb'
|
||||
- 'ee/app/graphql/types/analytics/contribution_analytics/contribution_metadata_type.rb'
|
||||
- 'ee/app/graphql/types/analytics/devops_adoption/enabled_namespace_type.rb'
|
||||
- 'ee/app/graphql/types/analytics/devops_adoption/snapshot_type.rb'
|
||||
|
|
@ -1739,7 +1736,6 @@ Style/InlineDisableAnnotation:
|
|||
- 'ee/lib/gitlab/insights/reducers/count_per_period_reducer.rb'
|
||||
- 'ee/lib/gitlab/llm/chain/tools/summarize_comments/executor.rb'
|
||||
- 'ee/lib/gitlab/llm/chat_storage.rb'
|
||||
- 'ee/lib/gitlab/llm/templates/explain_vulnerability.rb'
|
||||
- 'ee/lib/gitlab/middleware/ip_restrictor.rb'
|
||||
- 'ee/lib/gitlab/path_locks_finder.rb'
|
||||
- 'ee/lib/gitlab/root_excess_size_error_message.rb'
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
9408e90e561b2367e4bd408af8f271ef03710c77
|
||||
bfae77d1f4867c0fc8d7a9b22c68f7f4a7e98d19
|
||||
|
|
|
|||
|
|
@ -88,12 +88,7 @@ export default {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<crud-component
|
||||
class="deploy-freeze-table"
|
||||
:title="$options.i18n.title"
|
||||
icon="deployments"
|
||||
:count="freezePeriods.length"
|
||||
>
|
||||
<crud-component :title="$options.i18n.title" icon="deployments" :count="freezePeriods.length">
|
||||
<template #actions>
|
||||
<gl-button v-gl-modal.deploy-freeze-modal size="small" data-testid="add-deploy-freeze">{{
|
||||
$options.i18n.addDeployFreeze
|
||||
|
|
|
|||
|
|
@ -12,7 +12,9 @@ const translations = {
|
|||
'DeployTokens|Create a new deploy token for all projects in this group. %{link_start}What are deploy tokens?%{link_end}',
|
||||
),
|
||||
addTokenNameLabel: s__('DeployTokens|Name'),
|
||||
addTokenNameDescription: s__('DeployTokens|Enter a unique name for your deploy token.'),
|
||||
addTokenNameDescription: s__(
|
||||
'DeployTokens|Enter a unique name for your deploy token. Name the token gitlab-deploy-token to expose it to CI/CD jobs.',
|
||||
),
|
||||
addTokenScopesLabel: s__('DeployTokens|Scopes (select at least one)'),
|
||||
addTokenUsernameDescription: s__(
|
||||
'DeployTokens|Enter a username for your token. Defaults to %{code_start}gitlab+deploy-token-{n}%{code_end}.',
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ export default {
|
|||
<div class="js-file-title file-title file-title-flex-parent cursor-default">
|
||||
<div class="file-header-content" data-testid="file-name">
|
||||
<file-icon :file-name="file.filePath" :size="16" css-classes="gl-mr-2" />
|
||||
<strong class="file-title-name">{{ file.filePath }}</strong>
|
||||
<strong class="file-title-name gl-break-all">{{ file.filePath }}</strong>
|
||||
<clipboard-button
|
||||
:title="__('Copy file path')"
|
||||
:text="file.filePath"
|
||||
|
|
|
|||
|
|
@ -29,18 +29,11 @@
|
|||
}
|
||||
}
|
||||
|
||||
// doorkeeper-authorize refers to an element rendered by 'doorkeeper' gem, do not remove unless removing doorkeeper gem
|
||||
.doorkeeper-authorize {
|
||||
max-width: px-to-rem(500px);
|
||||
}
|
||||
|
||||
.deploy-freeze-table {
|
||||
@media(max-width: map-get($grid-breakpoints, lg)-1) {
|
||||
.truncated-container {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.settings-section::after {
|
||||
content: '';
|
||||
display: block;
|
||||
|
|
|
|||
|
|
@ -77,12 +77,10 @@ module HasWikiPageMetaAttributes
|
|||
}
|
||||
end
|
||||
|
||||
def container_key
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def container_attrs(container)
|
||||
{ container_key => container.id }
|
||||
return { project_id: container.id } if container.is_a?(Project)
|
||||
|
||||
{ namespace_id: container.id } if container.is_a?(Group)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -150,11 +148,11 @@ module HasWikiPageMetaAttributes
|
|||
end
|
||||
|
||||
def no_two_metarecords_in_same_container_can_have_same_canonical_slug
|
||||
container_id = attributes[self.class.container_key.to_s]
|
||||
container_id = attributes[container_key.to_s]
|
||||
|
||||
return unless container_id.present? && canonical_slug.present?
|
||||
|
||||
offending = self.class.with_canonical_slug(canonical_slug).where(self.class.container_key => container_id)
|
||||
offending = self.class.with_canonical_slug(canonical_slug).where(container_key => container_id)
|
||||
offending = offending.where.not(id: id) if persisted?
|
||||
|
||||
if offending.exists?
|
||||
|
|
|
|||
|
|
@ -7,12 +7,16 @@ class PersonalAccessToken < ApplicationRecord
|
|||
include EachBatch
|
||||
include CreatedAtFilterable
|
||||
include Gitlab::SQL::Pattern
|
||||
include SafelyChangeColumnDefault
|
||||
|
||||
extend ::Gitlab::Utils::Override
|
||||
|
||||
add_authentication_token_field :token,
|
||||
digest: true,
|
||||
format_with_prefix: :prefix_from_application_current_settings
|
||||
|
||||
columns_changing_default :organization_id
|
||||
|
||||
# PATs are 20 characters + optional configurable settings prefix (0..20)
|
||||
TOKEN_LENGTH_RANGE = (20..40)
|
||||
MAX_PERSONAL_ACCESS_TOKEN_LIFETIME_IN_DAYS = 365
|
||||
|
|
|
|||
|
|
@ -6,16 +6,34 @@ class WikiPage
|
|||
|
||||
self.table_name = 'wiki_page_meta'
|
||||
|
||||
belongs_to :project
|
||||
belongs_to :project, optional: true
|
||||
belongs_to :namespace, optional: true
|
||||
|
||||
has_many :slugs, class_name: 'WikiPage::Slug', foreign_key: 'wiki_page_meta_id', inverse_of: :wiki_page_meta
|
||||
|
||||
validates :project_id, presence: true
|
||||
validate :project_or_namespace_present?
|
||||
|
||||
alias_method :resource_parent, :project
|
||||
|
||||
def self.container_key
|
||||
:project_id
|
||||
def container_key
|
||||
namespace.present? ? :namespace_id : :project_id
|
||||
end
|
||||
|
||||
def container
|
||||
project || namespace
|
||||
end
|
||||
|
||||
def container=(value)
|
||||
self.project = value if value.is_a?(Project)
|
||||
self.namespace = value if value.is_a?(Namespace)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def project_or_namespace_present?
|
||||
return unless (project_id.nil? && namespace_id.nil?) || (project_id.present? && namespace_id.present?)
|
||||
|
||||
errors.add(:base, s_('Wiki|WikiPage::Meta should belong to either project or namespace.'))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
.form-group
|
||||
= f.label :name, class: 'label-bold'
|
||||
= f.text_field :name, class: 'form-control gl-form-input', data: { testid: 'deploy-token-name-field' }, required: true
|
||||
.text-secondary= s_('DeployTokens|Enter a unique name for your deploy token.')
|
||||
.text-secondary= s_('DeployTokens|Enter a unique name for your deploy token. Name the token gitlab-deploy-token to expose it to CI/CD jobs.')
|
||||
|
||||
.form-group
|
||||
= f.label :expires_at, _('Expiration date (optional)'), class: 'label-bold'
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
---
|
||||
name: ai_settings_vue_group
|
||||
feature_issue_url: https://gitlab.com/groups/gitlab-org/-/epics/13782
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/issues/470519
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/470584
|
||||
milestone: '17.2'
|
||||
group: group::ai framework
|
||||
type: wip
|
||||
default_enabled: false
|
||||
|
|
@ -1036,7 +1036,7 @@ Gitlab.ee do
|
|||
executor_version = Rails.root.join('DUO_WORKFLOW_EXECUTOR_VERSION').read.chomp
|
||||
Settings.duo_workflow.reverse_merge!(
|
||||
secure: true,
|
||||
executor_binary_url: "https://gitlab.com/api/v4/projects/58711783/packages/generic/#{executor_version}/duo-workflow-executor.tar.gz",
|
||||
executor_binary_url: "https://gitlab.com/api/v4/projects/58711783/packages/generic/duo-workflow-executor/#{executor_version}/duo-workflow-executor.tar.gz",
|
||||
executor_version: executor_version
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -12,3 +12,4 @@ allow_cross_foreign_keys:
|
|||
- gitlab_main_clusterwide
|
||||
sharding_key:
|
||||
project_id: projects
|
||||
namespace_id: namespaces
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ Gitlab::Seeder.quiet do
|
|||
user.personal_access_tokens.build(params).tap do |pat|
|
||||
pat.expires_at = 365.days.from_now
|
||||
pat.set_token(token)
|
||||
pat.organization = Organizations::Organization.default_organization
|
||||
pat.save!
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddNamespaceIdToWikiPageMeta < Gitlab::Database::Migration[2.2]
|
||||
milestone '17.4'
|
||||
|
||||
def change
|
||||
add_column :wiki_page_meta, :namespace_id, :bigint
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddNamespacesForeignKeyToWikiPageMeta < Gitlab::Database::Migration[2.2]
|
||||
milestone '17.4'
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
add_concurrent_foreign_key :wiki_page_meta, :namespaces, column: :namespace_id, on_delete: :cascade
|
||||
end
|
||||
|
||||
def down
|
||||
with_lock_retries do
|
||||
remove_foreign_key :wiki_page_meta, column: :namespace_id
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class RemoveNotNullConstraintOnProjectIdWikiPageMeta < Gitlab::Database::Migration[2.2]
|
||||
milestone '17.4'
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
change_column_null :wiki_page_meta, :project_id, true
|
||||
end
|
||||
|
||||
def down
|
||||
change_column_null :wiki_page_meta, :project_id, false
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddIndexToWikiPageMetaNamespaceId < Gitlab::Database::Migration[2.2]
|
||||
milestone '17.4'
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
INDEX_NAME = 'index_wiki_page_meta_on_namespace_id'
|
||||
|
||||
def up
|
||||
add_concurrent_index :wiki_page_meta, :namespace_id, name: INDEX_NAME
|
||||
end
|
||||
|
||||
def down
|
||||
remove_concurrent_index_by_name :wiki_page_meta, name: INDEX_NAME
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddWikiPageMetaMultiColumnNullConstraint < Gitlab::Database::Migration[2.2]
|
||||
milestone '17.4'
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
add_multi_column_not_null_constraint(:wiki_page_meta, :namespace_id, :project_id)
|
||||
end
|
||||
|
||||
def down
|
||||
remove_multi_column_not_null_constraint(:wiki_page_meta, :namespace_id, :project_id)
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
# 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 RemoveColumnDefaultPatOrganization < Gitlab::Database::Migration[2.2]
|
||||
disable_ddl_transaction!
|
||||
milestone '17.4'
|
||||
|
||||
def change
|
||||
change_column_default(:personal_access_tokens, :organization_id, from: 1, to: nil)
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1 @@
|
|||
0d9489da0ea1102509944b51f3343ae833d2e9042925c8916cda1aa732b1a753
|
||||
|
|
@ -0,0 +1 @@
|
|||
03b6b24d96304a6d19001833d18300f594ed534d8974ad70569ba9137dcad46b
|
||||
|
|
@ -0,0 +1 @@
|
|||
a8f88a7a359302759febf4c97143651f95fe57496b9b7cec260f7ae94f6c6c72
|
||||
|
|
@ -0,0 +1 @@
|
|||
ea7af6c33f8606739dd25f2390dfcdc318805ef7414ff21e6740a4881eb90a25
|
||||
|
|
@ -0,0 +1 @@
|
|||
6967aef6e31b678f80aeb531e47fc58f176193a5412b518fee8d2bba9260121c
|
||||
|
|
@ -0,0 +1 @@
|
|||
328856f89bfd023c0469a13528f0b799479173ee471776969e42404a5e35f680
|
||||
|
|
@ -15461,7 +15461,7 @@ CREATE TABLE personal_access_tokens (
|
|||
after_expiry_notification_delivered boolean DEFAULT false NOT NULL,
|
||||
previous_personal_access_token_id bigint,
|
||||
advanced_scopes text,
|
||||
organization_id bigint DEFAULT 1 NOT NULL,
|
||||
organization_id bigint NOT NULL,
|
||||
seven_days_notification_sent_at timestamp with time zone,
|
||||
thirty_days_notification_sent_at timestamp with time zone,
|
||||
sixty_days_notification_sent_at timestamp with time zone,
|
||||
|
|
@ -20292,10 +20292,12 @@ ALTER SEQUENCE webauthn_registrations_id_seq OWNED BY webauthn_registrations.id;
|
|||
|
||||
CREATE TABLE wiki_page_meta (
|
||||
id bigint NOT NULL,
|
||||
project_id bigint NOT NULL,
|
||||
project_id bigint,
|
||||
created_at timestamp with time zone NOT NULL,
|
||||
updated_at timestamp with time zone NOT NULL,
|
||||
title character varying(255) NOT NULL
|
||||
title character varying(255) NOT NULL,
|
||||
namespace_id bigint,
|
||||
CONSTRAINT check_d858755109 CHECK ((num_nonnulls(namespace_id, project_id) = 1))
|
||||
);
|
||||
|
||||
CREATE SEQUENCE wiki_page_meta_id_seq
|
||||
|
|
@ -30786,6 +30788,8 @@ 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 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_slugs_on_project_id ON wiki_page_slugs USING btree (project_id);
|
||||
|
|
@ -34243,6 +34247,9 @@ ALTER TABLE ONLY issue_assignment_events
|
|||
ALTER TABLE ONLY custom_emoji
|
||||
ADD CONSTRAINT fk_custom_emoji_creator_id FOREIGN KEY (creator_id) REFERENCES users(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY wiki_page_meta
|
||||
ADD CONSTRAINT fk_d054484f21 FOREIGN KEY (namespace_id) REFERENCES namespaces(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY bulk_import_entities
|
||||
ADD CONSTRAINT fk_d06d023c30 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
|
||||
|
||||
|
|
|
|||
|
|
@ -465,22 +465,6 @@ Returns [`EpicList`](#epiclist).
|
|||
| <a id="queryepicboardlistepicfilters"></a>`epicFilters` | [`EpicFilters`](#epicfilters) | Filters applied when getting epic metadata in the epic board list. |
|
||||
| <a id="queryepicboardlistid"></a>`id` | [`BoardsEpicListID!`](#boardsepiclistid) | Global ID of the list. |
|
||||
|
||||
### `Query.explainVulnerabilityPrompt`
|
||||
|
||||
GitLab Duo Vulnerability Explanation prompt for a specified vulnerability.
|
||||
|
||||
DETAILS:
|
||||
**Introduced** in GitLab 16.2.
|
||||
**Status**: Experiment.
|
||||
|
||||
Returns [`ExplainVulnerabilityPrompt`](#explainvulnerabilityprompt).
|
||||
|
||||
#### Arguments
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="queryexplainvulnerabilitypromptvulnerabilityid"></a>`vulnerabilityId` | [`VulnerabilityID!`](#vulnerabilityid) | Vulnerability to generate a prompt for. |
|
||||
|
||||
### `Query.featureFlagEnabled`
|
||||
|
||||
Check if a feature flag is enabled.
|
||||
|
|
@ -15355,6 +15339,29 @@ The edge type for [`ProjectSavedReply`](#projectsavedreply).
|
|||
| <a id="projectsavedreplyedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
|
||||
| <a id="projectsavedreplyedgenode"></a>`node` | [`ProjectSavedReply`](#projectsavedreply) | The item at the end of the edge. |
|
||||
|
||||
#### `ProjectSecurityExclusionConnection`
|
||||
|
||||
The connection type for [`ProjectSecurityExclusion`](#projectsecurityexclusion).
|
||||
|
||||
##### Fields
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="projectsecurityexclusionconnectionedges"></a>`edges` | [`[ProjectSecurityExclusionEdge]`](#projectsecurityexclusionedge) | A list of edges. |
|
||||
| <a id="projectsecurityexclusionconnectionnodes"></a>`nodes` | [`[ProjectSecurityExclusion]`](#projectsecurityexclusion) | A list of nodes. |
|
||||
| <a id="projectsecurityexclusionconnectionpageinfo"></a>`pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. |
|
||||
|
||||
#### `ProjectSecurityExclusionEdge`
|
||||
|
||||
The edge type for [`ProjectSecurityExclusion`](#projectsecurityexclusion).
|
||||
|
||||
##### Fields
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="projectsecurityexclusionedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
|
||||
| <a id="projectsecurityexclusionedgenode"></a>`node` | [`ProjectSecurityExclusion`](#projectsecurityexclusion) | The item at the end of the edge. |
|
||||
|
||||
#### `ProjectWikiRepositoryRegistryConnection`
|
||||
|
||||
The connection type for [`ProjectWikiRepositoryRegistry`](#projectwikirepositoryregistry).
|
||||
|
|
@ -22373,25 +22380,6 @@ Representing an event.
|
|||
| <a id="eventid"></a>`id` | [`ID!`](#id) | ID of the event. |
|
||||
| <a id="eventupdatedat"></a>`updatedAt` | [`Time!`](#time) | When this event was updated. |
|
||||
|
||||
### `ExplainVulnerabilityPresubmissionCheckResults`
|
||||
|
||||
#### Fields
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="explainvulnerabilitypresubmissioncheckresultspotentialsecretsincode"></a>`potentialSecretsInCode` | [`Boolean!`](#boolean) | This flag is true if we think there might be a secret in the code that would be sent in the LLM prompt. |
|
||||
| <a id="explainvulnerabilitypresubmissioncheckresultssecretdetectionresult"></a>`secretDetectionResult` | [`Boolean!`](#boolean) | This flag is true if the vulnerability being explained is specifically a secret detection vulnerability. |
|
||||
|
||||
### `ExplainVulnerabilityPrompt`
|
||||
|
||||
#### Fields
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="explainvulnerabilitypromptpresubmissionchecks"></a>`presubmissionChecks` | [`ExplainVulnerabilityPresubmissionCheckResults!`](#explainvulnerabilitypresubmissioncheckresults) | An object containing booleans. Each booolean indicates the result of a presubmission check: `true` for passed, and `false` for failed. |
|
||||
| <a id="explainvulnerabilitypromptpromptwithcode"></a>`promptWithCode` | [`String`](#string) | AI text prompt generated using the vulnerability's information, including the vulnerable code. |
|
||||
| <a id="explainvulnerabilitypromptpromptwithoutcode"></a>`promptWithoutCode` | [`String`](#string) | AI text prompt generated using the vulnerability's information, excluding the vulnerable code. |
|
||||
|
||||
### `ExternalAuditEventDestination`
|
||||
|
||||
Represents an external resource to send audit events to.
|
||||
|
|
@ -30931,6 +30919,28 @@ four standard [pagination arguments](#pagination-arguments):
|
|||
| <a id="projectscanresultpoliciesincludeunscoped"></a>`includeUnscoped` **{warning-solid}** | [`Boolean`](#boolean) | **Introduced** in GitLab 17.3. **Status**: Experiment. Filter policies that are scoped to the project. |
|
||||
| <a id="projectscanresultpoliciesrelationship"></a>`relationship` | [`SecurityPolicyRelationType`](#securitypolicyrelationtype) | Filter policies by the given policy relationship. |
|
||||
|
||||
##### `Project.securityExclusions`
|
||||
|
||||
Security exclusions of the project.
|
||||
|
||||
DETAILS:
|
||||
**Introduced** in GitLab 17.4.
|
||||
**Status**: Experiment.
|
||||
|
||||
Returns [`ProjectSecurityExclusionConnection`](#projectsecurityexclusionconnection).
|
||||
|
||||
This field returns a [connection](#connections). It accepts the
|
||||
four standard [pagination arguments](#pagination-arguments):
|
||||
`before: String`, `after: String`, `first: Int`, and `last: Int`.
|
||||
|
||||
###### Arguments
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="projectsecurityexclusionsactive"></a>`active` | [`Boolean`](#boolean) | Filter entries by active status. |
|
||||
| <a id="projectsecurityexclusionsscanner"></a>`scanner` | [`ExclusionScannerEnum`](#exclusionscannerenum) | Filter entries by scanner. |
|
||||
| <a id="projectsecurityexclusionstype"></a>`type` | [`ExclusionTypeEnum`](#exclusiontypeenum) | Filter entries by exclusion type. |
|
||||
|
||||
##### `Project.securityPolicyProjectSuggestions`
|
||||
|
||||
Security policy project suggestions.
|
||||
|
|
@ -31448,6 +31458,21 @@ Representation of a project secrets manager.
|
|||
| <a id="projectsecretsmanagerproject"></a>`project` | [`Project!`](#project) | Project the secrets manager belong to. |
|
||||
| <a id="projectsecretsmanagerstatus"></a>`status` | [`ProjectSecretsManagerStatus`](#projectsecretsmanagerstatus) | Status of the project secrets manager. |
|
||||
|
||||
### `ProjectSecurityExclusion`
|
||||
|
||||
Represents a project-level security scanner exclusion.
|
||||
|
||||
#### Fields
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="projectsecurityexclusionactive"></a>`active` | [`Boolean!`](#boolean) | Whether the exclusion is active. |
|
||||
| <a id="projectsecurityexclusiondescription"></a>`description` | [`String`](#string) | Optional description for the exclusion. |
|
||||
| <a id="projectsecurityexclusionid"></a>`id` | [`ID!`](#id) | ID of the exclusion. |
|
||||
| <a id="projectsecurityexclusionscanner"></a>`scanner` | [`ExclusionScannerEnum!`](#exclusionscannerenum) | Security scanner the exclusion will be used for. |
|
||||
| <a id="projectsecurityexclusiontype"></a>`type` | [`ExclusionTypeEnum!`](#exclusiontypeenum) | Type of the exclusion. |
|
||||
| <a id="projectsecurityexclusionvalue"></a>`value` | [`String!`](#string) | Value of the exclusion. |
|
||||
|
||||
### `ProjectSecurityPolicySource`
|
||||
|
||||
Represents the source of a security policy belonging to a project.
|
||||
|
|
@ -36683,6 +36708,25 @@ Event action.
|
|||
| <a id="eventactionreopened"></a>`REOPENED` | Reopened action. |
|
||||
| <a id="eventactionupdated"></a>`UPDATED` | Updated action. |
|
||||
|
||||
### `ExclusionScannerEnum`
|
||||
|
||||
Enum for the security scanners used with exclusions.
|
||||
|
||||
| Value | Description |
|
||||
| ----- | ----------- |
|
||||
| <a id="exclusionscannerenumsecret_push_protection"></a>`SECRET_PUSH_PROTECTION` | Secret Push Protection. |
|
||||
|
||||
### `ExclusionTypeEnum`
|
||||
|
||||
Enum for types of exclusion for a security scanner.
|
||||
|
||||
| Value | Description |
|
||||
| ----- | ----------- |
|
||||
| <a id="exclusiontypeenumpath"></a>`PATH` | File or directory location. |
|
||||
| <a id="exclusiontypeenumraw_value"></a>`RAW_VALUE` | Raw value to ignore. |
|
||||
| <a id="exclusiontypeenumregex_pattern"></a>`REGEX_PATTERN` | Regex pattern matching rules. |
|
||||
| <a id="exclusiontypeenumrule"></a>`RULE` | Scanner rule identifier. |
|
||||
|
||||
### `ExtensionsMarketplaceOptInStatus`
|
||||
|
||||
Values for status of the Web IDE Extension Marketplace opt-in for the user.
|
||||
|
|
|
|||
|
|
@ -719,7 +719,8 @@ which variables take precedence.
|
|||
|
||||
The order of precedence for variables is (from highest to lowest):
|
||||
|
||||
1. [Scan Execution Policies variables](../../user/application_security/policies/scan_execution_policies.md).
|
||||
1. [Pipeline execution policy variables](../../user/application_security/policies/pipeline_execution_policies.md#cicd-variables).
|
||||
1. [Scan execution policy variables](../../user/application_security/policies/scan_execution_policies.md).
|
||||
1. Pipeline variables. These variables all have the same precedence:
|
||||
- [Variables passed to downstream pipelines](../pipelines/downstream_pipelines.md#pass-cicd-variables-to-a-downstream-pipeline).
|
||||
- [Trigger variables](../triggers/index.md#pass-cicd-variables-in-the-api-call).
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ DETAILS:
|
|||
**Tier:** Free, Premium, Ultimate
|
||||
**Offering:** GitLab.com, Self-managed, GitLab Dedicated
|
||||
|
||||
GLab is an open source GitLab CLI tool. It brings GitLab to your terminal:
|
||||
`glab` is an open source GitLab CLI tool. It brings GitLab to your terminal:
|
||||
next to where you are already working with Git and your code, without
|
||||
switching between windows and browser tabs.
|
||||
|
||||
|
|
@ -78,11 +78,11 @@ DETAILS:
|
|||
|
||||
The GitLab CLI includes features powered by [GitLab Duo](../../user/ai_features.md). These include:
|
||||
|
||||
- [`glab duo ask`](https://gitlab.com/gitlab-org/cli/-/tree/main/docs/source/duo/ask)
|
||||
- [`glab duo ask`](https://gitlab.com/gitlab-org/cli/-/blob/main/docs/source/duo/ask.md)
|
||||
|
||||
To ask questions about `git` commands while you work, type:
|
||||
|
||||
- [`glab duo ask`](https://gitlab.com/gitlab-org/cli/-/tree/main/docs/source/duo/ask)
|
||||
- [`glab duo ask`](https://gitlab.com/gitlab-org/cli/-/blob/main/docs/source/duo/ask.md)
|
||||
|
||||
The `glab duo ask` command can help you remember a `git` command you forgot,
|
||||
or provide suggestions on how to run `git` commands to perform other tasks.
|
||||
|
|
|
|||
|
|
@ -18615,7 +18615,7 @@ msgstr ""
|
|||
msgid "DeployTokens|Deploy tokens allow access to packages, your repository, and registry images."
|
||||
msgstr ""
|
||||
|
||||
msgid "DeployTokens|Enter a unique name for your deploy token."
|
||||
msgid "DeployTokens|Enter a unique name for your deploy token. Name the token gitlab-deploy-token to expose it to CI/CD jobs."
|
||||
msgstr ""
|
||||
|
||||
msgid "DeployTokens|Enter a username for your token. Defaults to %{code_start}gitlab+deploy-token-{n}%{code_end}."
|
||||
|
|
@ -61458,6 +61458,9 @@ msgstr ""
|
|||
msgid "Wiki|Wiki templates"
|
||||
msgstr ""
|
||||
|
||||
msgid "Wiki|WikiPage::Meta should belong to either project or namespace."
|
||||
msgstr ""
|
||||
|
||||
msgid "Will be created"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@
|
|||
"@gitlab/query-language": "^0.0.5-a-20240903",
|
||||
"@gitlab/svgs": "3.112.0",
|
||||
"@gitlab/ui": "91.1.2",
|
||||
"@gitlab/web-ide": "^0.0.1-dev-20240909013227",
|
||||
"@gitlab/web-ide": "^0.0.1-dev-20240816130114",
|
||||
"@mattiasbuelens/web-streams-adapter": "^0.1.0",
|
||||
"@rails/actioncable": "7.0.8-4",
|
||||
"@rails/ujs": "7.0.8-4",
|
||||
|
|
|
|||
|
|
@ -2950,5 +2950,5 @@ index 6a16dd1..99b1df4 100644
|
|||
- const parentOrigin = searchParams.get('parentOrigin') || window.origin;
|
||||
+ const parentOrigin = window.origin;
|
||||
const salt = searchParams.get('salt');
|
||||
|
||||
|
||||
(async function () {
|
||||
|
|
@ -32,6 +32,8 @@ The main feature `cng` is to programmatically manage different deployment type c
|
|||
1. Define a cleanup class based on the [`Base`](lib/gitlab/cng/lib/deployment/configurations/cleanup/_base.rb) cleanup class. Implement a single method
|
||||
that deletes all objects created by `pre-deployment` and `post-deployment` setup.
|
||||
|
||||
All different options for `GitLab` deployment on `Kubernetes` cluster are described in [GitLab Helm chart](https://docs.gitlab.com/charts/) documentation page.
|
||||
|
||||
## Tips
|
||||
|
||||
### kubectl context
|
||||
|
|
@ -50,3 +52,7 @@ function cng() {
|
|||
(cd $PATH_TO_GITLAB_REPO/gems/gitlab-cng && BUNDLE_AUTO_INSTALL=true bundle exec cng "$@")
|
||||
}
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
Because `cng` tool essentially wraps `helm upgrade --install` command, official [Troubleshooting the GitLab chart](https://docs.gitlab.com/charts/troubleshooting/index.html) guide can be used for troubleshooting deployment failures.
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ module Gitlab
|
|||
extend Helpers::Shell
|
||||
|
||||
LICENSE_SECRET = "gitlab-license"
|
||||
TROUBLESHOOTING_LINK = "https://gitlab.com/gitlab-org/gitlab/-/tree/master/qa/gems/gitlab-cng?ref_type=heads#troubleshooting"
|
||||
|
||||
# Delete installation
|
||||
#
|
||||
|
|
@ -185,15 +186,7 @@ module Gitlab
|
|||
Helpers::Spinner.spin("running helm deployment") do
|
||||
helm.upgrade(name, chart_reference, namespace: namespace, timeout: timeout, values: values, args: args)
|
||||
rescue Helm::Client::Error => e
|
||||
log("Helm upgrade failed", :error)
|
||||
events = get_warning_events
|
||||
|
||||
if events
|
||||
log("Following events of Warning type present in cluster:", :warn)
|
||||
log(events)
|
||||
end
|
||||
|
||||
raise e
|
||||
handle_deploy_failure(e)
|
||||
end
|
||||
log("Deployment successful and app is available via: #{configuration.gitlab_url}", :success, bright: true)
|
||||
end
|
||||
|
|
@ -228,6 +221,23 @@ module Gitlab
|
|||
puts mask_secrets(kubeclient.create_resource(secret), [license, Base64.encode64(license)])
|
||||
end
|
||||
|
||||
# Handle helm upgrade failure
|
||||
#
|
||||
# @param [StandardError] error
|
||||
# @return [void]
|
||||
def handle_deploy_failure(error)
|
||||
log("Helm upgrade failed!", :error)
|
||||
log("For more information on troubleshooting failures, see: '#{TROUBLESHOOTING_LINK}'", :warn)
|
||||
|
||||
events = get_warning_events
|
||||
if events
|
||||
log("Following events of Warning type present in cluster:", :warn)
|
||||
log(events)
|
||||
end
|
||||
|
||||
raise error
|
||||
end
|
||||
|
||||
# Get cluster events with warning type
|
||||
#
|
||||
# @return [String]
|
||||
|
|
|
|||
|
|
@ -102,9 +102,9 @@ RSpec.describe Gitlab::Cng::Deployment::Installation, :aggregate_failures do
|
|||
|
||||
it "automatically prints warning events" do
|
||||
expect { expect { installation.create }.to raise_error(SystemExit) }.to output(
|
||||
match("#{warn_event[:involvedObject][:kind]}/#{warn_event[:involvedObject][:name]}").and(
|
||||
match(warn_event[:message])
|
||||
)
|
||||
match("#{warn_event[:involvedObject][:kind]}/#{warn_event[:involvedObject][:name]}")
|
||||
.and(match(warn_event[:message]))
|
||||
.and(match(/For more information on troubleshooting failures, see: \S+/))
|
||||
).to_stdout
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ FactoryBot.define do
|
|||
|
||||
factory :wiki_page_meta, class: 'WikiPage::Meta' do
|
||||
title { generate(:wiki_page_title) }
|
||||
project { association(:project) }
|
||||
project { association(:project) unless namespace.present? || container.present? }
|
||||
|
||||
trait :for_wiki_page do
|
||||
transient do
|
||||
|
|
|
|||
|
|
@ -25,24 +25,6 @@ describe('asset patching in @gitlab/web-ide', () => {
|
|||
});
|
||||
const htmlChildren = allChildren.filter((x) => x.endsWith('.html'));
|
||||
|
||||
/**
|
||||
* ## What in the world is this test doing!?
|
||||
*
|
||||
* This test was introduced when we were fixing a [security vulnerability][1] related to GitLab self-hosting
|
||||
* problematic `.html` files. These files could be exploited through an `iframe` on an `evil.com` and will
|
||||
* assume the user's cookie authentication. Boom!
|
||||
*
|
||||
* ## How do I know if an `.html` file is vulnerable?
|
||||
*
|
||||
* - The `.html` file used the `postMessage` API and allowed any `origin` which enabled any external site to
|
||||
* open it in an `iframe` and communicate to it.
|
||||
* - The `iframe` exposed some internal VSCode message bus that could allow arbitrary requests. So watch out for
|
||||
* `fetch`.
|
||||
*
|
||||
* [1]: https://gitlab.com/gitlab-org/security/gitlab-web-ide-vscode-fork/-/issues/1#note_1905417620
|
||||
*
|
||||
* ========== If expectation fails and you can't see the full comment... LOOK UP! ==============
|
||||
*/
|
||||
expect(htmlChildren).toEqual([
|
||||
// This is the only HTML file we expect and it's protected by the other test.
|
||||
'out/vs/workbench/services/extensions/worker/webWorkerExtensionHostIframe.html',
|
||||
|
|
@ -51,9 +33,6 @@ describe('asset patching in @gitlab/web-ide', () => {
|
|||
'extensions/microsoft-authentication/media/index.html',
|
||||
'extensions/gitlab-vscode-extension/webviews/security_finding/index.html',
|
||||
'extensions/gitlab-vscode-extension/webviews/gitlab_duo_chat/index.html',
|
||||
'extensions/gitlab-vscode-extension/assets/language-server/webviews/duo-workflow/index.html',
|
||||
'extensions/gitlab-vscode-extension/assets/language-server/webviews/duo-chat/index.html',
|
||||
'extensions/gitlab-vscode-extension/assets/language-server/webviews/chat/index.html',
|
||||
'extensions/github-authentication/media/index.html',
|
||||
]);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -34,47 +34,7 @@ RSpec.describe 'cross-database foreign keys' do
|
|||
'user_group_callouts.user_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/421287
|
||||
'subscription_user_add_on_assignments.user_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/444666
|
||||
'subscription_add_on_purchases.subscription_add_on_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/444666
|
||||
|
||||
'sbom_occurrences_vulnerabilities.sbom_occurrence_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/479142
|
||||
'sbom_occurrences_vulnerabilities.vulnerability_id',
|
||||
|
||||
'security_findings.scanner_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/479141
|
||||
'security_findings.scan_id',
|
||||
|
||||
'vulnerabilities.finding_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/479142
|
||||
'vulnerability_export_parts.vulnerability_export_id',
|
||||
'vulnerability_external_issue_links.vulnerability_id',
|
||||
'vulnerability_finding_evidences.vulnerability_occurrence_id',
|
||||
'vulnerability_finding_links.vulnerability_occurrence_id',
|
||||
'vulnerability_finding_signatures.finding_id',
|
||||
'vulnerability_findings_remediations.vulnerability_occurrence_id',
|
||||
'vulnerability_findings_remediations.vulnerability_remediation_id',
|
||||
'vulnerability_flags.vulnerability_occurrence_id',
|
||||
'vulnerability_issue_links.vulnerability_id',
|
||||
'vulnerability_merge_request_links.vulnerability_id',
|
||||
'vulnerability_occurrence_identifiers.occurrence_id',
|
||||
'vulnerability_occurrence_identifiers.identifier_id',
|
||||
'vulnerability_occurrence_pipelines.occurrence_id',
|
||||
'vulnerability_occurrences.scanner_id',
|
||||
'vulnerability_occurrences.primary_identifier_id',
|
||||
'vulnerability_occurrences.vulnerability_id',
|
||||
'vulnerability_reads.vulnerability_id',
|
||||
'vulnerability_reads.scanner_id',
|
||||
'vulnerability_state_transitions.vulnerability_id',
|
||||
'vulnerability_user_mentions.vulnerability_id',
|
||||
|
||||
'dast_profile_schedules.dast_profile_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/479141
|
||||
'dast_profiles.dast_scanner_profile_id',
|
||||
'dast_profiles.dast_site_profile_id',
|
||||
'dast_profiles_pipelines.dast_profile_id',
|
||||
'dast_scanner_profiles_builds.dast_scanner_profile_id',
|
||||
'dast_site_profile_secret_variables.dast_site_profile_id',
|
||||
'dast_site_profiles.dast_site_id',
|
||||
'dast_site_profiles_builds.dast_site_profile_id',
|
||||
'dast_site_validations.dast_site_token_id',
|
||||
'dast_sites.dast_site_validation_id',
|
||||
'dast_site_profile_secret_variables.dast_site_profile_id',
|
||||
'dast_pre_scan_verifications.dast_profile_id'
|
||||
'dast_pre_scan_verifications.dast_profile_id' # https://gitlab.com/gitlab-org/gitlab/-/issues/479141
|
||||
]
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe WikiPage::Meta do
|
||||
RSpec.describe WikiPage::Meta, feature_category: :wiki do
|
||||
let_it_be(:project) { create(:project, :wiki_repo) }
|
||||
let_it_be(:other_project) { create(:project) }
|
||||
|
||||
|
|
@ -24,7 +24,6 @@ RSpec.describe WikiPage::Meta do
|
|||
described_class.new(title: 'some title', project: project)
|
||||
end
|
||||
|
||||
it { is_expected.to validate_presence_of(:project_id) }
|
||||
it { is_expected.to validate_length_of(:title).is_at_most(255) }
|
||||
it { is_expected.not_to allow_value(nil).for(:title) }
|
||||
|
||||
|
|
@ -36,6 +35,12 @@ RSpec.describe WikiPage::Meta do
|
|||
|
||||
expect(in_violation).not_to be_valid
|
||||
end
|
||||
|
||||
it 'is forbidden to have both project_id and namespace_id empty' do
|
||||
in_violation = build(:wiki_page_meta, namespace: nil, project: nil)
|
||||
|
||||
expect(in_violation).not_to be_valid
|
||||
end
|
||||
end
|
||||
|
||||
describe '#canonical_slug' do
|
||||
|
|
@ -162,32 +167,6 @@ RSpec.describe WikiPage::Meta do
|
|||
let(:title) { wiki_page.title }
|
||||
let(:wiki_page) { create(:wiki_page, project: project) }
|
||||
|
||||
def find_record
|
||||
described_class.find_or_create(last_known_slug, wiki_page)
|
||||
end
|
||||
|
||||
def create_previous_version(title: old_title, slug: last_known_slug, date: wiki_page.version.commit.committed_date)
|
||||
create(
|
||||
:wiki_page_meta,
|
||||
title: title, project: project,
|
||||
created_at: date, updated_at: date,
|
||||
canonical_slug: slug
|
||||
)
|
||||
end
|
||||
|
||||
def create_context
|
||||
# Ensure that we behave nicely with respect to other projects
|
||||
# We have:
|
||||
# - page in other project with same canonical_slug
|
||||
create(:wiki_page_meta, project: other_project, canonical_slug: wiki_page.slug)
|
||||
|
||||
# - page in same project with different canonical_slug, but with
|
||||
# an old slug that = canonical_slug
|
||||
different_slug = generate(:sluggified_title)
|
||||
create(:wiki_page_meta, project: project, canonical_slug: different_slug)
|
||||
.slugs.create!(slug: wiki_page.slug)
|
||||
end
|
||||
|
||||
shared_examples 'metadata examples' do
|
||||
it 'establishes the correct state', :aggregate_failures do
|
||||
create_context
|
||||
|
|
@ -198,7 +177,7 @@ RSpec.describe WikiPage::Meta do
|
|||
valid?: true,
|
||||
canonical_slug: wiki_page.slug,
|
||||
title: wiki_page.title,
|
||||
project: wiki_page.wiki.project
|
||||
container: wiki_page.wiki.container
|
||||
)
|
||||
expect(meta.updated_at).to eq(wiki_page.version.commit.committed_date)
|
||||
expect(meta.created_at).not_to be_after(meta.updated_at)
|
||||
|
|
@ -207,39 +186,15 @@ RSpec.describe WikiPage::Meta do
|
|||
end
|
||||
|
||||
it 'makes a reasonable number of DB queries' do
|
||||
expect(project).to eq(wiki_page.wiki.project)
|
||||
expect(container).to eq(wiki_page.wiki.container)
|
||||
|
||||
expect { find_record }.not_to exceed_query_limit(query_limit)
|
||||
end
|
||||
end
|
||||
|
||||
context 'there are problems' do
|
||||
context 'the slug is too long' do
|
||||
let(:last_known_slug) { FFaker::Lorem.characters(2050) }
|
||||
|
||||
it 'raises an error' do
|
||||
expect { find_record }.to raise_error ActiveRecord::ValueTooLong
|
||||
end
|
||||
end
|
||||
|
||||
context 'a conflicting record exists' do
|
||||
before do
|
||||
create(:wiki_page_meta, project: project, canonical_slug: last_known_slug)
|
||||
create(:wiki_page_meta, project: project, canonical_slug: current_slug)
|
||||
end
|
||||
|
||||
it 'raises an error' do
|
||||
expect { find_record }.to raise_error(ActiveRecord::RecordInvalid)
|
||||
end
|
||||
end
|
||||
|
||||
context 'the wiki page is not valid' do
|
||||
let(:wiki_page) { build(:wiki_page, project: project, title: nil) }
|
||||
|
||||
it 'raises an error' do
|
||||
expect { find_record }.to raise_error(described_class::WikiPageInvalid)
|
||||
end
|
||||
end
|
||||
include_examples 'creating wiki page meta record examples' do
|
||||
let(:container) { project }
|
||||
let(:other_container) { other_project }
|
||||
end
|
||||
|
||||
context 'no existing record exists' do
|
||||
|
|
@ -269,6 +224,7 @@ RSpec.describe WikiPage::Meta do
|
|||
#
|
||||
# RELEASE SAVEPOINT active_record_2
|
||||
let(:query_limit) { 5 }
|
||||
let(:container) { project }
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -280,6 +236,7 @@ RSpec.describe WikiPage::Meta do
|
|||
include_examples 'metadata examples' do
|
||||
# Identical to the base case.
|
||||
let(:query_limit) { 5 }
|
||||
let(:container) { project }
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -289,6 +246,7 @@ RSpec.describe WikiPage::Meta do
|
|||
include_examples 'metadata examples' do
|
||||
# Identical to the base case.
|
||||
let(:query_limit) { 5 }
|
||||
let(:container) { project }
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -314,6 +272,7 @@ RSpec.describe WikiPage::Meta do
|
|||
#
|
||||
# RELEASE SAVEPOINT active_record_2
|
||||
let(:query_limit) { 3 }
|
||||
let(:container) { project }
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -341,6 +300,7 @@ RSpec.describe WikiPage::Meta do
|
|||
#
|
||||
# RELEASE SAVEPOINT active_record_2
|
||||
let(:query_limit) { 4 }
|
||||
let(:container) { project }
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -380,6 +340,7 @@ RSpec.describe WikiPage::Meta do
|
|||
#
|
||||
# RELEASE SAVEPOINT active_record_2
|
||||
let(:query_limit) { 7 }
|
||||
let(:container) { project }
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -407,6 +368,7 @@ RSpec.describe WikiPage::Meta do
|
|||
#
|
||||
# RELEASE SAVEPOINT active_record_2
|
||||
let(:query_limit) { 4 }
|
||||
let(:container) { project }
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -424,6 +386,7 @@ RSpec.describe WikiPage::Meta do
|
|||
|
||||
include_examples 'metadata examples' do
|
||||
let(:query_limit) { 7 }
|
||||
let(:container) { project }
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -437,6 +400,7 @@ RSpec.describe WikiPage::Meta do
|
|||
|
||||
include_examples 'metadata examples' do
|
||||
let(:query_limit) { 7 }
|
||||
let(:container) { project }
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -476,6 +440,7 @@ RSpec.describe WikiPage::Meta do
|
|||
#
|
||||
# RELEASE SAVEPOINT active_record_2
|
||||
let(:query_limit) { 8 }
|
||||
let(:container) { project }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,66 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.shared_examples 'creating wiki page meta record examples' do
|
||||
let(:old_title) { generate(:wiki_page_title) }
|
||||
let(:last_known_slug) { generate(:sluggified_title) }
|
||||
let(:current_slug) { wiki_page.slug }
|
||||
let(:title) { wiki_page.title }
|
||||
let(:wiki_page) { create(:wiki_page, container: container) }
|
||||
|
||||
def find_record
|
||||
described_class.find_or_create(last_known_slug, wiki_page)
|
||||
end
|
||||
|
||||
def create_previous_version(
|
||||
title: old_title, slug: last_known_slug,
|
||||
date: wiki_page.version.commit.committed_date)
|
||||
create(
|
||||
:wiki_page_meta,
|
||||
title: title, container: container,
|
||||
created_at: date, updated_at: date,
|
||||
canonical_slug: slug
|
||||
)
|
||||
end
|
||||
|
||||
def create_context
|
||||
# Ensure that we behave nicely with respect to other containers
|
||||
# We have:
|
||||
# - page in other container with same canonical_slug
|
||||
create(:wiki_page_meta, container: other_container, canonical_slug: wiki_page.slug)
|
||||
|
||||
# - page in same container with different canonical_slug, but with
|
||||
# an old slug that = canonical_slug
|
||||
different_slug = generate(:sluggified_title)
|
||||
create(:wiki_page_meta, container: container, canonical_slug: different_slug)
|
||||
.slugs.create!(slug: wiki_page.slug)
|
||||
end
|
||||
|
||||
describe 'when there are problems' do
|
||||
context 'when the slug is too long' do
|
||||
let(:last_known_slug) { FFaker::Lorem.characters(2050) }
|
||||
|
||||
it 'raises an error' do
|
||||
expect { find_record }.to raise_error ActiveRecord::ValueTooLong
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a conflicting record exists' do
|
||||
before do
|
||||
create(:wiki_page_meta, container: container, canonical_slug: last_known_slug)
|
||||
create(:wiki_page_meta, container: container, canonical_slug: current_slug)
|
||||
end
|
||||
|
||||
it 'raises an error' do
|
||||
expect { find_record }.to raise_error(ActiveRecord::RecordInvalid)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the wiki page is not valid' do
|
||||
let(:wiki_page) { build(:wiki_page, container: container, title: nil) }
|
||||
|
||||
it 'raises an error' do
|
||||
expect { find_record }.to raise_error(described_class::WikiPageInvalid)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -4,6 +4,7 @@ RSpec.shared_examples 'WikiPages::CreateService#execute' do |container_type|
|
|||
let(:container) { create(container_type, :wiki_repo) }
|
||||
let(:user) { create(:user) }
|
||||
let(:page_title) { 'Title' }
|
||||
let(:container_key) { container.is_a?(Group) ? :namespace_id : :project_id }
|
||||
|
||||
let(:opts) do
|
||||
{
|
||||
|
|
@ -27,6 +28,15 @@ RSpec.shared_examples 'WikiPages::CreateService#execute' do |container_type|
|
|||
expect(page.format).to eq(opts[:format].to_sym)
|
||||
end
|
||||
|
||||
it 'creates a WikiPage::Meta record' do
|
||||
expect { service.execute }.to change { WikiPage::Meta.count }.by 1
|
||||
|
||||
expect(WikiPage::Meta.all.last).to have_attributes(
|
||||
title: page_title,
|
||||
container_key => container.id
|
||||
)
|
||||
end
|
||||
|
||||
it 'executes webhooks' do
|
||||
expect(service).to receive(:execute_hooks).once.with(WikiPage)
|
||||
|
||||
|
|
@ -57,9 +67,6 @@ RSpec.shared_examples 'WikiPages::CreateService#execute' do |container_type|
|
|||
|
||||
shared_examples 'correct event created' do
|
||||
it 'creates appropriate events' do
|
||||
# TODO: https://gitlab.com/gitlab-org/gitlab/-/issues/216904
|
||||
pending('group wiki support') if container_type == :group
|
||||
|
||||
expect { service.execute }.to change { Event.count }.by 1
|
||||
|
||||
expect(Event.recent.first).to have_attributes(
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ RSpec.shared_examples 'WikiPages::DestroyService#execute' do |container_type|
|
|||
|
||||
let(:user) { create(:user) }
|
||||
let(:page) { create(:wiki_page) }
|
||||
let!(:wiki_page_meta) { create(:wiki_page_meta, container: container) }
|
||||
|
||||
subject(:service) { described_class.new(container: container, current_user: user) }
|
||||
|
||||
|
|
@ -36,10 +37,13 @@ RSpec.shared_examples 'WikiPages::DestroyService#execute' do |container_type|
|
|||
end
|
||||
end
|
||||
|
||||
it 'creates a new wiki page deletion event' do
|
||||
# TODO: https://gitlab.com/gitlab-org/gitlab/-/issues/216904
|
||||
pending('group wiki support') if container_type == :group
|
||||
# This test fails, because deleting a page seems to orphan WikiPage;:Meta and WikiPage::Slug records,
|
||||
# but it's included for completeness for now.
|
||||
pending 'deletes a WikiPage::Meta record' do
|
||||
expect { service.execute(page) }.to change { WikiPage::Meta.count }.by(-1)
|
||||
end
|
||||
|
||||
it 'creates a new wiki page deletion event' do
|
||||
expect { service.execute(page) }.to change { Event.count }.by 1
|
||||
|
||||
expect(Event.recent.first).to have_attributes(
|
||||
|
|
|
|||
|
|
@ -4,8 +4,9 @@ RSpec.shared_examples 'WikiPages::UpdateService#execute' do |container_type|
|
|||
let(:container) { create(container_type, :wiki_repo) }
|
||||
|
||||
let(:user) { create(:user) }
|
||||
let(:page) { create(:wiki_page) }
|
||||
let(:page) { create(:wiki_page, container: container) }
|
||||
let(:page_title) { 'New Title' }
|
||||
let(:container_key) { container.is_a?(Group) ? :namespace_id : :project_id }
|
||||
|
||||
let(:opts) do
|
||||
{
|
||||
|
|
@ -30,6 +31,27 @@ RSpec.shared_examples 'WikiPages::UpdateService#execute' do |container_type|
|
|||
expect(updated_page.title).to eq(page_title)
|
||||
end
|
||||
|
||||
it 'creates the WikiPage::Meta record if it does not exist' do
|
||||
expect { service.execute(page) }.to change { WikiPage::Meta.count }.by 1
|
||||
|
||||
expect(WikiPage::Meta.all.last).to have_attributes(
|
||||
title: page_title,
|
||||
container_key => container.id
|
||||
)
|
||||
end
|
||||
|
||||
context 'when WikiPage::Meta record exists' do
|
||||
let!(:wiki_page_meta) { create(:wiki_page_meta, container: container) }
|
||||
|
||||
before do
|
||||
allow(WikiPage::Meta).to receive(:find_by_canonical_slug).and_return(wiki_page_meta)
|
||||
end
|
||||
|
||||
it 'doesn not create a WikiPage::Meta record' do
|
||||
expect { service.execute(page) }.to change { WikiPage::Meta.count }.by 0
|
||||
end
|
||||
end
|
||||
|
||||
it 'executes webhooks' do
|
||||
expect(service).to receive(:execute_hooks).once.with(WikiPage)
|
||||
|
||||
|
|
|
|||
39
yarn.lock
39
yarn.lock
|
|
@ -1378,10 +1378,10 @@
|
|||
vue-functional-data-merge "^3.1.0"
|
||||
vue-runtime-helpers "^1.1.2"
|
||||
|
||||
"@gitlab/web-ide@^0.0.1-dev-20240909013227":
|
||||
version "0.0.1-dev-20240909013227"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/web-ide/-/web-ide-0.0.1-dev-20240909013227.tgz#6ba20cabe4b3dee8eacbb0e3aa4d71b49b30fecc"
|
||||
integrity sha512-fWkkQ3Vm03NmDrJVmEO7nteRzXHj2J4GGfKifILpMeWjKp2X7nPjatHsbOWS8TqEVQUTrL5SB6yV+p6242fAtA==
|
||||
"@gitlab/web-ide@^0.0.1-dev-20240816130114":
|
||||
version "0.0.1-dev-20240816130114"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/web-ide/-/web-ide-0.0.1-dev-20240816130114.tgz#25a88d945095ea10bab9fbed5de1daea205b0bf1"
|
||||
integrity sha512-Uv3n+l3oS5ywBWxzXhriFvxYUYw4KBHxlQJEIN3w0gzEiFgV7sYwQmJjCjhukN0PNCIX0akHZYwMm+ow/vD9IA==
|
||||
|
||||
"@graphql-eslint/eslint-plugin@3.20.1":
|
||||
version "3.20.1"
|
||||
|
|
@ -13104,7 +13104,16 @@ string-length@^4.0.1:
|
|||
char-regex "^1.0.2"
|
||||
strip-ansi "^6.0.0"
|
||||
|
||||
"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
|
||||
"string-width-cjs@npm:string-width@^4.2.0":
|
||||
version "4.2.3"
|
||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
||||
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
||||
dependencies:
|
||||
emoji-regex "^8.0.0"
|
||||
is-fullwidth-code-point "^3.0.0"
|
||||
strip-ansi "^6.0.1"
|
||||
|
||||
"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
|
||||
version "4.2.3"
|
||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
||||
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
||||
|
|
@ -13157,7 +13166,7 @@ string_decoder@^1.0.0, string_decoder@^1.1.1, string_decoder@~1.1.1:
|
|||
dependencies:
|
||||
safe-buffer "~5.1.0"
|
||||
|
||||
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
|
||||
"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
|
||||
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
|
||||
|
|
@ -13171,6 +13180,13 @@ strip-ansi@^5.2.0:
|
|||
dependencies:
|
||||
ansi-regex "^4.1.0"
|
||||
|
||||
strip-ansi@^6.0.0, strip-ansi@^6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
|
||||
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
|
||||
dependencies:
|
||||
ansi-regex "^5.0.1"
|
||||
|
||||
strip-ansi@^7.0.1, strip-ansi@^7.1.0:
|
||||
version "7.1.0"
|
||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"
|
||||
|
|
@ -14862,7 +14878,7 @@ worker-loader@^3.0.8:
|
|||
loader-utils "^2.0.0"
|
||||
schema-utils "^3.0.0"
|
||||
|
||||
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
|
||||
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
|
||||
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
|
||||
|
|
@ -14880,6 +14896,15 @@ wrap-ansi@^6.2.0:
|
|||
string-width "^4.1.0"
|
||||
strip-ansi "^6.0.0"
|
||||
|
||||
wrap-ansi@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
|
||||
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
|
||||
dependencies:
|
||||
ansi-styles "^4.0.0"
|
||||
string-width "^4.1.0"
|
||||
strip-ansi "^6.0.0"
|
||||
|
||||
wrap-ansi@^8.1.0:
|
||||
version "8.1.0"
|
||||
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"
|
||||
|
|
|
|||
Loading…
Reference in New Issue