summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-08-13 21:09:54 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2021-08-13 21:09:54 +0000
commit79ecd9a7489305e8357ca1df74ac7d7cc775b0d3 (patch)
tree28ba38ec1b11d580386cdda4930536566dac992d
parentc575d3cfde0cba06c37d5a5dae0ca7288f68f1e3 (diff)
downloadgitlab-ce-79ecd9a7489305e8357ca1df74ac7d7cc775b0d3.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--app/controllers/projects/merge_requests_controller.rb9
-rw-r--r--app/finders/ci/pipelines_finder.rb9
-rw-r--r--app/helpers/groups_helper.rb7
-rw-r--r--app/models/ci/pipeline.rb1
-rw-r--r--app/views/groups/dependency_proxies/_url.html.haml4
-rw-r--r--app/workers/pipeline_hooks_worker.rb2
-rw-r--r--config/feature_flags/development/pipeline_source_filter.yml8
-rw-r--r--config/initializers/1_settings.rb2
-rw-r--r--config/initializers/content_security_policy.rb9
-rw-r--r--doc/operations/error_tracking.md25
-rw-r--r--doc/user/application_security/security_dashboard/img/pipeline_security_dashboard_v13_10.pngbin80367 -> 0 bytes
-rw-r--r--doc/user/application_security/security_dashboard/img/pipeline_security_dashboard_v14_2.pngbin0 -> 83851 bytes
-rw-r--r--doc/user/application_security/security_dashboard/index.md2
-rw-r--r--doc/user/application_security/vulnerability_report/img/group_vulnerability_report_v13_9.pngbin54478 -> 0 bytes
-rw-r--r--doc/user/application_security/vulnerability_report/img/group_vulnerability_report_v14_2.pngbin0 -> 109933 bytes
-rw-r--r--doc/user/application_security/vulnerability_report/img/project_security_dashboard_status_change_v13_10.pngbin41154 -> 0 bytes
-rw-r--r--doc/user/application_security/vulnerability_report/img/project_security_dashboard_status_change_v14_2.pngbin0 -> 63558 bytes
-rw-r--r--doc/user/application_security/vulnerability_report/index.md20
-rw-r--r--doc/user/packages/helm_repository/index.md9
-rw-r--r--lib/api/ci/pipelines.rb3
-rw-r--r--lib/api/entities/ci/pipeline_basic.rb2
-rw-r--r--lib/gitlab/background_migration/backfill_integrations_type_new.rb30
-rw-r--r--lib/gitlab/content_security_policy/config_loader.rb78
-rw-r--r--lib/gitlab/data_builder/pipeline.rb26
-rw-r--r--lib/gitlab/database/async_indexes/index_creator.rb2
-rw-r--r--locale/gitlab.pot23
-rw-r--r--spec/factories/ci/builds.rb9
-rw-r--r--spec/finders/ci/pipelines_finder_spec.rb23
-rw-r--r--spec/fixtures/api/schemas/pipeline_schedule.json1
-rw-r--r--spec/helpers/groups_helper_spec.rb10
-rw-r--r--spec/lib/gitlab/background_migration/backfill_integrations_type_new_spec.rb22
-rw-r--r--spec/lib/gitlab/content_security_policy/config_loader_spec.rb47
-rw-r--r--spec/lib/gitlab/data_builder/pipeline_spec.rb36
-rw-r--r--spec/models/ci/pipeline_spec.rb14
-rw-r--r--spec/requests/api/ci/pipelines_spec.rb65
-rw-r--r--spec/requests/projects/merge_requests_spec.rb54
36 files changed, 409 insertions, 143 deletions
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index ca7c675f4a8..1a14b2f99da 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -131,9 +131,16 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
Gitlab::PollingInterval.set_header(response, interval: 10_000)
if params[:serializer] == 'sidebar_extras' && Feature.enabled?(:merge_request_show_render_cached, @project, default_enabled: :yaml)
+ cache_context = [
+ params[:serializer],
+ current_user&.cache_key,
+ @merge_request.assignees.map(&:cache_key),
+ @merge_request.reviewers.map(&:cache_key)
+ ]
+
render_cached(@merge_request,
with: serializer,
- cache_context: -> (_) { [params[:serializer], current_user&.cache_key, project.emails_disabled?, issuable.subscribed?(current_user, project)] },
+ cache_context: -> (_) { [Digest::SHA256.hexdigest(cache_context.to_s)] },
serializer: params[:serializer])
else
render json: serializer.represent(@merge_request, serializer: params[:serializer])
diff --git a/app/finders/ci/pipelines_finder.rb b/app/finders/ci/pipelines_finder.rb
index a4f4a4395f7..a79840216da 100644
--- a/app/finders/ci/pipelines_finder.rb
+++ b/app/finders/ci/pipelines_finder.rb
@@ -29,6 +29,9 @@ module Ci
items = by_username(items)
items = by_yaml_errors(items)
items = by_updated_at(items)
+
+ items = by_source(items) if Feature.enabled?(:pipeline_source_filter, project, default_enabled: :yaml)
+
sort_items(items)
end
@@ -87,6 +90,12 @@ module Ci
end
# rubocop: enable CodeReuse/ActiveRecord
+ def by_source(items)
+ return items unless ::Ci::Pipeline.sources.key?(params[:source])
+
+ items.with_pipeline_source(params[:source])
+ end
+
# rubocop: disable CodeReuse/ActiveRecord
def by_ref(items)
if params[:ref].present?
diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb
index 9670fe13ac8..25dd6a357d5 100644
--- a/app/helpers/groups_helper.rb
+++ b/app/helpers/groups_helper.rb
@@ -64,10 +64,13 @@ module GroupsHelper
.count
end
- def group_dependency_proxy_url(group)
+ def group_dependency_proxy_image_prefix(group)
# The namespace path can include uppercase letters, which
# Docker doesn't allow. The proxy expects it to be downcased.
- "#{group_url(group).downcase}#{DependencyProxy::URL_SUFFIX}"
+ url = "#{group_url(group).downcase}#{DependencyProxy::URL_SUFFIX}"
+
+ # Docker images do not include the protocol
+ url.partition('//').last
end
def group_icon_url(group, options = {})
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index 53003152791..40adccead18 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -318,6 +318,7 @@ module Ci
scope :created_before_id, -> (id) { where('ci_pipelines.id < ?', id) }
scope :before_pipeline, -> (pipeline) { created_before_id(pipeline.id).outside_pipeline_family(pipeline) }
scope :eager_load_project, -> { eager_load(project: [:route, { namespace: :route }]) }
+ scope :with_pipeline_source, -> (source) { where(source: source)}
scope :outside_pipeline_family, ->(pipeline) do
where.not(id: pipeline.same_family_pipeline_ids)
diff --git a/app/views/groups/dependency_proxies/_url.html.haml b/app/views/groups/dependency_proxies/_url.html.haml
index b3fb1cf05a2..a8034c50ed8 100644
--- a/app/views/groups/dependency_proxies/_url.html.haml
+++ b/app/views/groups/dependency_proxies/_url.html.haml
@@ -1,6 +1,6 @@
-- proxy_url = group_dependency_proxy_url(@group)
+- proxy_url = group_dependency_proxy_image_prefix(@group)
-%h5.prepend-top-20= _('Dependency proxy URL')
+%h5.prepend-top-20= _('Dependency proxy image prefix')
.row
.col-lg-8.col-md-12.input-group
diff --git a/app/workers/pipeline_hooks_worker.rb b/app/workers/pipeline_hooks_worker.rb
index 40d138752b4..322f92d376b 100644
--- a/app/workers/pipeline_hooks_worker.rb
+++ b/app/workers/pipeline_hooks_worker.rb
@@ -12,7 +12,7 @@ class PipelineHooksWorker # rubocop:disable Scalability/IdempotentWorker
# rubocop: disable CodeReuse/ActiveRecord
def perform(pipeline_id)
- Ci::Pipeline.includes({ builds: { runner: :tags } })
+ Ci::Pipeline
.find_by(id: pipeline_id)
.try(:execute_hooks)
end
diff --git a/config/feature_flags/development/pipeline_source_filter.yml b/config/feature_flags/development/pipeline_source_filter.yml
new file mode 100644
index 00000000000..be24f6936e4
--- /dev/null
+++ b/config/feature_flags/development/pipeline_source_filter.yml
@@ -0,0 +1,8 @@
+---
+name: pipeline_source_filter
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/67846
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/338347
+milestone: '14.2'
+type: development
+group: group::pipeline execution
+default_enabled: false
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index 3c520515e8b..7aa1acb1c0f 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -210,7 +210,7 @@ Settings.gitlab.default_projects_features['visibility_level'] = Settings.__sen
Settings.gitlab['domain_allowlist'] ||= []
Settings.gitlab['import_sources'] ||= Gitlab::ImportSources.values
Settings.gitlab['trusted_proxies'] ||= []
-Settings.gitlab['content_security_policy'] ||= Gitlab::ContentSecurityPolicy::ConfigLoader.default_settings_hash(Settings.gitlab['cdn_host'])
+Settings.gitlab['content_security_policy'] ||= {}
Settings.gitlab['allowed_hosts'] ||= []
Settings.gitlab['no_todos_messages'] ||= YAML.load_file(Rails.root.join('config', 'no_todos_messages.yml'))
Settings.gitlab['impersonation_enabled'] ||= true if Settings.gitlab['impersonation_enabled'].nil?
diff --git a/config/initializers/content_security_policy.rb b/config/initializers/content_security_policy.rb
index c19fb65017f..3c6606066cd 100644
--- a/config/initializers/content_security_policy.rb
+++ b/config/initializers/content_security_policy.rb
@@ -2,11 +2,16 @@
csp_settings = Settings.gitlab.content_security_policy
+csp_settings['enabled'] = Gitlab::ContentSecurityPolicy::ConfigLoader.default_enabled if csp_settings['enabled'].nil?
+csp_settings['report_only'] = false if csp_settings['report_only'].nil?
+csp_settings['directives'] ||= {}
+
if csp_settings['enabled']
+ csp_settings['directives'] = ::Gitlab::ContentSecurityPolicy::ConfigLoader.default_directives if csp_settings['directives'].empty?
+
# See https://guides.rubyonrails.org/security.html#content-security-policy
Rails.application.config.content_security_policy do |policy|
- directives = csp_settings.fetch('directives', {})
- loader = ::Gitlab::ContentSecurityPolicy::ConfigLoader.new(directives)
+ loader = ::Gitlab::ContentSecurityPolicy::ConfigLoader.new(csp_settings['directives'].to_h)
loader.load(policy)
end
diff --git a/doc/operations/error_tracking.md b/doc/operations/error_tracking.md
index 4cbcb6d2f13..9234ca36634 100644
--- a/doc/operations/error_tracking.md
+++ b/doc/operations/error_tracking.md
@@ -27,16 +27,21 @@ least Maintainer [permissions](../user/permissions.md) to enable the Sentry inte
1. [Create](https://docs.sentry.io/product/sentry-basics/guides/integrate-frontend/create-new-project/) a new Sentry project. For each GitLab project that you want to integrate, we recommend that you create a new Sentry project.
1. [Find or generate](https://docs.sentry.io/api/auth/) a Sentry auth token for your Sentry project.
Make sure to give the token at least the following scopes: `event:read`, `project:read`, and `event:write` (for resolving events).
-1. In GitLab, navigate to your project's **Monitor > Error Tracking** page, and
- click **Enable Error Tracking**.
-1. Navigate to your project's **Settings > Monitor**. In the **Error Tracking** section,
- ensure the **Active** checkbox is set.
-1. In the **Sentry API URL** field, enter your Sentry hostname. For example, enter `https://sentry.example.com` if this is the address at which your Sentry instance is available. For the SaaS version of Sentry, the hostname is `https://sentry.io`.
-1. In the **Auth Token** field, enter the token you previously generated.
-1. Click the **Connect** button to test the connection to Sentry and populate the **Project** dropdown.
-1. From the **Project** dropdown, choose a Sentry project to link to your GitLab project.
-1. Click **Save changes** for the changes to take effect.
-1. You can now visit **Monitor > Error Tracking** in your project's sidebar to [view a list](#error-tracking-list) of Sentry errors.
+1. In GitLab, enable error tracking:
+ 1. On the top bar, select **Menu > Projects** and find your project.
+ 1. On the left sidebar, select **Monitor > Error Tracking**.
+ 1. Select **Enable error tracking**.
+1. In GitLab, ensure error tracking is active.
+ 1. On the left sidebar, select **Settings > Monitor**.
+ 1. Expand **Error Tracking**.
+ 1. Ensure the **Active** checkbox is selected.
+1. In the **Sentry API URL** box, enter your Sentry hostname. For example, enter `https://sentry.example.com`. For the SaaS version of Sentry, the hostname is `https://sentry.io`.
+1. In the **Auth Token** box, enter the token you previously generated.
+1. To test the connection to Sentry and populate the **Project** dropdown, select **Connect**.
+1. From the **Project** list, choose a Sentry project to link to your GitLab project.
+1. Select **Save changes**.
+
+You can now visit **Monitor > Error Tracking** in your project's sidebar to [view a list](#error-tracking-list) of Sentry errors.
### Enabling GitLab issues links
diff --git a/doc/user/application_security/security_dashboard/img/pipeline_security_dashboard_v13_10.png b/doc/user/application_security/security_dashboard/img/pipeline_security_dashboard_v13_10.png
deleted file mode 100644
index 72b24a3fd28..00000000000
--- a/doc/user/application_security/security_dashboard/img/pipeline_security_dashboard_v13_10.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/application_security/security_dashboard/img/pipeline_security_dashboard_v14_2.png b/doc/user/application_security/security_dashboard/img/pipeline_security_dashboard_v14_2.png
new file mode 100644
index 00000000000..3a195a5ce8d
--- /dev/null
+++ b/doc/user/application_security/security_dashboard/img/pipeline_security_dashboard_v14_2.png
Binary files differ
diff --git a/doc/user/application_security/security_dashboard/index.md b/doc/user/application_security/security_dashboard/index.md
index 806bc03e30e..b799177ec5a 100644
--- a/doc/user/application_security/security_dashboard/index.md
+++ b/doc/user/application_security/security_dashboard/index.md
@@ -51,7 +51,7 @@ The security dashboard and vulnerability report displays information about vulne
At the pipeline level, the Security section displays the vulnerabilities present in the branch of
the project the pipeline ran against.
-![Pipeline Security Dashboard](img/pipeline_security_dashboard_v13_10.png)
+![Pipeline Security Dashboard](img/pipeline_security_dashboard_v14_2.png)
Visit the page for any pipeline that ran any of the [supported reports](#supported-reports). To view
the pipeline's security findings, select the **Security** tab when viewing the pipeline.
diff --git a/doc/user/application_security/vulnerability_report/img/group_vulnerability_report_v13_9.png b/doc/user/application_security/vulnerability_report/img/group_vulnerability_report_v13_9.png
deleted file mode 100644
index 72443180e09..00000000000
--- a/doc/user/application_security/vulnerability_report/img/group_vulnerability_report_v13_9.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/application_security/vulnerability_report/img/group_vulnerability_report_v14_2.png b/doc/user/application_security/vulnerability_report/img/group_vulnerability_report_v14_2.png
new file mode 100644
index 00000000000..193efe9c386
--- /dev/null
+++ b/doc/user/application_security/vulnerability_report/img/group_vulnerability_report_v14_2.png
Binary files differ
diff --git a/doc/user/application_security/vulnerability_report/img/project_security_dashboard_status_change_v13_10.png b/doc/user/application_security/vulnerability_report/img/project_security_dashboard_status_change_v13_10.png
deleted file mode 100644
index f9f60810f20..00000000000
--- a/doc/user/application_security/vulnerability_report/img/project_security_dashboard_status_change_v13_10.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/application_security/vulnerability_report/img/project_security_dashboard_status_change_v14_2.png b/doc/user/application_security/vulnerability_report/img/project_security_dashboard_status_change_v14_2.png
new file mode 100644
index 00000000000..056e051363d
--- /dev/null
+++ b/doc/user/application_security/vulnerability_report/img/project_security_dashboard_status_change_v14_2.png
Binary files differ
diff --git a/doc/user/application_security/vulnerability_report/index.md b/doc/user/application_security/vulnerability_report/index.md
index da59c0fbe79..c2c2e7459ba 100644
--- a/doc/user/application_security/vulnerability_report/index.md
+++ b/doc/user/application_security/vulnerability_report/index.md
@@ -16,7 +16,7 @@ At all levels, the Vulnerability Report contains:
- Filters for common vulnerability attributes.
- Details of each vulnerability, presented in tabular layout.
-![Vulnerability Report](img/group_vulnerability_report_v13_9.png)
+![Vulnerability Report](img/group_vulnerability_report_v14_2.png)
## Project-level Vulnerability Report
@@ -49,7 +49,7 @@ You can filter the vulnerabilities table by:
|:---------|:------------------|
| Status | Detected, Confirmed, Dismissed, Resolved. |
| Severity | Critical, High, Medium, Low, Info, Unknown. |
-| Scanner | For more details, see [Scanner filter](#scanner-filter). |
+| Tool | For more details, see [Tool filter](#tool-filter). |
| Project | For more details, see [Project filter](#project-filter). |
| Activity | For more details, see [Activity filter](#activity-filter). |
@@ -70,17 +70,17 @@ The filters' criteria are combined to show only vulnerabilities matching all cri
An exception to this behavior is the Activity filter. For more details about how it works, see
[Activity filter](#activity-filter).
-## Scanner filter
+## Tool filter
-The scanner filter allows you to focus on vulnerabilities detected by selected scanners.
+The tool filter allows you to focus on vulnerabilities detected by selected tools.
-When using the scanner filter, you can choose:
+When using the tool filter, you can choose:
-- **All scanners** (default).
-- Individual GitLab-provided scanners.
-- Any integrated 3rd-party scanner. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/229661) in GitLab 13.12.
+- **All tools** (default).
+- Individual GitLab-provided tools.
+- Any integrated 3rd-party tool. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/229661) in GitLab 13.12.
-For details of each of the available scanners, see [Security scanning tools](../index.md#security-scanning-tools).
+For details of each of the available tools, see [Security scanning tools](../index.md#security-scanning-tools).
### Project filter
@@ -143,7 +143,7 @@ To change the status of vulnerabilities in the table:
1. Select the checkbox for each vulnerability you want to update the status of.
1. In the dropdown that appears select the desired status, then select **Change status**.
-![Project Vulnerability Report](img/project_security_dashboard_status_change_v13_10.png)
+![Project Vulnerability Report](img/project_security_dashboard_status_change_v14_2.png)
## Export vulnerability details
diff --git a/doc/user/packages/helm_repository/index.md b/doc/user/packages/helm_repository/index.md
index 87c8afc7826..f98fc352ab5 100644
--- a/doc/user/packages/helm_repository/index.md
+++ b/doc/user/packages/helm_repository/index.md
@@ -90,3 +90,12 @@ helm repo update
To update the Helm client with the most currently available charts.
See [Using Helm](https://helm.sh/docs/intro/using_helm/) for more information.
+
+## Troubleshooting
+
+### The chart is not visible in the Package Registry after uploading
+
+Check the [Sidekiq log](../../../administration/logs.md#sidekiqlog)
+for any related errors. If you see `Validation failed: Version is invalid`, it means that the
+version in your `Chart.yaml` file does not follow [Helm Chart versioning specifications](https://helm.sh/docs/topics/charts/#charts-and-versioning).
+To fix the error, use the correct version syntax and upload the chart again.
diff --git a/lib/api/ci/pipelines.rb b/lib/api/ci/pipelines.rb
index 3abeceba8f3..4d6d38f2dce 100644
--- a/lib/api/ci/pipelines.rb
+++ b/lib/api/ci/pipelines.rb
@@ -52,13 +52,14 @@ module API
desc: 'Order pipelines'
optional :sort, type: String, values: %w[asc desc], default: 'desc',
desc: 'Sort pipelines'
+ optional :source, type: String, values: ::Ci::Pipeline.sources.keys
end
get ':id/pipelines' do
authorize! :read_pipeline, user_project
authorize! :read_build, user_project
pipelines = ::Ci::PipelinesFinder.new(user_project, current_user, params).execute
- present paginate(pipelines), with: Entities::Ci::PipelineBasic
+ present paginate(pipelines), with: Entities::Ci::PipelineBasic, project: user_project
end
desc 'Create a new pipeline' do
diff --git a/lib/api/entities/ci/pipeline_basic.rb b/lib/api/entities/ci/pipeline_basic.rb
index f4f2356c812..8086062dc9b 100644
--- a/lib/api/entities/ci/pipeline_basic.rb
+++ b/lib/api/entities/ci/pipeline_basic.rb
@@ -7,6 +7,8 @@ module API
expose :id, :project_id, :sha, :ref, :status
expose :created_at, :updated_at
+ expose :source, if: ->(pipeline, options) { ::Feature.enabled?(:pipeline_source_filter, options[:project], default_enabled: :yaml) }
+
expose :web_url do |pipeline, _options|
Gitlab::Routing.url_helpers.project_pipeline_url(pipeline.project, pipeline)
end
diff --git a/lib/gitlab/background_migration/backfill_integrations_type_new.rb b/lib/gitlab/background_migration/backfill_integrations_type_new.rb
index 6a2d82aaeee..d1a939af58e 100644
--- a/lib/gitlab/background_migration/backfill_integrations_type_new.rb
+++ b/lib/gitlab/background_migration/backfill_integrations_type_new.rb
@@ -6,8 +6,32 @@ module Gitlab
# the real class name, rather than the legacy class name in `type`
# which is mapped via `Gitlab::Integrations::StiType`.
class BackfillIntegrationsTypeNew
- def perform(start_id, stop_id, *args)
- ActiveRecord::Base.connection.execute(<<~SQL)
+ include Gitlab::Database::DynamicModelHelpers
+
+ def perform(start_id, stop_id, batch_table, batch_column, sub_batch_size, pause_ms)
+ parent_batch_relation = define_batchable_model(batch_table)
+ .where(batch_column => start_id..stop_id)
+
+ parent_batch_relation.each_batch(column: batch_column, of: sub_batch_size) do |sub_batch|
+ process_sub_batch(sub_batch)
+
+ sleep(pause_ms * 0.001) if pause_ms > 0
+ end
+ end
+
+ private
+
+ def connection
+ ActiveRecord::Base.connection
+ end
+
+ def process_sub_batch(sub_batch)
+ # Extract the start/stop IDs from the current sub-batch
+ sub_start_id, sub_stop_id = sub_batch.pluck(Arel.sql('MIN(id), MAX(id)')).first
+
+ # This matches the mapping from the INSERT trigger added in
+ # db/migrate/20210721135638_add_triggers_to_integrations_type_new.rb
+ connection.execute(<<~SQL)
WITH mapping(old_type, new_type) AS (VALUES
('AsanaService', 'Integrations::Asana'),
('AssemblaService', 'Integrations::Assembla'),
@@ -53,7 +77,7 @@ module Gitlab
UPDATE integrations SET type_new = mapping.new_type
FROM mapping
- WHERE integrations.id BETWEEN #{start_id} AND #{stop_id}
+ WHERE integrations.id BETWEEN #{sub_start_id} AND #{sub_stop_id}
AND integrations.type = mapping.old_type
SQL
end
diff --git a/lib/gitlab/content_security_policy/config_loader.rb b/lib/gitlab/content_security_policy/config_loader.rb
index 2c7839f55b0..bdcedd1896d 100644
--- a/lib/gitlab/content_security_policy/config_loader.rb
+++ b/lib/gitlab/content_security_policy/config_loader.rb
@@ -7,40 +7,40 @@ module Gitlab
form_action frame_ancestors frame_src img_src manifest_src
media_src object_src report_uri script_src style_src worker_src).freeze
- def self.default_settings_hash(cdn_host)
- settings_hash = {
- 'enabled' => Rails.env.development? || Rails.env.test?,
- 'report_only' => false,
- 'directives' => {
- 'default_src' => "'self'",
- 'base_uri' => "'self'",
- 'connect_src' => "'self'",
- 'font_src' => "'self'",
- 'form_action' => "'self' https: http:",
- 'frame_ancestors' => "'self'",
- 'frame_src' => "'self' https://www.google.com/recaptcha/ https://www.recaptcha.net/ https://content.googleapis.com https://content-compute.googleapis.com https://content-cloudbilling.googleapis.com https://content-cloudresourcemanager.googleapis.com",
- 'img_src' => "'self' data: blob: http: https:",
- 'manifest_src' => "'self'",
- 'media_src' => "'self'",
- 'script_src' => "'strict-dynamic' 'self' 'unsafe-inline' 'unsafe-eval' https://www.google.com/recaptcha/ https://www.recaptcha.net https://apis.google.com",
- 'style_src' => "'self' 'unsafe-inline'",
- 'worker_src' => "'self' blob: data:",
- 'object_src' => "'none'",
- 'report_uri' => nil
- }
+ def self.default_enabled
+ Rails.env.development? || Rails.env.test?
+ end
+
+ def self.default_directives
+ directives = {
+ 'default_src' => "'self'",
+ 'base_uri' => "'self'",
+ 'connect_src' => "'self'",
+ 'font_src' => "'self'",
+ 'form_action' => "'self' https: http:",
+ 'frame_ancestors' => "'self'",
+ 'frame_src' => "'self' https://www.google.com/recaptcha/ https://www.recaptcha.net/ https://content.googleapis.com https://content-compute.googleapis.com https://content-cloudbilling.googleapis.com https://content-cloudresourcemanager.googleapis.com",
+ 'img_src' => "'self' data: blob: http: https:",
+ 'manifest_src' => "'self'",
+ 'media_src' => "'self'",
+ 'script_src' => "'strict-dynamic' 'self' 'unsafe-inline' 'unsafe-eval' https://www.google.com/recaptcha/ https://www.recaptcha.net https://apis.google.com",
+ 'style_src' => "'self' 'unsafe-inline'",
+ 'worker_src' => "'self' blob: data:",
+ 'object_src' => "'none'",
+ 'report_uri' => nil
}
# frame-src was deprecated in CSP level 2 in favor of child-src
# CSP level 3 "undeprecated" frame-src and browsers fall back on child-src if it's missing
# However Safari seems to read child-src first so we'll just keep both equal
- settings_hash['directives']['child_src'] = settings_hash['directives']['frame_src']
+ directives['child_src'] = directives['frame_src']
- allow_webpack_dev_server(settings_hash) if Rails.env.development?
- allow_cdn(settings_hash, cdn_host) if cdn_host.present?
- allow_customersdot(settings_hash) if Rails.env.development? && ENV['CUSTOMER_PORTAL_URL'].present?
- allow_sentry(settings_hash) if Gitlab.config.sentry&.enabled && Gitlab.config.sentry&.clientside_dsn
+ allow_webpack_dev_server(directives) if Rails.env.development?
+ allow_cdn(directives, Settings.gitlab.cdn_host) if Settings.gitlab.cdn_host.present?
+ allow_customersdot(directives) if Rails.env.development? && ENV['CUSTOMER_PORTAL_URL'].present?
+ allow_sentry(directives) if Gitlab.config.sentry&.enabled && Gitlab.config.sentry&.clientside_dsn
- settings_hash
+ directives
end
def initialize(csp_directives)
@@ -67,37 +67,37 @@ module Gitlab
arguments.strip.split(' ').map(&:strip)
end
- def self.allow_webpack_dev_server(settings_hash)
+ def self.allow_webpack_dev_server(directives)
secure = Settings.webpack.dev_server['https']
host_and_port = "#{Settings.webpack.dev_server['host']}:#{Settings.webpack.dev_server['port']}"
http_url = "#{secure ? 'https' : 'http'}://#{host_and_port}"
ws_url = "#{secure ? 'wss' : 'ws'}://#{host_and_port}"
- append_to_directive(settings_hash, 'connect_src', "#{http_url} #{ws_url}")
+ append_to_directive(directives, 'connect_src', "#{http_url} #{ws_url}")
end
- def self.allow_cdn(settings_hash, cdn_host)
- append_to_directive(settings_hash, 'script_src', cdn_host)
- append_to_directive(settings_hash, 'style_src', cdn_host)
- append_to_directive(settings_hash, 'font_src', cdn_host)
+ def self.allow_cdn(directives, cdn_host)
+ append_to_directive(directives, 'script_src', cdn_host)
+ append_to_directive(directives, 'style_src', cdn_host)
+ append_to_directive(directives, 'font_src', cdn_host)
end
- def self.append_to_directive(settings_hash, directive, text)
- settings_hash['directives'][directive] = "#{settings_hash['directives'][directive]} #{text}".strip
+ def self.append_to_directive(directives, directive, text)
+ directives[directive] = "#{directives[directive]} #{text}".strip
end
- def self.allow_customersdot(settings_hash)
+ def self.allow_customersdot(directives)
customersdot_host = ENV['CUSTOMER_PORTAL_URL']
- append_to_directive(settings_hash, 'frame_src', customersdot_host)
+ append_to_directive(directives, 'frame_src', customersdot_host)
end
- def self.allow_sentry(settings_hash)
+ def self.allow_sentry(directives)
sentry_dsn = Gitlab.config.sentry.clientside_dsn
sentry_uri = URI(sentry_dsn)
sentry_uri.user = nil
- append_to_directive(settings_hash, 'connect_src', sentry_uri.to_s)
+ append_to_directive(directives, 'connect_src', sentry_uri.to_s)
end
end
end
diff --git a/lib/gitlab/data_builder/pipeline.rb b/lib/gitlab/data_builder/pipeline.rb
index b8ccef03e63..385f1e57705 100644
--- a/lib/gitlab/data_builder/pipeline.rb
+++ b/lib/gitlab/data_builder/pipeline.rb
@@ -19,18 +19,40 @@ module Gitlab
user: pipeline.user.try(:hook_attrs),
project: pipeline.project.hook_attrs(backward: false),
commit: pipeline.commit.try(:hook_attrs),
- builds: Gitlab::Lazy.new { pipeline.builds.latest.map(&method(:build_hook_attrs)) }
+ builds: Gitlab::Lazy.new do
+ preload_builds(pipeline, :latest_builds)
+ pipeline.latest_builds.map(&method(:build_hook_attrs))
+ end
)
end
def with_retried_builds
merge(
- builds: Gitlab::Lazy.new { @pipeline.builds.map(&method(:build_hook_attrs)) }
+ builds: Gitlab::Lazy.new do
+ preload_builds(@pipeline, :builds)
+ @pipeline.builds.map(&method(:build_hook_attrs))
+ end
)
end
private
+ # rubocop: disable CodeReuse/ActiveRecord
+ def preload_builds(pipeline, association)
+ ActiveRecord::Associations::Preloader.new.preload(pipeline,
+ {
+ association => {
+ **::Ci::Pipeline::PROJECT_ROUTE_AND_NAMESPACE_ROUTE,
+ runner: :tags,
+ job_artifacts_archive: [],
+ user: [],
+ metadata: []
+ }
+ }
+ )
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
def hook_attrs(pipeline)
{
id: pipeline.id,
diff --git a/lib/gitlab/database/async_indexes/index_creator.rb b/lib/gitlab/database/async_indexes/index_creator.rb
index 82f2a921f10..00de79ec970 100644
--- a/lib/gitlab/database/async_indexes/index_creator.rb
+++ b/lib/gitlab/database/async_indexes/index_creator.rb
@@ -23,6 +23,8 @@ module Gitlab
set_statement_timeout do
connection.execute(async_index.definition)
end
+
+ log_index_info('Finished creating async index')
end
async_index.destroy
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 3ee31ff6e59..f394359f7ac 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -663,15 +663,15 @@ msgstr ""
msgid "%{labelStart}Scan Type:%{labelEnd} %{reportType}"
msgstr ""
-msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
-msgstr ""
-
msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
+msgid "%{labelStart}Tool:%{labelEnd} %{scanner}"
+msgstr ""
+
msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
@@ -11011,10 +11011,10 @@ msgstr ""
msgid "Dependency proxy"
msgstr ""
-msgid "Dependency proxy URL"
+msgid "Dependency proxy feature is limited to public groups for now."
msgstr ""
-msgid "Dependency proxy feature is limited to public groups for now."
+msgid "Dependency proxy image prefix"
msgstr ""
msgid "DependencyProxy|Toggle Dependency Proxy"
@@ -28092,6 +28092,9 @@ msgstr ""
msgid "Reports|Test summary results are being parsed"
msgstr ""
+msgid "Reports|Tool"
+msgstr ""
+
msgid "Reports|Vulnerability"
msgstr ""
@@ -29757,9 +29760,6 @@ msgstr ""
msgid "SecurityReports|Scan details"
msgstr ""
-msgid "SecurityReports|Scanner"
-msgstr ""
-
msgid "SecurityReports|Security Dashboard"
msgstr ""
@@ -29826,6 +29826,9 @@ msgstr ""
msgid "SecurityReports|To widen your search, change or remove filters above"
msgstr ""
+msgid "SecurityReports|Tool"
+msgstr ""
+
msgid "SecurityReports|Unable to add %{invalidProjectsMessage}: %{errorMessage}"
msgstr ""
@@ -38904,10 +38907,10 @@ msgstr ""
msgid "ciReport|All projects"
msgstr ""
-msgid "ciReport|All scanners"
+msgid "ciReport|All severities"
msgstr ""
-msgid "ciReport|All severities"
+msgid "ciReport|All tools"
msgstr ""
msgid "ciReport|Automatically apply the patch in a new branch"
diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb
index c83f60bf8b2..f3500301e22 100644
--- a/spec/factories/ci/builds.rb
+++ b/spec/factories/ci/builds.rb
@@ -237,8 +237,13 @@ FactoryBot.define do
# to the job. If `build.deployment` has already been set, it doesn't
# build a new instance.
environment = Gitlab::Ci::Pipeline::Seed::Environment.new(build).to_resource
- build.deployment =
- Gitlab::Ci::Pipeline::Seed::Deployment.new(build, environment).to_resource
+
+ build.assign_attributes(
+ deployment: Gitlab::Ci::Pipeline::Seed::Deployment.new(build, environment).to_resource,
+ metadata_attributes: {
+ expanded_environment_name: environment.name
+ }
+ )
end
end
diff --git a/spec/finders/ci/pipelines_finder_spec.rb b/spec/finders/ci/pipelines_finder_spec.rb
index 16561aa65b6..c7bd52576e8 100644
--- a/spec/finders/ci/pipelines_finder_spec.rb
+++ b/spec/finders/ci/pipelines_finder_spec.rb
@@ -252,6 +252,29 @@ RSpec.describe Ci::PipelinesFinder do
end
end
+ context 'when source is specified' do
+ let(:params) { { source: 'web' } }
+ let!(:web_pipeline) { create(:ci_pipeline, project: project, source: 'web') }
+ let!(:push_pipeline) { create(:ci_pipeline, project: project, source: 'push') }
+ let!(:api_pipeline) { create(:ci_pipeline, project: project, source: 'api') }
+
+ context 'when `pipeline_source_filter` feature flag is disabled' do
+ before do
+ stub_feature_flags(pipeline_source_filter: false)
+ end
+
+ it 'returns all the pipelines' do
+ is_expected.to contain_exactly(web_pipeline, push_pipeline, api_pipeline)
+ end
+ end
+
+ context 'when `pipeline_source_filter` feature flag is enabled' do
+ it 'returns only the matched pipeline' do
+ is_expected.to eq([web_pipeline])
+ end
+ end
+ end
+
describe 'ordering' do
using RSpec::Parameterized::TableSyntax
diff --git a/spec/fixtures/api/schemas/pipeline_schedule.json b/spec/fixtures/api/schemas/pipeline_schedule.json
index 8a175ba081f..cdb4aea76da 100644
--- a/spec/fixtures/api/schemas/pipeline_schedule.json
+++ b/spec/fixtures/api/schemas/pipeline_schedule.json
@@ -18,6 +18,7 @@
"sha": { "type": "string" },
"ref": { "type": "string" },
"status": { "type": "string" },
+ "source": { "type": "string" },
"web_url": { "type": ["string", "null"] },
"created_at": { "type": ["string", "null"], "format": "date-time" },
"updated_at": { "type": ["string", "null"], "format": "date-time" }
diff --git a/spec/helpers/groups_helper_spec.rb b/spec/helpers/groups_helper_spec.rb
index 8447b92adbf..0b037a45945 100644
--- a/spec/helpers/groups_helper_spec.rb
+++ b/spec/helpers/groups_helper_spec.rb
@@ -19,11 +19,15 @@ RSpec.describe GroupsHelper do
end
end
- describe '#group_dependency_proxy_url' do
+ describe '#group_dependency_proxy_image_prefix' do
+ let_it_be(:group) { build_stubbed(:group, path: 'GroupWithUPPERcaseLetters') }
+
it 'converts uppercase letters to lowercase' do
- group = build_stubbed(:group, path: 'GroupWithUPPERcaseLetters')
+ expect(group_dependency_proxy_image_prefix(group)).to end_with("/groupwithuppercaseletters#{DependencyProxy::URL_SUFFIX}")
+ end
- expect(group_dependency_proxy_url(group)).to end_with("/groupwithuppercaseletters#{DependencyProxy::URL_SUFFIX}")
+ it 'removes the protocol' do
+ expect(group_dependency_proxy_image_prefix(group)).not_to include('http')
end
end
diff --git a/spec/lib/gitlab/background_migration/backfill_integrations_type_new_spec.rb b/spec/lib/gitlab/background_migration/backfill_integrations_type_new_spec.rb
index eaad5f8158b..8f765a7a536 100644
--- a/spec/lib/gitlab/background_migration/backfill_integrations_type_new_spec.rb
+++ b/spec/lib/gitlab/background_migration/backfill_integrations_type_new_spec.rb
@@ -3,6 +3,7 @@
require 'spec_helper'
RSpec.describe Gitlab::BackgroundMigration::BackfillIntegrationsTypeNew do
+ let(:migration) { described_class.new }
let(:integrations) { table(:integrations) }
let(:namespaced_integrations) { Gitlab::Integrations::StiType.namespaced_integrations }
@@ -12,12 +13,31 @@ RSpec.describe Gitlab::BackgroundMigration::BackfillIntegrationsTypeNew do
namespaced_integrations.each_with_index do |type, i|
integrations.create!(id: i + 1, type: "#{type}Service")
end
+
+ integrations.create!(id: namespaced_integrations.size + 1, type: 'LegacyService')
ensure
integrations.connection.execute 'ALTER TABLE integrations ENABLE TRIGGER "trigger_type_new_on_insert"'
end
it 'backfills `type_new` for the selected records' do
- described_class.new.perform(2, 10)
+ # We don't want to mock `Kernel.sleep`, so instead we mock it on the migration
+ # class before it gets forwarded.
+ expect(migration).to receive(:sleep).with(0.05).exactly(5).times
+
+ queries = ActiveRecord::QueryRecorder.new do
+ migration.perform(2, 10, :integrations, :id, 2, 50)
+ end
+
+ expect(queries.count).to be(16)
+ expect(queries.log.grep(/^SELECT/).size).to be(11)
+ expect(queries.log.grep(/^UPDATE/).size).to be(5)
+ expect(queries.log.grep(/^UPDATE/).join.scan(/WHERE .*/)).to eq([
+ 'WHERE integrations.id BETWEEN 2 AND 3',
+ 'WHERE integrations.id BETWEEN 4 AND 5',
+ 'WHERE integrations.id BETWEEN 6 AND 7',
+ 'WHERE integrations.id BETWEEN 8 AND 9',
+ 'WHERE integrations.id BETWEEN 10 AND 10'
+ ])
expect(integrations.where(id: 2..10).pluck(:type, :type_new)).to contain_exactly(
['AssemblaService', 'Integrations::Assembla'],
diff --git a/spec/lib/gitlab/content_security_policy/config_loader_spec.rb b/spec/lib/gitlab/content_security_policy/config_loader_spec.rb
index 436666c3011..239eff11bf3 100644
--- a/spec/lib/gitlab/content_security_policy/config_loader_spec.rb
+++ b/spec/lib/gitlab/content_security_policy/config_loader_spec.rb
@@ -4,7 +4,6 @@ require 'spec_helper'
RSpec.describe Gitlab::ContentSecurityPolicy::ConfigLoader do
let(:policy) { ActionDispatch::ContentSecurityPolicy.new }
- let(:cdn_host) { nil }
let(:csp_config) do
{
enabled: true,
@@ -20,14 +19,28 @@ RSpec.describe Gitlab::ContentSecurityPolicy::ConfigLoader do
}
end
- describe '.default_settings_hash' do
- let(:settings) { described_class.default_settings_hash(cdn_host) }
+ describe '.default_enabled' do
+ let(:enabled) { described_class.default_enabled }
- it 'returns defaults for all keys' do
- expect(settings['enabled']).to be_truthy
- expect(settings['report_only']).to be_falsey
+ it 'is enabled' do
+ expect(enabled).to be_truthy
+ end
- directives = settings['directives']
+ context 'when in production' do
+ before do
+ allow(Rails).to receive(:env).and_return(ActiveSupport::StringInquirer.new('production'))
+ end
+
+ it 'is disabled' do
+ expect(enabled).to be_falsey
+ end
+ end
+ end
+
+ describe '.default_directives' do
+ let(:directives) { described_class.default_directives }
+
+ it 'returns default directives' do
directive_names = (described_class::DIRECTIVES - ['report_uri'])
directive_names.each do |directive|
expect(directives.has_key?(directive)).to be_truthy
@@ -39,22 +52,12 @@ RSpec.describe Gitlab::ContentSecurityPolicy::ConfigLoader do
expect(directives['child_src']).to eq(directives['frame_src'])
end
- context 'when in production' do
+ context 'when CDN host is defined' do
before do
- allow(Rails).to receive(:env).and_return(ActiveSupport::StringInquirer.new('production'))
- end
-
- it 'is disabled' do
- expect(settings['enabled']).to be_falsey
+ stub_config_setting(cdn_host: 'https://example.com')
end
- end
-
- context 'when CDN host is defined' do
- let(:cdn_host) { 'https://example.com' }
it 'adds CDN host to CSP' do
- directives = settings['directives']
-
expect(directives['script_src']).to eq("'strict-dynamic' 'self' 'unsafe-inline' 'unsafe-eval' https://www.google.com/recaptcha/ https://www.recaptcha.net https://apis.google.com https://example.com")
expect(directives['style_src']).to eq("'self' 'unsafe-inline' https://example.com")
expect(directives['font_src']).to eq("'self' https://example.com")
@@ -67,8 +70,6 @@ RSpec.describe Gitlab::ContentSecurityPolicy::ConfigLoader do
end
it 'adds sentry path to CSP without user' do
- directives = settings['directives']
-
expect(directives['connect_src']).to eq("'self' dummy://example.com/43")
end
end
@@ -84,8 +85,6 @@ RSpec.describe Gitlab::ContentSecurityPolicy::ConfigLoader do
end
it 'does not add CUSTOMER_PORTAL_URL to CSP' do
- directives = settings['directives']
-
expect(directives['frame_src']).to eq("'self' https://www.google.com/recaptcha/ https://www.recaptcha.net/ https://content.googleapis.com https://content-compute.googleapis.com https://content-cloudbilling.googleapis.com https://content-cloudresourcemanager.googleapis.com")
end
end
@@ -96,8 +95,6 @@ RSpec.describe Gitlab::ContentSecurityPolicy::ConfigLoader do
end
it 'adds CUSTOMER_PORTAL_URL to CSP' do
- directives = settings['directives']
-
expect(directives['frame_src']).to eq("'self' https://www.google.com/recaptcha/ https://www.recaptcha.net/ https://content.googleapis.com https://content-compute.googleapis.com https://content-cloudbilling.googleapis.com https://content-cloudresourcemanager.googleapis.com https://customers.example.com")
end
end
diff --git a/spec/lib/gitlab/data_builder/pipeline_spec.rb b/spec/lib/gitlab/data_builder/pipeline_spec.rb
index 4c23b24aa31..0e574c7aa84 100644
--- a/spec/lib/gitlab/data_builder/pipeline_spec.rb
+++ b/spec/lib/gitlab/data_builder/pipeline_spec.rb
@@ -131,5 +131,41 @@ RSpec.describe Gitlab::DataBuilder::Pipeline do
expect(build_environment_data[:deployment_tier]).to eq(build.persisted_environment.try(:tier))
end
end
+
+ context 'avoids N+1 database queries' do
+ it "with multiple builds" do
+ # Preparing the pipeline with the minimal builds
+ pipeline = create(:ci_pipeline, user: user, project: project)
+ create(:ci_build, user: user, project: project, pipeline: pipeline)
+ create(:ci_build, :deploy_to_production, :with_deployment, user: user, project: project, pipeline: pipeline)
+
+ # We need `.to_json` as the build hook data is wrapped within `Gitlab::Lazy`
+ control_count = ActiveRecord::QueryRecorder.new { described_class.build(pipeline.reload).to_json }.count
+
+ # Adding more builds to the pipeline and serializing the data again
+ create_list(:ci_build, 3, user: user, project: project, pipeline: pipeline)
+ create(:ci_build, :start_review_app, :with_deployment, user: user, project: project, pipeline: pipeline)
+ create(:ci_build, :stop_review_app, :with_deployment, user: user, project: project, pipeline: pipeline)
+
+ expect { described_class.build(pipeline.reload).to_json }.not_to exceed_query_limit(control_count)
+ end
+
+ it "with multiple retried builds" do
+ # Preparing the pipeline with the minimal builds
+ pipeline = create(:ci_pipeline, user: user, project: project)
+ create(:ci_build, :retried, user: user, project: project, pipeline: pipeline)
+ create(:ci_build, :deploy_to_production, :retried, :with_deployment, user: user, project: project, pipeline: pipeline)
+
+ # We need `.to_json` as the build hook data is wrapped within `Gitlab::Lazy`
+ control_count = ActiveRecord::QueryRecorder.new { described_class.build(pipeline.reload).with_retried_builds.to_json }.count
+
+ # Adding more builds to the pipeline and serializing the data again
+ create_list(:ci_build, 3, :retried, user: user, project: project, pipeline: pipeline)
+ create(:ci_build, :start_review_app, :retried, :with_deployment, user: user, project: project, pipeline: pipeline)
+ create(:ci_build, :stop_review_app, :retried, :with_deployment, user: user, project: project, pipeline: pipeline)
+
+ expect { described_class.build(pipeline.reload).with_retried_builds.to_json }.not_to exceed_query_limit(control_count)
+ end
+ end
end
end
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index 74a476a6422..1d0b67c6902 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -263,6 +263,20 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
end
+ describe '.with_pipeline_source' do
+ subject { described_class.with_pipeline_source(source) }
+
+ let(:source) { 'web' }
+
+ let_it_be(:push_pipeline) { create(:ci_pipeline, source: :push) }
+ let_it_be(:web_pipeline) { create(:ci_pipeline, source: :web) }
+ let_it_be(:api_pipeline) { create(:ci_pipeline, source: :api) }
+
+ it 'contains pipelines created due to specified source' do
+ expect(subject).to contain_exactly(web_pipeline)
+ end
+ end
+
describe '.ci_sources' do
subject { described_class.ci_sources }
diff --git a/spec/requests/api/ci/pipelines_spec.rb b/spec/requests/api/ci/pipelines_spec.rb
index 6c0a1b2502f..640e1ee6422 100644
--- a/spec/requests/api/ci/pipelines_spec.rb
+++ b/spec/requests/api/ci/pipelines_spec.rb
@@ -34,7 +34,28 @@ RSpec.describe API::Ci::Pipelines do
expect(json_response.first['sha']).to match(/\A\h{40}\z/)
expect(json_response.first['id']).to eq pipeline.id
expect(json_response.first['web_url']).to be_present
- expect(json_response.first.keys).to contain_exactly(*%w[id project_id sha ref status web_url created_at updated_at])
+ end
+
+ describe 'keys in the response' do
+ context 'when `pipeline_source_filter` feature flag is disabled' do
+ before do
+ stub_feature_flags(pipeline_source_filter: false)
+ end
+
+ it 'does not includes pipeline source' do
+ get api("/projects/#{project.id}/pipelines", user)
+
+ expect(json_response.first.keys).to contain_exactly(*%w[id project_id sha ref status web_url created_at updated_at])
+ end
+ end
+
+ context 'when `pipeline_source_filter` feature flag is disabled' do
+ it 'includes pipeline source' do
+ get api("/projects/#{project.id}/pipelines", user)
+
+ expect(json_response.first.keys).to contain_exactly(*%w[id project_id sha ref status web_url created_at updated_at source])
+ end
+ end
end
context 'when parameter is passed' do
@@ -294,6 +315,48 @@ RSpec.describe API::Ci::Pipelines do
end
end
end
+
+ context 'when a source is specified' do
+ before do
+ create(:ci_pipeline, project: project, source: :push)
+ create(:ci_pipeline, project: project, source: :web)
+ create(:ci_pipeline, project: project, source: :api)
+ end
+
+ context 'when `pipeline_source_filter` feature flag is disabled' do
+ before do
+ stub_feature_flags(pipeline_source_filter: false)
+ end
+
+ it 'returns all pipelines' do
+ get api("/projects/#{project.id}/pipelines", user), params: { source: 'web' }
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to include_pagination_headers
+ expect(json_response).not_to be_empty
+ expect(json_response.length).to be >= 3
+ end
+ end
+
+ context 'when `pipeline_source_filter` feature flag is enabled' do
+ it 'returns matched pipelines' do
+ get api("/projects/#{project.id}/pipelines", user), params: { source: 'web' }
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to include_pagination_headers
+ expect(json_response).not_to be_empty
+ json_response.each { |r| expect(r['source']).to eq('web') }
+ end
+
+ context 'when source is invalid' do
+ it 'returns bad_request' do
+ get api("/projects/#{project.id}/pipelines", user), params: { source: 'invalid-source' }
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ end
+ end
+ end
+ end
end
end
diff --git a/spec/requests/projects/merge_requests_spec.rb b/spec/requests/projects/merge_requests_spec.rb
index 1c5104b39cb..59fde803560 100644
--- a/spec/requests/projects/merge_requests_spec.rb
+++ b/spec/requests/projects/merge_requests_spec.rb
@@ -5,10 +5,18 @@ require 'spec_helper'
RSpec.describe 'merge requests actions' do
let_it_be(:project) { create(:project, :repository) }
- let(:merge_request) { create(:merge_request_with_diffs, target_project: project, source_project: project) }
+ let(:merge_request) do
+ create(:merge_request_with_diffs, target_project: project,
+ source_project: project,
+ assignees: [user],
+ reviewers: [user2])
+ end
+
let(:user) { project.owner }
+ let(:user2) { create(:user) }
before do
+ project.add_maintainer(user2)
sign_in(user)
end
@@ -61,17 +69,11 @@ RSpec.describe 'merge requests actions' do
end
context 'when the merge request is updated' do
- let(:user2) { create(:user) }
-
- before do
- project.add_maintainer(user2)
- end
-
def update_service(params)
MergeRequests::UpdateService.new(project: project, current_user: user, params: params).execute(merge_request)
end
- context 'when the user is different' do
+ context 'when the logged in user is different' do
before do
sign_in(user2)
end
@@ -79,61 +81,61 @@ RSpec.describe 'merge requests actions' do
it_behaves_like 'a non-cached request'
end
- context 'when the reviewer is changed' do
+ context 'when the assignee is changed' do
before do
- update_service(reviewer_ids: [user2.id])
+ update_service( assignee_ids: [] )
end
it_behaves_like 'a non-cached request'
end
- context 'when the assignee is changed' do
+ context 'when the existing assignee gets updated' do
before do
- update_service( assignee_ids: [user2.id] )
+ user.update_attribute(:avatar, 'uploads/avatar.png')
end
it_behaves_like 'a non-cached request'
end
- context 'when the time_estimate is changed' do
+ context 'when the reviewer is changed' do
before do
- update_service(time_estimate: 7200)
+ update_service(reviewer_ids: [])
end
it_behaves_like 'a non-cached request'
end
- context 'when the spend_time is changed' do
+ context 'when the existing reviewer gets updated' do
before do
- update_service(spend_time: { duration: 7200, user_id: user.id, spent_at: Time.now, note_id: nil })
+ user2.update_attribute(:avatar, 'uploads/avatar.png')
end
it_behaves_like 'a non-cached request'
end
- context 'when a user leaves a note' do
+ context 'when the time_estimate is changed' do
before do
- # We have 1 minute ThrottledTouch to account for.
- # It's not ideal as it means that our participants cache could be stale for about a day if a new note is created by another person or gets a mention.
- travel_to(Time.current + 61) do
- Notes::CreateService.new(project, user2, { note: 'Looks good', noteable_type: 'MergeRequest', noteable_id: merge_request.id }).execute
- end
+ update_service(time_estimate: 7200)
end
it_behaves_like 'a non-cached request'
end
- context 'when the email setting has changed in project' do
+ context 'when the spend_time is changed' do
before do
- project.namespace.update_attribute(:emails_disabled, true)
+ update_service(spend_time: { duration: 7200, user_id: user.id, spent_at: Time.now, note_id: nil })
end
it_behaves_like 'a non-cached request'
end
- context 'when the user changes unsubscribes' do
+ context 'when a user leaves a note' do
before do
- merge_request.set_subscription(user, false, project)
+ # We have 1 minute ThrottledTouch to account for.
+ # It's not ideal as it means that our participants cache could be stale for about a day if a new note is created by another person or gets a mention.
+ travel_to(Time.current + 61) do
+ Notes::CreateService.new(project, user2, { note: 'Looks good', noteable_type: 'MergeRequest', noteable_id: merge_request.id }).execute
+ end
end
it_behaves_like 'a non-cached request'