diff options
45 files changed, 442 insertions, 244 deletions
diff --git a/GITLAB_PAGES_VERSION b/GITLAB_PAGES_VERSION index 0a3db35b241..21998d3c2d9 100644 --- a/GITLAB_PAGES_VERSION +++ b/GITLAB_PAGES_VERSION @@ -1 +1 @@ -1.46.0 +1.47.0 diff --git a/app/assets/javascripts/lib/apollo/suppress_network_errors_during_navigation_link.js b/app/assets/javascripts/lib/apollo/suppress_network_errors_during_navigation_link.js index ad92bd4de42..9b7901685b6 100644 --- a/app/assets/javascripts/lib/apollo/suppress_network_errors_during_navigation_link.js +++ b/app/assets/javascripts/lib/apollo/suppress_network_errors_during_navigation_link.js @@ -9,10 +9,6 @@ import { isNavigatingAway } from '~/lib/utils/is_navigating_away'; * @returns {ApolloLink|null} */ export const getSuppressNetworkErrorsDuringNavigationLink = () => { - if (!gon.features?.suppressApolloErrorsDuringNavigation) { - return null; - } - return onError(({ networkError }) => { if (networkError && isNavigatingAway()) { // Return an observable that will never notify any subscribers with any diff --git a/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab_section_link.vue b/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab_section_link.vue index 69fb5878f5c..0995947f3e7 100644 --- a/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab_section_link.vue +++ b/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab_section_link.vue @@ -40,6 +40,7 @@ export default { data-track-action="click_link" :data-track-label="$options.i18n.ACTION_LABELS[action].title" data-track-property="Growth::Conversion::Experiment::LearnGitLab" + data-track-experiment="change_continuous_onboarding_link_urls" > {{ $options.i18n.ACTION_LABELS[action].title }} </gl-link> diff --git a/app/assets/stylesheets/pages/deploy_keys.scss b/app/assets/stylesheets/pages/deploy_keys.scss index 2fafe052106..997e42a8fd5 100644 --- a/app/assets/stylesheets/pages/deploy_keys.scss +++ b/app/assets/stylesheets/pages/deploy_keys.scss @@ -1,12 +1,3 @@ -.deploy-keys-list { - width: 100%; - overflow: auto; - - table { - border: 1px solid $table-border-color; - } -} - .deploy-keys-title { padding-bottom: 2px; line-height: 2; diff --git a/app/experiments/change_continuous_onboarding_link_urls_experiment.rb b/app/experiments/change_continuous_onboarding_link_urls_experiment.rb new file mode 100644 index 00000000000..680cb8eadd8 --- /dev/null +++ b/app/experiments/change_continuous_onboarding_link_urls_experiment.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class ChangeContinuousOnboardingLinkUrlsExperiment < ApplicationExperiment # rubocop:disable Gitlab/NamespacedClass + attr_writer :namespace + + def track(action, **event_args) + super(action, **event_args.merge(namespace: @namespace)) + end +end diff --git a/app/helpers/learn_gitlab_helper.rb b/app/helpers/learn_gitlab_helper.rb index 4f69d1bba0e..a7c77bcad67 100644 --- a/app/helpers/learn_gitlab_helper.rb +++ b/app/helpers/learn_gitlab_helper.rb @@ -13,6 +13,7 @@ module LearnGitlabHelper urls_to_use = nil experiment(:change_continuous_onboarding_link_urls) do |e| + e.namespace = project.namespace e.use { urls_to_use = action_urls } e.try { urls_to_use = new_action_urls(project) } end diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb index c2d1447bf8b..d81db357162 100644 --- a/app/policies/project_policy.rb +++ b/app/policies/project_policy.rb @@ -47,6 +47,9 @@ class ProjectPolicy < BasePolicy desc "Project is archived" condition(:archived, scope: :subject, score: 0) { project.archived? } + desc "Project is in the process of being deleted" + condition(:pending_delete) { project.pending_delete? } + condition(:default_issues_tracker, scope: :subject) { project.default_issues_tracker? } desc "Container registry is disabled" @@ -457,7 +460,13 @@ class ProjectPolicy < BasePolicy prevent(*readonly_abilities) readonly_features.each do |feature| - prevent(*create_update_admin_destroy(feature)) + prevent(*create_update_admin(feature)) + end + end + + rule { archived & ~pending_delete }.policy do + readonly_features.each do |feature| + prevent(:"destroy_#{feature}") end end diff --git a/app/views/admin/deploy_keys/index.html.haml b/app/views/admin/deploy_keys/index.html.haml index 1cbb5296f8d..ba4abdc02e4 100644 --- a/app/views/admin/deploy_keys/index.html.haml +++ b/app/views/admin/deploy_keys/index.html.haml @@ -7,31 +7,38 @@ %h3.page-title.deploy-keys-title = _('Public deploy keys (%{deploy_keys_count})') % { deploy_keys_count: @deploy_keys.load.size } = link_to _('New deploy key'), new_admin_deploy_key_path, class: 'float-right btn gl-button btn-confirm btn-md gl-button' - .table-holder.deploy-keys-list - %table.table - %thead + %table.table.b-table.gl-table.b-table-stacked-lg{ data: { testid: 'deploy-keys-list' } } + %thead + %tr + %th= _('Title') + %th= _('Fingerprint') + %th= _('Projects with write access') + %th= _('Created') + %th.gl-lg-w-1px.gl-white-space-nowrap + %span.gl-sr-only + = _('Actions') + %tbody + - @deploy_keys.each do |deploy_key| %tr - %th.col-sm-2= _('Title') - %th.col-sm-4= _('Fingerprint') - %th.col-sm-2= _('Projects with write access') - %th.col-sm-2= _('Added at') - %th.col-sm-2 - %tbody - - @deploy_keys.each do |deploy_key| - %tr - %td - %strong= deploy_key.title - %td - %code.key-fingerprint= deploy_key.fingerprint - %td + %td{ data: { label: _('Title') } } + %div + = deploy_key.title + %td{ data: { label: _('Fingerprint') } } + %div + %code= deploy_key.fingerprint + %td{ data: { label: _('Projects with write access') } } + %div - deploy_key.projects_with_write_access.each do |project| - = link_to project.full_name, admin_project_path(project), class: 'label deploy-project-label' - %td - %span.cgray - = _('added %{created_at_timeago}').html_safe % { created_at_timeago: time_ago_with_tooltip(deploy_key.created_at) } - %td - .float-right - = link_to _('Edit'), edit_admin_deploy_key_path(deploy_key), class: 'btn gl-button btn-sm' - = link_to _('Remove'), admin_deploy_key_path(deploy_key), data: { confirm: _('Are you sure?') }, method: :delete, class: 'gl-button btn btn-sm btn-danger delete-key' + = link_to project.full_name, admin_project_path(project), class: 'gl-display-block' + %td{ data: { label: _('Created') } } + %div + = time_ago_with_tooltip(deploy_key.created_at) + %td.gl-lg-w-1px.gl-white-space-nowrap{ data: { label: _('Actions') } } + %div + = link_to edit_admin_deploy_key_path(deploy_key), class: 'btn btn-default btn-md gl-button btn-icon gl-mr-3', aria: { label: _('Edit deploy key') } do + = sprite_icon('pencil', css_class: 'gl-button-icon') + = link_to admin_deploy_key_path(deploy_key), data: { confirm: _('Are you sure?') }, method: :delete, class: 'btn btn-danger btn-md gl-button btn-icon', aria: { label: _('Remove deploy key') } do + = sprite_icon('remove', css_class: 'gl-button-icon') + - else = render 'shared/empty_states/deploy_keys' diff --git a/app/views/explore/groups/_nav.html.haml b/app/views/explore/groups/_nav.html.haml index c337149a2f3..3c9c4e9f76b 100644 --- a/app/views/explore/groups/_nav.html.haml +++ b/app/views/explore/groups/_nav.html.haml @@ -1,8 +1,6 @@ .top-area - %ul.nav-links.nav.nav-tabs - = nav_link(page: explore_groups_path) do - = link_to explore_groups_path do - = _("Explore Groups") + = gl_tabs_nav({ class: 'gl-display-flex gl-flex-grow-1 gl-border-none'}) do + = gl_tab_link_to _("Explore Groups"), explore_groups_path .nav-controls = render 'shared/groups/search_form' = render 'shared/groups/dropdown' diff --git a/app/views/explore/projects/_nav.html.haml b/app/views/explore/projects/_nav.html.haml index 65b7d055843..9d7a6f1ccfb 100644 --- a/app/views/explore/projects/_nav.html.haml +++ b/app/views/explore/projects/_nav.html.haml @@ -1,14 +1,8 @@ .top-area - %ul.nav-links.nav.nav-tabs - = nav_link(page: [explore_projects_path, explore_root_path]) do - = link_to explore_projects_path do - = _('All') - = nav_link(page: starred_explore_projects_path) do - = link_to starred_explore_projects_path do - = _('Most stars') - = nav_link(page: trending_explore_projects_path) do - = link_to trending_explore_projects_path do - = _('Trending') + = gl_tabs_nav({ class: 'gl-display-flex gl-flex-grow-1 gl-border-none'}) do + = gl_tab_link_to _('All'), explore_projects_path, { item_active: current_page?(explore_projects_path) || current_page?(explore_root_path) } + = gl_tab_link_to _('Most stars'), starred_explore_projects_path + = gl_tab_link_to _('Trending'), trending_explore_projects_path .nav-controls - unless current_user diff --git a/app/views/projects/blob/edit.html.haml b/app/views/projects/blob/edit.html.haml index ecbef9a11a7..8378ce2c7e5 100644 --- a/app/views/projects/blob/edit.html.haml +++ b/app/views/projects/blob/edit.html.haml @@ -16,14 +16,10 @@ %h3.page-title.blob-edit-page-title Edit file .file-editor - %ul.nav-links.no-bottom.js-edit-mode.nav.nav-tabs - %li.active - = link_to '#editor' do - Write + = gl_tabs_nav({ class: 'js-edit-mode nav-links gl-border-0'}) do + = gl_tab_link_to _('Write'), '#editor', { tab_class: 'active' } - %li - = link_to '#preview', 'data-preview-url' => project_preview_blob_path(@project, @id) do - = editing_preview_title(@blob.name) + = gl_tab_link_to editing_preview_title(@blob.name), '#preview', { data: { 'preview-url': project_preview_blob_path(@project, @id) } } = form_tag(project_update_blob_path(@project, @id), method: :put, class: 'js-quick-submit js-requires-input js-edit-blob-form', data: blob_editor_paths(@project)) do = render 'projects/blob/editor', ref: @ref, path: @path, blob_data: @blob.data diff --git a/app/views/projects/merge_requests/_widget.html.haml b/app/views/projects/merge_requests/_widget.html.haml index 459742c3b81..0c8af873095 100644 --- a/app/views/projects/merge_requests/_widget.html.haml +++ b/app/views/projects/merge_requests/_widget.html.haml @@ -14,8 +14,8 @@ window.gl.mrWidgetData.pipeline_must_succeed_docs_path = '#{help_page_path('user/project/merge_requests/merge_when_pipeline_succeeds.md', anchor: 'only-allow-merge-requests-to-be-merged-if-the-pipeline-succeeds')}'; window.gl.mrWidgetData.security_approvals_help_page_path = '#{help_page_path('user/application_security/index.md', anchor: 'security-approvals-in-merge-requests')}'; window.gl.mrWidgetData.license_compliance_docs_path = '#{help_page_path('user/compliance/license_compliance/index.md', anchor: 'policies')}'; - window.gl.mrWidgetData.eligible_approvers_docs_path = '#{help_page_path('user/project/merge_requests/merge_request_approvals', anchor: 'eligible-approvers')}'; - window.gl.mrWidgetData.approvals_help_path = '#{help_page_path("user/project/merge_requests/merge_request_approvals")}'; + window.gl.mrWidgetData.eligible_approvers_docs_path = '#{help_page_path('user/project/merge_requests/approvals/rules.md', anchor: 'eligible-approvers')}'; + window.gl.mrWidgetData.approvals_help_path = '#{help_page_path("user/project/merge_requests/approvals/index.md")}'; window.gl.mrWidgetData.pipelines_empty_svg_path = '#{image_path('illustrations/pipelines_empty.svg')}'; window.gl.mrWidgetData.codequality_help_path = '#{help_page_path("user/project/merge_requests/code_quality", anchor: "code-quality-reports")}'; window.gl.mrWidgetData.false_positive_doc_url = '#{help_page_path('user/application_security/vulnerabilities/index')}'; diff --git a/app/views/search/_category.html.haml b/app/views/search/_category.html.haml index ca6f2369bd8..54aa9aad8a5 100644 --- a/app/views/search/_category.html.haml +++ b/app/views/search/_category.html.haml @@ -5,7 +5,7 @@ .scrolling-tabs-container.inner-page-scroll-tabs.is-smaller .fade-left= sprite_icon('chevron-lg-left', size: 12) .fade-right= sprite_icon('chevron-lg-right', size: 12) - %ul.nav-links.search-filter.scrolling-tabs.nav.nav-tabs + = gl_tabs_nav({ class: 'search-filter scrolling-tabs nav-links'}) do - if @project - if project_search_tabs?(:blobs) = search_filter_link 'blobs', _("Code"), data: { qa_selector: 'code_tab' } diff --git a/app/views/shared/_md_preview.html.haml b/app/views/shared/_md_preview.html.haml index 0976defea1b..a49a0667d84 100644 --- a/app/views/shared/_md_preview.html.haml +++ b/app/views/shared/_md_preview.html.haml @@ -9,7 +9,7 @@ .md-area.position-relative .md-header - %ul.nav.nav-tabs.nav-links.clearfix + = gl_tabs_nav({ class: 'clearfix nav-links'}) do %li.md-header-tab.active %button.js-md-write-button = _("Write") diff --git a/config/feature_flags/development/suppress_apollo_errors_during_navigation.yml b/config/feature_flags/development/suppress_apollo_errors_during_navigation.yml deleted file mode 100644 index 21548fa4dbb..00000000000 --- a/config/feature_flags/development/suppress_apollo_errors_during_navigation.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: suppress_apollo_errors_during_navigation -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/72031 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/342745 -milestone: '14.4' -type: development -group: group::foundations -default_enabled: false diff --git a/db/post_migrate/20211102103127_add_temp_index_to_vulnerability_occurrences.rb b/db/post_migrate/20211102103127_add_temp_index_to_vulnerability_occurrences.rb new file mode 100644 index 00000000000..4aee79950dc --- /dev/null +++ b/db/post_migrate/20211102103127_add_temp_index_to_vulnerability_occurrences.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class AddTempIndexToVulnerabilityOccurrences < Gitlab::Database::Migration[1.0] + INDEX_NAME = 'vulnerability_occurrences_location_temp_index' + + disable_ddl_transaction! + + def up + add_concurrent_index :vulnerability_occurrences, :id, where: 'location IS NULL', name: INDEX_NAME + end + + def down + remove_concurrent_index_by_name :vulnerability_occurrences, name: INDEX_NAME + end +end diff --git a/db/post_migrate/20211102114802_update_vulnerability_occurrences_location.rb b/db/post_migrate/20211102114802_update_vulnerability_occurrences_location.rb new file mode 100644 index 00000000000..20618b78391 --- /dev/null +++ b/db/post_migrate/20211102114802_update_vulnerability_occurrences_location.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +class UpdateVulnerabilityOccurrencesLocation < Gitlab::Database::Migration[1.0] + BATCH_SIZE = 20_000 + DELAY_INTERVAL = 3.minutes + MIGRATION_NAME = 'UpdateVulnerabilityOccurrencesLocation' + + disable_ddl_transaction! + + def up + relation = Gitlab::BackgroundMigration::UpdateVulnerabilityOccurrencesLocation::Occurrence.where(location: nil) + queue_background_migration_jobs_by_range_at_intervals(relation, + MIGRATION_NAME, + DELAY_INTERVAL, + batch_size: BATCH_SIZE, + track_jobs: true) + end + + def down + # no-op + end +end diff --git a/db/schema_migrations/20211102103127 b/db/schema_migrations/20211102103127 new file mode 100644 index 00000000000..2ce33ad085a --- /dev/null +++ b/db/schema_migrations/20211102103127 @@ -0,0 +1 @@ +450028c90cb92f5ce3f8239eb56364b83ed025839aaf305b7ceb4fda077681b1
\ No newline at end of file diff --git a/db/schema_migrations/20211102114802 b/db/schema_migrations/20211102114802 new file mode 100644 index 00000000000..35d3a25f3cb --- /dev/null +++ b/db/schema_migrations/20211102114802 @@ -0,0 +1 @@ +a3f9fcac354cccfdfc42b8f5baab651cb65ca60e4474ce937ab25b552bfe483c
\ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index c2bf310d3c6..f1c6725d927 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -27795,6 +27795,8 @@ CREATE UNIQUE INDEX vulnerability_feedback_unique_idx ON vulnerability_feedback CREATE UNIQUE INDEX vulnerability_occurrence_pipelines_on_unique_keys ON vulnerability_occurrence_pipelines USING btree (occurrence_id, pipeline_id); +CREATE INDEX vulnerability_occurrences_location_temp_index ON vulnerability_occurrences USING btree (id) WHERE (location IS NULL); + CREATE UNIQUE INDEX work_item_types_namespace_id_and_name_unique ON work_item_types USING btree (namespace_id, btrim(lower(name))); ALTER INDEX analytics_cycle_analytics_issue_stage_events_pkey ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_00_pkey; diff --git a/doc/development/service_ping/index.md b/doc/development/service_ping/index.md index 152eec87d5d..6007468db3f 100644 --- a/doc/development/service_ping/index.md +++ b/doc/development/service_ping/index.md @@ -51,7 +51,7 @@ We use the following terminology to describe the Service Ping components: metric has a corresponding [metric definition](metrics_dictionary.md#metrics-definition-and-validation) in a YAML file. - **MAU**: monthly active users. -- **WAU**: weekly active users. +- **WAU**: weekly active users. ### Why should we enable Service Ping? @@ -64,29 +64,43 @@ We use the following terminology to describe the Service Ping components: - Service Ping is enabled by default. To disable it, see [Disable Service Ping](#disable-service-ping). - When Service Ping is enabled, you have the option to participate in our [Registration Features Program](#registration-features-program) and receive free paid features. -#### Registration Features Program +### Limitations + +- Service Ping does not track frontend events things like page views, link clicks, or user sessions. +- Service Ping focuses only on aggregated backend events. + +Because of these limitations we recommend you: + +- Instrument your products with Snowplow for more detailed analytics on GitLab.com. +- Use Service Ping to track aggregated backend events on self-managed instances. + +### Registration Features Program > Introduced in GitLab 14.1. -Starting with GitLab version 14.1, free self-managed users running [GitLab EE](../ee_features.md) can receive paid features by registering with GitLab and sending us activity data via [Service Ping](#what-is-service-ping). Features introduced here do not remove the feature from its paid tier. Users can continue to access the features in a paid tier without sharing usage data. +In GitLab versions 14.1 and later, free self-managed users running [GitLab EE](../ee_features.md) can receive paid features by registering with GitLab and sending us activity data through [Service Ping](#what-is-service-ping). Features introduced here do not remove the feature from its paid tier. Users can continue to access the features in a paid tier without sharing usage data. -##### Features available in 14.1 and later +#### Features available in 14.1 and later 1. [Email from GitLab](../../tools/email.md). -##### Features available in 14.4 and later +#### Features available in 14.4 and later 1. [Repository size limit](../../user/admin_area/settings/account_and_limit_settings.md#repository-size-limit). - 1. [Restrict group access by IP address](../../user/group/index.md#restrict-group-access-by-ip-address). NOTE: Registration is not yet required for participation, but will be added in a future milestone. -### Limitations +#### Enable Registration Features -- Service Ping does not track frontend events things like page views, link clicks, or user sessions, and only focuses on aggregated backend events. -- Because of these limitations we recommend instrumenting your products with Snowplow for more detailed analytics on GitLab.com and use Service Ping to track aggregated backend events on self-managed. +1. Sign in as a user with the [Administrator](../../user/permissions.md) role. +1. On the top bar, select **Menu > Admin**. +1. On the left sidebar, select **Settings > Metrics and profiling**. +1. Expand the **Usage statistics** section. +1. If not enabled, select the **Enable Service Ping** checkbox. +1. Select the **Enable Registration Features** checkbox. +1. Select **Save changes**. ## View the Service Ping payload **(FREE SELF)** diff --git a/doc/operations/incident_management/integrations.md b/doc/operations/incident_management/integrations.md index 3e498454c55..92f5a50b1c3 100644 --- a/doc/operations/incident_management/integrations.md +++ b/doc/operations/incident_management/integrations.md @@ -196,7 +196,9 @@ WARNING: Using your authorization key in the URL is insecure, as it's visible in server logs. We recommend using one of the above header options if your tooling supports it. -## Response Body +## Response body + +> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/342730) in GitLab 14.5. The JSON response body contains a list of any alerts created within the request: diff --git a/doc/user/infrastructure/index.md b/doc/user/infrastructure/index.md index 3dcce47105b..3bb518596cc 100644 --- a/doc/user/infrastructure/index.md +++ b/doc/user/infrastructure/index.md @@ -31,7 +31,7 @@ Learn more about how GitLab can help you run [Infrastructure as Code](iac/index. The GitLab integration with Kubernetes helps you to install, configure, manage, deploy, and troubleshoot cluster applications. With the GitLab Kubernetes Agent, you can connect clusters behind a firewall, -have real-time access to API endpoints, perform pull-beased or push-based deployments for production +have real-time access to API endpoints, perform pull-based or push-based deployments for production and non-production environments, and much more. Learn more about the [GitLab Kubernetes Agent](../clusters/agent/index.md). diff --git a/doc/user/project/merge_requests/approvals/index.md b/doc/user/project/merge_requests/approvals/index.md index aff55419a88..d873f715557 100644 --- a/doc/user/project/merge_requests/approvals/index.md +++ b/doc/user/project/merge_requests/approvals/index.md @@ -3,7 +3,7 @@ stage: Create group: Source Code info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments" type: reference, concepts -disqus_identifier: 'https://docs.gitlab.com/ee/user/project/merge_requests/merge_request_approvals.html' +disqus_identifier: 'https://docs.gitlab.com/ee/user/project/merge_requests/approvals/index.html' --- # Merge request approvals **(FREE)** diff --git a/lib/gitlab/background_migration/update_vulnerability_occurrences_location.rb b/lib/gitlab/background_migration/update_vulnerability_occurrences_location.rb new file mode 100644 index 00000000000..458e0537f1c --- /dev/null +++ b/lib/gitlab/background_migration/update_vulnerability_occurrences_location.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module Gitlab + module BackgroundMigration + # rubocop: disable Style/Documentation + class UpdateVulnerabilityOccurrencesLocation + def perform(start_id, stop_id) + end + end + # rubocop: enable Style/Documentation + end +end + +Gitlab::BackgroundMigration::UpdateVulnerabilityOccurrencesLocation.prepend_mod_with('Gitlab::BackgroundMigration::UpdateVulnerabilityOccurrencesLocation') diff --git a/lib/gitlab/content_security_policy/config_loader.rb b/lib/gitlab/content_security_policy/config_loader.rb index b55eb4f1263..bdae59e7e3c 100644 --- a/lib/gitlab/content_security_policy/config_loader.rb +++ b/lib/gitlab/content_security_policy/config_loader.rb @@ -19,11 +19,11 @@ module Gitlab 'font_src' => "'self'", 'form_action' => "'self' https: http:", 'frame_ancestors' => "'self'", - 'frame_src' => "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", + 'frame_src' => ContentSecurityPolicy::Directives.frame_src, '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", + 'script_src' => ContentSecurityPolicy::Directives.script_src, 'style_src' => "'self' 'unsafe-inline'", 'worker_src' => "#{Gitlab::Utils.append_path(Gitlab.config.gitlab.url, 'assets/')} blob: data:", 'object_src' => "'none'", diff --git a/lib/gitlab/content_security_policy/directives.rb b/lib/gitlab/content_security_policy/directives.rb new file mode 100644 index 00000000000..30f3c16247d --- /dev/null +++ b/lib/gitlab/content_security_policy/directives.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +# This module is used to return various SaaS related +# ContentSecurityPolicy Directives src which may be +# overridden in other variants of GitLab + +module Gitlab + module ContentSecurityPolicy + module Directives + def self.frame_src + "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 + + def self.script_src + "'strict-dynamic' 'self' 'unsafe-inline' 'unsafe-eval' https://www.google.com/recaptcha/ https://www.recaptcha.net https://apis.google.com" + end + end + end +end + +Gitlab::ContentSecurityPolicy::Directives.prepend_mod diff --git a/lib/gitlab/gon_helper.rb b/lib/gitlab/gon_helper.rb index 027551df15d..9ad902efb3a 100644 --- a/lib/gitlab/gon_helper.rb +++ b/lib/gitlab/gon_helper.rb @@ -56,7 +56,6 @@ module Gitlab push_frontend_feature_flag(:security_auto_fix, default_enabled: false) push_frontend_feature_flag(:improved_emoji_picker, default_enabled: :yaml) push_frontend_feature_flag(:new_header_search, default_enabled: :yaml) - push_frontend_feature_flag(:suppress_apollo_errors_during_navigation, current_user, default_enabled: :yaml) push_frontend_feature_flag(:configure_iac_scanning_via_mr, current_user, default_enabled: :yaml) push_frontend_feature_flag(:bootstrap_confirmation_modals, default_enabled: :yaml) end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 9fa1f64f025..f9fcc07c367 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -2166,9 +2166,6 @@ msgstr "" msgid "Added an issue to an epic." msgstr "" -msgid "Added at" -msgstr "" - msgid "Added for this merge request" msgstr "" @@ -12465,6 +12462,9 @@ msgstr "" msgid "Edit deploy freeze" msgstr "" +msgid "Edit deploy key" +msgstr "" + msgid "Edit description" msgstr "" @@ -13927,9 +13927,6 @@ msgstr "" msgid "Exceptions" msgstr "" -msgid "Excess storage" -msgstr "" - msgid "Excluding merge commits. Limited to %{limit} commits." msgstr "" @@ -37263,18 +37260,6 @@ msgstr "" msgid "UsageQuota|This namespace has no projects which use shared runners" msgstr "" -msgid "UsageQuota|This project is at risk of being locked because purchased storage is running low." -msgstr "" - -msgid "UsageQuota|This project is locked because it is using %{actualRepositorySizeLimit} of free storage and there is no purchased storage available." -msgstr "" - -msgid "UsageQuota|This project is locked because it used %{actualRepositorySizeLimit} of free storage and all the purchased storage." -msgstr "" - -msgid "UsageQuota|This project is near the free %{actualRepositorySizeLimit} limit and at risk of being locked." -msgstr "" - msgid "UsageQuota|Total excess storage used" msgstr "" @@ -40188,9 +40173,6 @@ msgstr "" msgid "added" msgstr "" -msgid "added %{created_at_timeago}" -msgstr "" - msgid "added %{emails}" msgstr "" diff --git a/package.json b/package.json index a51f3d180c5..b6f7725d068 100644 --- a/package.json +++ b/package.json @@ -205,7 +205,7 @@ "@babel/plugin-transform-modules-commonjs": "^7.10.1", "@gitlab/eslint-plugin": "10.0.0", "@gitlab/stylelint-config": "2.6.0", - "@graphql-eslint/eslint-plugin": "^2.3.0", + "@graphql-eslint/eslint-plugin": "2.3.0", "@testing-library/dom": "^7.16.2", "@vue/test-utils": "1.2.0", "acorn": "^6.3.0", diff --git a/qa/qa/resource/project.rb b/qa/qa/resource/project.rb index 43f2039fd78..864f3a14c3d 100644 --- a/qa/qa/resource/project.rb +++ b/qa/qa/resource/project.rb @@ -217,6 +217,10 @@ module QA "#{api_get_path}/wikis" end + def api_push_rules_path + "#{api_get_path}/push_rule" + end + def api_post_body post_body = { name: name, @@ -361,6 +365,15 @@ module QA parse_body(response) end + def push_rules + response = get(request_url(api_push_rules_path)) + parse_body(response) + end + + def add_push_rules(rules) + api_post_to(api_push_rules_path, rules) + end + # Object comparison # # @param [QA::Resource::Project] other diff --git a/qa/qa/specs/features/api/1_manage/bulk_import_project_spec.rb b/qa/qa/specs/features/api/1_manage/bulk_import_project_spec.rb index 2bf9a7e62bf..7d0bb92f13a 100644 --- a/qa/qa/specs/features/api/1_manage/bulk_import_project_spec.rb +++ b/qa/qa/specs/features/api/1_manage/bulk_import_project_spec.rb @@ -33,6 +33,7 @@ module QA Resource::Project.fabricate_via_api! do |project| project.api_client = api_client project.group = source_group + project.initialize_with_readme = true end end @@ -60,7 +61,7 @@ module QA sandbox.add_member(user, Resource::Members::AccessLevel::MAINTAINER) - source_project # fabricate source group and project + source_project.tap { |project| project.add_push_rules(member_check: true) } # fabricate source group and project end after do diff --git a/qa/qa/specs/features/browser_ui/5_package/package_registry/npm/npm_project_level_spec.rb b/qa/qa/specs/features/browser_ui/5_package/package_registry/npm/npm_project_level_spec.rb index f4b306d6830..f269cac4492 100644 --- a/qa/qa/specs/features/browser_ui/5_package/package_registry/npm/npm_project_level_spec.rb +++ b/qa/qa/specs/features/browser_ui/5_package/package_registry/npm/npm_project_level_spec.rb @@ -128,7 +128,7 @@ module QA end end - it "push and pull a npm package via CI using a #{params[:token_name]}" do + it "push and pull a npm package via CI using a #{params[:token_name]}", quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/344537', type: :investigating } do Resource::Repository::Commit.fabricate_via_api! do |commit| commit.project = project commit.commit_message = 'Add .gitlab-ci.yml' diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb index 382ce0feda0..0f1c6ae4c70 100644 --- a/spec/controllers/projects/issues_controller_spec.rb +++ b/spec/controllers/projects/issues_controller_spec.rb @@ -1408,7 +1408,7 @@ RSpec.describe Projects::IssuesController do end end - context 'when the endpoint receives requests above the limit' do + context 'when the endpoint receives requests above the limit', :freeze_time, :clean_gitlab_redis_rate_limiting do before do stub_application_setting(issues_create_limit: 5) end diff --git a/spec/experiments/change_continuous_onboarding_link_urls_experiment_spec.rb b/spec/experiments/change_continuous_onboarding_link_urls_experiment_spec.rb new file mode 100644 index 00000000000..815aaf7c397 --- /dev/null +++ b/spec/experiments/change_continuous_onboarding_link_urls_experiment_spec.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe ChangeContinuousOnboardingLinkUrlsExperiment, :snowplow do + before do + stub_experiments(change_continuous_onboarding_link_urls: 'control') + end + + describe '#track' do + context 'when no namespace has been set' do + it 'tracks the action as normal' do + subject.track(:some_action) + + expect_snowplow_event( + category: subject.name, + action: 'some_action', + namespace: nil, + context: [ + { + schema: 'iglu:com.gitlab/gitlab_experiment/jsonschema/1-0-0', + data: an_instance_of(Hash) + } + ] + ) + end + end + + context 'when a namespace has been set' do + let_it_be(:namespace) { create(:namespace) } + + before do + subject.namespace = namespace + end + + it 'tracks the action and merges the namespace into the event args' do + subject.track(:some_action) + + expect_snowplow_event( + category: subject.name, + action: 'some_action', + namespace: namespace, + context: [ + { + schema: 'iglu:com.gitlab/gitlab_experiment/jsonschema/1-0-0', + data: an_instance_of(Hash) + } + ] + ) + end + end + end +end diff --git a/spec/features/admin/admin_deploy_keys_spec.rb b/spec/features/admin/admin_deploy_keys_spec.rb index fd74a13deff..53caf0fac33 100644 --- a/spec/features/admin/admin_deploy_keys_spec.rb +++ b/spec/features/admin/admin_deploy_keys_spec.rb @@ -17,7 +17,7 @@ RSpec.describe 'admin deploy keys' do it 'show all public deploy keys' do visit admin_deploy_keys_path - page.within(find('.deploy-keys-list', match: :first)) do + page.within(find('[data-testid="deploy-keys-list"]', match: :first)) do expect(page).to have_content(deploy_key.title) expect(page).to have_content(another_deploy_key.title) end @@ -28,7 +28,7 @@ RSpec.describe 'admin deploy keys' do visit admin_deploy_keys_path - page.within(find('.deploy-keys-list', match: :first)) do + page.within(find('[data-testid="deploy-keys-list"]', match: :first)) do expect(page).to have_content(write_key.project.full_name) end end @@ -48,7 +48,7 @@ RSpec.describe 'admin deploy keys' do expect(current_path).to eq admin_deploy_keys_path - page.within(find('.deploy-keys-list', match: :first)) do + page.within(find('[data-testid="deploy-keys-list"]', match: :first)) do expect(page).to have_content('laptop') end end @@ -66,7 +66,7 @@ RSpec.describe 'admin deploy keys' do expect(current_path).to eq admin_deploy_keys_path - page.within(find('.deploy-keys-list', match: :first)) do + page.within(find('[data-testid="deploy-keys-list"]', match: :first)) do expect(page).to have_content('new-title') end end @@ -81,7 +81,7 @@ RSpec.describe 'admin deploy keys' do find('tr', text: deploy_key.title).click_link('Remove') expect(current_path).to eq admin_deploy_keys_path - page.within(find('.deploy-keys-list', match: :first)) do + page.within(find('[data-testid="deploy-keys-list"]', match: :first)) do expect(page).not_to have_content(deploy_key.title) end end diff --git a/spec/frontend/lib/apollo/suppress_network_errors_during_navigation_link_spec.js b/spec/frontend/lib/apollo/suppress_network_errors_during_navigation_link_spec.js index 852106db44e..7b604724977 100644 --- a/spec/frontend/lib/apollo/suppress_network_errors_during_navigation_link_spec.js +++ b/spec/frontend/lib/apollo/suppress_network_errors_during_navigation_link_spec.js @@ -47,107 +47,95 @@ describe('getSuppressNetworkErrorsDuringNavigationLink', () => { subscription = link.request(mockOperation).subscribe(observer); }; - describe('when disabled', () => { - it('returns null', () => { - expect(getSuppressNetworkErrorsDuringNavigationLink()).toBe(null); - }); + it('returns an ApolloLink', () => { + expect(getSuppressNetworkErrorsDuringNavigationLink()).toEqual(expect.any(ApolloLink)); }); - describe('when enabled', () => { - beforeEach(() => { - window.gon = { features: { suppressApolloErrorsDuringNavigation: true } }; - }); - - it('returns an ApolloLink', () => { - expect(getSuppressNetworkErrorsDuringNavigationLink()).toEqual(expect.any(ApolloLink)); - }); - - describe('suppression case', () => { - describe('when navigating away', () => { - beforeEach(() => { - isNavigatingAway.mockReturnValue(true); - }); - - describe('given a network error', () => { - it('does not forward the error', async () => { - const spy = jest.fn(); + describe('suppression case', () => { + describe('when navigating away', () => { + beforeEach(() => { + isNavigatingAway.mockReturnValue(true); + }); - createSubscription(makeMockNetworkErrorLink(), { - next: spy, - error: spy, - complete: spy, - }); + describe('given a network error', () => { + it('does not forward the error', async () => { + const spy = jest.fn(); - // It's hard to test for something _not_ happening. The best we can - // do is wait a bit to make sure nothing happens. - await waitForPromises(); - expect(spy).not.toHaveBeenCalled(); + createSubscription(makeMockNetworkErrorLink(), { + next: spy, + error: spy, + complete: spy, }); + + // It's hard to test for something _not_ happening. The best we can + // do is wait a bit to make sure nothing happens. + await waitForPromises(); + expect(spy).not.toHaveBeenCalled(); }); }); }); + }); - describe('non-suppression cases', () => { - describe('when not navigating away', () => { - beforeEach(() => { - isNavigatingAway.mockReturnValue(false); - }); + describe('non-suppression cases', () => { + describe('when not navigating away', () => { + beforeEach(() => { + isNavigatingAway.mockReturnValue(false); + }); - it('forwards successful requests', (done) => { - createSubscription(makeMockSuccessLink(), { - next({ data }) { - expect(data).toEqual({ foo: { id: 1 } }); - }, - error: () => done.fail('Should not happen'), - complete: () => done(), - }); + it('forwards successful requests', (done) => { + createSubscription(makeMockSuccessLink(), { + next({ data }) { + expect(data).toEqual({ foo: { id: 1 } }); + }, + error: () => done.fail('Should not happen'), + complete: () => done(), }); + }); - it('forwards GraphQL errors', (done) => { - createSubscription(makeMockGraphQLErrorLink(), { - next({ errors }) { - expect(errors).toEqual([{ message: 'foo' }]); - }, - error: () => done.fail('Should not happen'), - complete: () => done(), - }); + it('forwards GraphQL errors', (done) => { + createSubscription(makeMockGraphQLErrorLink(), { + next({ errors }) { + expect(errors).toEqual([{ message: 'foo' }]); + }, + error: () => done.fail('Should not happen'), + complete: () => done(), }); + }); - it('forwards network errors', (done) => { - createSubscription(makeMockNetworkErrorLink(), { - next: () => done.fail('Should not happen'), - error: (error) => { - expect(error.message).toBe('NetworkError'); - done(); - }, - complete: () => done.fail('Should not happen'), - }); + it('forwards network errors', (done) => { + createSubscription(makeMockNetworkErrorLink(), { + next: () => done.fail('Should not happen'), + error: (error) => { + expect(error.message).toBe('NetworkError'); + done(); + }, + complete: () => done.fail('Should not happen'), }); }); + }); - describe('when navigating away', () => { - beforeEach(() => { - isNavigatingAway.mockReturnValue(true); - }); + describe('when navigating away', () => { + beforeEach(() => { + isNavigatingAway.mockReturnValue(true); + }); - it('forwards successful requests', (done) => { - createSubscription(makeMockSuccessLink(), { - next({ data }) { - expect(data).toEqual({ foo: { id: 1 } }); - }, - error: () => done.fail('Should not happen'), - complete: () => done(), - }); + it('forwards successful requests', (done) => { + createSubscription(makeMockSuccessLink(), { + next({ data }) { + expect(data).toEqual({ foo: { id: 1 } }); + }, + error: () => done.fail('Should not happen'), + complete: () => done(), }); + }); - it('forwards GraphQL errors', (done) => { - createSubscription(makeMockGraphQLErrorLink(), { - next({ errors }) { - expect(errors).toEqual([{ message: 'foo' }]); - }, - error: () => done.fail('Should not happen'), - complete: () => done(), - }); + it('forwards GraphQL errors', (done) => { + createSubscription(makeMockGraphQLErrorLink(), { + next({ errors }) { + expect(errors).toEqual([{ message: 'foo' }]); + }, + error: () => done.fail('Should not happen'), + complete: () => done(), }); }); }); diff --git a/spec/frontend/pages/projects/learn_gitlab/components/__snapshots__/learn_gitlab_spec.js.snap b/spec/frontend/pages/projects/learn_gitlab/components/__snapshots__/learn_gitlab_spec.js.snap index 3aa0e99a858..3e371a8765f 100644 --- a/spec/frontend/pages/projects/learn_gitlab/components/__snapshots__/learn_gitlab_spec.js.snap +++ b/spec/frontend/pages/projects/learn_gitlab/components/__snapshots__/learn_gitlab_spec.js.snap @@ -135,6 +135,7 @@ exports[`Learn GitLab renders correctly 1`] = ` <a class="gl-link" data-track-action="click_link" + data-track-experiment="change_continuous_onboarding_link_urls" data-track-label="Set up CI/CD" data-track-property="Growth::Conversion::Experiment::LearnGitLab" href="http://example.com/" @@ -156,6 +157,7 @@ exports[`Learn GitLab renders correctly 1`] = ` <a class="gl-link" data-track-action="click_link" + data-track-experiment="change_continuous_onboarding_link_urls" data-track-label="Start a free Ultimate trial" data-track-property="Growth::Conversion::Experiment::LearnGitLab" href="http://example.com/" @@ -177,6 +179,7 @@ exports[`Learn GitLab renders correctly 1`] = ` <a class="gl-link" data-track-action="click_link" + data-track-experiment="change_continuous_onboarding_link_urls" data-track-label="Add code owners" data-track-property="Growth::Conversion::Experiment::LearnGitLab" href="http://example.com/" @@ -205,6 +208,7 @@ exports[`Learn GitLab renders correctly 1`] = ` <a class="gl-link" data-track-action="click_link" + data-track-experiment="change_continuous_onboarding_link_urls" data-track-label="Add merge request approval" data-track-property="Growth::Conversion::Experiment::LearnGitLab" href="http://example.com/" @@ -269,6 +273,7 @@ exports[`Learn GitLab renders correctly 1`] = ` <a class="gl-link" data-track-action="click_link" + data-track-experiment="change_continuous_onboarding_link_urls" data-track-label="Create an issue" data-track-property="Growth::Conversion::Experiment::LearnGitLab" href="http://example.com/" @@ -290,6 +295,7 @@ exports[`Learn GitLab renders correctly 1`] = ` <a class="gl-link" data-track-action="click_link" + data-track-experiment="change_continuous_onboarding_link_urls" data-track-label="Submit a merge request" data-track-property="Growth::Conversion::Experiment::LearnGitLab" href="http://example.com/" @@ -347,6 +353,7 @@ exports[`Learn GitLab renders correctly 1`] = ` <a class="gl-link" data-track-action="click_link" + data-track-experiment="change_continuous_onboarding_link_urls" data-track-label="Run a Security scan using CI/CD" data-track-property="Growth::Conversion::Experiment::LearnGitLab" href="http://example.com/" 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 5892bae5c0b..c0476d38380 100644 --- a/spec/lib/gitlab/content_security_policy/config_loader_spec.rb +++ b/spec/lib/gitlab/content_security_policy/config_loader_spec.rb @@ -81,11 +81,11 @@ RSpec.describe Gitlab::ContentSecurityPolicy::ConfigLoader do end it 'adds CDN host to CSP' do - 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://cdn.example.com") + expect(directives['script_src']).to eq(::Gitlab::ContentSecurityPolicy::Directives.script_src + " https://cdn.example.com") expect(directives['style_src']).to eq("'self' 'unsafe-inline' https://cdn.example.com") expect(directives['font_src']).to eq("'self' https://cdn.example.com") expect(directives['worker_src']).to eq('http://localhost/assets/ blob: data: https://cdn.example.com') - expect(directives['frame_src']).to eq("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://cdn.example.com http://localhost/admin/sidekiq http://localhost/admin/sidekiq/ http://localhost/-/speedscope/index.html") + expect(directives['frame_src']).to eq(::Gitlab::ContentSecurityPolicy::Directives.frame_src + " https://cdn.example.com http://localhost/admin/sidekiq http://localhost/admin/sidekiq/ http://localhost/-/speedscope/index.html") end end @@ -113,7 +113,7 @@ RSpec.describe Gitlab::ContentSecurityPolicy::ConfigLoader do end it 'does not add CUSTOMER_PORTAL_URL to CSP' do - expect(directives['frame_src']).to eq("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 http://localhost/admin/sidekiq http://localhost/admin/sidekiq/ http://localhost/-/speedscope/index.html") + expect(directives['frame_src']).to eq(::Gitlab::ContentSecurityPolicy::Directives.frame_src + " http://localhost/admin/sidekiq http://localhost/admin/sidekiq/ http://localhost/-/speedscope/index.html") end end @@ -123,7 +123,7 @@ RSpec.describe Gitlab::ContentSecurityPolicy::ConfigLoader do end it 'adds CUSTOMER_PORTAL_URL to CSP' do - expect(directives['frame_src']).to eq("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 http://localhost/rails/letter_opener/ https://customers.example.com http://localhost/admin/sidekiq http://localhost/admin/sidekiq/ http://localhost/-/speedscope/index.html") + expect(directives['frame_src']).to eq(::Gitlab::ContentSecurityPolicy::Directives.frame_src + " http://localhost/rails/letter_opener/ https://customers.example.com http://localhost/admin/sidekiq http://localhost/admin/sidekiq/ http://localhost/-/speedscope/index.html") end end end diff --git a/spec/lib/gitlab/database/partitioning_spec.rb b/spec/lib/gitlab/database/partitioning_spec.rb index 7855e0aabf9..154cc2b7972 100644 --- a/spec/lib/gitlab/database/partitioning_spec.rb +++ b/spec/lib/gitlab/database/partitioning_spec.rb @@ -30,6 +30,39 @@ RSpec.describe Gitlab::Database::Partitioning do end end + describe '.sync_partitions_ignore_db_error' do + it 'calls sync_partitions' do + expect(described_class).to receive(:sync_partitions) + + described_class.sync_partitions_ignore_db_error + end + + [ActiveRecord::ActiveRecordError, PG::Error].each do |error| + context "when #{error} is raised" do + before do + expect(described_class).to receive(:sync_partitions) + .and_raise(error) + end + + it 'ignores it' do + described_class.sync_partitions_ignore_db_error + end + end + end + + context 'when DISABLE_POSTGRES_PARTITION_CREATION_ON_STARTUP is set' do + before do + stub_env('DISABLE_POSTGRES_PARTITION_CREATION_ON_STARTUP', '1') + end + + it 'does not call sync_partitions' do + expect(described_class).to receive(:sync_partitions).never + + described_class.sync_partitions_ignore_db_error + end + end + end + describe '.sync_partitions' do let(:table_names) { %w[partitioning_test1 partitioning_test2] } let(:models) do diff --git a/spec/lib/gitlab/email/handler/service_desk_handler_spec.rb b/spec/lib/gitlab/email/handler/service_desk_handler_spec.rb index 942ac67c39d..93b3566b709 100644 --- a/spec/lib/gitlab/email/handler/service_desk_handler_spec.rb +++ b/spec/lib/gitlab/email/handler/service_desk_handler_spec.rb @@ -259,7 +259,7 @@ RSpec.describe Gitlab::Email::Handler::ServiceDeskHandler do end end - context 'when rate limiting is in effect', :clean_gitlab_redis_cache do + context 'when rate limiting is in effect', :freeze_time, :clean_gitlab_redis_rate_limiting do let(:receiver) { Gitlab::Email::Receiver.new(email_raw) } subject { 2.times { receiver.execute } } @@ -271,18 +271,14 @@ RSpec.describe Gitlab::Email::Handler::ServiceDeskHandler do context 'when too many requests are sent by one user' do it 'raises an error' do - freeze_time do - expect { subject }.to raise_error(RateLimitedService::RateLimitedError) - end + expect { subject }.to raise_error(RateLimitedService::RateLimitedError) end it 'creates 1 issue' do - freeze_time do - expect do - subject - rescue RateLimitedService::RateLimitedError - end.to change { Issue.count }.by(1) - end + expect do + subject + rescue RateLimitedService::RateLimitedError + end.to change { Issue.count }.by(1) end context 'when requests are sent by different users' do @@ -295,9 +291,7 @@ RSpec.describe Gitlab::Email::Handler::ServiceDeskHandler do end it 'creates 2 issues' do - freeze_time do - expect { subject }.to change { Issue.count }.by(2) - end + expect { subject }.to change { Issue.count }.by(2) end end end @@ -308,9 +302,7 @@ RSpec.describe Gitlab::Email::Handler::ServiceDeskHandler do end it 'creates 2 issues' do - freeze_time do - expect { subject }.to change { Issue.count }.by(2) - end + expect { subject }.to change { Issue.count }.by(2) end end end diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb index 67d7e49f8db..2953c198af6 100644 --- a/spec/policies/project_policy_spec.rb +++ b/spec/policies/project_policy_spec.rb @@ -104,29 +104,71 @@ RSpec.describe ProjectPolicy do end context 'pipeline feature' do - let(:project) { private_project } + let(:project) { private_project } + let(:current_user) { developer } + let(:pipeline) { create(:ci_pipeline, project: project) } - before do - private_project.add_developer(current_user) + describe 'for confirmed user' do + it 'allows modify pipelines' do + expect_allowed(:create_pipeline) + expect_allowed(:update_pipeline) + expect_allowed(:create_pipeline_schedule) + end end describe 'for unconfirmed user' do - let(:current_user) { create(:user, confirmed_at: nil) } + let(:current_user) { project.owner.tap { |u| u.update!(confirmed_at: nil) } } it 'disallows to modify pipelines' do expect_disallowed(:create_pipeline) expect_disallowed(:update_pipeline) + expect_disallowed(:destroy_pipeline) expect_disallowed(:create_pipeline_schedule) end end - describe 'for confirmed user' do - let(:current_user) { developer } + describe 'destroy permission' do + describe 'for developers' do + it 'prevents :destroy_pipeline' do + expect(current_user.can?(:destroy_pipeline, pipeline)).to be_falsey + end + end - it 'allows modify pipelines' do - expect_allowed(:create_pipeline) - expect_allowed(:update_pipeline) - expect_allowed(:create_pipeline_schedule) + describe 'for maintainers' do + let(:current_user) { maintainer } + + it 'prevents :destroy_pipeline' do + project.add_maintainer(maintainer) + expect(current_user.can?(:destroy_pipeline, pipeline)).to be_falsey + end + end + + describe 'for project owner' do + let(:current_user) { project.owner } + + it 'allows :destroy_pipeline' do + expect(current_user.can?(:destroy_pipeline, pipeline)).to be_truthy + end + + context 'on archived projects' do + before do + project.update!(archived: true) + end + + it 'prevents :destroy_pipeline' do + expect(current_user.can?(:destroy_pipeline, pipeline)).to be_falsey + end + end + + context 'on archived pending_delete projects' do + before do + project.update!(archived: true, pending_delete: true) + end + + it 'allows :destroy_pipeline' do + expect(current_user.can?(:destroy_pipeline, pipeline)).to be_truthy + end + end end end end diff --git a/spec/services/issues/create_service_spec.rb b/spec/services/issues/create_service_spec.rb index 3ea447b2384..57fcdf84163 100644 --- a/spec/services/issues/create_service_spec.rb +++ b/spec/services/issues/create_service_spec.rb @@ -302,7 +302,7 @@ RSpec.describe Issues::CreateService do described_class.new(project: project, current_user: user, params: opts, spam_params: spam_params).execute end - context 'when rate limiting is in effect', :clean_gitlab_redis_cache do + context 'when rate limiting is in effect', :freeze_time, :clean_gitlab_redis_rate_limiting do let(:user) { create(:user) } before do @@ -316,20 +316,16 @@ RSpec.describe Issues::CreateService do context 'when too many requests are sent by one user' do it 'raises an error' do - freeze_time do - expect do - subject - end.to raise_error(RateLimitedService::RateLimitedError) - end + expect do + subject + end.to raise_error(RateLimitedService::RateLimitedError) end it 'creates 1 issue' do - freeze_time do - expect do - subject - rescue RateLimitedService::RateLimitedError - end.to change { Issue.count }.by(1) - end + expect do + subject + rescue RateLimitedService::RateLimitedError + end.to change { Issue.count }.by(1) end end @@ -339,9 +335,7 @@ RSpec.describe Issues::CreateService do end it 'creates 2 issues' do - freeze_time do - expect { subject }.to change { Issue.count }.by(2) - end + expect { subject }.to change { Issue.count }.by(2) end end end diff --git a/spec/services/projects/destroy_service_spec.rb b/spec/services/projects/destroy_service_spec.rb index 9bdd9800fcc..ac84614121a 100644 --- a/spec/services/projects/destroy_service_spec.rb +++ b/spec/services/projects/destroy_service_spec.rb @@ -331,6 +331,14 @@ RSpec.describe Projects::DestroyService, :aggregate_failures do end end end + + context 'for an archived project' do + before do + project.update!(archived: true) + end + + it_behaves_like 'deleting the project with pipeline and build' + end end describe 'container registry' do diff --git a/yarn.lock b/yarn.lock index 7a861505d2c..a286b762a50 100644 --- a/yarn.lock +++ b/yarn.lock @@ -946,7 +946,7 @@ resolved "https://registry.yarnpkg.com/@gitlab/visual-review-tools/-/visual-review-tools-1.6.1.tgz#0d8f3ff9f51b05f7c80b9a107727703d48997e4e" integrity sha512-vY8K1igwZFoEOmU0h4E7XTLlilsQ4ylPr27O01UsSe6ZTKi6oEMREsRAEpNIUgRlxUARCsf+Opp4pgSFzFkFcw== -"@graphql-eslint/eslint-plugin@^2.3.0": +"@graphql-eslint/eslint-plugin@2.3.0": version "2.3.0" resolved "https://registry.yarnpkg.com/@graphql-eslint/eslint-plugin/-/eslint-plugin-2.3.0.tgz#4e500466fa56b64680c67d7639f1bdf11d890f8a" integrity sha512-YYTBKhadvdTO6myWFm3O8A8dP/ca5NsyB2FKYoHGUIToEl25xAMuj2yzvhIjIBwA/yhlLRPe9+EIQ+8f0kjBDg== |