Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
ba0514ff4e
commit
26510a4da3
|
|
@ -92,7 +92,7 @@ class ProfilesController < Profiles::ApplicationController
|
|||
:location,
|
||||
:mastodon,
|
||||
:name,
|
||||
:organization,
|
||||
:user_detail_organization,
|
||||
:private_profile,
|
||||
:pronouns,
|
||||
:pronunciation,
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ module UserSettings
|
|||
:mastodon,
|
||||
:name,
|
||||
:orcid,
|
||||
:organization,
|
||||
:user_detail_organization,
|
||||
:private_profile,
|
||||
:pronouns,
|
||||
:pronunciation,
|
||||
|
|
|
|||
|
|
@ -220,7 +220,8 @@ module Types
|
|||
field :organization,
|
||||
type: ::GraphQL::Types::String,
|
||||
null: true,
|
||||
description: 'Who the user represents or works for.'
|
||||
description: 'Who the user represents or works for.',
|
||||
method: :user_detail_organization
|
||||
|
||||
field :job_title,
|
||||
type: ::GraphQL::Types::String,
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ module ProfilesHelper
|
|||
pronunciation: user.pronunciation,
|
||||
website_url: user.website_url,
|
||||
job_title: user.job_title,
|
||||
organization: user.organization,
|
||||
organization: user.user_detail_organization,
|
||||
bio: user.bio,
|
||||
include_private_contributions: user.include_private_contributions?.to_s,
|
||||
achievements_enabled: user.achievements_enabled.to_s,
|
||||
|
|
|
|||
|
|
@ -148,7 +148,7 @@ module UsersHelper
|
|||
def work_information(user, with_schema_markup: false)
|
||||
return unless user
|
||||
|
||||
organization = user.organization
|
||||
organization = user.user_detail_organization
|
||||
job_title = user.job_title
|
||||
|
||||
if organization.present? && job_title.present?
|
||||
|
|
|
|||
|
|
@ -169,6 +169,7 @@ module Ci
|
|||
)
|
||||
end
|
||||
|
||||
# TODO: Remove this scope when FF `ci_stop_using_has_exposed_artifacts_metadata_col` is removed
|
||||
scope :with_exposed_artifacts, -> do
|
||||
joins(:metadata).merge(Ci::BuildMetadata.with_exposed_artifacts)
|
||||
.includes(:metadata, :job_artifacts_metadata)
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ module Ci
|
|||
end
|
||||
|
||||
scope :with_interruptible, -> { where(interruptible: true) }
|
||||
# TODO: Remove this scope when FF `ci_stop_using_has_exposed_artifacts_metadata_col` is removed
|
||||
scope :with_exposed_artifacts, -> { where(has_exposed_artifacts: true) }
|
||||
|
||||
enum :timeout_source, {
|
||||
|
|
|
|||
|
|
@ -1280,7 +1280,11 @@ module Ci
|
|||
end
|
||||
|
||||
def has_exposed_artifacts?
|
||||
complete? && builds.latest.with_exposed_artifacts.exists?
|
||||
if Feature.enabled?(:ci_stop_using_has_exposed_artifacts_metadata_col, project)
|
||||
complete? && builds.latest.any_with_exposed_artifacts?
|
||||
else
|
||||
complete? && builds.latest.with_exposed_artifacts.exists?
|
||||
end
|
||||
end
|
||||
|
||||
def has_erasable_artifacts?
|
||||
|
|
|
|||
|
|
@ -29,10 +29,41 @@ module Ci
|
|||
scope :with_project_and_metadata, -> do
|
||||
joins(:metadata).includes(:metadata).preload(:project)
|
||||
end
|
||||
|
||||
def self.any_with_exposed_artifacts?
|
||||
found_exposed_artifacts = false
|
||||
|
||||
# TODO: Remove :project preload when FF `ci_stop_using_has_exposed_artifacts_metadata_col` is removed
|
||||
includes(:project).each_batch do |batch|
|
||||
# We only load what we need for `has_exposed_artifacts?`
|
||||
records = batch.select(:id, :partition_id, :project_id, :options).to_a
|
||||
|
||||
ActiveRecord::Associations::Preloader.new(
|
||||
records: records,
|
||||
associations: :metadata,
|
||||
scope: Ci::BuildMetadata.select(:build_id, :partition_id, :config_options)
|
||||
).call
|
||||
|
||||
next unless records.any?(&:has_exposed_artifacts?)
|
||||
|
||||
found_exposed_artifacts = true
|
||||
break
|
||||
end
|
||||
|
||||
found_exposed_artifacts
|
||||
end
|
||||
|
||||
def self.select_with_exposed_artifacts
|
||||
includes(:metadata, :job_artifacts_metadata, :project).select(&:has_exposed_artifacts?)
|
||||
end
|
||||
end
|
||||
|
||||
def has_exposed_artifacts?
|
||||
!!metadata&.has_exposed_artifacts?
|
||||
if Feature.enabled?(:ci_stop_using_has_exposed_artifacts_metadata_col, project)
|
||||
options.dig(:artifacts, :expose_as).present?
|
||||
else
|
||||
!!metadata&.has_exposed_artifacts?
|
||||
end
|
||||
end
|
||||
|
||||
def ensure_metadata
|
||||
|
|
@ -63,6 +94,7 @@ module Ci
|
|||
def options=(value)
|
||||
write_metadata_attribute(:options, :config_options, value)
|
||||
|
||||
# TODO: Remove code below when FF `ci_stop_using_has_exposed_artifacts_metadata_col` is removed
|
||||
ensure_metadata.tap do |metadata|
|
||||
# Store presence of exposed artifacts in build metadata to make it easier to query
|
||||
metadata.has_exposed_artifacts = value&.dig(:artifacts, :expose_as).present?
|
||||
|
|
|
|||
|
|
@ -471,7 +471,7 @@ class User < ApplicationRecord
|
|||
delegate :twitter, :twitter=, to: :user_detail, allow_nil: true
|
||||
delegate :website_url, :website_url=, to: :user_detail, allow_nil: true
|
||||
delegate :location, :location=, to: :user_detail, allow_nil: true
|
||||
delegate :organization, :organization=, to: :user_detail, allow_nil: true
|
||||
delegate :organization, :organization=, to: :user_detail, prefix: true, allow_nil: true
|
||||
delegate :discord, :discord=, to: :user_detail, allow_nil: true
|
||||
delegate :github, :github=, to: :user_detail, allow_nil: true
|
||||
delegate :email_reset_offered_at, :email_reset_offered_at=, to: :user_detail, allow_nil: true
|
||||
|
|
|
|||
|
|
@ -15,12 +15,25 @@ module Ci
|
|||
def for_pipeline(pipeline, limit: MAX_EXPOSED_ARTIFACTS)
|
||||
results = []
|
||||
|
||||
pipeline.builds.latest.with_exposed_artifacts.find_each do |job|
|
||||
if job_exposed_artifacts = for_job(job)
|
||||
results << job_exposed_artifacts
|
||||
end
|
||||
if Feature.enabled?(:ci_stop_using_has_exposed_artifacts_metadata_col, project)
|
||||
pipeline.builds.latest.each_batch do |job_batch|
|
||||
job_batch.select_with_exposed_artifacts.each do |job|
|
||||
exposed_artifacts = for_job(job)
|
||||
results << exposed_artifacts if exposed_artifacts
|
||||
|
||||
break if results.size >= limit
|
||||
break if results.size >= limit
|
||||
end
|
||||
|
||||
break if results.size >= limit
|
||||
end
|
||||
else
|
||||
pipeline.builds.latest.with_exposed_artifacts.find_each do |job|
|
||||
if job_exposed_artifacts = for_job(job)
|
||||
results << job_exposed_artifacts
|
||||
end
|
||||
|
||||
break if results.size >= limit
|
||||
end
|
||||
end
|
||||
|
||||
results
|
||||
|
|
|
|||
|
|
@ -195,7 +195,7 @@ module Users
|
|||
:location,
|
||||
:name,
|
||||
:note,
|
||||
:organization,
|
||||
:user_detail_organization,
|
||||
:password,
|
||||
:password_automatically_set,
|
||||
:password_expires_at,
|
||||
|
|
|
|||
|
|
@ -107,10 +107,10 @@
|
|||
.form-text.gl-text-subtle
|
||||
= s_("Profiles|Your job title was automatically set based on your %{provider_label} account") % { provider_label: attribute_provider_label(:job_title) }
|
||||
.form-group.gl-form-group
|
||||
= f.label :organization, s_('Profiles|Organization')
|
||||
= f.text_field :organization, class: 'gl-form-input form-control gl-md-form-input-lg', readonly: @user.read_only_attribute?(:organization)
|
||||
= f.label :user_detail_organization, s_('Profiles|Organization')
|
||||
= f.text_field :user_detail_organization, class: 'gl-form-input form-control gl-md-form-input-lg', readonly: @user.read_only_attribute?(:user_detail_organization)
|
||||
.form-text.gl-text-subtle
|
||||
- if @user.read_only_attribute?(:organization)
|
||||
- if @user.read_only_attribute?(:user_detail_organization)
|
||||
= s_("Profiles|Your organization was automatically set based on your %{provider_label} account") % { provider_label: attribute_provider_label(:organization) }
|
||||
- else
|
||||
= s_("Profiles|Who you represent or work for.")
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
---
|
||||
name: policy_yaml_invalidated
|
||||
description: The policy YAML is invalidated in security policy project
|
||||
introduced_by_issue: https://gitlab.com/gitlab-org/gitlab/-/work_items/550892
|
||||
introduced_by_mr: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/196721
|
||||
feature_category: security_policy_management
|
||||
milestone: '18.2'
|
||||
saved_to_database: true
|
||||
streamed: true
|
||||
scope: [Project]
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
---
|
||||
name: ci_stop_using_has_exposed_artifacts_metadata_col
|
||||
description: >
|
||||
Updates code to stop using p_ci_builds_metadata.has_exposed_artifacts. This means that
|
||||
finding exposed artifacts requires iterating through all latest builds of a pipeline,
|
||||
not just ones with has_exposed_artifacts = true.
|
||||
feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/work_items/550223
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/195693
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/551510
|
||||
milestone: '18.2'
|
||||
group: group::ci platform
|
||||
type: gitlab_com_derisk
|
||||
default_enabled: false
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
migration_job_name: BackfillAnalyzerProjectStatusesFromProjectSecuritySettings
|
||||
description: Backfills AnalyzerProjectStatuses from ProjectSecuritySettings based on the container_scanning_for_registry_enabled and secret_push_protection_enabled columns
|
||||
feature_category: security_asset_inventories
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/195569
|
||||
milestone: '18.2'
|
||||
queued_migration_version: 20250625184737
|
||||
finalized_by:
|
||||
|
|
@ -5,4 +5,4 @@ feature_category: source_code_management
|
|||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/183124
|
||||
milestone: '17.10'
|
||||
queued_migration_version: 20250301123510
|
||||
finalized_by: # version of the migration that finalized this BBM
|
||||
finalized_by: '20250707162531'
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# See https://docs.gitlab.com/ee/development/database/batched_background_migrations.html
|
||||
# for more information on when/how to queue batched background migrations
|
||||
|
||||
# Update below commented lines with appropriate values.
|
||||
|
||||
class QueueBackfillAnalyzerProjectStatusesFromProjectSecuritySettings < Gitlab::Database::Migration[2.3]
|
||||
milestone '18.2'
|
||||
|
||||
# Select the applicable gitlab schema for your batched background migration
|
||||
# the project_security_settings are on gitlab_main
|
||||
# but analyzer_project_statuses is on gitlab_sec
|
||||
restrict_gitlab_migration gitlab_schema: :gitlab_main
|
||||
|
||||
MIGRATION = "BackfillAnalyzerProjectStatusesFromProjectSecuritySettings"
|
||||
DELAY_INTERVAL = 2.minutes
|
||||
BATCH_SIZE = 1000
|
||||
SUB_BATCH_SIZE = 100
|
||||
|
||||
def up
|
||||
queue_batched_background_migration(
|
||||
MIGRATION,
|
||||
:project_security_settings,
|
||||
:project_id,
|
||||
job_interval: DELAY_INTERVAL,
|
||||
batch_size: BATCH_SIZE,
|
||||
sub_batch_size: SUB_BATCH_SIZE
|
||||
)
|
||||
end
|
||||
|
||||
def down
|
||||
delete_batched_background_migration(MIGRATION, :project_security_settings, :project_id, [])
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class FinalizeBackfillSnippetStatisticsSnippetOrganizationId < Gitlab::Database::Migration[2.3]
|
||||
milestone '18.2'
|
||||
disable_ddl_transaction!
|
||||
|
||||
restrict_gitlab_migration gitlab_schema: :gitlab_main_cell
|
||||
|
||||
def up
|
||||
ensure_batched_background_migration_is_finished(
|
||||
job_class_name: 'BackfillSnippetStatisticsSnippetOrganizationId',
|
||||
table_name: :snippet_statistics,
|
||||
column_name: :snippet_id,
|
||||
job_arguments: [:snippet_organization_id, :snippets, :organization_id, :snippet_id],
|
||||
finalize: true
|
||||
)
|
||||
end
|
||||
|
||||
def down; end
|
||||
end
|
||||
|
|
@ -0,0 +1 @@
|
|||
aba9fc447811624de352d725b46c1e065ca6eb00fa55e7933cff07f27703647b
|
||||
|
|
@ -0,0 +1 @@
|
|||
43d2f3d57a2b26945bf37508b98e14f135acd68dace467d4756e4bfe31034d93
|
||||
|
|
@ -52,16 +52,34 @@ GitLab Dedicated leverages GitLab Geo for [disaster recovery](../../subscription
|
|||
|
||||
Geo does not use an active-active failover configuration. For more information, see [Geo](../geo/_index.md).
|
||||
|
||||
### AWS PrivateLink connection (optional)
|
||||
### AWS PrivateLink connection
|
||||
|
||||
>Required for Geo migrations to Dedicated. Otherwise, optional
|
||||
|
||||
Optionally, private connectivity is available for your GitLab Dedicated instance, using [AWS PrivateLink](https://aws.amazon.com/privatelink/) as a connection gateway.
|
||||
|
||||
Both [inbound](configure_instance/network_security.md#inbound-private-link) and [outbound](configure_instance/network_security.md#outbound-private-link) private links are supported.
|
||||
|
||||

|
||||
#### Inbound
|
||||
|
||||

|
||||
|
||||
GitLab team members with edit access can update the [source](https://lucid.app/lucidchart/933b958b-bfad-4898-a8ae-182815f159ca/edit?invitationId=inv_38b9a265-dff2-4db6-abdb-369ea1e92f5f) files for the diagram in Lucidchart.
|
||||
|
||||
#### Outbound
|
||||
|
||||

|
||||
|
||||
GitLab team members with edit access can update the [source](https://lucid.app/lucidchart/5aeae97e-a3c4-43e3-8b9d-27900d944147/edit?invitationId=inv_0e4fee9f-cf63-439c-9bf9-71ecbfbd8979&page=F5pcfQybsAYU8#) files for the diagram in Lucidchart.
|
||||
|
||||
#### AWS PrivateLink for migration
|
||||
|
||||
Additionally, AWS PrivateLink is also used for migration purposes. The customer's Dedicated GitLab instance can use AWS PrivateLink to pull data for a migration to GitLab Dedicated.
|
||||
|
||||

|
||||
|
||||
GitLab team members with edit access can update the [source](https://lucid.app/lucidchart/1e83e102-37b3-48a9-885d-e72122683bce/edit?view_items=AzvnMfovRJe3p&invitationId=inv_c02140dd-416b-41b5-b14a-7288b54bb9b5) files for the diagram in Lucidchart.
|
||||
|
||||
## Hosted runners for GitLab Dedicated
|
||||
|
||||
The following diagram illustrates a GitLab-managed AWS account that contains GitLab runners, which are interconnected to a GitLab Dedicated instance, the public internet, and optionally a customer AWS account that uses AWS PrivateLink.
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 68 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 39 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 79 KiB |
|
|
@ -38995,6 +38995,7 @@ Compliance violation for a project.
|
|||
| <a id="projectcomplianceviolationcreatedat"></a>`createdAt` | [`Time!`](#time) | Timestamp when the violation was detected. |
|
||||
| <a id="projectcomplianceviolationdiscussions"></a>`discussions` | [`DiscussionConnection!`](#discussionconnection) | All discussions on the noteable. (see [Connections](#connections)) |
|
||||
| <a id="projectcomplianceviolationid"></a>`id` | [`ID!`](#id) | Compliance violation ID. |
|
||||
| <a id="projectcomplianceviolationissues"></a>`issues` | [`IssueConnection`](#issueconnection) | Project issues linked to the violation. (see [Connections](#connections)) |
|
||||
| <a id="projectcomplianceviolationproject"></a>`project` | [`Project!`](#project) | Project of the compliance violation. |
|
||||
| <a id="projectcomplianceviolationstatus"></a>`status` | [`ComplianceViolationStatus!`](#complianceviolationstatus) | Compliance violation status of the project. |
|
||||
|
||||
|
|
|
|||
|
|
@ -45813,12 +45813,12 @@ definitions:
|
|||
type: string
|
||||
github:
|
||||
type: string
|
||||
organization:
|
||||
type: string
|
||||
job_title:
|
||||
type: string
|
||||
pronouns:
|
||||
type: string
|
||||
organization:
|
||||
type: string
|
||||
bot:
|
||||
type: string
|
||||
work_information:
|
||||
|
|
@ -50218,12 +50218,12 @@ definitions:
|
|||
type: string
|
||||
github:
|
||||
type: string
|
||||
organization:
|
||||
type: string
|
||||
job_title:
|
||||
type: string
|
||||
pronouns:
|
||||
type: string
|
||||
organization:
|
||||
type: string
|
||||
bot:
|
||||
type: string
|
||||
work_information:
|
||||
|
|
@ -65529,12 +65529,12 @@ definitions:
|
|||
type: string
|
||||
github:
|
||||
type: string
|
||||
organization:
|
||||
type: string
|
||||
job_title:
|
||||
type: string
|
||||
pronouns:
|
||||
type: string
|
||||
organization:
|
||||
type: string
|
||||
bot:
|
||||
type: string
|
||||
work_information:
|
||||
|
|
|
|||
|
|
@ -159,7 +159,7 @@ Instead of:
|
|||
|
||||
Use lowercase for **advanced search** to refer to the faster, more efficient search across the entire GitLab instance.
|
||||
|
||||
## agent
|
||||
## agent for Kubernetes
|
||||
|
||||
Use lowercase to refer to the [GitLab agent for Kubernetes](https://gitlab.com/gitlab-org/cluster-integration/gitlab-agent).
|
||||
For example:
|
||||
|
|
@ -198,6 +198,17 @@ Instead of **agnostic**, use **platform-independent** or **vendor-neutral**.
|
|||
|
||||
Use **AI**. Do not spell out **artificial intelligence**.
|
||||
|
||||
## AI agent
|
||||
|
||||
When writing about AI, the **AI agent** is an entity that performs actions for the user.
|
||||
|
||||
After first use, you can use **agent** without **AI**.
|
||||
|
||||
When you're interacting with an AI agent, a [**session**](#session) is running.
|
||||
The user can stop a session.
|
||||
|
||||
One or more AI agents can be part of a [**flow**](#flows), where they are orchestrated to work together on a problem.
|
||||
|
||||
## AI gateway
|
||||
|
||||
Use lowercase for **AI gateway** and do not hyphenate.
|
||||
|
|
@ -1052,6 +1063,13 @@ the available attributes. For example, you might filter by assignee or reviewer.
|
|||
|
||||
Filtering is different from [searching](#search).
|
||||
|
||||
## flows
|
||||
|
||||
GitLab provides multiple **flows** that are run by [AI agents](#ai-agent).
|
||||
Both **flow** and **agent flow** are acceptable.
|
||||
|
||||
You choose a flow. You start a [**session**](#session).
|
||||
|
||||
## foo
|
||||
|
||||
Do not use **foo** in product documentation. You can use it in our API and contributor documentation, but try to use a clearer and more meaningful example instead.
|
||||
|
|
@ -2311,6 +2329,11 @@ See [GitLab Self-Managed](#gitlab-self-managed).
|
|||
|
||||
Use title case for **Service Desk**.
|
||||
|
||||
## session
|
||||
|
||||
When an [AI agent](#ai-agent) is working on a [**flow**](#flows), a **session** is running.
|
||||
The session can start and stop.
|
||||
|
||||
## setup, set up
|
||||
|
||||
Use **setup** as a noun, and **set up** as a verb. For example:
|
||||
|
|
|
|||
|
|
@ -5,6 +5,12 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
title: Configure GitLab as an OAuth 2.0 authentication identity provider
|
||||
---
|
||||
|
||||
{{< history >}}
|
||||
|
||||
- Group SAML SSO support for OAuth applications [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/461212) in GitLab 18.2 [with a flag](../administration/feature_flags/_index.md) named `ff_oauth_redirect_to_sso_login`. Disabled by default.
|
||||
|
||||
{{< /history >}}
|
||||
|
||||
[OAuth 2.0](https://oauth.net/2/) provides secure delegated server resource
|
||||
access to client applications on behalf of a resource owner. OAuth 2 allows
|
||||
authorization servers to issue access tokens to third-party clients with the approval
|
||||
|
|
@ -24,9 +30,10 @@ You can use a non-SSL URL instead, but you should use an SSL URL.
|
|||
After adding an OAuth 2 application to an instance, you can use OAuth 2 to:
|
||||
|
||||
- Enable users to sign in to your application with their GitLab.com account.
|
||||
- Enable users to sign in to your application using [SAML SSO](../user/group/saml_sso/_index.md)
|
||||
when SAML is configured for the associated group.
|
||||
- Set up GitLab.com for authentication to your GitLab instance. For more information,
|
||||
see [integrating your server with GitLab.com](gitlab.md).
|
||||
|
||||
- After an application is created, external services can manage access tokens using the
|
||||
[OAuth 2 API](../api/oauth2.md).
|
||||
|
||||
|
|
|
|||
|
|
@ -211,7 +211,7 @@ This section lists the features that are not available for GitLab Dedicated.
|
|||
|
||||
The following GitLab application features are not available:
|
||||
|
||||
- LDAP, smart card, or Kerberos authentication
|
||||
- LDAP (Git clone with username / password), smart card, or Kerberos authentication
|
||||
- Multiple login providers
|
||||
- FortiAuthenticator or FortiToken 2FA
|
||||
- Reply by email
|
||||
|
|
@ -222,6 +222,7 @@ The following GitLab application features are not available:
|
|||
- Features other than [available features](#available-features) that must be configured outside of the GitLab user interface
|
||||
- Any functionality or feature behind a feature flag that is turned `off` by default
|
||||
- [Sigstore for keyless signing and verification](../../ci/yaml/signing_examples.md)
|
||||
- OAuth providers outside of standard SAML
|
||||
|
||||
The following features are not supported:
|
||||
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ The **Metric trends** table displays metrics for the last six months, with month
|
|||
|
||||
### AI usage metrics
|
||||
|
||||
**Code Suggestions usage**: Monthly user engagement with AI Code Suggestions.
|
||||
**Code Suggestions: Usage**: Monthly user engagement with AI Code Suggestions.
|
||||
|
||||
On GitLab.com, data updates every fives minutes.
|
||||
GitLab counts Code Suggestions usage only if the user has pushed code to the project in the current month.
|
||||
|
|
|
|||
|
|
@ -154,8 +154,8 @@ This method gives information about dependencies which are direct.
|
|||
To enable the CI/CD component on a Gradle project:
|
||||
|
||||
1. Edit the `build.gradle` or `build.gradle.kts` to use the
|
||||
[gradle-dependency-lock-plugin](https://github.com/nebula-plugins/gradle-dependency-lock-plugin/wiki/Usage#example).
|
||||
1. Configure the `.gitlab-ci.yml` file to generate the `dependencies.lock` artifacts, and pass them
|
||||
[gradle-dependency-lock-plugin](https://github.com/nebula-plugins/gradle-dependency-lock-plugin/wiki/Usage#example) or use an init script.
|
||||
1. Configure the `.gitlab-ci.yml` file to generate the `dependencies.lock` and `dependencies.direct.lock` artifacts, and pass them
|
||||
to the `dependency-scanning` job.
|
||||
|
||||
The following example demonstrates how to configure the component
|
||||
|
|
@ -166,30 +166,44 @@ stages:
|
|||
- build
|
||||
- test
|
||||
|
||||
# Define the image that contains Java and Gradle
|
||||
image: gradle:8.0-jdk11
|
||||
|
||||
include:
|
||||
- component: $CI_SERVER_FQDN/components/dependency-scanning/main@0
|
||||
|
||||
build:
|
||||
generate nebula lockfile:
|
||||
# Running in the build stage ensures that the dependency-scanning job
|
||||
# receives the dependencies.lock artifacts.
|
||||
# receives the scannable artifacts.
|
||||
stage: build
|
||||
script:
|
||||
- gradle generateLock saveLock
|
||||
- gradle assemble
|
||||
# generateLock saves the lock file in the build/ directory of a project
|
||||
# and saveLock copies it into the root of a project. To avoid duplicates
|
||||
# and get an accurate location of the dependency, use find to remove the
|
||||
# lock files in the build/ directory only.
|
||||
- |
|
||||
cat << EOF > nebula.gradle
|
||||
initscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.netflix.nebula:gradle-dependency-lock-plugin:12.7.1'
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
apply plugin: nebula.plugin.dependencylock.DependencyLockPlugin
|
||||
}
|
||||
EOF
|
||||
./gradlew --init-script nebula.gradle -PdependencyLock.includeTransitives=true -PdependencyLock.lockFile=dependencies.lock generateLock saveLock
|
||||
./gradlew --init-script nebula.gradle -PdependencyLock.includeTransitives=false -PdependencyLock.lockFile=dependencies.direct.lock generateLock saveLock
|
||||
# generateLock saves the lock file in the build/ directory of a project
|
||||
# and saveLock copies it into the root of a project. To avoid duplicates
|
||||
# and get an accurate location of the dependency, use find to remove the
|
||||
# lock files in the build/ directory only.
|
||||
after_script:
|
||||
- find . -path '*/build/dependencies.lock' -print -delete
|
||||
# Collect all dependencies.lock artifacts and pass them onto jobs
|
||||
# in sequential stages.
|
||||
- find . -path '*/build/dependencies*.lock' -print -delete
|
||||
# Collect all generated artifacts and pass them onto jobs in sequential stages.
|
||||
artifacts:
|
||||
paths:
|
||||
- "**/dependencies.lock"
|
||||
- '**/dependencies*.lock'
|
||||
- '**/dependencies*.lock'
|
||||
```
|
||||
|
||||
###### HtmlDependencyReportTask
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ the top of the vulnerability's page.
|
|||
- Tier: Ultimate
|
||||
- Add-on: GitLab Duo Enterprise, GitLab Duo with Amazon Q
|
||||
- Offering: GitLab.com, GitLab Self-Managed, GitLab Dedicated
|
||||
- LLM: Anthropic [Claude 3 Haiku](https://docs.anthropic.com/en/docs/about-claude/models#claude-3-a-new-generation-of-ai)
|
||||
- LLM: Anthropic [Claude 3.7 Sonnet](https://console.cloud.google.com/vertex-ai/publishers/anthropic/model-garden/claude-3-7-sonnet)
|
||||
- LLM for Amazon Q: Amazon Q Developer
|
||||
|
||||
{{< /details >}}
|
||||
|
|
|
|||
|
|
@ -542,6 +542,7 @@ Audit event types belong to the following product categories.
|
|||
| [`merge_request_merged_with_policy_violations`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/195775) | A merge request merged with security policy violations | {{< icon name="check-circle" >}} Yes | GitLab [18.2](https://gitlab.com/gitlab-org/gitlab/-/work_items/549813) | Project |
|
||||
| [`policy_violations_detected`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/193482) | Security policy violation is detected in the merge request | {{< icon name="dotted-circle" >}} No | GitLab [18.2](https://gitlab.com/gitlab-org/gitlab/-/work_items/549811) | Project |
|
||||
| [`policy_violations_resolved`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/193482) | Security policy violations are resolved in the merge request | {{< icon name="dotted-circle" >}} No | GitLab [18.2](https://gitlab.com/gitlab-org/gitlab/-/issues/549812) | Project |
|
||||
| [`policy_yaml_invalidated`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/196721) | The policy YAML is invalidated in security policy project | {{< icon name="check-circle" >}} Yes | GitLab [18.2](https://gitlab.com/gitlab-org/gitlab/-/work_items/550892) | Project |
|
||||
|
||||
### Security testing configuration
|
||||
|
||||
|
|
|
|||
|
|
@ -675,10 +675,11 @@ To troubleshoot a failed CI/CD job from the job log:
|
|||
{{< details >}}
|
||||
|
||||
- Tier: Ultimate
|
||||
- Add-on: GitLab Duo Enterprise
|
||||
- Add-on: GitLab Duo Enterprise, GitLab Duo with Amazon Q
|
||||
- Offering: GitLab.com, GitLab Self-Managed, GitLab Dedicated
|
||||
- Editors: GitLab UI
|
||||
- LLM: Anthropic [Claude 3.7 Sonnet](https://console.cloud.google.com/vertex-ai/publishers/anthropic/model-garden/claude-3-7-sonnet)
|
||||
- LLM for Amazon Q: Amazon Q Developer
|
||||
|
||||
{{< /details >}}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,12 @@ module API
|
|||
include Gitlab::Utils::StrongMemoize
|
||||
|
||||
expose :created_at, if: ->(user, opts) { Ability.allowed?(opts[:current_user], :read_user_profile, user) }
|
||||
expose :bio, :location, :linkedin, :twitter, :discord, :website_url, :github, :organization, :job_title, :pronouns
|
||||
expose :bio, :location, :linkedin, :twitter, :discord, :website_url, :github, :job_title, :pronouns
|
||||
# rubocop:disable Style/SymbolProc -- we're not able to pass &:user_detail_organization as this tries to pass Grape::Entity::Options to said method
|
||||
expose :organization do |user|
|
||||
user.user_detail_organization
|
||||
end
|
||||
# rubocop:enable Style/SymbolProc
|
||||
expose :bot?, as: :bot
|
||||
expose :work_information do |user|
|
||||
work_information(user)
|
||||
|
|
|
|||
|
|
@ -438,6 +438,8 @@ module API
|
|||
user_params[:password_expires_at] = Time.current if admin_making_changes_for_another_user
|
||||
end
|
||||
|
||||
user_params[:user_detail_organization] = user_params.delete(:organization) if user_params[:organization]
|
||||
|
||||
result = ::Users::UpdateService.new(current_user, user_params.merge(user: user)).execute do |user|
|
||||
user.send_only_admin_changed_your_password_notification! if admin_making_changes_for_another_user
|
||||
end
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ module Banzai
|
|||
# If sanitization times out, we can not return partial un-sanitized results.
|
||||
# It's ok to allow any following filters to run since this is safe HTML.
|
||||
def returned_timeout_value
|
||||
HTML::Pipeline.parse(COMPLEX_MARKDOWN_MESSAGE)
|
||||
Banzai::PipelineBase.parse(COMPLEX_MARKDOWN_MESSAGE)
|
||||
end
|
||||
|
||||
class << self
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ module Banzai
|
|||
private
|
||||
|
||||
def render_documents(objects, attribute)
|
||||
pipeline = HTML::Pipeline.new([])
|
||||
pipeline = Banzai::PipelineBase.new([])
|
||||
|
||||
objects.map do |object|
|
||||
document = pipeline.to_document(Banzai.render_field(object, attribute))
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ module Banzai
|
|||
end
|
||||
|
||||
def self.html_pipeline
|
||||
@html_pipeline ||= HTML::Pipeline.new(filters)
|
||||
@html_pipeline ||= Banzai::PipelineBase.new(filters)
|
||||
@html_pipeline.setup_instrumentation(name)
|
||||
|
||||
@html_pipeline
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Banzai
|
||||
# Custom pipeline class that extends HTML::Pipeline to allow GitLab-specific
|
||||
# customizations and overrides of the html-pipeline gem behavior.
|
||||
class PipelineBase < ::HTML::Pipeline
|
||||
extend ::Gitlab::Utils::Override
|
||||
|
||||
HTML_PIPELINE_SUBSCRIPTION = 'call_filter.html_pipeline'
|
||||
|
||||
# Use thread ID to make subscription unique per thread
|
||||
def self.filter_subscription_name
|
||||
"#{HTML_PIPELINE_SUBSCRIPTION}_#{Thread.current.object_id}"
|
||||
end
|
||||
|
||||
# overridden to use our own filter subscription name
|
||||
override :perform_filter
|
||||
def perform_filter(filter, doc, context, result)
|
||||
payload = default_payload(filter: filter.name, context: context, result: result)
|
||||
|
||||
instrument Banzai::PipelineBase.filter_subscription_name, payload do
|
||||
filter.call(doc, context, result)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -3,7 +3,6 @@
|
|||
module Banzai
|
||||
module Renderer
|
||||
USER_CONTENT_ID_PREFIX = 'user-content-'
|
||||
HTML_PIPELINE_SUBSCRIPTION = 'call_filter.html_pipeline'
|
||||
|
||||
# Convert a Markdown String into an HTML-safe String of HTML
|
||||
#
|
||||
|
|
@ -193,15 +192,19 @@ module Banzai
|
|||
Rails.cache.__send__(:expanded_key, full_cache_key(cache_key, pipeline_name)) # rubocop:disable GitlabSecurity/PublicSend
|
||||
end
|
||||
|
||||
# this is built specifically for outputting debug timing/information for the Banzai pipeline.
|
||||
# Example usage:
|
||||
# Banzai.render(markdown, project: nil, debug_timing: true)
|
||||
# Banzai.render(markdown, project: Project.first, debug: true)
|
||||
# Instrumentation is used for a couple purposes
|
||||
# - tracking timing of filters and the pipeline to manage performance and security.
|
||||
# See `PipelineTimingCheck`
|
||||
# - outputting debug timing/information for the pipeline.
|
||||
# Example usage:
|
||||
# Banzai.render(markdown, project: nil, debug_timing: true)
|
||||
# Banzai.render(markdown, project: Project.first, debug: true)
|
||||
def self.instrument_filters
|
||||
service = ActiveSupport::Notifications
|
||||
HTML::Pipeline.default_instrumentation_service = service
|
||||
Banzai::PipelineBase.default_instrumentation_service = service
|
||||
subscription_name = Banzai::PipelineBase.filter_subscription_name
|
||||
|
||||
service.monotonic_subscribe(HTML_PIPELINE_SUBSCRIPTION) do |_event, start, ending, _transaction_id, payload|
|
||||
service.monotonic_subscribe(subscription_name) do |_event, start, ending, _transaction_id, payload|
|
||||
duration = ending - start
|
||||
payload[:result][:pipeline_timing] = payload[:result][:pipeline_timing].to_f + duration
|
||||
|
||||
|
|
@ -222,7 +225,7 @@ module Banzai
|
|||
|
||||
yield
|
||||
ensure
|
||||
service.unsubscribe(HTML_PIPELINE_SUBSCRIPTION) if service
|
||||
service.unsubscribe(subscription_name) if service
|
||||
end
|
||||
|
||||
def self.formatted_duration(duration)
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ module Gitlab
|
|||
SignupDisabledError = Class.new(StandardError)
|
||||
SigninDisabledForProviderError = Class.new(StandardError)
|
||||
IdentityWithUntrustedExternUidError = Class.new(StandardError)
|
||||
UnknownAttributeMappingError = Class.new(StandardError)
|
||||
|
||||
attr_reader :auth_hash
|
||||
|
||||
|
|
@ -271,8 +272,7 @@ module Gitlab
|
|||
|
||||
if sync_profile_from_provider?
|
||||
UserSyncedAttributesMetadata.syncable_attributes(auth_hash.provider).each do |key|
|
||||
if auth_hash.has_attribute?(key) && gl_user.sync_attribute?(key)
|
||||
gl_user.public_send("#{key}=".to_sym, auth_hash.public_send(key)) # rubocop:disable GitlabSecurity/PublicSend
|
||||
if assign_value_to_user(gl_user, auth_hash, key)
|
||||
metadata.set_attribute_synced(key, true)
|
||||
else
|
||||
metadata.set_attribute_synced(key, false)
|
||||
|
|
@ -323,6 +323,23 @@ module Gitlab
|
|||
gl_user.errors.add(attr, error)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def assign_value_to_user(user, auth_hash, key)
|
||||
return unless auth_hash.has_attribute?(key) && user.sync_attribute?(key)
|
||||
|
||||
value = auth_hash.public_send(key) # rubocop:disable GitlabSecurity/PublicSend -- we validate that `key` is a supported value by calling `auth_hash.has_attribute?(key)` on L330
|
||||
if key.to_sym == :organization
|
||||
user.user_detail_organization = value
|
||||
elsif user.respond_to?(:"#{key}=")
|
||||
user.public_send("#{key}=".to_sym, value) # rubocop:disable GitlabSecurity/PublicSend -- we validate that `key` is a supported value by calling `gl_user.sync_attribute?(key)` on L330
|
||||
else
|
||||
raise UnknownAttributeMappingError
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module BackgroundMigration
|
||||
class BackfillAnalyzerProjectStatusesFromProjectSecuritySettings < BatchedMigrationJob
|
||||
feature_category :security_asset_inventories
|
||||
operation_name :backfill_analyzer_project_statuses_from_project_security_settings
|
||||
|
||||
def perform; end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Gitlab::BackgroundMigration::BackfillAnalyzerProjectStatusesFromProjectSecuritySettings.prepend_mod
|
||||
|
|
@ -76,7 +76,7 @@ module Gitlab
|
|||
user_pb = ::Spamcheck::User.new
|
||||
user_pb.username = user.username
|
||||
user_pb.id = user.id
|
||||
user_pb.org = user.organization || ''
|
||||
user_pb.org = user.user_detail_organization || ''
|
||||
user_pb.created_at = convert_to_pb_timestamp(user.created_at)
|
||||
user_pb.abuse_metadata = Google::Protobuf::Map.new(:string, :float, user.abuse_metadata)
|
||||
|
||||
|
|
|
|||
|
|
@ -5856,19 +5856,19 @@ msgstr ""
|
|||
msgid "AiAnalytics|the ClickHouse data store is not available"
|
||||
msgstr ""
|
||||
|
||||
msgid "AiImpactAnalytics|Code Suggestions acceptance rate"
|
||||
msgid "AiImpactAnalytics|Assigned Duo seat engagement"
|
||||
msgstr ""
|
||||
|
||||
msgid "AiImpactAnalytics|Code Suggestions usage"
|
||||
msgid "AiImpactAnalytics|Code Suggestions: Acceptance rate"
|
||||
msgstr ""
|
||||
|
||||
msgid "AiImpactAnalytics|Duo Chat: Unique users"
|
||||
msgid "AiImpactAnalytics|Code Suggestions: Usage"
|
||||
msgstr ""
|
||||
|
||||
msgid "AiImpactAnalytics|Duo RCA: Unique users"
|
||||
msgid "AiImpactAnalytics|Duo Chat: Usage"
|
||||
msgstr ""
|
||||
|
||||
msgid "AiImpactAnalytics|Duo seats: Assigned and used"
|
||||
msgid "AiImpactAnalytics|Duo RCA: Usage"
|
||||
msgstr ""
|
||||
|
||||
msgid "AiImpactAnalytics|Monthly GitLab Duo Code Suggestions accepted / total Code Suggestions generated."
|
||||
|
|
@ -9067,12 +9067,6 @@ msgstr ""
|
|||
msgid "Attribute 'pod-overrides' is not yet supported"
|
||||
msgstr ""
|
||||
|
||||
msgid "Audit Event Author Placeholder"
|
||||
msgstr ""
|
||||
|
||||
msgid "Audit Event Details placeholder"
|
||||
msgstr ""
|
||||
|
||||
msgid "Audit events"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -16109,6 +16103,9 @@ msgstr ""
|
|||
msgid "ComplianceReport|Avoid creating new compliance pipelines and use pipeline execution policies instead. %{linkStart}Pipeline execution policies%{linkEnd} provide the ability to enforce CI/CD jobs, execute security scans, and better manage compliance enforcement in pipelines."
|
||||
msgstr ""
|
||||
|
||||
msgid "ComplianceReport|By %{name}"
|
||||
msgstr ""
|
||||
|
||||
msgid "ComplianceReport|Change status"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -16169,6 +16166,9 @@ msgstr ""
|
|||
msgid "ComplianceReport|Full target branch name"
|
||||
msgstr ""
|
||||
|
||||
msgid "ComplianceReport|Generic Audit event"
|
||||
msgstr ""
|
||||
|
||||
msgid "ComplianceReport|Have questions or thoughts on the new improvements? %{linkStart}Please provide feedback on your experience%{linkEnd}."
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -16181,6 +16181,9 @@ msgstr ""
|
|||
msgid "ComplianceReport|Migrate pipeline to a policy"
|
||||
msgstr ""
|
||||
|
||||
msgid "ComplianceReport|No audit event available"
|
||||
msgstr ""
|
||||
|
||||
msgid "ComplianceReport|No frameworks"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -16235,6 +16238,9 @@ msgstr ""
|
|||
msgid "ComplianceReport|Unable to load the compliance violations report. Refresh the page and try again."
|
||||
msgstr ""
|
||||
|
||||
msgid "ComplianceReport|Unknown author"
|
||||
msgstr ""
|
||||
|
||||
msgid "ComplianceReport|Update filtered results?"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -62607,6 +62613,12 @@ msgstr ""
|
|||
msgid "The file has been successfully deleted."
|
||||
msgstr ""
|
||||
|
||||
msgid "The file is locked."
|
||||
msgstr ""
|
||||
|
||||
msgid "The file is unlocked."
|
||||
msgstr ""
|
||||
|
||||
msgid "The file you're about to delete is tracked by LFS"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ gem 'capybara', '~> 3.40.0'
|
|||
gem 'capybara-screenshot', '~> 1.0.26'
|
||||
gem 'rake', '~> 13', '>= 13.3.0'
|
||||
gem 'rspec', '~> 3.13', '>= 3.13.1'
|
||||
gem 'selenium-webdriver', '= 4.33.0'
|
||||
gem 'selenium-webdriver', '= 4.34.0'
|
||||
gem 'rest-client', '~> 2.1.0'
|
||||
gem 'rspec_junit_formatter', '~> 0.6.0'
|
||||
gem 'faker', '~> 3.5', '>= 3.5.2'
|
||||
|
|
|
|||
|
|
@ -323,7 +323,7 @@ GEM
|
|||
addressable (>= 2.3.5)
|
||||
faraday (>= 0.17.3, < 3)
|
||||
securerandom (0.4.1)
|
||||
selenium-webdriver (4.33.0)
|
||||
selenium-webdriver (4.34.0)
|
||||
base64 (~> 0.2)
|
||||
logger (~> 1.4)
|
||||
rexml (~> 3.2, >= 3.2.5)
|
||||
|
|
@ -405,7 +405,7 @@ DEPENDENCIES
|
|||
rspec-parameterized (~> 2.0.0)
|
||||
rspec_junit_formatter (~> 0.6.0)
|
||||
ruby-debug-ide (~> 0.7.5)
|
||||
selenium-webdriver (= 4.33.0)
|
||||
selenium-webdriver (= 4.34.0)
|
||||
slack-notifier (~> 2.4)
|
||||
terminal-table (~> 4.0.0)
|
||||
warning (~> 1.5)
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ ee/spec/frontend/geo_sites/index_spec.js
|
|||
ee/spec/frontend/groups/settings/components/comma_separated_list_token_selector_spec.js
|
||||
ee/spec/frontend/issues_analytics/components/issues_analytics_table_spec.js
|
||||
ee/spec/frontend/iterations/components/iteration_form_spec.js
|
||||
ee/spec/frontend/iterations/components/iteration_report_issues_spec.js
|
||||
ee/spec/frontend/members/components/action_dropdowns/user_action_dropdown_spec.js
|
||||
ee/spec/frontend/members/components/modals/ldap_override_confirmation_modal_spec.js
|
||||
ee/spec/frontend/members/components/table/members_table_spec.js
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ RSpec.describe 'User edit profile', feature_category: :user_profile do
|
|||
fill_in 'user_location', with: 'Ukraine'
|
||||
fill_in 'user_bio', with: 'I <3 GitLab :tada:'
|
||||
fill_in 'user_job_title', with: 'Frontend Engineer'
|
||||
fill_in 'user_organization', with: 'GitLab'
|
||||
fill_in 'user_user_detail_organization', with: 'GitLab'
|
||||
submit_settings
|
||||
|
||||
expect(user.reload).to have_attributes(
|
||||
|
|
@ -43,7 +43,7 @@ RSpec.describe 'User edit profile', feature_category: :user_profile do
|
|||
website_url: 'http://testurl.com',
|
||||
bio: 'I <3 GitLab :tada:',
|
||||
job_title: 'Frontend Engineer',
|
||||
organization: 'GitLab'
|
||||
user_detail_organization: 'GitLab'
|
||||
)
|
||||
|
||||
expect(find('#user_location').value).to eq 'Ukraine'
|
||||
|
|
@ -51,7 +51,7 @@ RSpec.describe 'User edit profile', feature_category: :user_profile do
|
|||
end
|
||||
|
||||
it 'does not set secondary emails without user input' do
|
||||
fill_in 'user_organization', with: 'GitLab'
|
||||
fill_in 'user_user_detail_organization', with: 'GitLab'
|
||||
submit_settings
|
||||
|
||||
user.reload
|
||||
|
|
@ -559,7 +559,7 @@ RSpec.describe 'User edit profile', feature_category: :user_profile do
|
|||
context 'when job title and organziation are entered' do
|
||||
it "shows job title and organzation on user's profile" do
|
||||
fill_in 'user_job_title', with: 'Frontend Engineer'
|
||||
fill_in 'user_organization', with: 'GitLab - work info test'
|
||||
fill_in 'user_user_detail_organization', with: 'GitLab - work info test'
|
||||
submit_settings
|
||||
|
||||
visit_user
|
||||
|
|
@ -581,7 +581,7 @@ RSpec.describe 'User edit profile', feature_category: :user_profile do
|
|||
|
||||
context 'when only organization is entered' do
|
||||
it "shows only organization on user's profile" do
|
||||
fill_in 'user_organization', with: 'GitLab - work info test'
|
||||
fill_in 'user_user_detail_organization', with: 'GitLab - work info test'
|
||||
submit_settings
|
||||
|
||||
visit_user
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ RSpec.describe 'User page', feature_category: :user_profile do
|
|||
|
||||
context 'work information' do
|
||||
it 'shows job title and organization details' do
|
||||
user.update!(organization: 'GitLab - work info test', job_title: 'Frontend Engineer')
|
||||
user.update!(user_detail_organization: 'GitLab - work info test', job_title: 'Frontend Engineer')
|
||||
|
||||
subject
|
||||
|
||||
|
|
@ -44,7 +44,7 @@ RSpec.describe 'User page', feature_category: :user_profile do
|
|||
end
|
||||
|
||||
it 'shows job title' do
|
||||
user.update!(organization: nil, job_title: 'Frontend Engineer - work info test')
|
||||
user.update!(user_detail_organization: nil, job_title: 'Frontend Engineer - work info test')
|
||||
|
||||
subject
|
||||
|
||||
|
|
@ -52,7 +52,7 @@ RSpec.describe 'User page', feature_category: :user_profile do
|
|||
end
|
||||
|
||||
it 'shows organization details' do
|
||||
user.update!(organization: 'GitLab - work info test', job_title: '')
|
||||
user.update!(user_detail_organization: 'GitLab - work info test', job_title: '')
|
||||
|
||||
subject
|
||||
|
||||
|
|
@ -210,7 +210,7 @@ RSpec.describe 'User page', feature_category: :user_profile do
|
|||
create(
|
||||
:user,
|
||||
state: :blocked,
|
||||
organization: 'GitLab - work info test',
|
||||
user_detail_organization: 'GitLab - work info test',
|
||||
job_title: 'Frontend Engineer',
|
||||
pronunciation: 'pruh-nuhn-see-ay-shn',
|
||||
bio: 'My personal bio'
|
||||
|
|
@ -260,7 +260,7 @@ RSpec.describe 'User page', feature_category: :user_profile do
|
|||
create(
|
||||
:user,
|
||||
:unconfirmed,
|
||||
organization: 'GitLab - work info test',
|
||||
user_detail_organization: 'GitLab - work info test',
|
||||
job_title: 'Frontend Engineer',
|
||||
pronunciation: 'pruh-nuhn-see-ay-shn',
|
||||
bio: 'My personal bio'
|
||||
|
|
@ -389,7 +389,7 @@ RSpec.describe 'User page', feature_category: :user_profile do
|
|||
end
|
||||
|
||||
context 'structured markup' do
|
||||
let_it_be(:user) { create(:user, website_url: 'https://gitlab.com', organization: 'GitLab', job_title: 'Frontend Engineer', email: 'public@example.com', public_email: 'public@example.com', location: 'Country', created_at: Time.zone.now, updated_at: Time.zone.now) }
|
||||
let_it_be(:user) { create(:user, website_url: 'https://gitlab.com', user_detail_organization: 'GitLab', job_title: 'Frontend Engineer', email: 'public@example.com', public_email: 'public@example.com', location: 'Country', created_at: Time.zone.now, updated_at: Time.zone.now) }
|
||||
|
||||
it 'shows Person structured markup' do
|
||||
subject
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ RSpec.describe ProfilesHelper, feature_category: :user_profile do
|
|||
pronouns: 'they/them',
|
||||
pronunciation: 'test-user',
|
||||
job_title: 'Developer',
|
||||
organization: 'GitLab',
|
||||
user_detail_organization: 'GitLab',
|
||||
location: 'Remote',
|
||||
website_url: 'https://example.com',
|
||||
bio: 'Test bio')
|
||||
|
|
|
|||
|
|
@ -412,7 +412,7 @@ RSpec.describe UsersHelper, feature_category: :user_management do
|
|||
|
||||
context 'without schema markup' do
|
||||
context 'when both job_title and organization are present' do
|
||||
let(:user) { build(:user, organization: 'GitLab', job_title: 'Frontend Engineer') }
|
||||
let(:user) { build(:user, user_detail_organization: 'GitLab', job_title: 'Frontend Engineer') }
|
||||
|
||||
it 'returns job title concatenated with organization' do
|
||||
is_expected.to eq('Frontend Engineer at GitLab')
|
||||
|
|
@ -420,7 +420,7 @@ RSpec.describe UsersHelper, feature_category: :user_management do
|
|||
end
|
||||
|
||||
context 'when only organization is present' do
|
||||
let(:user) { build(:user, organization: 'GitLab') }
|
||||
let(:user) { build(:user, user_detail_organization: 'GitLab') }
|
||||
|
||||
it "returns organization" do
|
||||
is_expected.to eq('GitLab')
|
||||
|
|
@ -440,7 +440,7 @@ RSpec.describe UsersHelper, feature_category: :user_management do
|
|||
let(:with_schema_markup) { true }
|
||||
|
||||
context 'when both job_title and organization are present' do
|
||||
let(:user) { build(:user, organization: 'GitLab', job_title: 'Frontend Engineer') }
|
||||
let(:user) { build(:user, user_detail_organization: 'GitLab', job_title: 'Frontend Engineer') }
|
||||
|
||||
it 'returns job title concatenated with organization' do
|
||||
is_expected.to eq('<span itemprop="jobTitle">Frontend Engineer</span> at <span itemprop="worksFor">GitLab</span>')
|
||||
|
|
@ -448,7 +448,7 @@ RSpec.describe UsersHelper, feature_category: :user_management do
|
|||
end
|
||||
|
||||
context 'when only organization is present' do
|
||||
let(:user) { build(:user, organization: 'GitLab') }
|
||||
let(:user) { build(:user, user_detail_organization: 'GitLab') }
|
||||
|
||||
it "returns organization" do
|
||||
is_expected.to eq('<span itemprop="worksFor">GitLab</span>')
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ RSpec.describe Banzai::Filter::References::ExternalIssueReferenceFilter, feature
|
|||
end
|
||||
|
||||
context 'with RequestStore enabled', :request_store do
|
||||
let(:reference_filter) { HTML::Pipeline.new([described_class]) }
|
||||
let(:reference_filter) { Banzai::PipelineBase.new([described_class]) }
|
||||
|
||||
it 'queries the collection on the first call' do
|
||||
expect_any_instance_of(Project).to receive(:default_issues_tracker?).once.and_call_original
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ RSpec.describe Banzai::Filter::TableOfContentsTagFilter, feature_category: :mark
|
|||
end
|
||||
|
||||
def result(html)
|
||||
HTML::Pipeline.new([Banzai::Filter::MarkdownFilter, described_class]).call(html)
|
||||
Banzai::PipelineBase.new([Banzai::Filter::MarkdownFilter, described_class]).call(html)
|
||||
end
|
||||
|
||||
let(:results) { result("[toc]\n\n#{header(1, 'Header 1')}#{header(2, 'Header 2')}") }
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ RSpec.describe Banzai::Pipeline::PostProcessPipeline, feature_category: :markdow
|
|||
HTML
|
||||
end
|
||||
|
||||
let(:doc) { HTML::Pipeline.parse(html) }
|
||||
let(:doc) { Banzai::PipelineBase.parse(html) }
|
||||
let(:non_related_xpath_calls) { 1 }
|
||||
|
||||
it 'searches for attributes only once' do
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Banzai::PipelineBase, feature_category: :markdown do
|
||||
it 'subclasses HTML::Pipeline' do
|
||||
expect(described_class <= ::HTML::Pipeline).to be_truthy
|
||||
end
|
||||
|
||||
describe '#filter_subscription_name' do
|
||||
it 'adds thread id to subscription name' do
|
||||
expect(described_class.filter_subscription_name)
|
||||
.to eq "call_filter.html_pipeline_#{Thread.current.object_id}"
|
||||
end
|
||||
end
|
||||
|
||||
describe '.perform_filter' do
|
||||
it 'overrides the perform_filter method' do
|
||||
expect(described_class).to receive(:filter_subscription_name).and_call_original.at_least(:once)
|
||||
|
||||
Banzai::Pipeline::QuickActionPipeline.call('foo', project: nil)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -172,8 +172,10 @@ RSpec.describe Banzai::Renderer, feature_category: :markdown do
|
|||
end
|
||||
|
||||
describe 'instrumentation in render_result' do
|
||||
let(:subscription_name) { "call_filter.html_pipeline_#{Thread.current.object_id}" }
|
||||
|
||||
it 'calculates pipeline timing' do
|
||||
expect(ActiveSupport::Notifications).to receive(:monotonic_subscribe).with('call_filter.html_pipeline')
|
||||
expect(ActiveSupport::Notifications).to receive(:monotonic_subscribe).with(subscription_name)
|
||||
.and_call_original.at_least(:once)
|
||||
expect(ActiveSupport::Notifications).to receive(:unsubscribe).and_call_original.at_least(:once)
|
||||
expect(Rainbow).not_to receive(:new)
|
||||
|
|
@ -184,7 +186,7 @@ RSpec.describe Banzai::Renderer, feature_category: :markdown do
|
|||
end
|
||||
|
||||
it 'enables debug output' do
|
||||
expect(ActiveSupport::Notifications).to receive(:monotonic_subscribe).with('call_filter.html_pipeline')
|
||||
expect(ActiveSupport::Notifications).to receive(:monotonic_subscribe).with(subscription_name)
|
||||
.and_call_original.at_least(:once)
|
||||
expect(ActiveSupport::Notifications).to receive(:unsubscribe).and_call_original.at_least(:once)
|
||||
expect(Rainbow).to receive(:new).and_call_original.at_least(:once)
|
||||
|
|
@ -194,7 +196,7 @@ RSpec.describe Banzai::Renderer, feature_category: :markdown do
|
|||
end
|
||||
|
||||
it 'enables debug_timing output' do
|
||||
expect(ActiveSupport::Notifications).to receive(:monotonic_subscribe).with('call_filter.html_pipeline')
|
||||
expect(ActiveSupport::Notifications).to receive(:monotonic_subscribe).with(subscription_name)
|
||||
.and_call_original.at_least(:once)
|
||||
expect(ActiveSupport::Notifications).to receive(:unsubscribe).and_call_original.at_least(:once)
|
||||
expect(Rainbow).to receive(:new).and_call_original.at_least(:once)
|
||||
|
|
|
|||
|
|
@ -1150,11 +1150,25 @@ RSpec.describe Gitlab::Auth::OAuth::User, :aggregate_failures, feature_category:
|
|||
end
|
||||
|
||||
it "updates the user organization and job title" do
|
||||
expect(gl_user.organization).to eq(info_hash[:organization])
|
||||
expect(gl_user.user_detail_organization).to eq(info_hash[:organization])
|
||||
expect(gl_user.job_title).to eq(info_hash[:job_title])
|
||||
expect(gl_user.user_synced_attributes_metadata.organization_synced).to be(true)
|
||||
expect(gl_user.user_synced_attributes_metadata.job_title_synced).to be(true)
|
||||
end
|
||||
|
||||
context "when there is a mismatch with what attributes can be synced" do
|
||||
before do
|
||||
allow(UserSyncedAttributesMetadata).to receive(:syncable_attributes).and_return([:random_key])
|
||||
info_hash[:random_key] = "random value"
|
||||
allow_next_instance_of(Gitlab::Auth::OAuth::AuthHash) do |instance|
|
||||
allow(instance).to receive(:random_key).and_return(info_hash[:random_key])
|
||||
end
|
||||
end
|
||||
|
||||
it "raises an error" do
|
||||
expect { oauth_user }.to raise_error Gitlab::Auth::OAuth::User::UnknownAttributeMappingError
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "update only requested info" do
|
||||
|
|
@ -1178,7 +1192,7 @@ RSpec.describe Gitlab::Auth::OAuth::User, :aggregate_failures, feature_category:
|
|||
end
|
||||
|
||||
it "updates the user organization and job title" do
|
||||
expect(gl_user.organization).to eq(info_hash[:organization])
|
||||
expect(gl_user.user_detail_organization).to eq(info_hash[:organization])
|
||||
expect(gl_user.job_title).to eq(info_hash[:job_title])
|
||||
expect(gl_user.user_synced_attributes_metadata.organization_synced).to be(true)
|
||||
expect(gl_user.user_synced_attributes_metadata.job_title_synced).to be(true)
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ RSpec.describe Gitlab::Spamcheck::Client, feature_category: :instance_resiliency
|
|||
include_context 'includes Spam constants'
|
||||
|
||||
let(:endpoint) { 'grpc://grpc.test.url' }
|
||||
let_it_be(:user) { create(:user, organization: 'GitLab') }
|
||||
let_it_be(:user) { create(:user, user_detail_organization: 'GitLab') }
|
||||
let(:verdict_value) { ::Spamcheck::SpamVerdict::Verdict::ALLOW }
|
||||
let(:verdict_score) { 0.01 }
|
||||
let(:verdict_evaluated) { true }
|
||||
|
|
@ -169,7 +169,7 @@ RSpec.describe Gitlab::Spamcheck::Client, feature_category: :instance_resiliency
|
|||
user_pb = described_class.new.send(:build_user_protobuf, user)
|
||||
expect(user_pb.username).to eq user.username
|
||||
expect(user_pb.id).to eq user.id
|
||||
expect(user_pb.org).to eq user.organization
|
||||
expect(user_pb.org).to eq user.user_detail_organization
|
||||
expect(user_pb.created_at).to eq timestamp_to_protobuf_timestamp(user.created_at)
|
||||
expect(user_pb.emails.count).to be 1
|
||||
expect(user_pb.emails.first.email).to eq user.email
|
||||
|
|
|
|||
|
|
@ -46,22 +46,22 @@ RSpec.describe Gitlab::Utils::SanitizeNodeLink do
|
|||
context "with the scheme: #{scheme}" do
|
||||
tags = {
|
||||
a: {
|
||||
doc: HTML::Pipeline.parse("<a href='#{scheme}alert(1);'>foo</a>"),
|
||||
doc: Banzai::PipelineBase.parse("<a href='#{scheme}alert(1);'>foo</a>"),
|
||||
attr: "href",
|
||||
node_to_check: ->(doc) { doc.children.first }
|
||||
},
|
||||
img: {
|
||||
doc: HTML::Pipeline.parse("<img src='#{scheme}alert(1);'>"),
|
||||
doc: Banzai::PipelineBase.parse("<img src='#{scheme}alert(1);'>"),
|
||||
attr: "src",
|
||||
node_to_check: ->(doc) { doc.children.first }
|
||||
},
|
||||
video: {
|
||||
doc: HTML::Pipeline.parse("<video><source src='#{scheme}alert(1);'></video>"),
|
||||
doc: Banzai::PipelineBase.parse("<video><source src='#{scheme}alert(1);'></video>"),
|
||||
attr: "src",
|
||||
node_to_check: ->(doc) { doc.children.first.children.filter("source").first }
|
||||
},
|
||||
audio: {
|
||||
doc: HTML::Pipeline.parse("<audio><source src='#{scheme}alert(1);'></audio>"),
|
||||
doc: Banzai::PipelineBase.parse("<audio><source src='#{scheme}alert(1);'></audio>"),
|
||||
attr: "src",
|
||||
node_to_check: ->(doc) { doc.children.first.children.filter("source").first }
|
||||
}
|
||||
|
|
@ -82,7 +82,7 @@ RSpec.describe Gitlab::Utils::SanitizeNodeLink do
|
|||
end
|
||||
|
||||
context 'handling child nodes' do
|
||||
let(:doc) { HTML::Pipeline.parse(nodes_with_children) }
|
||||
let(:doc) { Banzai::PipelineBase.parse(nodes_with_children) }
|
||||
let(:node) { doc.children.first }
|
||||
|
||||
it 'santizes child nodes' do
|
||||
|
|
@ -101,7 +101,7 @@ RSpec.describe Gitlab::Utils::SanitizeNodeLink do
|
|||
end
|
||||
|
||||
context 'when URI is valid' do
|
||||
let(:doc) { HTML::Pipeline.parse("<a href='http://example.com'>foo</a>") }
|
||||
let(:doc) { Banzai::PipelineBase.parse("<a href='http://example.com'>foo</a>") }
|
||||
let(:node) { doc.children.first }
|
||||
|
||||
it 'does not remove it' do
|
||||
|
|
@ -112,7 +112,7 @@ RSpec.describe Gitlab::Utils::SanitizeNodeLink do
|
|||
end
|
||||
|
||||
context 'when URI is invalid' do
|
||||
let(:doc) { HTML::Pipeline.parse("<a href='http://example:wrong_port.com'>foo</a>") }
|
||||
let(:doc) { Banzai::PipelineBase.parse("<a href='http://example:wrong_port.com'>foo</a>") }
|
||||
let(:node) { doc.children.first }
|
||||
|
||||
it 'removes the link' do
|
||||
|
|
@ -123,7 +123,7 @@ RSpec.describe Gitlab::Utils::SanitizeNodeLink do
|
|||
end
|
||||
|
||||
context 'when URI is encoded but still invalid' do
|
||||
let(:doc) { HTML::Pipeline.parse("<a href='http://example%EF%BC%9A%E7%BD%91'>foo</a>") }
|
||||
let(:doc) { Banzai::PipelineBase.parse("<a href='http://example%EF%BC%9A%E7%BD%91'>foo</a>") }
|
||||
let(:node) { doc.children.first }
|
||||
|
||||
it 'removes the link' do
|
||||
|
|
@ -137,7 +137,7 @@ RSpec.describe Gitlab::Utils::SanitizeNodeLink do
|
|||
describe "#safe_protocol?" do
|
||||
invalid_schemes.each do |scheme|
|
||||
context "with the scheme: #{scheme}" do
|
||||
let(:doc) { HTML::Pipeline.parse("<a href='#{scheme}alert(1);'>foo</a>") }
|
||||
let(:doc) { Banzai::PipelineBase.parse("<a href='#{scheme}alert(1);'>foo</a>") }
|
||||
let(:node) { doc.children.first }
|
||||
let(:uri) { Addressable::URI.parse(node['href']) }
|
||||
|
||||
|
|
@ -150,7 +150,7 @@ RSpec.describe Gitlab::Utils::SanitizeNodeLink do
|
|||
|
||||
describe '#sanitize_unsafe_links' do
|
||||
it 'sanitizes all nodes without specifically recursing children' do
|
||||
doc = HTML::Pipeline.parse(nodes_with_children)
|
||||
doc = Banzai::PipelineBase.parse(nodes_with_children)
|
||||
allowlist = { elements: %w[details summary a], attributes: { 'a' => ['href'] },
|
||||
transformers: [object.method(:sanitize_unsafe_links)] }
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
require_migration!
|
||||
|
||||
RSpec.describe QueueBackfillAnalyzerProjectStatusesFromProjectSecuritySettings, migration: :gitlab_main, feature_category: :security_asset_inventories do
|
||||
let!(:batched_migration) { described_class::MIGRATION }
|
||||
|
||||
it 'schedules a new batched migration' do
|
||||
reversible_migration do |migration|
|
||||
migration.before -> {
|
||||
expect(batched_migration).not_to have_scheduled_batched_migration
|
||||
}
|
||||
|
||||
migration.after -> {
|
||||
expect(batched_migration).to have_scheduled_batched_migration(
|
||||
gitlab_schema: :gitlab_main,
|
||||
table_name: :project_security_settings,
|
||||
column_name: :project_id,
|
||||
interval: described_class::DELAY_INTERVAL,
|
||||
batch_size: described_class::BATCH_SIZE,
|
||||
sub_batch_size: described_class::SUB_BATCH_SIZE
|
||||
)
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -2682,6 +2682,38 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep, feature_category:
|
|||
end
|
||||
end
|
||||
|
||||
describe '#has_exposed_artifacts?' do
|
||||
let(:pipeline) { create(:ci_pipeline, :success) }
|
||||
let(:options) { nil }
|
||||
|
||||
subject { pipeline.has_exposed_artifacts? }
|
||||
|
||||
before do
|
||||
create(:ci_build, pipeline: pipeline)
|
||||
create(:ci_build, options: options, pipeline: pipeline)
|
||||
end
|
||||
|
||||
it { is_expected.to eq(false) }
|
||||
|
||||
context 'with unexposed artifacts' do
|
||||
let(:options) { { artifacts: { paths: ['test'] } } }
|
||||
|
||||
it { is_expected.to eq(false) }
|
||||
end
|
||||
|
||||
context 'with exposed artifacts' do
|
||||
let(:options) { { artifacts: { expose_as: 'test', paths: ['test'] } } }
|
||||
|
||||
it { is_expected.to eq(true) }
|
||||
|
||||
context 'when the pipeline is not complete' do
|
||||
let(:pipeline) { create(:ci_pipeline, :running) }
|
||||
|
||||
it { is_expected.to eq(false) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#manual_actions' do
|
||||
subject { pipeline.manual_actions }
|
||||
|
||||
|
|
|
|||
|
|
@ -477,7 +477,7 @@ RSpec.describe UserDetail, feature_category: :system_access do
|
|||
bluesky: 'did:plc:ewvi7nxzyoun6zhxrhs64oiz',
|
||||
orcid: '1234-1234-1234-1234',
|
||||
mastodon: '@robin@example.com',
|
||||
organization: 'organization',
|
||||
user_detail_organization: 'organization',
|
||||
twitter: 'twitter',
|
||||
website_url: 'https://example.com'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -196,8 +196,8 @@ RSpec.describe User, feature_category: :user_profile do
|
|||
it { is_expected.to delegate_method(:location).to(:user_detail).allow_nil }
|
||||
it { is_expected.to delegate_method(:location=).to(:user_detail).with_arguments(:args).allow_nil }
|
||||
|
||||
it { is_expected.to delegate_method(:organization).to(:user_detail).allow_nil }
|
||||
it { is_expected.to delegate_method(:organization=).to(:user_detail).with_arguments(:args).allow_nil }
|
||||
it { is_expected.to delegate_method(:organization).to(:user_detail).with_prefix.allow_nil }
|
||||
it { is_expected.to delegate_method(:organization=).to(:user_detail).with_arguments(:args).with_prefix.allow_nil }
|
||||
|
||||
it { is_expected.to delegate_method(:email_reset_offered_at).to(:user_detail).allow_nil }
|
||||
it { is_expected.to delegate_method(:email_reset_offered_at=).to(:user_detail).with_arguments(:args).allow_nil }
|
||||
|
|
@ -354,26 +354,27 @@ RSpec.describe User, feature_category: :user_profile do
|
|||
expect(create(:user).user_detail).to be_persisted
|
||||
end
|
||||
|
||||
shared_examples 'delegated field' do |field|
|
||||
shared_examples 'delegated field' do |field, prefix: nil|
|
||||
field_name = prefix ? :"#{prefix}_#{field}" : field
|
||||
it 'correctly stores the `user_detail` attribute when the field is given on user creation' do
|
||||
user = create(:user, field => 'my field')
|
||||
user = create(:user, field_name => 'my field')
|
||||
|
||||
expect(user.user_detail).to be_persisted
|
||||
expect(user.user_detail[field]).to eq('my field')
|
||||
end
|
||||
|
||||
it 'delegates to `user_detail`' do
|
||||
user = create(:user, field => 'my field')
|
||||
user = create(:user, field_name => 'my field')
|
||||
|
||||
expect(user.public_send(field)).to eq(user.user_detail[field])
|
||||
expect(user.public_send(field_name)).to eq(user.user_detail[field])
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'delegated field', :organization, prefix: 'user_detail'
|
||||
it_behaves_like 'delegated field', :bio
|
||||
it_behaves_like 'delegated field', :linkedin
|
||||
it_behaves_like 'delegated field', :twitter
|
||||
it_behaves_like 'delegated field', :location
|
||||
it_behaves_like 'delegated field', :organization
|
||||
|
||||
it 'creates `user_detail` when `website_url` is given' do
|
||||
user = create(:user, website_url: 'https://example.com')
|
||||
|
|
@ -5656,10 +5657,12 @@ RSpec.describe User, feature_category: :user_profile do
|
|||
describe '#ci_owned_runners', feature_category: :runner do
|
||||
let_it_be_with_refind(:user) { create(:user) }
|
||||
|
||||
subject(:ci_owned_runners) { user.ci_owned_runners }
|
||||
|
||||
shared_examples 'nested groups owner' do
|
||||
context 'when the user is the owner of a multi-level group' do
|
||||
it 'loads all the runners in the tree of groups' do
|
||||
expect(user.ci_owned_runners).to contain_exactly(runner, group_runner)
|
||||
is_expected.to contain_exactly(runner, group_runner)
|
||||
end
|
||||
|
||||
it 'returns true for owns_runner?' do
|
||||
|
|
@ -5670,6 +5673,8 @@ RSpec.describe User, feature_category: :user_profile do
|
|||
end
|
||||
|
||||
shared_examples 'project member' do
|
||||
let_it_be(:another_project_runner) { create(:ci_runner, :project, projects: [another_project, project]) }
|
||||
|
||||
before do
|
||||
add_user # this method has to be defined in the caller block
|
||||
end
|
||||
|
|
@ -5678,11 +5683,12 @@ RSpec.describe User, feature_category: :user_profile do
|
|||
let(:access) { :maintainer }
|
||||
|
||||
it 'loads the runners of the project' do
|
||||
expect(user.ci_owned_runners).to contain_exactly(project_runner)
|
||||
is_expected.to contain_exactly(project_runner, another_project_runner)
|
||||
end
|
||||
|
||||
it 'returns true for owns_runner?' do
|
||||
expect(user.owns_runner?(project_runner)).to eq(true)
|
||||
expect(user.owns_runner?(another_project_runner)).to eq(true)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -5690,11 +5696,12 @@ RSpec.describe User, feature_category: :user_profile do
|
|||
let(:access) { :developer }
|
||||
|
||||
it 'does not load any runner' do
|
||||
expect(user.ci_owned_runners).to be_empty
|
||||
is_expected.to be_empty
|
||||
end
|
||||
|
||||
it 'returns false for owns_runner?' do
|
||||
expect(user.owns_runner?(project_runner)).to eq(false)
|
||||
expect(user.owns_runner?(another_project_runner)).to eq(false)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -5702,11 +5709,12 @@ RSpec.describe User, feature_category: :user_profile do
|
|||
let(:access) { :reporter }
|
||||
|
||||
it 'does not load any runner' do
|
||||
expect(user.ci_owned_runners).to be_empty
|
||||
is_expected.to be_empty
|
||||
end
|
||||
|
||||
it 'returns false for owns_runner?' do
|
||||
expect(user.owns_runner?(project_runner)).to eq(false)
|
||||
expect(user.owns_runner?(another_project_runner)).to eq(false)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -5714,11 +5722,12 @@ RSpec.describe User, feature_category: :user_profile do
|
|||
let(:access) { :guest }
|
||||
|
||||
it 'does not load any runner' do
|
||||
expect(user.ci_owned_runners).to be_empty
|
||||
is_expected.to be_empty
|
||||
end
|
||||
|
||||
it 'returns false for owns_runner?' do
|
||||
expect(user.owns_runner?(project_runner)).to eq(false)
|
||||
expect(user.owns_runner?(another_project_runner)).to eq(false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -5730,7 +5739,7 @@ RSpec.describe User, feature_category: :user_profile do
|
|||
end
|
||||
|
||||
it 'does not load the runners of the group' do
|
||||
expect(user.ci_owned_runners).to be_empty
|
||||
is_expected.to be_empty
|
||||
end
|
||||
|
||||
it 'returns false for owns_runner?' do
|
||||
|
|
@ -5744,7 +5753,7 @@ RSpec.describe User, feature_category: :user_profile do
|
|||
end
|
||||
|
||||
it 'does not load any runner' do
|
||||
expect(user.ci_owned_runners).to be_empty
|
||||
is_expected.to be_empty
|
||||
end
|
||||
|
||||
it 'returns false for owns_runner?' do
|
||||
|
|
@ -5880,6 +5889,7 @@ RSpec.describe User, feature_category: :user_profile do
|
|||
let_it_be(:group) { create(:group) }
|
||||
let_it_be(:project) { create(:project, group: group) }
|
||||
let_it_be(:runner) { create(:ci_runner, :project, projects: [project]) }
|
||||
let_it_be(:another_project) { create(:project) }
|
||||
|
||||
it 'does not load any runner' do
|
||||
expect(user.ci_owned_runners).to be_empty
|
||||
|
|
@ -5902,6 +5912,17 @@ RSpec.describe User, feature_category: :user_profile do
|
|||
|
||||
it_behaves_like 'nested groups owner'
|
||||
end
|
||||
|
||||
context 'with a project runner owned by inaccessible project' do
|
||||
let_it_be(:another_project_runner) { create(:ci_runner, :project, projects: [another_project, project]) }
|
||||
|
||||
it 'returns runner shared from inaccessible project' do
|
||||
expect(ci_owned_runners).to contain_exactly(runner, another_project_runner)
|
||||
|
||||
expect(user.owns_runner?(another_project_runner)).to be true
|
||||
expect(user.owns_runner?(runner)).to be true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user is member of project' do
|
||||
|
|
@ -5944,7 +5965,21 @@ RSpec.describe User, feature_category: :user_profile do
|
|||
)
|
||||
expect(user).not_to receive(:ci_owned_project_runners)
|
||||
|
||||
user.ci_owned_runners
|
||||
ci_owned_runners
|
||||
end
|
||||
|
||||
context 'with a project runner owned by inaccessible project' do
|
||||
let_it_be(:project) { create(:project, owners: user) }
|
||||
let_it_be(:runner) { create(:ci_runner, :project, projects: [project]) }
|
||||
let_it_be(:another_project) { create(:project) }
|
||||
let_it_be(:another_project_runner) { create(:ci_runner, :project, projects: [another_project, project]) }
|
||||
|
||||
it 'returns runner shared from inaccessible project' do
|
||||
expect(ci_owned_runners).to contain_exactly(runner, another_project_runner)
|
||||
|
||||
expect(user.owns_runner?(another_project_runner)).to be true
|
||||
expect(user.owns_runner?(runner)).to be true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -5961,7 +5996,7 @@ RSpec.describe User, feature_category: :user_profile do
|
|||
expect(user).not_to receive(:ci_owned_project_runners_from_project_members)
|
||||
expect(user).not_to receive(:ci_owned_project_runners_from_group_members)
|
||||
|
||||
user.ci_owned_runners
|
||||
ci_owned_runners
|
||||
end
|
||||
|
||||
context 'when feature flag is only enabled for other user' do
|
||||
|
|
@ -5972,7 +6007,7 @@ RSpec.describe User, feature_category: :user_profile do
|
|||
expect(user).to receive(:ci_owned_project_runners_from_group_members).and_return([])
|
||||
expect(user).to receive(:ci_owned_group_runners).and_return([])
|
||||
|
||||
user.ci_owned_runners
|
||||
ci_owned_runners
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -443,7 +443,7 @@ RSpec.describe 'Query.runner(id)', :freeze_time, feature_category: :fleet_visibi
|
|||
end
|
||||
|
||||
let_it_be(:owned_project_owner) { create(:user) }
|
||||
let_it_be(:owned_project) { create(:project) }
|
||||
let_it_be(:owned_project) { create(:project, owners: owned_project_owner) }
|
||||
let_it_be(:other_project) { create(:project) }
|
||||
let_it_be(:project_runner) { create(:ci_runner, :project_type, projects: [other_project, owned_project]) }
|
||||
let_it_be(:owned_project_pipeline) { create(:ci_pipeline, project: owned_project) }
|
||||
|
|
@ -458,10 +458,6 @@ RSpec.describe 'Query.runner(id)', :freeze_time, feature_category: :fleet_visibi
|
|||
tag_list: %i[d e f], created_at: 30.minutes.ago, started_at: 19.minutes.ago, finished_at: 1.minute.ago)
|
||||
end
|
||||
|
||||
before_all do
|
||||
owned_project.add_owner(owned_project_owner)
|
||||
end
|
||||
|
||||
it 'returns empty values for sensitive fields in non-owned jobs' do
|
||||
post_graphql(query, current_user: owned_project_owner)
|
||||
|
||||
|
|
@ -825,11 +821,7 @@ RSpec.describe 'Query.runner(id)', :freeze_time, feature_category: :fleet_visibi
|
|||
end
|
||||
|
||||
describe 'by non-admin user' do
|
||||
let(:user) { create(:user) }
|
||||
|
||||
before do
|
||||
group.add_member(user, Gitlab::Access::OWNER)
|
||||
end
|
||||
let(:user) { create(:user, owner_of: group) }
|
||||
|
||||
it_behaves_like 'retrieval with no admin url' do
|
||||
let(:runner) { active_group_runner }
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ require 'spec_helper'
|
|||
|
||||
RSpec.describe API::GroupMilestones, feature_category: :team_planning do
|
||||
let_it_be(:organization) { create(:organization) }
|
||||
let_it_be(:user) { create(:user, organization: organization) }
|
||||
let_it_be(:user) { create(:user, user_detail_organization: organization) }
|
||||
let_it_be_with_refind(:group) { create(:group, :private) }
|
||||
let_it_be(:project) { create(:project, namespace: group, organization: organization) }
|
||||
let_it_be(:group_member) { create(:group_member, group: group, user: user) }
|
||||
|
|
|
|||
|
|
@ -1857,7 +1857,7 @@ RSpec.describe API::Users, :with_current_organization, :aggregate_failures, feat
|
|||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(json_response['organization']).to eq('GitLab')
|
||||
expect(user.reload.organization).to eq('GitLab')
|
||||
expect(user.reload.user_detail_organization).to eq('GitLab')
|
||||
end
|
||||
|
||||
it 'updates user with avatar' do
|
||||
|
|
|
|||
|
|
@ -137,7 +137,7 @@ RSpec.describe Users::BuildService, feature_category: :user_management do
|
|||
username: 1,
|
||||
website_url: 1,
|
||||
private_profile: 1,
|
||||
organization: 1,
|
||||
user_detail_organization: 1,
|
||||
location: 1,
|
||||
public_email: 1,
|
||||
user_type: 'project_bot',
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ module FilterSpecHelper
|
|||
def pipeline_result(body, context = {})
|
||||
context.reverse_merge!(project: project) if defined?(project)
|
||||
|
||||
pipeline = HTML::Pipeline.new([described_class], context)
|
||||
pipeline = Banzai::PipelineBase.new([described_class], context)
|
||||
pipeline.call(body)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -317,6 +317,7 @@ type GitAuditEventRequest struct {
|
|||
Repo string `json:"gl_repository"`
|
||||
Username string `json:"username"`
|
||||
PackfileStats *gitalypb.PackfileNegotiationStatistics `json:"packfile_stats,omitempty"`
|
||||
Changes string `json:"changes"`
|
||||
}
|
||||
|
||||
// SendGitAuditEvent sends a Git audit event using the API client.
|
||||
|
|
|
|||
|
|
@ -222,6 +222,7 @@ func TestSendGitAuditEvent(t *testing.T) {
|
|||
Protocol: "http",
|
||||
Repo: "project-1",
|
||||
Username: "GitLab-Shell",
|
||||
Changes: "_any",
|
||||
PackfileStats: &gitalypb.PackfileNegotiationStatistics{
|
||||
Wants: 3,
|
||||
Haves: 23,
|
||||
|
|
|
|||
|
|
@ -109,6 +109,7 @@ func sendGitAuditEvent(action string) func(*api.API, *http.Request, *api.Respons
|
|||
Repo: response.GL_REPOSITORY,
|
||||
Username: response.GL_USERNAME,
|
||||
PackfileStats: stats,
|
||||
Changes: "_any",
|
||||
})
|
||||
if err != nil {
|
||||
log.WithContextFields(ctx, log.Fields{
|
||||
|
|
|
|||
Loading…
Reference in New Issue