Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
c038ef70ff
commit
ca4942bdc4
|
|
@ -18,7 +18,7 @@ variables:
|
|||
# Helm chart ref used by test-on-cng pipeline
|
||||
GITLAB_HELM_CHART_REF: "6cdb0e1cd4ceb7c9fd01ffa2f62c4a7a4c77a23b"
|
||||
# Specific ref for cng-mirror project to trigger builds for
|
||||
GITLAB_CNG_MIRROR_REF: "8c4bbd04b509dc6cc3cb0469066ef053db028607"
|
||||
GITLAB_CNG_MIRROR_REF: "00736d96dbee30eaf6fa3701f0cfa99ad8621cf4"
|
||||
# Makes sure some of the common scripts from pipeline-common use bundler to execute commands
|
||||
RUN_WITH_BUNDLE: "true"
|
||||
# Makes sure reporting script defined in .gitlab-qa-report from pipeline-common is executed from correct folder
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ workflow:
|
|||
--print-deploy-args \
|
||||
$(cat $CI_PROJECT_DIR/EXTRA_DEPLOY_VALUES)
|
||||
artifacts:
|
||||
expire_in: 1 day
|
||||
expire_in: 7 days
|
||||
when: always
|
||||
reports:
|
||||
junit: qa/tmp/rspec-*.xml
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
311c173844ce85e28e51818063bcac8e75dee41e
|
||||
6093f9c42ad8d328c37423e3067f3c9e7cd1ddcf
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { __ } from '~/locale';
|
|||
import { validateAdditionalProperties } from '~/tracking/utils';
|
||||
import * as Sentry from '~/sentry/sentry_browser_wrapper';
|
||||
import axios from './lib/utils/axios_utils';
|
||||
import { joinPaths } from './lib/utils/url_utility';
|
||||
import { joinPaths, isAbsolute } from './lib/utils/url_utility';
|
||||
|
||||
export const DEFAULT_PER_PAGE = 20;
|
||||
|
||||
|
|
@ -963,8 +963,30 @@ const Api = {
|
|||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* buildUrl (1) replaces `:version` placeholder by `gon.api_version` and
|
||||
* (2) prepends the url with `gon.relative_url_root`, if the `url` argument
|
||||
* is not an absolute URL (http://...).
|
||||
*
|
||||
* Using `gon.relative_url_root` is vital for GitLab instances installed on
|
||||
* relative paths: https://docs.gitlab.com/install/relative_url/.
|
||||
*
|
||||
* In Rails, **API** paths, like `api_v..._path`, do not include the
|
||||
* `gon.relative_url_root`. Since Rails doesn't provide `api_v..._url` helpers,
|
||||
* `expose_url` backend method can be used as an alternative to `buildUrl`.
|
||||
*
|
||||
* buildUrl('/api/:version/projects/1') => '/[relative_url_root]/api/v4/projects/1'
|
||||
*
|
||||
* @param {string} url -
|
||||
*/
|
||||
buildUrl(url) {
|
||||
return joinPaths(gon.relative_url_root || '', url.replace(':version', gon.api_version));
|
||||
const withVersion = url.replace(':version', gon.api_version);
|
||||
|
||||
if (isAbsolute(withVersion)) {
|
||||
return withVersion;
|
||||
}
|
||||
|
||||
return joinPaths(gon.relative_url_root || '', withVersion);
|
||||
},
|
||||
|
||||
fetchFeatureFlagUserLists(id, page) {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ export const createRouter = () => {
|
|||
{ name: ACTIVE_TAB_SHARED, path: '/groups/:group*/-/shared' },
|
||||
{ name: ACTIVE_TAB_SHARED_GROUPS, path: '/groups/:group*/-/shared_groups' },
|
||||
{ name: ACTIVE_TAB_INACTIVE, path: '/groups/:group*/-/inactive' },
|
||||
{ name: ACTIVE_TAB_SUBGROUPS_AND_PROJECTS, path: '/:group*' },
|
||||
{ name: ACTIVE_TAB_SUBGROUPS_AND_PROJECTS, path: '/(groups)?/:group*' },
|
||||
];
|
||||
|
||||
const router = new VueRouter({
|
||||
|
|
|
|||
|
|
@ -29,12 +29,8 @@ export default {
|
|||
|
||||
<template>
|
||||
<div>
|
||||
<gl-link
|
||||
:href="group.webUrl"
|
||||
target="_blank"
|
||||
class="gl-inline-flex gl-h-7 gl-items-center gl-gap-2"
|
||||
>
|
||||
{{ group.fullPath }} <gl-icon name="external-link" class="gl-fill-icon-link" />
|
||||
<gl-link :href="group.webUrl" target="_blank">
|
||||
{{ group.fullPath }} <gl-icon name="external-link" class="gl-fill-icon-link" />
|
||||
</gl-link>
|
||||
<div v-if="group.flags.isFinished && fullLastImportPath" class="gl-text-sm gl-text-subtle">
|
||||
<gl-sprintf :message="s__('BulkImport|Last imported to %{link}')">
|
||||
|
|
|
|||
|
|
@ -2,6 +2,13 @@
|
|||
|
||||
module Groups
|
||||
class ObservabilityController < Groups::ApplicationController
|
||||
content_security_policy do |p|
|
||||
next if p.directives.blank? || ENV['O11Y_URL'].blank?
|
||||
|
||||
frame_src_values = Array.wrap(p.directives['frame-src']) | ["'self'", ENV['O11Y_URL'].to_s]
|
||||
p.frame_src(*frame_src_values)
|
||||
end
|
||||
|
||||
before_action :authenticate_user!
|
||||
before_action :authorize_read_observability!
|
||||
|
||||
|
|
|
|||
|
|
@ -50,6 +50,11 @@ module Mutations
|
|||
required: false,
|
||||
description: 'Input for labels widget.'
|
||||
|
||||
argument :hierarchy_widget, ::Types::WorkItems::Widgets::HierarchyCreateInputType,
|
||||
required: false,
|
||||
description: 'Input for hierarchy widget.',
|
||||
experiment: { milestone: '18.2' }
|
||||
|
||||
field :updated_work_item_count, GraphQL::Types::Int,
|
||||
null: true,
|
||||
description: 'Number of work items that were successfully updated.'
|
||||
|
|
|
|||
|
|
@ -115,6 +115,8 @@
|
|||
- 1
|
||||
- - authz_user_group_member_roles_destroy_for_shared_group
|
||||
- 1
|
||||
- - authz_user_group_member_roles_update_for_group
|
||||
- 1
|
||||
- - auto_devops
|
||||
- 2
|
||||
- - auto_merge
|
||||
|
|
@ -277,6 +279,8 @@
|
|||
- 1
|
||||
- - compliance_management_compliance_framework_projects_compliance_enqueue
|
||||
- 1
|
||||
- - compliance_management_compliance_violation_detection
|
||||
- 1
|
||||
- - compliance_management_framework_export_mailer
|
||||
- 1
|
||||
- - compliance_management_merge_requests_compliance_violations
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class UpdateUserGroupMemberRolesIndexes < Gitlab::Database::Migration[2.3]
|
||||
milestone '18.2'
|
||||
disable_ddl_transaction!
|
||||
|
||||
OLD_INDEX_NAME = 'unique_user_group_member_roles_all_ids'
|
||||
OLD_INDEX_COLUMNS = %i[user_id group_id shared_with_group_id member_role_id]
|
||||
|
||||
def up
|
||||
remove_concurrent_index_by_name :user_group_member_roles, name: OLD_INDEX_NAME
|
||||
|
||||
add_concurrent_index :user_group_member_roles, :user_id,
|
||||
name: 'index_user_group_member_roles_on_user_id'
|
||||
|
||||
add_concurrent_index :user_group_member_roles, %i[user_id group_id],
|
||||
name: 'unique_user_group_member_roles_user_group', unique: true,
|
||||
where: 'shared_with_group_id IS NULL'
|
||||
|
||||
add_concurrent_index :user_group_member_roles, %i[user_id group_id shared_with_group_id],
|
||||
name: 'unique_user_group_member_roles_user_group_shared_with_group',
|
||||
unique: true, where: 'shared_with_group_id IS NOT NULL'
|
||||
end
|
||||
|
||||
def down
|
||||
remove_concurrent_index_by_name :user_group_member_roles,
|
||||
name: 'index_user_group_member_roles_on_user_id',
|
||||
if_exists: true
|
||||
|
||||
remove_concurrent_index_by_name :user_group_member_roles,
|
||||
name: 'unique_user_group_member_roles_user_group',
|
||||
if_exists: true
|
||||
|
||||
remove_concurrent_index_by_name :user_group_member_roles,
|
||||
name: 'unique_user_group_member_roles_user_group_shared_with_group',
|
||||
if_exists: true
|
||||
|
||||
add_concurrent_index :user_group_member_roles, OLD_INDEX_COLUMNS,
|
||||
name: OLD_INDEX_NAME, unique: true
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1 @@
|
|||
34deeac3e26ea820bbc31642d64f4cbb7f88186359c4fce3938c86b6953e0acd
|
||||
|
|
@ -38271,6 +38271,8 @@ CREATE INDEX index_user_group_member_roles_on_member_role_id ON user_group_membe
|
|||
|
||||
CREATE INDEX index_user_group_member_roles_on_shared_with_group_id ON user_group_member_roles USING btree (shared_with_group_id);
|
||||
|
||||
CREATE INDEX index_user_group_member_roles_on_user_id ON user_group_member_roles USING btree (user_id);
|
||||
|
||||
CREATE INDEX index_user_highest_roles_on_user_id_and_highest_access_level ON user_highest_roles USING btree (user_id, highest_access_level);
|
||||
|
||||
CREATE INDEX index_user_id_and_notification_email_to_notification_settings ON notification_settings USING btree (user_id, notification_email, id) WHERE (notification_email IS NOT NULL);
|
||||
|
|
@ -39421,7 +39423,9 @@ CREATE UNIQUE INDEX unique_streaming_event_type_filters_destination_id ON audit_
|
|||
|
||||
CREATE UNIQUE INDEX unique_streaming_instance_event_type_filters_destination_id ON audit_events_streaming_instance_event_type_filters USING btree (instance_external_audit_event_destination_id, audit_event_type);
|
||||
|
||||
CREATE UNIQUE INDEX unique_user_group_member_roles_all_ids ON user_group_member_roles USING btree (user_id, group_id, shared_with_group_id, member_role_id);
|
||||
CREATE UNIQUE INDEX unique_user_group_member_roles_user_group ON user_group_member_roles USING btree (user_id, group_id) WHERE (shared_with_group_id IS NULL);
|
||||
|
||||
CREATE UNIQUE INDEX unique_user_group_member_roles_user_group_shared_with_group ON user_group_member_roles USING btree (user_id, group_id, shared_with_group_id) WHERE (shared_with_group_id IS NOT NULL);
|
||||
|
||||
CREATE UNIQUE INDEX unique_user_id_setting_type_and_settings_context_hash ON vs_code_settings USING btree (user_id, setting_type, settings_context_hash);
|
||||
|
||||
|
|
|
|||
|
|
@ -396,7 +396,7 @@ These are only examples and may not work on all setups. Further adjustments may
|
|||
- **Data received**: `rate(node_network_receive_bytes_total{device!="lo"}[5m])`
|
||||
- **Disk read IOPS**: `sum by (instance) (rate(node_disk_reads_completed_total[1m]))`
|
||||
- **Disk write IOPS**: `sum by (instance) (rate(node_disk_writes_completed_total[1m]))`
|
||||
- **RPS via GitLab transaction count**: `sum(irate(gitlab_transaction_duration_seconds_count{controller!~'HealthController|MetricsController|'}[1m])) by (controller, action)`
|
||||
- **RPS via GitLab transaction count**: `sum(irate(gitlab_transaction_duration_seconds_count{controller!~'HealthController|MetricsController'}[1m])) by (controller, action)`
|
||||
|
||||
## Prometheus as a Grafana data source
|
||||
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ Each architecture is designed to handle specific RPS targets for different types
|
|||
|
||||
Finding out the RPS can depend notably on the specific environment setup and monitoring stack. Some potential options include:
|
||||
|
||||
- [GitLab Prometheus](../monitoring/prometheus/_index.md#sample-prometheus-queries) with queries like `sum(irate(gitlab_transaction_duration_seconds_count{controller!~'HealthController|MetricsController|'}[1m])) by (controller, action)`.
|
||||
- [GitLab Prometheus](../monitoring/prometheus/_index.md#sample-prometheus-queries) with queries like `sum(irate(gitlab_transaction_duration_seconds_count{controller!~'HealthController|MetricsController'}[1m])) by (controller, action)`.
|
||||
- [`get-rps` script](https://gitlab.com/gitlab-com/support/toolbox/dotfiles/-/blob/main/scripts/get-rps.rb?ref_type=heads) from GitLab Support.
|
||||
- Other monitoring solutions.
|
||||
- Load Balancer statistics.
|
||||
|
|
|
|||
|
|
@ -2224,6 +2224,34 @@ Input type: `AiAgentUpdateInput`
|
|||
| <a id="mutationaiagentupdateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
|
||||
| <a id="mutationaiagentupdateerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during the mutation. |
|
||||
|
||||
### `Mutation.aiCatalogAgentCreate`
|
||||
|
||||
{{< details >}}
|
||||
**Introduced** in GitLab 18.2.
|
||||
**Status**: Experiment.
|
||||
{{< /details >}}
|
||||
|
||||
Input type: `AiCatalogAgentCreateInput`
|
||||
|
||||
#### Arguments
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="mutationaicatalogagentcreateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
|
||||
| <a id="mutationaicatalogagentcreatedescription"></a>`description` | [`String!`](#string) | Description for the agent. |
|
||||
| <a id="mutationaicatalogagentcreatename"></a>`name` | [`String!`](#string) | Name for the agent. |
|
||||
| <a id="mutationaicatalogagentcreateprojectid"></a>`projectId` | [`ProjectID!`](#projectid) | Project for the agent. |
|
||||
| <a id="mutationaicatalogagentcreatesystemprompt"></a>`systemPrompt` | [`String!`](#string) | System prompt for the agent. |
|
||||
| <a id="mutationaicatalogagentcreateuserprompt"></a>`userPrompt` | [`String!`](#string) | User prompt for the agent. |
|
||||
|
||||
#### Fields
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="mutationaicatalogagentcreateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
|
||||
| <a id="mutationaicatalogagentcreateerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during the mutation. |
|
||||
| <a id="mutationaicatalogagentcreateitem"></a>`item` | [`AiCatalogItem`](#aicatalogitem) | Item created. |
|
||||
|
||||
### `Mutation.aiDuoWorkflowCreate`
|
||||
|
||||
{{< details >}}
|
||||
|
|
@ -13048,6 +13076,7 @@ Input type: `WorkItemBulkUpdateInput`
|
|||
| <a id="mutationworkitembulkupdateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
|
||||
| <a id="mutationworkitembulkupdateconfidential"></a>`confidential` {{< icon name="warning-solid" >}} | [`Boolean`](#boolean) | **Deprecated**: **Status**: Experiment. Introduced in GitLab 18.2. |
|
||||
| <a id="mutationworkitembulkupdatehealthstatuswidget"></a>`healthStatusWidget` {{< icon name="warning-solid" >}} | [`WorkItemWidgetHealthStatusInput`](#workitemwidgethealthstatusinput) | **Deprecated**: **Status**: Experiment. Introduced in GitLab 18.2. |
|
||||
| <a id="mutationworkitembulkupdatehierarchywidget"></a>`hierarchyWidget` {{< icon name="warning-solid" >}} | [`WorkItemWidgetHierarchyCreateInput`](#workitemwidgethierarchycreateinput) | **Deprecated**: **Status**: Experiment. Introduced in GitLab 18.2. |
|
||||
| <a id="mutationworkitembulkupdateids"></a>`ids` | [`[WorkItemID!]!`](#workitemid) | Global ID array of the issues that will be updated. IDs that the user can't update will be ignored. A max of 100 can be provided. |
|
||||
| <a id="mutationworkitembulkupdateiterationwidget"></a>`iterationWidget` {{< icon name="warning-solid" >}} | [`WorkItemWidgetIterationInput`](#workitemwidgetiterationinput) | **Deprecated**: **Status**: Experiment. Introduced in GitLab 18.2. |
|
||||
| <a id="mutationworkitembulkupdatelabelswidget"></a>`labelsWidget` | [`WorkItemWidgetLabelsUpdateInput`](#workitemwidgetlabelsupdateinput) | Input for labels widget. |
|
||||
|
|
@ -21755,6 +21784,7 @@ An AI catalog item.
|
|||
| <a id="aicatalogitemid"></a>`id` | [`ID!`](#id) | ID of the item. |
|
||||
| <a id="aicatalogitemitemtype"></a>`itemType` | [`AiCatalogItemType!`](#aicatalogitemtype) | Type of the item. |
|
||||
| <a id="aicatalogitemname"></a>`name` | [`String!`](#string) | Name of the item. |
|
||||
| <a id="aicatalogitemproject"></a>`project` | [`Project`](#project) | Project for the item. |
|
||||
|
||||
### `AiConversationsThread`
|
||||
|
||||
|
|
@ -25253,6 +25283,7 @@ Presets for GitLab Duo Chat window based on current context.
|
|||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="contextpresetairesourcedata"></a>`aiResourceData` | [`String`](#string) | Serialized representation of the AI resource in the current context. |
|
||||
| <a id="contextpresetquestions"></a>`questions` | [`[String!]`](#string) | Array of questions that the user can ask GitLab Duo Chat from the current page. |
|
||||
|
||||
### `ContributionAnalyticsContribution`
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ module Gitlab
|
|||
|
||||
def log_events(entity_type, entity_events)
|
||||
event_class = ENTITY_TYPE_TO_CLASS[entity_type.to_s]
|
||||
|
||||
if entity_events.one?
|
||||
[event_class.create!(build_event_attributes(entity_events.first))]
|
||||
else
|
||||
|
|
@ -65,3 +66,5 @@ module Gitlab
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
Gitlab::Audit::Logging.prepend_mod
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ variables:
|
|||
SECURE_ANALYZERS_PREFIX: "$CI_TEMPLATE_REGISTRY_HOST/security-products"
|
||||
#
|
||||
DS_EXCLUDED_ANALYZERS: ""
|
||||
DS_EXCLUDED_PATHS: "spec, test, tests, tmp"
|
||||
DS_EXCLUDED_PATHS: "spec, test, tests, tmp, node_modules"
|
||||
DS_MAJOR_VERSION: 6
|
||||
DS_SCHEMA_MODEL: 15
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ variables:
|
|||
AST_ENABLE_MR_PIPELINES: "true"
|
||||
#
|
||||
DS_EXCLUDED_ANALYZERS: ""
|
||||
DS_EXCLUDED_PATHS: "spec, test, tests, tmp"
|
||||
DS_EXCLUDED_PATHS: "spec, test, tests, tmp, node_modules"
|
||||
DS_MAJOR_VERSION: 6
|
||||
DS_SCHEMA_MODEL: 15
|
||||
# Use this variable to enforce the new Dependency Scanning analyzer for all projects
|
||||
|
|
@ -318,7 +318,7 @@ dependency-scanning:
|
|||
exists:
|
||||
- '**/{conda-lock.yml,pubspec.lock,Podfile.lock,Cargo.lock,Package.resolved}'
|
||||
variables:
|
||||
DS_EXCLUDED_PATHS: 'spec, test, tests, tmp, **/build.gradle, **/build.gradle.kts, **/build.sbt, **/pom.xml, **/requirements.txt, **/requirements.pip, **/Pipfile, **/Pipfile.lock, **/requires.txt, **/setup.py, **/poetry.lock, **/uv.lock, **/packages.lock.json, **/conan.lock, **/package-lock.json, **/npm-shrinkwrap.json, **/pnpm-lock.yaml, **/yarn.lock, **/composer.lock, **/Gemfile.lock, **/gems.locked, **/go.graph, **/ivy-report.xml, **/maven.graph.json, **/dependencies.lock, **/pipdeptree.json, **/pipenv.graph.json, **/dependencies-compile.dot'
|
||||
DS_EXCLUDED_PATHS: 'spec, test, tests, tmp, node_modules, **/build.gradle, **/build.gradle.kts, **/build.sbt, **/pom.xml, **/requirements.txt, **/requirements.pip, **/Pipfile, **/Pipfile.lock, **/requires.txt, **/setup.py, **/poetry.lock, **/uv.lock, **/packages.lock.json, **/conan.lock, **/package-lock.json, **/npm-shrinkwrap.json, **/pnpm-lock.yaml, **/yarn.lock, **/composer.lock, **/Gemfile.lock, **/gems.locked, **/go.graph, **/ivy-report.xml, **/maven.graph.json, **/dependencies.lock, **/pipdeptree.json, **/pipenv.graph.json, **/dependencies-compile.dot'
|
||||
|
||||
# 2. Don't run the job in a *branch pipeline* if *MR pipelines* for AST are enabled and there's an open merge request.
|
||||
- if: $AST_ENABLE_MR_PIPELINES == "true" &&
|
||||
|
|
@ -348,4 +348,4 @@ dependency-scanning:
|
|||
exists:
|
||||
- '**/{conda-lock.yml,pubspec.lock,Podfile.lock,Cargo.lock,Package.resolved}'
|
||||
variables:
|
||||
DS_EXCLUDED_PATHS: 'spec, test, tests, tmp, **/build.gradle, **/build.gradle.kts, **/build.sbt, **/pom.xml, **/requirements.txt, **/requirements.pip, **/Pipfile, **/Pipfile.lock, **/requires.txt, **/setup.py, **/poetry.lock, **/uv.lock, **/packages.lock.json, **/conan.lock, **/package-lock.json, **/npm-shrinkwrap.json, **/pnpm-lock.yaml, **/yarn.lock, **/composer.lock, **/Gemfile.lock, **/gems.locked, **/go.graph, **/ivy-report.xml, **/maven.graph.json, **/dependencies.lock, **/pipdeptree.json, **/pipenv.graph.json, **/dependencies-compile.dot'
|
||||
DS_EXCLUDED_PATHS: 'spec, test, tests, tmp, node_modules, **/build.gradle, **/build.gradle.kts, **/build.sbt, **/pom.xml, **/requirements.txt, **/requirements.pip, **/Pipfile, **/Pipfile.lock, **/requires.txt, **/setup.py, **/poetry.lock, **/uv.lock, **/packages.lock.json, **/conan.lock, **/package-lock.json, **/npm-shrinkwrap.json, **/pnpm-lock.yaml, **/yarn.lock, **/composer.lock, **/Gemfile.lock, **/gems.locked, **/go.graph, **/ivy-report.xml, **/maven.graph.json, **/dependencies.lock, **/pipdeptree.json, **/pipenv.graph.json, **/dependencies-compile.dot'
|
||||
|
|
|
|||
|
|
@ -12,9 +12,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def self.frame_src
|
||||
base_urls = "https://www.google.com/recaptcha/ https://www.recaptcha.net/ https://www.googletagmanager.com/ns.html"
|
||||
|
||||
ENV['O11Y_URL'].present? ? "#{base_urls} #{ENV['O11Y_URL']}" : base_urls
|
||||
"https://www.google.com/recaptcha/ https://www.recaptcha.net/ https://www.googletagmanager.com/ns.html"
|
||||
end
|
||||
|
||||
def self.script_src
|
||||
|
|
|
|||
|
|
@ -173,6 +173,40 @@ RSpec.describe 'Group show page', feature_category: :groups_and_projects do
|
|||
expect(page).to have_content(content)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'tab frontend routing' do
|
||||
context 'when route is not prefixed with group' do
|
||||
before do
|
||||
group.add_developer(user)
|
||||
sign_in(user)
|
||||
visit group_path(group)
|
||||
end
|
||||
|
||||
it 'still allows for tab navigation and reloading', :js do
|
||||
click_link _('Shared projects')
|
||||
wait_for_requests
|
||||
page.refresh
|
||||
|
||||
expect(page).to have_link('Shared projects')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when route is prefixed with group' do
|
||||
before do
|
||||
group.add_developer(user)
|
||||
sign_in(user)
|
||||
visit group_canonical_path(group)
|
||||
end
|
||||
|
||||
it 'still allows for tab navigation and reloading', :js do
|
||||
click_link _('Shared projects')
|
||||
wait_for_requests
|
||||
page.refresh
|
||||
|
||||
expect(page).to have_link('Shared projects')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when signed out' do
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ RSpec.describe 'Protected Tags', :js, :with_license, feature_category: :source_c
|
|||
end
|
||||
end
|
||||
|
||||
context "with access control", quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/509488' do
|
||||
context "with access control" do
|
||||
before do
|
||||
stub_licensed_features(protected_refs_for_users: false)
|
||||
end
|
||||
|
|
@ -95,8 +95,7 @@ RSpec.describe 'Protected Tags', :js, :with_license, feature_category: :source_c
|
|||
include_examples "protected tags > access control > CE"
|
||||
end
|
||||
|
||||
context 'when the users for protected tags feature is off',
|
||||
quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/509488' do
|
||||
context 'when the users for protected tags feature is off' do
|
||||
before do
|
||||
stub_licensed_features(protected_refs_for_users: false)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ jest.mock('~/lib/utils/url_utility', () => ({
|
|||
visitUrl: jest.fn().mockName('visitUrlMock'),
|
||||
joinPaths: jest.requireActual('~/lib/utils/url_utility').joinPaths,
|
||||
setUrlFragment: jest.requireActual('~/lib/utils/url_utility').setUrlFragment,
|
||||
isAbsolute: jest.requireActual('~/lib/utils/url_utility').isAbsolute,
|
||||
}));
|
||||
|
||||
describe('AlertManagementTable', () => {
|
||||
|
|
|
|||
|
|
@ -28,7 +28,15 @@ describe('Api', () => {
|
|||
});
|
||||
|
||||
describe('buildUrl', () => {
|
||||
it('adds URL root and fills in API version', () => {
|
||||
describe('when input is an absolute URL', () => {
|
||||
it('fills in API version but does not add relative URL root', () => {
|
||||
const input = 'https://gitlab.com/api/:version/foo/bar';
|
||||
|
||||
expect(Api.buildUrl(input)).toEqual(`https://gitlab.com/api/${dummyApiVersion}/foo/bar`);
|
||||
});
|
||||
});
|
||||
|
||||
it('adds relative URL root and fills in API version', () => {
|
||||
const input = '/api/:version/foo/bar';
|
||||
const expectedOutput = `${dummyUrlRoot}/api/${dummyApiVersion}/foo/bar`;
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,13 @@ RSpec.describe 'Bulk update work items', feature_category: :team_planning do
|
|||
}
|
||||
end
|
||||
|
||||
before_all do
|
||||
# Ensure support bot user is created so creation doesn't count towards query limit
|
||||
# and we don't try to obtain an exclusive lease within a transaction.
|
||||
# See https://gitlab.com/gitlab-org/gitlab/-/issues/509629
|
||||
Users::Internal.support_bot_id
|
||||
end
|
||||
|
||||
context 'when Gitlab is FOSS only' do
|
||||
unless Gitlab.ee?
|
||||
context 'when parent is a group' do
|
||||
|
|
@ -206,6 +213,24 @@ RSpec.describe 'Bulk update work items', feature_category: :team_planning do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when updating parent' do
|
||||
let_it_be(:parent_work_item) { create(:work_item, :issue, project: project) }
|
||||
let_it_be(:task_work_item) { create(:work_item, :task, project: project) }
|
||||
let_it_be(:issue_work_item) { updatable_work_items.first }
|
||||
let_it_be(:updatable_work_items) { [task_work_item, issue_work_item] }
|
||||
|
||||
let(:additional_arguments) { { hierarchy_widget: { parent_id: parent_work_item.to_gid.to_s } } }
|
||||
|
||||
it 'updates the parent for the appropriate work item(s)' do
|
||||
expect do
|
||||
post_graphql_mutation(mutation, current_user: current_user)
|
||||
end.to not_change { issue_work_item.reload.work_item_parent }.from(nil)
|
||||
.and change { task_work_item.reload.work_item_parent }.from(nil).to(parent_work_item)
|
||||
|
||||
expect(mutation_response).to include('updatedWorkItemCount' => 1)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when updating multiple attributes simultaneously' do
|
||||
let_it_be(:assignee) { create(:user, developer_of: group) }
|
||||
let_it_be(:milestone) { create(:milestone, project: project) }
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Groups::ObservabilityController, feature_category: :observability do
|
||||
include ContentSecurityPolicyHelpers
|
||||
|
||||
let(:group) { create(:group, :public) }
|
||||
let(:user) { create(:user) }
|
||||
|
||||
|
|
@ -98,4 +100,144 @@ RSpec.describe Groups::ObservabilityController, feature_category: :observability
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'Content Security Policy' do
|
||||
subject(:csp_header) { response.headers['Content-Security-Policy'] }
|
||||
|
||||
before do
|
||||
stub_feature_flags(observability_sass_features: group)
|
||||
end
|
||||
|
||||
context 'when O11Y_URL environment variable is set' do
|
||||
let(:o11y_url) { 'https://observability.example.com' }
|
||||
|
||||
before do
|
||||
stub_env('O11Y_URL', o11y_url)
|
||||
end
|
||||
|
||||
context 'when CSP directives are present' do
|
||||
let(:csp) do
|
||||
ActionDispatch::ContentSecurityPolicy.new do |p|
|
||||
p.frame_src "'self'", 'https://existing-frame.example.com'
|
||||
end
|
||||
end
|
||||
|
||||
before do
|
||||
stub_csp_for_controller(described_class, csp)
|
||||
get group_observability_path(group, 'services')
|
||||
end
|
||||
|
||||
it 'adds O11Y_URL to frame-src directive' do
|
||||
frame_src_values = find_csp_directive('frame-src', header: csp_header)
|
||||
expect(frame_src_values).to include("'self'", 'https://existing-frame.example.com', o11y_url)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when CSP frame-src directive is not present' do
|
||||
let(:csp) do
|
||||
ActionDispatch::ContentSecurityPolicy.new do |p|
|
||||
p.script_src "'self'"
|
||||
end
|
||||
end
|
||||
|
||||
before do
|
||||
stub_csp_for_controller(described_class, csp)
|
||||
get group_observability_path(group, 'services')
|
||||
end
|
||||
|
||||
it 'creates frame-src directive with O11Y_URL' do
|
||||
frame_src_values = find_csp_directive('frame-src', header: csp_header)
|
||||
expect(frame_src_values).to include("'self'", o11y_url)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when CSP has no directives' do
|
||||
let(:csp) { ActionDispatch::ContentSecurityPolicy.new }
|
||||
|
||||
before do
|
||||
stub_csp_for_controller(described_class, csp)
|
||||
get group_observability_path(group, 'services')
|
||||
end
|
||||
|
||||
it 'does not add frame-src directive' do
|
||||
expect(csp_header).to be_blank
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when O11Y_URL environment variable is not set' do
|
||||
before do
|
||||
stub_env('O11Y_URL', nil)
|
||||
end
|
||||
|
||||
context 'when CSP directives are present' do
|
||||
let(:csp) do
|
||||
ActionDispatch::ContentSecurityPolicy.new do |p|
|
||||
p.frame_src "'self'", 'https://existing-frame.example.com'
|
||||
end
|
||||
end
|
||||
|
||||
before do
|
||||
stub_csp_for_controller(described_class, csp)
|
||||
get group_observability_path(group, 'services')
|
||||
end
|
||||
|
||||
it 'does not modify frame-src directive' do
|
||||
frame_src_values = find_csp_directive('frame-src', header: csp_header)
|
||||
expect(frame_src_values).to contain_exactly("'self'", 'https://existing-frame.example.com')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when CSP has no directives' do
|
||||
let(:csp) { ActionDispatch::ContentSecurityPolicy.new }
|
||||
|
||||
before do
|
||||
stub_csp_for_controller(described_class, csp)
|
||||
get group_observability_path(group, 'services')
|
||||
end
|
||||
|
||||
it 'does not add frame-src directive' do
|
||||
expect(csp_header).to be_blank
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when O11Y_URL environment variable is empty string' do
|
||||
before do
|
||||
stub_env('O11Y_URL', '')
|
||||
end
|
||||
|
||||
context 'when CSP directives are present' do
|
||||
let(:csp) do
|
||||
ActionDispatch::ContentSecurityPolicy.new do |p|
|
||||
p.frame_src "'self'", 'https://existing-frame.example.com'
|
||||
end
|
||||
end
|
||||
|
||||
before do
|
||||
stub_csp_for_controller(described_class, csp)
|
||||
get group_observability_path(group, 'services')
|
||||
end
|
||||
|
||||
it 'does not modify frame-src directive' do
|
||||
frame_src_values = find_csp_directive('frame-src', header: csp_header)
|
||||
expect(frame_src_values).to contain_exactly("'self'", 'https://existing-frame.example.com')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when CSP directives are blank' do
|
||||
let(:csp) { ActionDispatch::ContentSecurityPolicy.new }
|
||||
|
||||
before do
|
||||
stub_env('O11Y_URL', 'https://observability.example.com')
|
||||
stub_csp_for_controller(described_class, csp)
|
||||
get group_observability_path(group, 'services')
|
||||
end
|
||||
|
||||
it 'does not add frame-src directive' do
|
||||
expect(csp_header).to be_blank
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ RSpec.shared_examples 'Deploy keys with protected tags' do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when deploy key is already selected for protected branch' do
|
||||
context 'when deploy key is already selected for protected tag' do
|
||||
let(:protected_tag) { create(:protected_tag, :no_one_can_create, project: project, name: 'v1.0.0') }
|
||||
let(:write_access_key) { create(:deploy_key, user: user, write_access_to: project) }
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue