Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2025-04-30 00:12:17 +00:00
parent 8bfaf69a3b
commit 12fc930567
17 changed files with 557 additions and 34 deletions

View File

@ -1,5 +1,5 @@
<script>
import { isEqual, omit } from 'lodash';
import { isEqual, omit, isEmpty } from 'lodash';
import {
GlAlert,
GlButton,
@ -323,6 +323,9 @@ export default {
}
return this.variable;
},
defaultVariableState() {
return { ...defaultVariableState, protected: this.isProtectedByDefault };
},
},
watch: {
mutationResponse: {
@ -338,12 +341,29 @@ export default {
handler(variable) {
this.trackVariableValidationErrors();
if (this.isMutationAlertVisible && !isEqual(variable, { ...defaultVariableState })) {
if (this.isMutationAlertVisible && !isEqual(variable, { ...this.defaultVariableState })) {
this.hideMutationAlert();
}
},
deep: true,
},
selectedVariable: {
handler(variable) {
if (!isEmpty(variable)) {
this.variable = { ...variable };
// translate masked and hidden flags to visibility options
let visibility = VISIBILITY_VISIBLE;
if (this.variable.hidden) visibility = VISIBILITY_HIDDEN;
else if (this.variable.masked) visibility = VISIBILITY_MASKED;
this.visibility = visibility;
} else {
this.resetForm();
}
},
immediate: true,
},
},
beforeMount() {
// reset to default environments list every time we open the drawer
@ -354,13 +374,6 @@ export default {
if (this.isProtectedByDefault && !this.isEditing) {
this.variable = { ...this.variable, protected: true };
}
// translate masked and hidden flags to visibility options
let visibility = VISIBILITY_VISIBLE;
if (this.variable.hidden) visibility = VISIBILITY_HIDDEN;
else if (this.variable.masked) visibility = VISIBILITY_MASKED;
this.visibility = visibility;
},
methods: {
close() {
@ -388,7 +401,7 @@ export default {
this.isMutationAlertVisible = false;
},
resetForm() {
this.variable = { ...defaultVariableState };
this.variable = { ...this.defaultVariableState };
this.visibility = VISIBILITY_VISIBLE;
},

View File

@ -380,9 +380,14 @@ export default {
{{ $options.i18n.reportAbuse }}
</template>
</gl-disclosure-dropdown-item>
<gl-disclosure-dropdown-item v-if="canEdit" class="js-note-delete" @action="onDelete">
<gl-disclosure-dropdown-item
v-if="canEdit"
class="js-note-delete"
variant="danger"
@action="onDelete"
>
<template #list-item>
<span class="gl-text-danger">{{ __('Delete comment') }}</span>
{{ __('Delete comment') }}
</template>
</gl-disclosure-dropdown-item>
</gl-disclosure-dropdown-group>

View File

@ -38,6 +38,20 @@ of Sidekiq threads from 25 to 50. See the
[Sidekiq concurrency documentation](../../sidekiq/extra_sidekiq_processes.md#concurrency)
for more details.
## Tuning low default settings
To avoid excessive load when setting up new Geo sites, starting with GitLab 18.0,
Geo's concurrency settings are set to low defaults for most environments.
To increase these settings:
1. On the left sidebar, at the bottom, select **Admin**.
1. Select **Geo > Sites**.
1. Decide which data types are progressing too slowly.
1. Watch load metrics of the primary and secondary sites.
1. Increase *concurrency limits* by 10 to be conservative.
1. Watch changes in progress and load metrics for at least 3 minutes.
1. Repeat increasing the limits until either load metrics reach your desired maximum, or syncing and verification is progressing as quickly as desired.
## Repository re-verification
See

View File

@ -3062,6 +3062,7 @@ Input type: `BoardListCreateInput`
| <a id="mutationboardlistcreatelabelid"></a>`labelId` | [`LabelID`](#labelid) | Global ID of an existing label. |
| <a id="mutationboardlistcreatemilestoneid"></a>`milestoneId` | [`MilestoneID`](#milestoneid) | Global ID of an existing milestone. |
| <a id="mutationboardlistcreateposition"></a>`position` | [`Int`](#int) | Position of the list. |
| <a id="mutationboardlistcreatestatusid"></a>`statusId` {{< icon name="warning-solid" >}} | [`WorkItemsStatusesStatusID`](#workitemsstatusesstatusid) | **Deprecated:** **Status**: Experiment. Introduced in GitLab 18.0. |
#### Fields

View File

@ -1160,6 +1160,7 @@ To run dependency scanning in an offline environment you must have:
- A GitLab Runner with the `docker` or `kubernetes` executor
- Local copies of the dependency scanning analyzer images
- Access to the [GitLab Advisory Database](https://gitlab.com/gitlab-org/security-products/gemnasium-db)
- Access to the [Package Metadata Database](../../../topics/offline/quick_start_guide.md#enabling-the-package-metadata-database)
### Local copies of analyzer images

View File

@ -119,7 +119,7 @@ Enable the Dependency Scanning using SBOM feature with one of the following opti
- The (deprecated) Gemnasium analyzer is used by default.
- To enable the new Dependency Scanning analyzer, set the CI/CD variable `DS_ENFORCE_NEW_ANALYZER` to `true`.
- Use the [Dependency Scanning CI/CD component](https://gitlab.com/explore/catalog/components/dependency-scanning) to enable the new Dependency Scanning analyzer.
- Provide your own CycloneDX SBOM document.
- Provide your own CycloneDX SBOM document as [a CI/CD artifact report](../../../../ci/yaml/artifacts_reports.md#artifactsreportscyclonedx) from a successful pipeline.
The preferred method is to use the new Dependency Scanning analyzer and this is what is documented in the next section.
To enable the (deprecated) Gemnasium analyzer please refer to the enablement instructions for the [legacy Dependency Scanning feature](../_index.md#enabling-the-analyzer).

View File

@ -36,6 +36,7 @@ The following policy types are available:
- [Merge request approval policy](merge_request_approval_policies.md). Enforce project-level settings and
approval rules based on scan results.
- [Pipeline execution policy](pipeline_execution_policies.md). Enforce CI/CD jobs as part of project pipelines.
- [Scheduled pipeline execution policy (experiment)](scheduled_pipeline_execution_policies.md). Enforce custom CI/CD jobs on a scheduled cadence across projects, independent of commit activity.
- [Vulnerability management policy](vulnerability_management_policy.md). Automatically resolve
vulnerabilities that are no longer detected in the default branch.

View File

@ -0,0 +1,240 @@
---
stage: Security Risk Management
group: Security Policies
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
title: Scheduled pipeline execution policies (Experimental)
---
{{< details >}}
- Tier: Ultimate
- Offering: GitLab.com, GitLab Self-Managed, GitLab Dedicated
{{< /details >}}
{{< history >}}
- [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/14147) as an experiment in GitLab 18.0 with an experimental flag named `scheduled_pipeline_execution_policy_type` defined in the `policy.yml` file.
{{< /history >}}
Pipeline execution policies enforce custom CI/CD jobs in your projects' pipelines. With scheduled pipeline execution policies, you can extend this enforcement to run the CI/CD job on a regular cadence (daily, weekly, or monthly), ensuring that compliance scripts, security scans, or other custom CI/CD job are executed even when there are no new commits.
## Scheduling your pipeline execution policies
Unlike regular pipeline execution policies that inject or override jobs in existing pipelines, scheduled policies create new pipelines that run independently on the schedule you define.
Common use cases include:
- Enforce security scans on a regular cadence to meet compliance requirements.
- Check project configurations periodically.
- Run dependency scans on inactive repositories to detect newly discovered vulnerabilities.
- Execute compliance reporting scripts on a schedule.
## Enable scheduled pipeline execution policies
Scheduled pipeline execution policies are available as an experimental feature. To enable this feature in your environment, enable the `pipeline_execution_schedule_policy` experiment in the security policy configuration. The `.gitlab/security-policies/policy.yml` YAML configuration file is stored in your Security Policy Project:
```yaml
experiments:
pipeline_execution_schedule_policy:
enabled: true
```
{{< alert type="note" >}}
This feature is experimental and may change in future releases. You should test it thoroughly in a non-production environment only. You should not use this feature in production environments as it may be unstable.
{{< /alert >}}
## Configure schedule pipeline execution policies
To configure a scheduled pipeline execution policy, add additional configuration fields to the `pipeline_execution_schedule_policy` section of your security policy project's `.gitlab/security-policies/policy.yml` file:
```yaml
pipeline_execution_schedule_policy:
- name: Scheduled Pipeline Execution Policy
description: ''
enabled: true
content:
include:
- project: your-group/your-project
file: security-scan.yml
schedules:
- type: daily
start_time: '10:00'
time_window:
value: 600
distribution: random
```
### Schedule configuration schema
The `schedules` section allows you to configure when security policy jobs run automatically. You can create daily, weekly, or monthly schedules with specific execution times and distribution windows.
### Schedules configuration options
The `schedules` section supports the following options:
| Parameter | Description |
|-----------|-------------|
| `type` | Schedule type: `daily`, `weekly`, or `monthly` |
| `start_time` | Time to start the schedule in 24-hour format (HH:MM) |
| `time_window` | Time window in which to distribute the pipeline executions |
| `time_window.value` | Duration in seconds (minimum: 600, maximum: 2629746) |
| `time_window.distribution` | Distribution method (currently, only `random` is supported) |
| `timezone` | IANA timezone identifier (defaults to UTC if not specified) |
| `days` | Use with weekly schedules only: Array of days when the schedule runs (for example, `["Monday", "Friday"]`) |
| `days_of_month` | Use with monthly schedules only: Array of dates when the schedule runs (for example, `[1, 15]`, can include values from 1 to 31) |
| `snooze` | Optional configuration to temporarily pause the schedule |
| `snooze.until` | ISO8601 date and time when the schedule resumes after the snooze (format: `2025-06-13T20:20:00+00:00`) |
| `snooze.reason` | Optional documentation explaining why the schedule is snoozed |
### Schedule examples
Use daily, weekly, or monthly schedules.
#### Daily schedule example
```yaml
schedules:
- type: daily
start_time: "01:00"
time_window:
value: 3600 # 1 hour window
distribution: random
timezone: "America/New_York"
```
#### Weekly schedule example
```yaml
schedules:
- type: weekly
days:
- Monday
- Wednesday
- Friday
start_time: "04:30"
time_window:
value: 7200 # 2 hour window
distribution: random
timezone: "Europe/Berlin"
```
#### Monthly schedule example
```yaml
schedules:
- type: monthly
days_of_month:
- 1
- 15
start_time: "02:15"
time_window:
value: 14400 # 4 hour window
distribution: random
timezone: "Asia/Tokyo"
```
### Time window distribution
To prevent overwhelming your CI/CD infrastructure when applying policies to multiple projects, scheduled pipeline execution policies distribute the creation of the pipelines across a time window with some common rules:
- All pipelines are scheduled at `random`. Pipelines are randomly distributed during the specified time window.
- The minimum time window is 10 minutes (600 seconds), and the maximum is approximately 1 month (2,629,746 seconds).
- For monthly schedules, if you specify dates that don't exist in certain months (like 31 for February), those runs are skipped.
- A scheduled policy can only have one schedule configuration at a time.
## Snooze scheduled pipeline execution policies
You can temporarily pause scheduled pipeline execution policies using the snooze feature. Use the snooze feature during maintenance windows, holidays, or when you need to prevent scheduled pipelines from running for a specific time period.
### How snoozing works
When you snooze a scheduled pipeline execution policy:
- No new scheduled pipelines are created during the snooze period.
- Pipelines that were created before the snooze continue to execute.
- The policy remains enabled but in a snoozed state.
- After the snooze period expires, scheduled pipeline execution resumes automatically.
### Configuring snooze
To snooze a scheduled pipeline execution policy, add a `snooze` section to the schedule configuration:
```yaml
pipeline_execution_schedule_policy:
- name: Weekly Security Scan
description: 'Run security scans every week'
enabled: true
content:
include:
- project: your-group/your-project
file: security-scan.yml
schedules:
- type: weekly
start_time: '02:00'
time_window:
value: 3600
distribution: random
timezone: UTC
days:
- Monday
snooze:
until: "2025-06-26T16:27:00+00:00" # ISO8601 format
reason: "Critical production deployment"
```
The `snooze.until` parameter specifies when the snooze period ends using the ISO8601 format: `YYYY-MM-DDThh:mm:ss+00:00` where:
- `YYYY-MM-DD`: Year, month, and day
- `T`: Separator between date and time
- `hh:mm:ss`: Hours, minutes, and seconds in 24-hour format
- `+00:00`: Time zone offset from UTC (or Z for UTC)
For example, `2025-06-26T16:27:00+00:00` represents June 26, 2025, at 4:27 PM UTC.
### Removing a snooze
To remove a snooze before its expiration time, remove the `snooze` section from the policy configuration or set a date in the past for the `until` value.
## Requirements
To use scheduled pipeline execution policies:
1. Store your CI/CD configuration in your security policy project or in a project accessible by the security policy bot.
1. In your security policy project's **Settings** > **General** > **Visibility, project features, permissions** section, enable the **Grant security policy project access to CI/CD configuration** setting.
1. Ensure your CI/CD configuration includes appropriate workflow rules for scheduled pipelines.
The security policy bot is a system account that GitLab automatically creates to handle the execution of security policies. When you enable the appropriate settings, this bot is granted the necessary permissions to access CI/CD configurations and run scheduled pipelines. The permissions are only necessary if the CI/CD configurations is not in a public project.
Note these limitations:
- Scheduled pipeline execution policies can only run on default branches.
- Time windows must be at least 10 minutes (600 seconds) to ensure proper distribution of pipelines.
- The maximum number of scheduled pipeline execution policies per security policy project is limited to 1 policy with 1 schedule.
- This feature is experimental and may change in future releases.
- Scheduled pipelines can be delayed if there are insufficient runners available.
- The maximum frequency for schedules is daily.
## Troubleshooting
If your scheduled pipelines are not running as expected, follow these troubleshooting steps:
1. **Verify experimental flag**: Ensure that the `pipeline_execution_schedule_policy: enabled: true` flag is set in the `experiments` section of your `policy.yml` file.
1. **Check policy access**: Verify that your security policy project has access to the CI/CD configuration:
- Go to the security policy project's **Settings** > **General** > **Visibility, project features, permissions** and ensure the "Pipeline execution policies" setting is enabled.
1. **Validate CI configuration**:
- Check that the CI/CD configuration file exists at the specified path.
- Verify the configuration is valid by running a manual pipeline.
- Ensure the configuration includes appropriate workflow rules for scheduled pipelines.
1. **Verify policy configuration**:
- Ensure the policy is enabled (`enabled: true`).
- Verify taht the schedule configuration has the correct format and valid values.
- Verify that the time zone setting is correct (if specified).
1. **Review logs and activity**:
- Check the security policy project's CI/CD pipeline logs for any errors.
1. **Check runner availability**:
- Ensure that runners are available and configured properly.
- Verify that runners have the capacity to handle the scheduled jobs.

View File

@ -180,7 +180,9 @@ We are actively working to expand coverage to more types of vulnerabilities.
<ul>
<li>CWE-23: Relative Path Traversal</li>
<li>CWE-73: External Control of File Name or Path</li>
<li>CWE-78: Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')</li>
<li>CWE-80: Improper Neutralization of Script-Related HTML Tags in a Web Page (Basic XSS)</li>
<li>CWE-89: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')</li>
<li>CWE-116: Improper Encoding or Escaping of Output</li>
<li>CWE-118: Incorrect Access of Indexable Resource ('Range Error')</li>
<li>CWE-119: Improper Restriction of Operations within the Bounds of a Memory Buffer</li>

View File

@ -13,7 +13,11 @@ module Backup
super(progress: progress, options: options)
end
def human_name = _('repositories')
def human_name
return _('repositories') unless @options.repositories_server_side_backup
_('repositories (Gitaly server-side)')
end
def destination_path = 'repositories'

View File

@ -42,17 +42,11 @@ module Gitlab
end
def frontend_client_options(group)
additional_features = Feature.enabled?(:additional_snowplow_tracking, group, type: :ops)
# Using camel case as these keys will be used only in JavaScript
{
namespace: SNOWPLOW_NAMESPACE,
hostname: hostname,
cookieDomain: cookie_domain,
appId: app_id,
formTracking: additional_features,
linkClickTracking: additional_features
}
if Gitlab::CurrentSettings.snowplow_enabled? || ::Feature.disabled?(:collect_product_usage_events, :instance)
snowplow_options(group)
else
product_usage_events_options
end
end
def enabled?
@ -70,6 +64,31 @@ module Gitlab
private
def snowplow_options(group)
additional_features = Feature.enabled?(:additional_snowplow_tracking, group, type: :ops)
# Using camel case as these keys will be used only in JavaScript
{
namespace: SNOWPLOW_NAMESPACE,
hostname: hostname,
cookieDomain: cookie_domain,
appId: app_id,
formTracking: additional_features,
linkClickTracking: additional_features
}
end
def product_usage_events_options
# Using camel case as these keys will be used only in JavaScript
{
namespace: SNOWPLOW_NAMESPACE,
hostname: Gitlab.host_with_port,
postPath: Rails.application.routes.url_helpers.event_forwarding_path,
forceSecureTracker: Gitlab.config.gitlab.https,
appId: app_id
}
end
def app_id
Gitlab::CurrentSettings.snowplow_app_id
end

View File

@ -11,8 +11,8 @@ module Gitlab
DEFAULT_URI = 'http://localhost:9090'
override :frontend_client_options
def frontend_client_options(group)
override :snowplow_options
def snowplow_options(group)
super.merge(
protocol: uri.scheme,
port: uri.port,

View File

@ -58830,6 +58830,9 @@ msgstr ""
msgid "Status checks must pass."
msgstr ""
msgid "Status lists not available with your current license"
msgstr ""
msgid "Status not supported"
msgstr ""
@ -73145,6 +73148,9 @@ msgstr ""
msgid "repositories"
msgstr ""
msgid "repositories (Gitaly server-side)"
msgstr ""
msgid "repository"
msgid_plural "repositories"
msgstr[0] ""

View File

@ -218,6 +218,90 @@ describe('CI Variable Drawer', () => {
});
});
describe('when selected variable changes', () => {
beforeEach(() => {
createComponent({
props: { selectedVariable: mockProjectVariable },
});
});
describe('when selected variable is not empty', () => {
const newVariable = mockVariablesWithScopes(projectString)[1];
it('updates the variable key', async () => {
expect(findKeyField().props('value')).toBe(mockProjectVariable.key);
await wrapper.setProps({
selectedVariable: newVariable,
});
expect(findKeyField().props('value')).toBe(newVariable.key);
});
it('updates the variable value', async () => {
expect(findValueField().props('value')).toBe(mockProjectVariable.value);
await wrapper.setProps({
selectedVariable: newVariable,
});
expect(findValueField().props('value')).toBe(newVariable.value);
});
it('updates the visibility value', async () => {
expect(findVisibilityRadioGroup().attributes('checked')).toBe(VISIBILITY_VISIBLE);
await wrapper.setProps({
selectedVariable: { ...newVariable, masked: true, hidden: true },
});
expect(findVisibilityRadioGroup().attributes('checked')).toBe(VISIBILITY_HIDDEN);
});
it('updates the protected value', async () => {
expect(findProtectedCheckbox().attributes('checked')).toBeDefined();
await wrapper.setProps({
selectedVariable: { ...newVariable, protected: false },
});
expect(findProtectedCheckbox().attributes('checked')).toBeUndefined();
});
});
describe('when selected variable is empty', () => {
const newVariable = {};
it('resets the variable key', async () => {
expect(findKeyField().props('value')).toBe(mockProjectVariable.key);
await wrapper.setProps({
selectedVariable: newVariable,
});
expect(findKeyField().props('value')).toBe('');
});
it('resets the variable value', async () => {
expect(findValueField().props('value')).toBe(mockProjectVariable.value);
await wrapper.setProps({
selectedVariable: newVariable,
});
expect(findValueField().props('value')).toBe('');
});
it('updates the protected value to true if isProtectedByDefault', async () => {
expect(findProtectedCheckbox().attributes('checked')).toBeDefined();
await wrapper.setProps({
selectedVariable: { ...mockProjectVariable, protected: false },
});
expect(findProtectedCheckbox().attributes('checked')).toBeUndefined();
await wrapper.setProps({
selectedVariable: newVariable,
});
expect(findProtectedCheckbox().attributes('checked')).toBeDefined();
});
});
});
describe('validations', () => {
describe('type dropdown', () => {
beforeEach(() => {

View File

@ -0,0 +1,27 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Backup::Tasks::Repositories, feature_category: :backup_restore do
let(:progress) { StringIO.new }
let(:options) { Backup::Options.new }
let(:server_side_callable) { StringIO.new }
subject(:task) { described_class.new(progress:, options:, server_side_callable:) }
describe '#human_name' do
context 'when repositories_server_side_backup is not enabled' do
it 'returns repositories' do
expect(task.human_name).to eq('repositories')
end
end
context 'when repositories_server_side_backup is enabled' do
let(:options) { Backup::Options.new(repositories_server_side_backup: true) }
it 'returns repositories with Gitaly server-side suffix' do
expect(task.human_name).to eq('repositories (Gitaly server-side)')
end
end
end
end

View File

@ -56,6 +56,39 @@ RSpec.describe Gitlab::Tracking::Destinations::SnowplowMicro, feature_category:
end
end
describe '#snowplow_options' do
let_it_be(:group) { create :group }
before do
stub_config(snowplow_micro: snowplow_micro_settings)
stub_application_setting(snowplow_enabled?: true)
end
it 'adds Snowplow micro specific options to the parent Snowplow options' do
base_options = {
namespace: 'gl',
hostname: subject.hostname,
cookieDomain: '.gitlab.com',
appId: nil,
formTracking: true,
linkClickTracking: true
}
allow_next_instance_of(Gitlab::Tracking::Destinations::Snowplow) do |snowplow_instance|
allow(snowplow_instance).to receive(:snowplow_options).with(group).and_return(base_options)
end
options = subject.snowplow_options(group)
expect(options).to include(
protocol: 'http',
port: 9091,
forceSecureTracker: false
)
expect(options).to include(base_options)
end
end
describe '#frontend_client_options' do
let_it_be(:group) { create :group }
@ -63,16 +96,43 @@ RSpec.describe Gitlab::Tracking::Destinations::SnowplowMicro, feature_category:
stub_config(snowplow_micro: snowplow_micro_settings)
end
it 'includes protocol with the correct value' do
expect(subject.frontend_client_options(group)[:protocol]).to eq 'http'
context 'when snowplow is enabled' do
before do
stub_application_setting(snowplow_enabled?: true)
stub_feature_flags(additional_snowplow_tracking: true)
end
it 'includes snowplow_options with Snowplow micro-specific overrides' do
expect(subject).to receive(:snowplow_options).with(group).and_call_original
options = subject.frontend_client_options(group)
expect(options).to include(
protocol: 'http',
port: 9091,
forceSecureTracker: false
)
end
end
it 'includes port with the correct value' do
expect(subject.frontend_client_options(group)[:port]).to eq 9091
end
context 'when snowplow is disabled' do
before do
stub_application_setting(snowplow_enabled?: false)
allow(Gitlab).to receive(:host_with_port).and_return('gitlab.example.com')
allow(Gitlab.config.gitlab).to receive(:https).and_return(true)
end
it 'includes forceSecureTracker with value false' do
expect(subject.frontend_client_options(group)[:forceSecureTracker]).to eq false
it 'returns product_usage_events options' do
expect(subject).not_to receive(:snowplow_options)
options = subject.frontend_client_options(group)
expect(options).to include(
hostname: 'gitlab.example.com',
postPath: '/-/collect_events',
forceSecureTracker: true
)
end
end
end
end

View File

@ -183,6 +183,52 @@ RSpec.describe Gitlab::Tracking::Destinations::Snowplow, :do_not_stub_snowplow_b
end
end
describe '#frontend_client_options' do
let_it_be(:group) { create(:group) }
context 'when snowplow is enabled' do
before do
stub_application_setting(snowplow_enabled?: true)
stub_feature_flags(additional_snowplow_tracking: true)
end
it 'returns snowplow options' do
expected = {
namespace: 'gl',
hostname: 'gitfoo.com',
cookieDomain: nil,
appId: '_abc123_',
formTracking: true,
linkClickTracking: true
}
expect(subject.frontend_client_options(group)).to eq(expected)
end
end
context 'when snowplow is disabled' do
before do
stub_application_setting(snowplow_enabled?: false, snowplow_app_id: nil)
allow(Gitlab).to receive(:host_with_port).and_return('gitlab.example.com')
allow(Gitlab.config.gitlab).to receive(:https).and_return(true)
allow(Rails.application.routes.url_helpers).to receive(:event_forwarding_path).and_return('/events')
end
it 'returns product_usage_events options' do
expected = {
namespace: 'gl',
hostname: 'gitlab.example.com',
postPath: '/events',
forceSecureTracker: true,
appId: nil
}
expect(subject.frontend_client_options(group)).to eq(expected)
end
end
end
describe '#enabled?' do
context 'when snowplow is enabled' do
before do