Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-11-11 18:14:04 +00:00
parent e40f19ef83
commit 11c2b8eff6
45 changed files with 454 additions and 256 deletions

View File

@ -1 +1 @@
1.46.0
1.47.0

View File

@ -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

View File

@ -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>

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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'

View File

@ -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'

View File

@ -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

View File

@ -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

View File

@ -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')}';

View File

@ -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' }

View File

@ -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")

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1 @@
450028c90cb92f5ce3f8239eb56364b83ed025839aaf305b7ceb4fda077681b1

View File

@ -0,0 +1 @@
a3f9fcac354cccfdfc42b8f5baab651cb65ca60e4474ce937ab25b552bfe483c

View File

@ -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;

View File

@ -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)**

View File

@ -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:

View File

@ -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).

View File

@ -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)**

View File

@ -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')

View File

@ -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'",

View File

@ -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

View File

@ -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

View File

@ -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 ""

View File

@ -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",

View File

@ -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

View File

@ -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

View File

@ -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'

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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('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();
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('when enabled', () => {
beforeEach(() => {
window.gon = { features: { suppressApolloErrorsDuringNavigation: true } };
});
describe('non-suppression cases', () => {
describe('when not navigating away', () => {
beforeEach(() => {
isNavigatingAway.mockReturnValue(false);
});
it('returns an ApolloLink', () => {
expect(getSuppressNetworkErrorsDuringNavigationLink()).toEqual(expect.any(ApolloLink));
});
describe('suppression case', () => {
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(),
});
});
describe('given a network error', () => {
it('does not forward the error', async () => {
const spy = jest.fn();
it('forwards GraphQL errors', (done) => {
createSubscription(makeMockGraphQLErrorLink(), {
next({ errors }) {
expect(errors).toEqual([{ message: 'foo' }]);
},
error: () => done.fail('Should not happen'),
complete: () => done(),
});
});
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();
});
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('non-suppression cases', () => {
describe('when not navigating away', () => {
beforeEach(() => {
isNavigatingAway.mockReturnValue(false);
});
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 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 successful requests', (done) => {
createSubscription(makeMockSuccessLink(), {
next({ data }) {
expect(data).toEqual({ foo: { id: 1 } });
},
error: () => done.fail('Should not happen'),
complete: () => done(),
});
});
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 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(),
});
});
});

View File

@ -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/"

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -104,31 +104,73 @@ RSpec.describe ProjectPolicy do
end
context 'pipeline feature' do
let(:project) { private_project }
before do
private_project.add_developer(current_user)
end
describe 'for unconfirmed user' do
let(:current_user) { create(:user, confirmed_at: nil) }
it 'disallows to modify pipelines' do
expect_disallowed(:create_pipeline)
expect_disallowed(:update_pipeline)
expect_disallowed(:create_pipeline_schedule)
end
end
let(:project) { private_project }
let(:current_user) { developer }
let(:pipeline) { create(:ci_pipeline, project: project) }
describe 'for confirmed user' do
let(:current_user) { developer }
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) { 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 'destroy permission' do
describe 'for developers' do
it 'prevents :destroy_pipeline' do
expect(current_user.can?(:destroy_pipeline, pipeline)).to be_falsey
end
end
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
context 'builds feature' do

View File

@ -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

View File

@ -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

View File

@ -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==