Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
70fe7ce74b
commit
90c386a7b0
|
@ -57,7 +57,7 @@ If you include multiple screenshots it can be helpful to hide all but the first
|
|||
/label ~Quality ~QA ~test
|
||||
|
||||
<!-- Test failure type label, please use just one.-->
|
||||
/label ~"failure::broken-test" ~"failure::flaky-test" ~"failure::stale-test" ~"failure::test-environment" ~"failure::investigating"
|
||||
/label ~"failure::broken-test" ~"failure::flaky-test" ~"failure::stale-test" ~"failure::test-environment" ~"failure::investigating" ~"failure::new"
|
||||
|
||||
<!--
|
||||
Choose the stage that appears in the test path, e.g. ~"devops::create" for
|
||||
|
|
|
@ -68,7 +68,7 @@ export default {
|
|||
:href="newReleasePath"
|
||||
:aria-describedby="shouldRenderEmptyState && 'releases-description'"
|
||||
category="primary"
|
||||
variant="success"
|
||||
variant="confirm"
|
||||
data-testid="new-release-button"
|
||||
>
|
||||
{{ __('New release') }}
|
||||
|
|
|
@ -9,6 +9,7 @@ module Types
|
|||
present_using ::AlertManagement::AlertPresenter
|
||||
|
||||
implements(Types::Notes::NoteableInterface)
|
||||
implements(Types::TodoableInterface)
|
||||
|
||||
authorize :read_alert_management_alert
|
||||
|
||||
|
@ -127,6 +128,12 @@ module Types
|
|||
null: true,
|
||||
description: 'Alert condition for Prometheus.'
|
||||
|
||||
field :web_url,
|
||||
GraphQL::Types::String,
|
||||
method: :details_url,
|
||||
null: false,
|
||||
description: 'URL of the alert.'
|
||||
|
||||
def notes
|
||||
object.ordered_notes
|
||||
end
|
||||
|
|
|
@ -8,6 +8,8 @@ module Types
|
|||
|
||||
present_using CommitPresenter
|
||||
|
||||
implements(Types::TodoableInterface)
|
||||
|
||||
field :id, type: GraphQL::Types::ID, null: false,
|
||||
description: 'ID (global ID) of the commit.'
|
||||
|
||||
|
|
|
@ -13,6 +13,12 @@ module Types
|
|||
implements(Types::Notes::NoteableInterface)
|
||||
implements(Types::DesignManagement::DesignFields)
|
||||
implements(Types::CurrentUserTodos)
|
||||
implements(Types::TodoableInterface)
|
||||
|
||||
field :web_url,
|
||||
GraphQL::Types::String,
|
||||
null: false,
|
||||
description: 'URL of the design.'
|
||||
|
||||
field :versions,
|
||||
Types::DesignManagement::VersionType.connection_type,
|
||||
|
@ -40,6 +46,10 @@ module Types
|
|||
def request_cache_base_key
|
||||
self.class.name
|
||||
end
|
||||
|
||||
def web_url
|
||||
Gitlab::UrlBuilder.build(object)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,6 +8,7 @@ module Types
|
|||
|
||||
implements(Types::Notes::NoteableInterface)
|
||||
implements(Types::CurrentUserTodos)
|
||||
implements(Types::TodoableInterface)
|
||||
|
||||
authorize :read_issue
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ module Types
|
|||
|
||||
implements(Types::Notes::NoteableInterface)
|
||||
implements(Types::CurrentUserTodos)
|
||||
implements(Types::TodoableInterface)
|
||||
|
||||
authorize :read_merge_request
|
||||
|
||||
|
|
|
@ -31,6 +31,11 @@ module Types
|
|||
description: 'Action of the to-do item.',
|
||||
null: false
|
||||
|
||||
field :target, Types::TodoableInterface,
|
||||
description: 'Target of the to-do item.',
|
||||
calls_gitaly: true,
|
||||
null: false
|
||||
|
||||
field :target_type, Types::TodoTargetEnum,
|
||||
description: 'Target type of the to-do item.',
|
||||
null: false
|
||||
|
@ -59,5 +64,28 @@ module Types
|
|||
def author
|
||||
Gitlab::Graphql::Loaders::BatchModelLoader.new(User, object.author_id).find
|
||||
end
|
||||
|
||||
def target
|
||||
if object.for_commit?
|
||||
Gitlab::Graphql::Loaders::BatchCommitLoader.new(
|
||||
container_class: Project,
|
||||
container_id: object.project_id,
|
||||
oid: object.commit_id
|
||||
).find
|
||||
else
|
||||
Gitlab::Graphql::Loaders::BatchModelLoader.new(target_type_class, object.target_id).find
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def target_type_class
|
||||
klass = object.target_type.safe_constantize
|
||||
raise "Invalid target type \"#{object.target_type}\"" unless klass < Todoable
|
||||
|
||||
klass
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Types::TodoType.prepend_mod
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Types
|
||||
module TodoableInterface
|
||||
include Types::BaseInterface
|
||||
|
||||
graphql_name 'Todoable'
|
||||
|
||||
field :web_url, GraphQL::Types::String, null: true, description: 'URL of this object.'
|
||||
|
||||
def self.resolve_type(object, context)
|
||||
case object
|
||||
when Issue
|
||||
Types::IssueType
|
||||
when MergeRequest
|
||||
Types::MergeRequestType
|
||||
when ::DesignManagement::Design
|
||||
Types::DesignManagement::DesignType
|
||||
when ::AlertManagement::Alert
|
||||
Types::AlertManagement::AlertType
|
||||
when Commit
|
||||
Types::CommitType
|
||||
else
|
||||
raise "Unknown GraphQL type for #{object}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Types::TodoableInterface.prepend_mod
|
|
@ -65,22 +65,19 @@ module Spam
|
|||
# ask the SpamVerdictService what to do with the target.
|
||||
spam_verdict_service.execute.tap do |result|
|
||||
case result
|
||||
when CONDITIONAL_ALLOW
|
||||
# at the moment, this means "ask for reCAPTCHA"
|
||||
create_spam_log
|
||||
|
||||
break if target.allow_possible_spam?
|
||||
|
||||
target.needs_recaptcha!
|
||||
when DISALLOW
|
||||
# TODO: remove `unless target.allow_possible_spam?` once this flag has been passed to `SpamVerdictService`
|
||||
# https://gitlab.com/gitlab-org/gitlab/-/issues/214739
|
||||
target.spam! unless target.allow_possible_spam?
|
||||
create_spam_log
|
||||
when BLOCK_USER
|
||||
# TODO: improve BLOCK_USER handling, non-existent until now
|
||||
# https://gitlab.com/gitlab-org/gitlab/-/issues/329666
|
||||
target.spam! unless target.allow_possible_spam?
|
||||
target.spam!
|
||||
create_spam_log
|
||||
when DISALLOW
|
||||
target.spam!
|
||||
create_spam_log
|
||||
when CONDITIONAL_ALLOW
|
||||
# This means "require a CAPTCHA to be solved"
|
||||
target.needs_recaptcha!
|
||||
create_spam_log
|
||||
when OVERRIDE_VIA_ALLOW_POSSIBLE_SPAM
|
||||
create_spam_log
|
||||
when ALLOW
|
||||
target.clear_spam_flags!
|
||||
|
|
|
@ -2,11 +2,12 @@
|
|||
|
||||
module Spam
|
||||
module SpamConstants
|
||||
CONDITIONAL_ALLOW = "conditional_allow"
|
||||
DISALLOW = "disallow"
|
||||
ALLOW = "allow"
|
||||
BLOCK_USER = "block"
|
||||
NOOP = "noop"
|
||||
BLOCK_USER = 'block'
|
||||
DISALLOW = 'disallow'
|
||||
CONDITIONAL_ALLOW = 'conditional_allow'
|
||||
OVERRIDE_VIA_ALLOW_POSSIBLE_SPAM = 'override_via_allow_possible_spam'
|
||||
ALLOW = 'allow'
|
||||
NOOP = 'noop'
|
||||
|
||||
SUPPORTED_VERDICTS = {
|
||||
BLOCK_USER => {
|
||||
|
@ -18,11 +19,14 @@ module Spam
|
|||
CONDITIONAL_ALLOW => {
|
||||
priority: 3
|
||||
},
|
||||
ALLOW => {
|
||||
OVERRIDE_VIA_ALLOW_POSSIBLE_SPAM => {
|
||||
priority: 4
|
||||
},
|
||||
NOOP => {
|
||||
ALLOW => {
|
||||
priority: 5
|
||||
},
|
||||
NOOP => {
|
||||
priority: 6
|
||||
}
|
||||
}.freeze
|
||||
end
|
||||
|
|
|
@ -39,21 +39,24 @@ module Spam
|
|||
return ALLOW unless valid_results.any?
|
||||
|
||||
# Favour the most restrictive result.
|
||||
final_verdict = valid_results.min_by { |v| SUPPORTED_VERDICTS[v][:priority] }
|
||||
verdict = valid_results.min_by { |v| SUPPORTED_VERDICTS[v][:priority] }
|
||||
|
||||
# The target can override the verdict via the `allow_possible_spam` feature flag
|
||||
verdict = OVERRIDE_VIA_ALLOW_POSSIBLE_SPAM if override_via_allow_possible_spam?(verdict: verdict)
|
||||
|
||||
logger.info(class: self.class.name,
|
||||
akismet_verdict: akismet_verdict,
|
||||
spam_check_verdict: original_spamcheck_result,
|
||||
extra_attributes: spamcheck_attribs,
|
||||
spam_check_rtt: external_spam_check_round_trip_time.real,
|
||||
final_verdict: final_verdict,
|
||||
final_verdict: verdict,
|
||||
username: user.username,
|
||||
user_id: user.id,
|
||||
target_type: target.class.to_s,
|
||||
project_id: target.project_id
|
||||
)
|
||||
|
||||
final_verdict
|
||||
verdict
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -87,6 +90,14 @@ module Spam
|
|||
end
|
||||
end
|
||||
|
||||
def override_via_allow_possible_spam?(verdict:)
|
||||
# If the verdict is already going to allow (because current verdict's priority value is greater
|
||||
# than the override verdict's priority value), then we don't need to override it.
|
||||
return false if SUPPORTED_VERDICTS[verdict][:priority] > SUPPORTED_VERDICTS[OVERRIDE_VIA_ALLOW_POSSIBLE_SPAM][:priority]
|
||||
|
||||
target.allow_possible_spam?
|
||||
end
|
||||
|
||||
def spamcheck_client
|
||||
@spamcheck_client ||= Gitlab::Spamcheck::Client.new
|
||||
end
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
= image_tag @appearance.header_logo_path, class: 'appearance-light-logo-preview'
|
||||
- if @appearance.persisted?
|
||||
%br
|
||||
= link_to _('Remove header logo'), header_logos_admin_application_settings_appearances_path, data: { confirm: _("Header logo will be removed. Are you sure?") }, method: :delete, class: "btn gl-button btn-danger btn-danger-secondary btn-sm"
|
||||
= link_to _('Remove header logo'), header_logos_admin_application_settings_appearances_path, data: { confirm: _("Header logo will be removed. Are you sure?"), confirm_btn_variant: "danger" }, aria: { label: _('Remove header logo') }, method: :delete, class: "btn gl-button btn-danger btn-danger-secondary btn-sm"
|
||||
%hr
|
||||
= f.hidden_field :header_logo_cache
|
||||
= f.file_field :header_logo, class: "", accept: 'image/*'
|
||||
|
@ -35,7 +35,7 @@
|
|||
= image_tag @appearance.favicon_path, class: 'appearance-light-logo-preview'
|
||||
- if @appearance.persisted?
|
||||
%br
|
||||
= link_to _('Remove favicon'), favicon_admin_application_settings_appearances_path, data: { confirm: _("Favicon will be removed. Are you sure?") }, method: :delete, class: "btn gl-button btn-danger btn-danger-secondary btn-sm"
|
||||
= link_to _('Remove favicon'), favicon_admin_application_settings_appearances_path, data: { confirm: _("Favicon will be removed. Are you sure?"), confirm_btn_variant: "danger" }, aria: { label: _('Remove favicon') }, method: :delete, class: "btn gl-button btn-danger btn-danger-secondary btn-sm"
|
||||
%hr
|
||||
= f.hidden_field :favicon_cache
|
||||
= f.file_field :favicon, class: '', accept: 'image/*'
|
||||
|
@ -67,7 +67,7 @@
|
|||
= image_tag @appearance.logo_path, class: 'appearance-logo-preview'
|
||||
- if @appearance.persisted?
|
||||
%br
|
||||
= link_to _('Remove logo'), logo_admin_application_settings_appearances_path, data: { confirm: _("Logo will be removed. Are you sure?") }, method: :delete, class: "btn gl-button btn-danger btn-danger-secondary btn-sm remove-logo"
|
||||
= link_to _('Remove logo'), logo_admin_application_settings_appearances_path, data: { confirm: _("Logo will be removed. Are you sure?"), confirm_btn_variant: "danger" }, aria: { label: _('Remove logo') }, method: :delete, class: "btn gl-button btn-danger btn-danger-secondary btn-sm remove-logo"
|
||||
%hr
|
||||
= f.hidden_field :logo_cache
|
||||
= f.file_field :logo, class: "", accept: 'image/*'
|
||||
|
|
|
@ -78,9 +78,6 @@ ci_pipelines:
|
|||
- table: merge_requests
|
||||
column: merge_request_id
|
||||
on_delete: async_delete
|
||||
- table: external_pull_requests
|
||||
column: external_pull_request_id
|
||||
on_delete: async_nullify
|
||||
- table: users
|
||||
column: user_id
|
||||
on_delete: async_nullify
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class RemovePackageFilesLimitFromApplicationSettings < Gitlab::Database::Migration[1.0]
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
return unless column_exists?(:application_settings, :max_package_files_for_package_destruction)
|
||||
|
||||
remove_column :application_settings, :max_package_files_for_package_destruction, :smallint
|
||||
end
|
||||
|
||||
def down
|
||||
add_column :application_settings, :max_package_files_for_package_destruction, :smallint, default: 100, null: false
|
||||
add_check_constraint :application_settings,
|
||||
'max_package_files_for_package_destruction > 0',
|
||||
'app_settings_max_package_files_for_package_destruction_positive'
|
||||
end
|
||||
end
|
|
@ -0,0 +1 @@
|
|||
481bc7b167ddf46bd11322e4458e48de10483bf34d0e393f7e76a3572c28e09f
|
|
@ -11243,7 +11243,6 @@ CREATE TABLE application_settings (
|
|||
container_registry_import_max_step_duration integer DEFAULT 300 NOT NULL,
|
||||
container_registry_import_target_plan text DEFAULT 'free'::text NOT NULL,
|
||||
container_registry_import_created_before timestamp with time zone DEFAULT '2022-01-23 00:00:00+00'::timestamp with time zone NOT NULL,
|
||||
max_package_files_for_package_destruction smallint DEFAULT 100 NOT NULL,
|
||||
runner_token_expiration_interval integer,
|
||||
group_runner_token_expiration_interval integer,
|
||||
project_runner_token_expiration_interval integer,
|
||||
|
@ -11254,7 +11253,6 @@ CREATE TABLE application_settings (
|
|||
CONSTRAINT app_settings_container_reg_cleanup_tags_max_list_size_positive CHECK ((container_registry_cleanup_tags_service_max_list_size >= 0)),
|
||||
CONSTRAINT app_settings_dep_proxy_ttl_policies_worker_capacity_positive CHECK ((dependency_proxy_ttl_group_policy_worker_capacity >= 0)),
|
||||
CONSTRAINT app_settings_ext_pipeline_validation_service_url_text_limit CHECK ((char_length(external_pipeline_validation_service_url) <= 255)),
|
||||
CONSTRAINT app_settings_max_package_files_for_package_destruction_positive CHECK ((max_package_files_for_package_destruction > 0)),
|
||||
CONSTRAINT app_settings_p_cleanup_package_file_worker_capacity_positive CHECK ((packages_cleanup_package_file_worker_capacity >= 0)),
|
||||
CONSTRAINT app_settings_registry_exp_policies_worker_capacity_positive CHECK ((container_registry_expiration_policies_worker_capacity >= 0)),
|
||||
CONSTRAINT app_settings_yaml_max_depth_positive CHECK ((max_yaml_depth > 0)),
|
||||
|
|
|
@ -8595,6 +8595,7 @@ Describes an alert from the project's Alert Management.
|
|||
| <a id="alertmanagementalertstatus"></a>`status` | [`AlertManagementStatus`](#alertmanagementstatus) | Status of the alert. |
|
||||
| <a id="alertmanagementalerttitle"></a>`title` | [`String`](#string) | Title of the alert. |
|
||||
| <a id="alertmanagementalertupdatedat"></a>`updatedAt` | [`Time`](#time) | Timestamp the alert was last updated. |
|
||||
| <a id="alertmanagementalertweburl"></a>`webUrl` | [`String!`](#string) | URL of the alert. |
|
||||
|
||||
#### Fields with arguments
|
||||
|
||||
|
@ -9991,6 +9992,7 @@ A single design.
|
|||
| <a id="designnotes"></a>`notes` | [`NoteConnection!`](#noteconnection) | All notes on this noteable. (see [Connections](#connections)) |
|
||||
| <a id="designnotescount"></a>`notesCount` | [`Int!`](#int) | Total count of user-created notes for this design. |
|
||||
| <a id="designproject"></a>`project` | [`Project!`](#project) | Project the design belongs to. |
|
||||
| <a id="designweburl"></a>`webUrl` | [`String!`](#string) | URL of the design. |
|
||||
|
||||
#### Fields with arguments
|
||||
|
||||
|
@ -15901,6 +15903,7 @@ Representing a to-do entry.
|
|||
| <a id="todoid"></a>`id` | [`ID!`](#id) | ID of the to-do item. |
|
||||
| <a id="todoproject"></a>`project` | [`Project`](#project) | Project this to-do item is associated with. |
|
||||
| <a id="todostate"></a>`state` | [`TodoStateEnum!`](#todostateenum) | State of the to-do item. |
|
||||
| <a id="todotarget"></a>`target` | [`Todoable!`](#todoable) | Target of the to-do item. |
|
||||
| <a id="todotargettype"></a>`targetType` | [`TodoTargetEnum!`](#todotargetenum) | Target type of the to-do item. |
|
||||
|
||||
### `Topic`
|
||||
|
@ -19414,6 +19417,25 @@ Returns [`TimeboxReport`](#timeboxreport).
|
|||
| ---- | ---- | ----------- |
|
||||
| <a id="timeboxreportinterfacereportfullpath"></a>`fullPath` | [`String`](#string) | Full path of the project or group used as a scope for report. For example, `gitlab-org` or `gitlab-org/gitlab`. |
|
||||
|
||||
#### `Todoable`
|
||||
|
||||
Implementations:
|
||||
|
||||
- [`AlertManagementAlert`](#alertmanagementalert)
|
||||
- [`BoardEpic`](#boardepic)
|
||||
- [`Commit`](#commit)
|
||||
- [`Design`](#design)
|
||||
- [`Epic`](#epic)
|
||||
- [`EpicIssue`](#epicissue)
|
||||
- [`Issue`](#issue)
|
||||
- [`MergeRequest`](#mergerequest)
|
||||
|
||||
##### Fields
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="todoableweburl"></a>`webUrl` | [`String`](#string) | URL of this object. |
|
||||
|
||||
#### `User`
|
||||
|
||||
Representation of a GitLab user.
|
||||
|
|
|
@ -55,7 +55,7 @@ You can view and modify existing policies from the [policies](#policies) tab.
|
|||
|
||||
## License expressions
|
||||
|
||||
GitLab has limited support for [composite licenses](https://spdx.github.io/spdx-spec/appendix-IV-SPDX-license-expressions/).
|
||||
GitLab has limited support for [composite licenses](https://spdx.github.io/spdx-spec/SPDX-license-expressions/).
|
||||
License compliance can read multiple licenses, but always considers them combined using the `AND` operator. For example,
|
||||
if a dependency has two licenses, and one of them is allowed and the other is denied by the project [policy](#policies),
|
||||
GitLab evaluates the composite license as _denied_, as this is the safer option.
|
||||
|
@ -90,7 +90,7 @@ The reported licenses might be incomplete or inaccurate.
|
|||
| Objective-C, Swift | [Carthage](https://github.com/Carthage/Carthage), [CocoaPods](https://cocoapods.org/) v0.39 and below |
|
||||
| Elixir | [Mix](https://elixir-lang.org/getting-started/mix-otp/introduction-to-mix.html) |
|
||||
| C++/C | [Conan](https://conan.io/) |
|
||||
| Rust | [Cargo](https://crates.io) |
|
||||
| Rust | [Cargo](https://crates.io/) |
|
||||
| PHP | [Composer](https://getcomposer.org/) |
|
||||
|
||||
## Enable License Compliance
|
||||
|
@ -219,7 +219,7 @@ license_scanning:
|
|||
MAVEN_CLI_OPTS: --debug
|
||||
```
|
||||
|
||||
`mvn install` runs through all of the [build life cycle](http://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html)
|
||||
`mvn install` runs through all of the [build life cycle](https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html)
|
||||
stages prior to `install`, including `test`. Running unit tests is not directly
|
||||
necessary for the license scanning purposes and consumes time, so it's skipped
|
||||
by having the default value of `MAVEN_CLI_OPTS` as `-DskipTests`. If you want
|
||||
|
@ -249,7 +249,7 @@ license_scanning:
|
|||
|
||||
Alternatively, you can use a Java key store to verify the TLS connection. For instructions on how to
|
||||
generate a key store file, see the
|
||||
[Maven Guide to Remote repository access through authenticated HTTPS](http://maven.apache.org/guides/mini/guide-repository-ssl.html).
|
||||
[Maven Guide to Remote repository access through authenticated HTTPS](https://maven.apache.org/guides/mini/guide-repository-ssl.html).
|
||||
|
||||
### Selecting the version of Java
|
||||
|
||||
|
|
|
@ -191,7 +191,7 @@ GitLab.com uses the IP ranges `34.74.90.64/28` and `34.74.226.0/24` for traffic
|
|||
fleet. This whole range is solely allocated to GitLab. You can expect connections from webhooks or repository mirroring to come
|
||||
from those IPs and allow them.
|
||||
|
||||
GitLab.com is fronted by Cloudflare. For incoming connections to GitLab.com, you might need to allow CIDR blocks of Cloudflare ([IPv4](https://www.cloudflare.com/ips-v4) and [IPv6](https://www.cloudflare.com/ips-v6)).
|
||||
GitLab.com is fronted by Cloudflare. For incoming connections to GitLab.com, you might need to allow CIDR blocks of Cloudflare ([IPv4](https://www.cloudflare.com/ips-v4/) and [IPv6](https://www.cloudflare.com/ips-v6/)).
|
||||
|
||||
For outgoing connections from CI/CD runners, we are not providing static IP
|
||||
addresses. All GitLab.com shared runners are deployed into Google Cloud Platform (GCP). Any
|
||||
|
|
|
@ -176,7 +176,7 @@ See the [troubleshooting page](../../../administration/troubleshooting/group_sam
|
|||
|
||||
### Okta setup notes
|
||||
|
||||
Please follow the Okta documentation on [setting up a SAML application in Okta](https://developer.okta.com/docs/guides/build-sso-integration/saml2/overview/) with the notes below for consideration.
|
||||
Please follow the Okta documentation on [setting up a SAML application in Okta](https://developer.okta.com/docs/guides/build-sso-integration/saml2/main/) with the notes below for consideration.
|
||||
|
||||
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
|
||||
For a demo of the Okta SAML setup including SCIM, see [Demo: Okta Group SAML & SCIM setup](https://youtu.be/0ES9HsZq0AQ).
|
||||
|
|
|
@ -101,7 +101,7 @@ Once synchronized, changing the field mapped to `id` and `externalId` may cause
|
|||
### Okta configuration steps
|
||||
|
||||
Before you start this section, complete the [GitLab configuration](#gitlab-configuration) process.
|
||||
Make sure that you've also set up a SAML application for [Okta](https://developer.okta.com/docs/guides/build-sso-integration/saml2/overview/),
|
||||
Make sure that you've also set up a SAML application for [Okta](https://developer.okta.com/docs/guides/build-sso-integration/saml2/main/),
|
||||
as described in the [Okta setup notes](index.md#okta-setup-notes)
|
||||
|
||||
Make sure that the Okta setup matches our documentation exactly, especially the NameID
|
||||
|
|
|
@ -24,6 +24,7 @@ Value stream analytics is also available for [projects](../../analytics/value_st
|
|||
|
||||
> - Date range filter [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/13216) in GitLab 12.4
|
||||
> - Filtering [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/13216) in GitLab 13.3
|
||||
> - Horizontal stage path [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12196) in 13.0 and [feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/323982) in 13.12
|
||||
|
||||
You must have at least the Reporter role to view value stream analytics for groups.
|
||||
|
||||
|
@ -45,6 +46,9 @@ To view value stream analytics for your group:
|
|||
header. The header name differs based on the stage you select.
|
||||
- To sort by most or least amount of time spent in each stage, select the **Time** header.
|
||||
|
||||
A badge next to the workflow items table header shows the number of workflow items that
|
||||
completed during the selected stage.
|
||||
|
||||
The table shows a list of related workflow items for the selected stage. Based on the stage you select, this can be:
|
||||
|
||||
- CI/CD jobs
|
||||
|
@ -52,9 +56,6 @@ The table shows a list of related workflow items for the selected stage. Based o
|
|||
- Merge requests
|
||||
- Pipelines
|
||||
|
||||
A badge next to the workflow items table header shows the number of workflow items that
|
||||
completed the selected stage.
|
||||
|
||||
## View metrics for each development stage
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/210315) in GitLab 13.0.
|
||||
|
@ -244,7 +245,8 @@ You can change the name of a project environment in your GitLab CI/CD configurat
|
|||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12196) in GitLab 12.9.
|
||||
|
||||
Use custom value streams to create custom stages that align with your own development processes,
|
||||
and hide default stages.
|
||||
and hide default stages. The dashboard depicts stages as a horizontal process
|
||||
flow.
|
||||
|
||||
### Create a value stream
|
||||
|
||||
|
@ -263,12 +265,12 @@ To create a value stream:
|
|||
1. On the left sidebar, select **Analytics > Value Stream**.
|
||||
1. In the top right, select the dropdown list and then **Create new Value Stream**.
|
||||
1. Enter a name for the new Value Stream.
|
||||
- You can [customize the stages](#creating-a-value-stream-with-stages).
|
||||
- You can [customize the stages](#create-a-value-stream-with-stages).
|
||||
1. Select **Create Value Stream**.
|
||||
|
||||
![New value stream](img/new_value_stream_v13_12.png "Creating a new value stream")
|
||||
|
||||
### Creating a value stream with stages
|
||||
### Create a value stream with stages
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/50229) in GitLab 13.7.
|
||||
> - [Enabled by default](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55572) in GitLab 13.10.
|
||||
|
@ -320,7 +322,7 @@ After you create a value stream, you can customize it to suit your purposes. To
|
|||
1. On the top bar, select **Menu > Groups** and find your group.
|
||||
1. On the left sidebar, select **Analytics > Value Stream**.
|
||||
1. In the top right, select the dropdown list and then select the relevant value stream.
|
||||
1. Next to the value stream dropdown, select **Edit**.
|
||||
1. Next to the value stream dropdown list, select **Edit**.
|
||||
The edit form is populated with the value stream details.
|
||||
1. Optional:
|
||||
- Rename the value stream.
|
||||
|
@ -345,20 +347,27 @@ To delete a custom value stream:
|
|||
|
||||
![Delete value stream](img/delete_value_stream_v13_12.png "Deleting a custom value stream")
|
||||
|
||||
## Total time chart
|
||||
## View number of days for a cycle to complete
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/21631) in GitLab 12.6.
|
||||
> - Chart median line [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/235455) in GitLab 13.4.
|
||||
> - Totals [replaced](https://gitlab.com/gitlab-org/gitlab/-/issues/262070) with averages in GitLab 13.12.
|
||||
|
||||
This chart visually depicts the average number of days it takes for cycles to be completed.
|
||||
The **Total time chart** shows the average number of days it takes for development cycles to complete.
|
||||
The chart shows data for the last 500 workflow items.
|
||||
|
||||
This chart uses the global page filters for displaying data based on the selected
|
||||
group, projects, and time frame.
|
||||
|
||||
When a stage is selected the chart only displays data relevant to the selected stage. On the overview the chart displays a sum of the times for all stages in the value stream.
|
||||
|
||||
The chart data is limited to the last 500 items.
|
||||
1. On the top bar, select **Menu > Groups** and find your group.
|
||||
1. On the left sidebar, select **Analytics > Value stream**.
|
||||
1. Above the **Filter results** box, select a stage:
|
||||
- To view a summary of the cycle time for all stages, select **Overview**.
|
||||
- To view the cycle time for specific stage, select a stage.
|
||||
1. Optional. Filter the results:
|
||||
1. Select the **Filter results** text box.
|
||||
1. Select a parameter.
|
||||
1. Select a value or enter text to refine the results.
|
||||
1. To adjust the date range:
|
||||
- In the **From** field, select a start date.
|
||||
- In the **To** field, select an end date.
|
||||
|
||||
## Type of work - Tasks by type chart
|
||||
|
||||
|
|
|
@ -60,9 +60,9 @@ This video from January 2021 walks you through all the GitLab Terraform integrat
|
|||
|
||||
## GitLab-managed Terraform state
|
||||
|
||||
[Terraform remote backends](https://www.terraform.io/docs/language/settings/backends/index.html)
|
||||
[Terraform remote backends](https://www.terraform.io/language/settings/backends)
|
||||
enable you to store the state file in a remote, shared store. GitLab uses the
|
||||
[Terraform HTTP backend](https://www.terraform.io/docs/language/settings/backends/http.html)
|
||||
[Terraform HTTP backend](https://www.terraform.io/language/settings/backends/http)
|
||||
to securely store the state files in local storage (the default) or
|
||||
[the remote store of your choice](../../../administration/terraform_state.md).
|
||||
|
||||
|
|
|
@ -8,9 +8,9 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
|
||||
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/2673) in GitLab 13.0.
|
||||
|
||||
[Terraform remote backends](https://www.terraform.io/docs/language/settings/backends/index.html)
|
||||
[Terraform remote backends](https://www.terraform.io/language/settings/backends)
|
||||
enable you to store the state file in a remote, shared store. GitLab uses the
|
||||
[Terraform HTTP backend](https://www.terraform.io/docs/language/settings/backends/http.html)
|
||||
[Terraform HTTP backend](https://www.terraform.io/language/settings/backends/http)
|
||||
to securely store the state files in local storage (the default) or
|
||||
[the remote store of your choice](../../../administration/terraform_state.md).
|
||||
|
||||
|
@ -222,9 +222,9 @@ See [this reference project](https://gitlab.com/gitlab-org/configure/examples/gi
|
|||
## Using a GitLab-managed Terraform state backend as a remote data source
|
||||
|
||||
You can use a GitLab-managed Terraform state as a
|
||||
[Terraform data source](https://www.terraform.io/docs/language/state/remote-state-data.html).
|
||||
[Terraform data source](https://www.terraform.io/language/state/remote-state-data).
|
||||
To use your existing Terraform state backend as a data source, provide the following details
|
||||
as [Terraform input variables](https://www.terraform.io/docs/language/values/variables.html):
|
||||
as [Terraform input variables](https://www.terraform.io/language/values/variables):
|
||||
|
||||
- **address**: The URL of the remote state backend you want to use as a data source.
|
||||
For example, `https://gitlab.com/api/v4/projects/<TARGET-PROJECT-ID>/terraform/state/<TARGET-STATE-NAME>`.
|
||||
|
|
|
@ -30,7 +30,7 @@ and the [main GitLab website](https://about.gitlab.com) use [Kramdown](https://k
|
|||
You should not view this page in the documentation, but instead [view these styles as they appear on GitLab](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/user/markdown.md).
|
||||
|
||||
GitLab Flavored Markdown extends the [CommonMark specification](https://spec.commonmark.org/current/).
|
||||
It was inspired by [GitHub Flavored Markdown](https://docs.github.com/en/github/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax).
|
||||
It was inspired by [GitHub Flavored Markdown](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax).
|
||||
|
||||
## Where you can use GitLab Flavored Markdown
|
||||
|
||||
|
|
|
@ -51,4 +51,4 @@ are some useful links to get you started:
|
|||
- [ClearCase to Git](https://therub.org/2013/07/19/clearcase-to-git/)
|
||||
- [Dual syncing ClearCase to Git](https://therub.org/2013/10/22/dual-syncing-clearcase-and-git/)
|
||||
- [Moving to Git from ClearCase](https://sateeshkumarb.wordpress.com/2011/01/15/moving-to-git-from-clearcase/)
|
||||
- [ClearCase to Git webinar](https://www.brighttalk.com/webcast/11817/162473/clearcase-to-git)
|
||||
- [ClearCase to Git webinar](https://www.brighttalk.com/webcast/11817/162473)
|
||||
|
|
|
@ -71,6 +71,6 @@ Here's a few links to get you started with the migration:
|
|||
|
||||
- [Migrate using the `cvs-fast-export` tool](https://gitlab.com/esr/cvs-fast-export)
|
||||
- [Stack Overflow post on importing the CVS repository](https://stackoverflow.com/a/11490134/974710)
|
||||
- [Convert a CVS repository to Git](https://www.techrepublic.com/blog/linux-and-open-source/convert-cvs-repositories-to-git/)
|
||||
- [Convert a CVS repository to Git](http://www.techrepublic.com/article/convert-cvs-repositories-to-git/)
|
||||
- [Man page of the `git-cvsimport` tool](https://mirrors.edge.kernel.org/pub/software/scm/git/docs/git-cvsimport.html)
|
||||
- [Migrate using `reposurgeon`](http://www.catb.org/~esr/reposurgeon/repository-editing.html#conversion)
|
||||
|
|
|
@ -165,4 +165,4 @@ different types of notifications. All events are sent to the specified channel.
|
|||
## Further reading
|
||||
|
||||
- [Mattermost slash commands documentation](https://docs.mattermost.com/developer/slash-commands.html)
|
||||
- [Omnibus GitLab Mattermost](https://docs.gitlab.com/omnibus/gitlab-mattermost/)
|
||||
- [Omnibus GitLab Mattermost](../../../integration/mattermost/)
|
||||
|
|
|
@ -37,7 +37,7 @@ for the most popular hosting services:
|
|||
- [Cloudflare](https://support.cloudflare.com/hc/en-us/articles/201720164-Creating-a-Cloudflare-account-and-adding-a-website)
|
||||
- [cPanel](https://documentation.cpanel.net/display/84Docs/Edit+DNS+Zone)
|
||||
- [DigitalOcean](https://docs.digitalocean.com/products/networking/dns/how-to/manage-records/)
|
||||
- [DreamHost](https://help.dreamhost.com/hc/en-us/articles/215414867-How-do-I-add-custom-DNS-records-)
|
||||
- [DreamHost](https://help.dreamhost.com/hc/en-us/articles/360035516812)
|
||||
- [Gandi](https://docs.gandi.net/en/domain_names/faq/dns_records.html)
|
||||
- [Go Daddy](https://www.godaddy.com/help/add-an-a-record-19238)
|
||||
- [Hostgator](https://www.hostgator.com/help/article/changing-dns-records)
|
||||
|
|
|
@ -315,7 +315,7 @@ To enable this setting:
|
|||
1. Tick the checkbox **Force HTTPS (requires valid certificates)**.
|
||||
|
||||
If you use Cloudflare CDN in front of GitLab Pages, make sure to set the SSL connection setting to
|
||||
`full` instead of `flexible`. For more details, see the [Cloudflare CDN directions](https://support.cloudflare.com/hc/en-us/articles/200170416-End-to-end-HTTPS-with-Cloudflare-Part-3-SSL-options#h_4e0d1a7c-eb71-4204-9e22-9d3ef9ef7fef).
|
||||
`full` instead of `flexible`. For more details, see the [Cloudflare CDN directions](https://developers.cloudflare.com/ssl/origin-configuration/ssl-modes#h_4e0d1a7c-eb71-4204-9e22-9d3ef9ef7fef).
|
||||
|
||||
<!-- ## Troubleshooting
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ To view a list of releases:
|
|||
|
||||
- On the left sidebar, select **Deployments > Releases**, or
|
||||
|
||||
- On the project's overview page, if at least one release exists, click the number of releases.
|
||||
- On the project's overview page, if at least one release exists, select the number of releases.
|
||||
|
||||
![Number of Releases](img/releases_count_v13_2.png "Incremental counter of Releases")
|
||||
|
||||
|
@ -52,10 +52,10 @@ To view a list of releases:
|
|||
|
||||
### Sort releases
|
||||
|
||||
To sort releases by **Released date** or **Created date**, select from the sort order dropdown. To
|
||||
To sort releases by **Released date** or **Created date**, select from the sort order dropdown list. To
|
||||
switch between ascending or descending order, select **Sort order**.
|
||||
|
||||
![Sort releases dropdown button](img/releases_sort_v13_6.png)
|
||||
![Sort releases dropdown list options](img/releases_sort_v13_6.png)
|
||||
|
||||
## Create a release
|
||||
|
||||
|
@ -307,9 +307,9 @@ Read more about [Release permissions](#release-permissions).
|
|||
To edit the details of a release:
|
||||
|
||||
1. On the left sidebar, select **Deployments > Releases**.
|
||||
1. In the top-right corner of the release you want to modify, click **Edit this release** (the pencil icon).
|
||||
1. In the top-right corner of the release you want to modify, select **Edit this release** (the pencil icon).
|
||||
1. On the **Edit Release** page, change the release's details.
|
||||
1. Click **Save changes**.
|
||||
1. Select **Save changes**.
|
||||
|
||||
You can edit the release title, notes, associated milestones, and asset links.
|
||||
To change the release date use the
|
||||
|
@ -330,16 +330,16 @@ the [Releases API](../../../api/releases/index.md#create-a-release).
|
|||
In the user interface, to associate milestones to a release:
|
||||
|
||||
1. On the left sidebar, select **Deployments > Releases**.
|
||||
1. In the top-right corner of the release you want to modify, click **Edit this release** (the pencil icon).
|
||||
1. In the top-right corner of the release you want to modify, select **Edit this release** (the pencil icon).
|
||||
1. From the **Milestones** list, select each milestone you want to associate. You can select multiple milestones.
|
||||
1. Click **Save changes**.
|
||||
1. Select **Save changes**.
|
||||
|
||||
On the **Deployments > Releases** page, the **Milestone** is listed in the top
|
||||
section, along with statistics about the issues in the milestones.
|
||||
|
||||
![A Release with one associated milestone](img/release_with_milestone_v12_9.png)
|
||||
|
||||
Releases are also visible on the **Issues > Milestones** page, and when you click a milestone
|
||||
Releases are also visible on the **Issues > Milestones** page, and when you select a milestone
|
||||
on this page.
|
||||
|
||||
Here is an example of milestones with no releases, one release, and two releases, respectively.
|
||||
|
@ -360,8 +360,8 @@ You can be notified by email when a new release is created for your project.
|
|||
To subscribe to notifications for releases:
|
||||
|
||||
1. On the left sidebar, select **Project information**.
|
||||
1. Click **Notification setting** (the bell icon).
|
||||
1. In the list, click **Custom**.
|
||||
1. Select **Notification setting** (the bell icon).
|
||||
1. In the list, select **Custom**.
|
||||
1. Select the **New release** checkbox.
|
||||
1. Close the dialog box to save.
|
||||
|
||||
|
@ -395,12 +395,12 @@ To set a deploy freeze window in the UI, complete these steps:
|
|||
|
||||
1. Sign in to GitLab as a user with the Maintainer role.
|
||||
1. On the left sidebar, select **Project information**.
|
||||
1. In the left navigation menu, navigate to **Settings > CI/CD**.
|
||||
1. In the left navigation menu, go to **Settings > CI/CD**.
|
||||
1. Scroll to **Deploy freezes**.
|
||||
1. Click **Expand** to see the deploy freeze table.
|
||||
1. Click **Add deploy freeze** to open the deploy freeze modal.
|
||||
1. Select **Expand** to see the deploy freeze table.
|
||||
1. Select **Add deploy freeze** to open the deploy freeze modal.
|
||||
1. Enter the start time, end time, and time zone of the desired deploy freeze period.
|
||||
1. Click **Add deploy freeze** in the modal.
|
||||
1. Select **Add deploy freeze** in the modal.
|
||||
1. After the deploy freeze is saved, you can edit it by selecting the edit button (**{pencil}**) and remove it by selecting the delete button (**{remove}**).
|
||||
![Deploy freeze modal for setting a deploy freeze period](img/deploy_freeze_v14_3.png)
|
||||
|
||||
|
@ -611,7 +611,7 @@ On [GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/releases), you can view t
|
|||
![Feature count](img/feature_count_v14_6.png "Number of features in a release")
|
||||
|
||||
The totals are displayed on [shields](https://shields.io/) and are generated per release by
|
||||
[a Rake task in the `www-gitlab-com` repo](https://gitlab.com/gitlab-com/www-gitlab-com/-/blob/master/lib/tasks/update_gitlab_project_releases_page.rake).
|
||||
[a Rake task in the `www-gitlab-com` repository](https://gitlab.com/gitlab-com/www-gitlab-com/-/blob/master/lib/tasks/update_gitlab_project_releases_page.rake).
|
||||
|
||||
| Item | Formula |
|
||||
| ------ | ------ |
|
||||
|
@ -634,7 +634,7 @@ This data is saved in a JSON file and called *release evidence*. The feature
|
|||
includes test artifacts and linked milestones to facilitate
|
||||
internal processes, like external audits.
|
||||
|
||||
To access the release evidence, on the Releases page, click the link to the JSON file that's listed
|
||||
To access the release evidence, on the Releases page, select the link to the JSON file that's listed
|
||||
under the **Evidence collection** heading.
|
||||
|
||||
You can also [use the API](../../../api/releases/index.md#collect-release-evidence) to
|
||||
|
@ -720,7 +720,7 @@ When you create a release, if [job artifacts](../../../ci/yaml/index.md#artifact
|
|||
|
||||
Although job artifacts normally expire, artifacts included in release evidence do not expire.
|
||||
|
||||
To enable job artifact collection you need to specify both:
|
||||
To enable job artifact collection you must specify both:
|
||||
|
||||
1. [`artifacts:paths`](../../../ci/yaml/index.md#artifactspaths)
|
||||
1. [`artifacts:reports`](../../../ci/yaml/index.md#artifactsreports)
|
||||
|
|
|
@ -76,7 +76,7 @@ through the [remote mirrors API](../../../../api/remote_mirrors.md).
|
|||
|
||||
To configure a mirror from GitLab to GitHub:
|
||||
|
||||
1. Create a [GitHub personal access token](https://docs.github.com/en/github/authenticating-to-github/keeping-your-account-and-data-secure/creating-a-personal-access-token)
|
||||
1. Create a [GitHub personal access token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token)
|
||||
with `public_repo` selected.
|
||||
1. Enter a **Git repository URL** with this format:
|
||||
`https://<your_access_token>@github.com/<github_group>/<github_project>.git`.
|
||||
|
|
|
@ -24,11 +24,11 @@ The main difference is the way GitLab determines whether or not the developer's
|
|||
to their account.
|
||||
|
||||
GitLab uses its own certificate store and therefore defines the
|
||||
[trust chain](https://www.ssl.com/faqs/what-is-a-chain-of-trust/).
|
||||
[trust chain](https://www.ssl.com/faqs/what-is-a-certificate-authority/).
|
||||
For a commit or tag to be *verified* by GitLab:
|
||||
|
||||
- The signing certificate email must match a verified email address in GitLab.
|
||||
- The GitLab instance must be able to establish a full [trust chain](https://www.ssl.com/faqs/what-is-a-chain-of-trust/)
|
||||
- The GitLab instance must be able to establish a full trust chain
|
||||
from the certificate in the signature to a trusted certificate in the GitLab certificate store.
|
||||
This chain may include intermediate certificates supplied in the signature. You may
|
||||
need to add certificates, such as Certificate Authority root certificates,
|
||||
|
|
|
@ -37,10 +37,12 @@ module Gitlab
|
|||
next unless dependencies.present?
|
||||
next unless needs_value.present?
|
||||
|
||||
missing_needs = dependencies - needs_value[:job].pluck(:name) # rubocop:disable CodeReuse/ActiveRecord (Array#pluck)
|
||||
if needs_value[:job].nil? && needs_value[:cross_dependency].present?
|
||||
errors.add(:needs, "corresponding to dependencies must be from the same pipeline")
|
||||
else
|
||||
missing_needs = dependencies - needs_value[:job].pluck(:name) # rubocop:disable CodeReuse/ActiveRecord (Array#pluck)
|
||||
|
||||
if missing_needs.any?
|
||||
errors.add(:dependencies, "the #{missing_needs.join(", ")} should be part of needs")
|
||||
errors.add(:dependencies, "the #{missing_needs.join(", ")} should be part of needs") if missing_needs.any?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Graphql
|
||||
module Loaders
|
||||
class BatchCommitLoader
|
||||
def initialize(container_class:, container_id:, oid:)
|
||||
@container_class = container_class
|
||||
@container_id = container_id
|
||||
@oid = oid
|
||||
end
|
||||
|
||||
def find
|
||||
Gitlab::Graphql::Lazy.with_value(find_containers) do |container|
|
||||
BatchLoader::GraphQL.for(oid).batch(key: container) do |oids, loader, args|
|
||||
container = args[:key]
|
||||
|
||||
container.repository.commits_by(oids: oids).each do |commit|
|
||||
loader.call(commit.id, commit) if commit
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def find_containers
|
||||
BatchLoader::GraphQL.for(container_id.to_i).batch(key: container_class) do |ids, loader, args|
|
||||
model = args[:key]
|
||||
results = model.includes(:route).id_in(ids) # rubocop: disable CodeReuse/ActiveRecord
|
||||
|
||||
results.each { |record| loader.call(record.id, record) }
|
||||
end
|
||||
end
|
||||
|
||||
attr_reader :container_class, :container_id, :oid
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -619,11 +619,11 @@ RSpec.describe Projects::IssuesController do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when the SpamVerdictService disallows' do
|
||||
context 'when an issue is identified as spam' do
|
||||
before do
|
||||
stub_application_setting(recaptcha_enabled: true)
|
||||
expect_next_instance_of(Spam::SpamVerdictService) do |verdict_service|
|
||||
expect(verdict_service).to receive(:execute).and_return(CONDITIONAL_ALLOW)
|
||||
allow_next_instance_of(Spam::AkismetService) do |akismet_service|
|
||||
allow(akismet_service).to receive(:spam?).and_return(true)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -940,8 +940,8 @@ RSpec.describe Projects::IssuesController do
|
|||
context 'when an issue is identified as spam' do
|
||||
context 'when recaptcha is not verified' do
|
||||
before do
|
||||
expect_next_instance_of(Spam::SpamVerdictService) do |verdict_service|
|
||||
expect(verdict_service).to receive(:execute).and_return(CONDITIONAL_ALLOW)
|
||||
allow_next_instance_of(Spam::AkismetService) do |akismet_service|
|
||||
allow(akismet_service).to receive(:spam?).and_return(true)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -1274,11 +1274,11 @@ RSpec.describe Projects::IssuesController do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when SpamVerdictService requires recaptcha' do
|
||||
context 'when an issue is identified as spam and requires recaptcha' do
|
||||
context 'when captcha is not verified' do
|
||||
before do
|
||||
expect_next_instance_of(Spam::SpamVerdictService) do |verdict_service|
|
||||
expect(verdict_service).to receive(:execute).and_return(CONDITIONAL_ALLOW)
|
||||
allow_next_instance_of(Spam::AkismetService) do |akismet_service|
|
||||
allow(akismet_service).to receive(:spam?).and_return(true)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,178 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe 'Spam detection on issue creation', :js do
|
||||
include StubENV
|
||||
|
||||
let(:project) { create(:project, :public) }
|
||||
let(:user) { create(:user) }
|
||||
|
||||
include_context 'includes Spam constants'
|
||||
|
||||
before do
|
||||
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
|
||||
|
||||
Gitlab::CurrentSettings.update!(
|
||||
akismet_enabled: true,
|
||||
akismet_api_key: 'testkey',
|
||||
spam_check_api_key: 'testkey',
|
||||
recaptcha_enabled: true,
|
||||
recaptcha_site_key: 'test site key',
|
||||
recaptcha_private_key: 'test private key'
|
||||
)
|
||||
|
||||
project.add_maintainer(user)
|
||||
sign_in(user)
|
||||
visit new_project_issue_path(project)
|
||||
|
||||
fill_in 'issue_title', with: 'issue title'
|
||||
fill_in 'issue_description', with: 'issue description'
|
||||
end
|
||||
|
||||
shared_examples 'disallows issue creation' do
|
||||
it 'disallows issue creation' do
|
||||
click_button 'Create issue'
|
||||
|
||||
expect(page).to have_content('discarded')
|
||||
expect(page).not_to have_css('.recaptcha')
|
||||
expect(page).not_to have_content('issue title')
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'allows issue creation with CAPTCHA' do
|
||||
it 'allows issue creation' do
|
||||
click_button 'Create issue'
|
||||
|
||||
# it is impossible to test reCAPTCHA automatically and there is no possibility to fill in recaptcha
|
||||
# reCAPTCHA verification is skipped in test environment and it always returns true
|
||||
expect(page).not_to have_content('issue title')
|
||||
expect(page).to have_css('.recaptcha')
|
||||
|
||||
click_button 'Create issue'
|
||||
|
||||
expect(page.find('.issue-details h1.title')).to have_content('issue title')
|
||||
expect(page.find('.issue-details .description')).to have_content('issue description')
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'allows issue creation without CAPTCHA' do
|
||||
it 'allows issue creation without need to solve CAPTCHA' do
|
||||
click_button 'Create issue'
|
||||
|
||||
expect(page).not_to have_css('.recaptcha')
|
||||
expect(page.find('.issue-details h1.title')).to have_content('issue title')
|
||||
expect(page.find('.issue-details .description')).to have_content('issue description')
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'creates a spam_log record' do
|
||||
it 'creates a spam_log record' do
|
||||
expect { click_button 'Create issue' }
|
||||
.to log_spam(title: 'issue title', description: 'issue description', user_id: user.id, noteable_type: 'Issue')
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'does not create a spam_log record' do
|
||||
it 'does not creates a spam_log record' do
|
||||
expect { click_button 'Create issue' }
|
||||
.not_to log_spam(title: 'issue title', description: 'issue description', user_id: user.id, noteable_type: 'Issue')
|
||||
end
|
||||
end
|
||||
|
||||
shared_context 'when spammable is identified as possible spam' do
|
||||
before do
|
||||
allow_next_instance_of(Spam::AkismetService) do |akismet_service|
|
||||
allow(akismet_service).to receive(:spam?).and_return(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
shared_context 'when spammable is not identified as possible spam' do
|
||||
before do
|
||||
allow_next_instance_of(Spam::AkismetService) do |akismet_service|
|
||||
allow(akismet_service).to receive(:spam?).and_return(false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
shared_context 'when CAPTCHA is enabled' do
|
||||
before do
|
||||
stub_application_setting(recaptcha_enabled: true)
|
||||
end
|
||||
end
|
||||
|
||||
shared_context 'when CAPTCHA is not enabled' do
|
||||
before do
|
||||
stub_application_setting(recaptcha_enabled: false)
|
||||
end
|
||||
end
|
||||
|
||||
shared_context 'when allow_possible_spam feature flag is true' do
|
||||
before do
|
||||
stub_feature_flags(allow_possible_spam: true)
|
||||
end
|
||||
end
|
||||
|
||||
shared_context 'when allow_possible_spam feature flag is false' do
|
||||
before do
|
||||
stub_feature_flags(allow_possible_spam: false)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'spam handling' do
|
||||
# verdict, spam_flagged, captcha_enabled, allow_possible_spam_flag, creates_spam_log
|
||||
# TODO: Add example for BLOCK_USER verdict when we add support for testing SpamCheck - see https://gitlab.com/groups/gitlab-org/-/epics/5527#lacking-coverage-for-spamcheck-vs-akismet
|
||||
# DISALLOW, true, false, false, true
|
||||
# CONDITIONAL_ALLOW, true, true, false, true
|
||||
# OVERRIDE_VIA_ALLOW_POSSIBLE_SPAM, true, true, true, true
|
||||
# OVERRIDE_VIA_ALLOW_POSSIBLE_SPAM, true, false, true, true
|
||||
# ALLOW, false, true, false, false
|
||||
# TODO: Add example for NOOP verdict when we add support for testing SpamCheck - see https://gitlab.com/groups/gitlab-org/-/epics/5527#lacking-coverage-for-spamcheck-vs-akismet
|
||||
|
||||
context 'DISALLOW: spam_flagged=true, captcha_enabled=true, allow_possible_spam=true' do
|
||||
include_context 'when spammable is identified as possible spam'
|
||||
include_context 'when CAPTCHA is enabled'
|
||||
include_context 'when allow_possible_spam feature flag is true'
|
||||
|
||||
it_behaves_like 'allows issue creation without CAPTCHA'
|
||||
it_behaves_like 'creates a spam_log record'
|
||||
end
|
||||
|
||||
context 'CONDITIONAL_ALLOW: spam_flagged=true, captcha_enabled=true, allow_possible_spam=false' do
|
||||
include_context 'when spammable is identified as possible spam'
|
||||
include_context 'when CAPTCHA is enabled'
|
||||
include_context 'when allow_possible_spam feature flag is false'
|
||||
|
||||
it_behaves_like 'allows issue creation with CAPTCHA'
|
||||
it_behaves_like 'creates a spam_log record'
|
||||
end
|
||||
|
||||
context 'OVERRIDE_VIA_ALLOW_POSSIBLE_SPAM: spam_flagged=true, captcha_enabled=true, allow_possible_spam=true' do
|
||||
include_context 'when spammable is identified as possible spam'
|
||||
include_context 'when CAPTCHA is enabled'
|
||||
include_context 'when allow_possible_spam feature flag is true'
|
||||
|
||||
it_behaves_like 'allows issue creation without CAPTCHA'
|
||||
it_behaves_like 'creates a spam_log record'
|
||||
end
|
||||
|
||||
context 'OVERRIDE_VIA_ALLOW_POSSIBLE_SPAM: spam_flagged=true, captcha_enabled=false, allow_possible_spam=true' do
|
||||
include_context 'when spammable is identified as possible spam'
|
||||
include_context 'when CAPTCHA is not enabled'
|
||||
include_context 'when allow_possible_spam feature flag is true'
|
||||
|
||||
it_behaves_like 'allows issue creation without CAPTCHA'
|
||||
it_behaves_like 'creates a spam_log record'
|
||||
end
|
||||
|
||||
context 'ALLOW: spam_flagged=false, captcha_enabled=true, allow_possible_spam=false' do
|
||||
include_context 'when spammable is not identified as possible spam'
|
||||
include_context 'when CAPTCHA is not enabled'
|
||||
include_context 'when allow_possible_spam feature flag is false'
|
||||
|
||||
it_behaves_like 'allows issue creation without CAPTCHA'
|
||||
it_behaves_like 'does not create a spam_log record'
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,188 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe 'New issue', :js do
|
||||
include StubENV
|
||||
|
||||
let(:project) { create(:project, :public) }
|
||||
let(:user) { create(:user)}
|
||||
|
||||
before do
|
||||
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
|
||||
|
||||
Gitlab::CurrentSettings.update!(
|
||||
akismet_enabled: true,
|
||||
akismet_api_key: 'testkey',
|
||||
spam_check_api_key: 'testkey',
|
||||
recaptcha_enabled: true,
|
||||
recaptcha_site_key: 'test site key',
|
||||
recaptcha_private_key: 'test private key'
|
||||
)
|
||||
|
||||
project.add_maintainer(user)
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
context 'when SpamVerdictService disallows' do
|
||||
include_context 'includes Spam constants'
|
||||
|
||||
before do
|
||||
allow_next_instance_of(Spam::SpamVerdictService) do |verdict_service|
|
||||
allow(verdict_service).to receive(:execute).and_return(DISALLOW)
|
||||
end
|
||||
|
||||
visit new_project_issue_path(project)
|
||||
end
|
||||
|
||||
context 'when allow_possible_spam feature flag is false' do
|
||||
before do
|
||||
stub_feature_flags(allow_possible_spam: false)
|
||||
|
||||
fill_in 'issue_title', with: 'issue title'
|
||||
fill_in 'issue_description', with: 'issue description'
|
||||
end
|
||||
|
||||
it 'rejects issue creation' do
|
||||
click_button 'Create issue'
|
||||
|
||||
expect(page).to have_content('discarded')
|
||||
expect(page).not_to have_content('potential spam')
|
||||
expect(page).not_to have_content('issue title')
|
||||
end
|
||||
|
||||
it 'creates a spam log record' do
|
||||
expect { click_button 'Create issue' }
|
||||
.to log_spam(title: 'issue title', description: 'issue description', user_id: user.id, noteable_type: 'Issue')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when allow_possible_spam feature flag is true' do
|
||||
before do
|
||||
fill_in 'issue_title', with: 'issue title'
|
||||
fill_in 'issue_description', with: 'issue description'
|
||||
end
|
||||
|
||||
it 'allows issue creation' do
|
||||
click_button 'Create issue'
|
||||
|
||||
expect(page.find('.issue-details h1.title')).to have_content('issue title')
|
||||
expect(page.find('.issue-details .description')).to have_content('issue description')
|
||||
end
|
||||
|
||||
it 'creates a spam log record' do
|
||||
expect { click_button 'Create issue' }
|
||||
.to log_spam(title: 'issue title', description: 'issue description', user_id: user.id, noteable_type: 'Issue')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when SpamVerdictService requires recaptcha' do
|
||||
include_context 'includes Spam constants'
|
||||
|
||||
before do
|
||||
allow_next_instance_of(Spam::SpamVerdictService) do |verdict_service|
|
||||
allow(verdict_service).to receive(:execute).and_return(CONDITIONAL_ALLOW)
|
||||
end
|
||||
|
||||
visit new_project_issue_path(project)
|
||||
end
|
||||
|
||||
context 'when recaptcha is enabled' do
|
||||
before do
|
||||
stub_application_setting(recaptcha_enabled: true)
|
||||
end
|
||||
|
||||
context 'when allow_possible_spam feature flag is false' do
|
||||
before do
|
||||
stub_feature_flags(allow_possible_spam: false)
|
||||
end
|
||||
|
||||
it 'creates an issue after solving reCaptcha' do
|
||||
fill_in 'issue_title', with: 'issue title'
|
||||
fill_in 'issue_description', with: 'issue description'
|
||||
|
||||
click_button 'Create issue'
|
||||
|
||||
# it is impossible to test reCAPTCHA automatically and there is no possibility to fill in recaptcha
|
||||
# reCAPTCHA verification is skipped in test environment and it always returns true
|
||||
expect(page).not_to have_content('issue title')
|
||||
expect(page).to have_css('.recaptcha')
|
||||
|
||||
click_button 'Create issue'
|
||||
|
||||
expect(page.find('.issue-details h1.title')).to have_content('issue title')
|
||||
expect(page.find('.issue-details .description')).to have_content('issue description')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when allow_possible_spam feature flag is true' do
|
||||
before do
|
||||
fill_in 'issue_title', with: 'issue title'
|
||||
fill_in 'issue_description', with: 'issue description'
|
||||
end
|
||||
|
||||
it 'creates an issue without a need to solve reCAPTCHA' do
|
||||
click_button 'Create issue'
|
||||
|
||||
expect(page).not_to have_css('.recaptcha')
|
||||
expect(page.find('.issue-details h1.title')).to have_content('issue title')
|
||||
expect(page.find('.issue-details .description')).to have_content('issue description')
|
||||
end
|
||||
|
||||
it 'creates a spam log record' do
|
||||
expect { click_button 'Create issue' }
|
||||
.to log_spam(title: 'issue title', description: 'issue description', user_id: user.id, noteable_type: 'Issue')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when reCAPTCHA is not enabled' do
|
||||
before do
|
||||
stub_application_setting(recaptcha_enabled: false)
|
||||
end
|
||||
|
||||
context 'when allow_possible_spam feature flag is true' do
|
||||
before do
|
||||
fill_in 'issue_title', with: 'issue title'
|
||||
fill_in 'issue_description', with: 'issue description'
|
||||
end
|
||||
|
||||
it 'creates an issue without a need to solve reCaptcha' do
|
||||
click_button 'Create issue'
|
||||
|
||||
expect(page).not_to have_css('.recaptcha')
|
||||
expect(page.find('.issue-details h1.title')).to have_content('issue title')
|
||||
expect(page.find('.issue-details .description')).to have_content('issue description')
|
||||
end
|
||||
|
||||
it 'creates a spam log record' do
|
||||
expect { click_button 'Create issue' }
|
||||
.to log_spam(title: 'issue title', description: 'issue description', user_id: user.id, noteable_type: 'Issue')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the SpamVerdictService allows' do
|
||||
include_context 'includes Spam constants'
|
||||
|
||||
before do
|
||||
allow_next_instance_of(Spam::SpamVerdictService) do |verdict_service|
|
||||
allow(verdict_service).to receive(:execute).and_return(ALLOW)
|
||||
end
|
||||
|
||||
visit new_project_issue_path(project)
|
||||
end
|
||||
|
||||
it 'creates an issue' do
|
||||
fill_in 'issue_title', with: 'issue title'
|
||||
fill_in 'issue_description', with: 'issue description'
|
||||
|
||||
click_button 'Create issue'
|
||||
|
||||
expect(page.find('.issue-details h1.title')).to have_content('issue title')
|
||||
expect(page.find('.issue-details .description')).to have_content('issue description')
|
||||
end
|
||||
end
|
||||
end
|
|
@ -7,6 +7,8 @@ RSpec.describe GitlabSchema.types['AlertManagementAlert'] do
|
|||
|
||||
specify { expect(described_class).to require_graphql_authorizations(:read_alert_management_alert) }
|
||||
|
||||
specify { expect(described_class.interfaces).to include(Types::TodoableInterface) }
|
||||
|
||||
it 'exposes the expected fields' do
|
||||
expected_fields = %i[
|
||||
iid
|
||||
|
@ -34,6 +36,7 @@ RSpec.describe GitlabSchema.types['AlertManagementAlert'] do
|
|||
details_url
|
||||
prometheus_alert
|
||||
environment
|
||||
web_url
|
||||
]
|
||||
|
||||
expect(described_class).to have_graphql_fields(*expected_fields)
|
||||
|
|
|
@ -7,6 +7,8 @@ RSpec.describe GitlabSchema.types['Commit'] do
|
|||
|
||||
specify { expect(described_class).to require_graphql_authorizations(:download_code) }
|
||||
|
||||
specify { expect(described_class).to include(Types::TodoableInterface) }
|
||||
|
||||
it 'contains attributes related to commit' do
|
||||
expect(described_class).to have_graphql_fields(
|
||||
:id, :sha, :short_id, :title, :full_title, :full_title_html, :description, :description_html, :message, :title_html, :authored_date,
|
||||
|
|
|
@ -5,8 +5,10 @@ require 'spec_helper'
|
|||
RSpec.describe GitlabSchema.types['Design'] do
|
||||
specify { expect(described_class.interfaces).to include(Types::CurrentUserTodos) }
|
||||
|
||||
specify { expect(described_class.interfaces).to include(Types::TodoableInterface) }
|
||||
|
||||
it_behaves_like 'a GraphQL type with design fields' do
|
||||
let(:extra_design_fields) { %i[notes current_user_todos discussions versions] }
|
||||
let(:extra_design_fields) { %i[notes current_user_todos discussions versions web_url] }
|
||||
let_it_be(:design) { create(:design, :with_versions) }
|
||||
let(:object_id) { GitlabSchema.id_from_object(design) }
|
||||
let_it_be(:object_id_b) { GitlabSchema.id_from_object(create(:design, :with_versions)) }
|
||||
|
|
|
@ -13,6 +13,8 @@ RSpec.describe GitlabSchema.types['MergeRequest'] do
|
|||
|
||||
specify { expect(described_class.interfaces).to include(Types::CurrentUserTodos) }
|
||||
|
||||
specify { expect(described_class.interfaces).to include(Types::TodoableInterface) }
|
||||
|
||||
it 'has the expected fields' do
|
||||
expected_fields = %w[
|
||||
notes discussions user_permissions id iid title title_html description
|
||||
|
|
|
@ -4,7 +4,7 @@ require 'spec_helper'
|
|||
|
||||
RSpec.describe GitlabSchema.types['Todo'] do
|
||||
it 'has the correct fields' do
|
||||
expected_fields = [:id, :project, :group, :author, :action, :target_type, :body, :state, :created_at]
|
||||
expected_fields = [:id, :project, :group, :author, :action, :target, :target_type, :body, :state, :created_at]
|
||||
|
||||
expect(described_class).to have_graphql_fields(*expected_fields)
|
||||
end
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Types::TodoableInterface do
|
||||
it 'exposes the expected fields' do
|
||||
expected_fields = %i[
|
||||
web_url
|
||||
]
|
||||
|
||||
expect(described_class).to have_graphql_fields(*expected_fields)
|
||||
end
|
||||
|
||||
describe ".resolve_type" do
|
||||
it 'knows the correct type for objects' do
|
||||
expect(described_class.resolve_type(build(:issue), {})).to eq(Types::IssueType)
|
||||
expect(described_class.resolve_type(build(:merge_request), {})).to eq(Types::MergeRequestType)
|
||||
expect(described_class.resolve_type(build(:design), {})).to eq(Types::DesignManagement::DesignType)
|
||||
expect(described_class.resolve_type(build(:alert_management_alert), {})).to eq(Types::AlertManagement::AlertType)
|
||||
expect(described_class.resolve_type(build(:commit), {})).to eq(Types::CommitType)
|
||||
end
|
||||
|
||||
it 'raises an error for an unknown type' do
|
||||
project = build(:project)
|
||||
|
||||
expect { described_class.resolve_type(project, {}) }.to raise_error("Unknown GraphQL type for #{project}")
|
||||
end
|
||||
end
|
||||
end
|
|
@ -420,7 +420,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Job do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when has dependencies' do
|
||||
context 'when it has dependencies' do
|
||||
context 'that are not a array of strings' do
|
||||
let(:config) do
|
||||
{ script: 'echo', dependencies: 'build-job' }
|
||||
|
@ -433,8 +433,8 @@ RSpec.describe Gitlab::Ci::Config::Entry::Job do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when has needs' do
|
||||
context 'when have dependencies that are not subset of needs' do
|
||||
context 'when the job has needs' do
|
||||
context 'and there are dependencies that are not included in needs' do
|
||||
let(:config) do
|
||||
{
|
||||
stage: 'test',
|
||||
|
@ -448,6 +448,24 @@ RSpec.describe Gitlab::Ci::Config::Entry::Job do
|
|||
expect(entry).not_to be_valid
|
||||
expect(entry.errors).to include 'job dependencies the another-job should be part of needs'
|
||||
end
|
||||
|
||||
context 'and they are only cross pipeline needs' do
|
||||
let(:config) do
|
||||
{
|
||||
script: 'echo',
|
||||
dependencies: ['rspec'],
|
||||
needs: [{
|
||||
job: 'rspec',
|
||||
pipeline: 'other'
|
||||
}]
|
||||
}
|
||||
end
|
||||
|
||||
it 'adds an error for dependency keyword usage' do
|
||||
expect(entry).not_to be_valid
|
||||
expect(entry.errors).to include 'job needs corresponding to dependencies must be from the same pipeline'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::Graphql::Loaders::BatchCommitLoader do
|
||||
include RepoHelpers
|
||||
|
||||
describe '#find' do
|
||||
let_it_be(:first_project) { create(:project, :repository) }
|
||||
let_it_be(:second_project) { create(:project, :repository) }
|
||||
|
||||
let_it_be(:first_commit) { first_project.commit(sample_commit.id) }
|
||||
let_it_be(:second_commit) { first_project.commit(another_sample_commit.id) }
|
||||
let_it_be(:third_commit) { second_project.commit(sample_big_commit.id) }
|
||||
|
||||
it 'finds a commit by id' do
|
||||
result = described_class.new(
|
||||
container_class: Project,
|
||||
container_id: first_project.id,
|
||||
oid: first_commit.id
|
||||
).find
|
||||
|
||||
expect(result.force).to eq(first_commit)
|
||||
end
|
||||
|
||||
before do
|
||||
ActiveSupport::Notifications.subscribe('sql.active_record') do |event|
|
||||
raise "no!" if event.payload[:sql].include?('SELECT "namespaces".')
|
||||
end
|
||||
end
|
||||
|
||||
it 'only queries once' do
|
||||
expect do
|
||||
[
|
||||
described_class.new(
|
||||
container_class: Project,
|
||||
container_id: first_project.id,
|
||||
oid: first_commit.id
|
||||
).find,
|
||||
described_class.new(
|
||||
container_class: Project,
|
||||
container_id: first_project.id,
|
||||
oid: second_commit.id
|
||||
).find,
|
||||
described_class.new(
|
||||
container_class: Project,
|
||||
container_id: second_project.id,
|
||||
oid: third_commit.id
|
||||
).find
|
||||
].map(&:force)
|
||||
end.not_to exceed_query_limit(2)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -233,10 +233,6 @@ RSpec.describe ExternalPullRequest do
|
|||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'it has loose foreign keys' do
|
||||
let(:factory_name) { :external_pull_request }
|
||||
end
|
||||
|
||||
context 'loose foreign key on external_pull_requests.project_id' do
|
||||
it_behaves_like 'cleanup by a loose foreign key' do
|
||||
let!(:parent) { create(:project) }
|
||||
|
|
|
@ -199,8 +199,8 @@ RSpec.describe API::Issues do
|
|||
expect(spam_service).to receive_messages(check_for_spam?: true)
|
||||
end
|
||||
|
||||
expect_next_instance_of(Spam::SpamVerdictService) do |verdict_service|
|
||||
expect(verdict_service).to receive(:execute).and_return(DISALLOW)
|
||||
allow_next_instance_of(Spam::AkismetService) do |akismet_service|
|
||||
allow(akismet_service).to receive(:spam?).and_return(true)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -170,26 +170,13 @@ RSpec.describe Spam::SpamActionService do
|
|||
allow(fake_verdict_service).to receive(:execute).and_return(DISALLOW)
|
||||
end
|
||||
|
||||
context 'when allow_possible_spam feature flag is false' do
|
||||
before do
|
||||
stub_feature_flags(allow_possible_spam: false)
|
||||
end
|
||||
it_behaves_like 'creates a spam log'
|
||||
|
||||
it 'marks as spam' do
|
||||
response = subject
|
||||
it 'marks as spam' do
|
||||
response = subject
|
||||
|
||||
expect(response.message).to match(expected_service_check_response_message)
|
||||
expect(issue).to be_spam
|
||||
end
|
||||
end
|
||||
|
||||
context 'when allow_possible_spam feature flag is true' do
|
||||
it 'does not mark as spam' do
|
||||
response = subject
|
||||
|
||||
expect(response.message).to match(expected_service_check_response_message)
|
||||
expect(issue).not_to be_spam
|
||||
end
|
||||
expect(response.message).to match(expected_service_check_response_message)
|
||||
expect(issue).to be_spam
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -198,26 +185,13 @@ RSpec.describe Spam::SpamActionService do
|
|||
allow(fake_verdict_service).to receive(:execute).and_return(BLOCK_USER)
|
||||
end
|
||||
|
||||
context 'when allow_possible_spam feature flag is false' do
|
||||
before do
|
||||
stub_feature_flags(allow_possible_spam: false)
|
||||
end
|
||||
it_behaves_like 'creates a spam log'
|
||||
|
||||
it 'marks as spam' do
|
||||
response = subject
|
||||
it 'marks as spam' do
|
||||
response = subject
|
||||
|
||||
expect(response.message).to match(expected_service_check_response_message)
|
||||
expect(issue).to be_spam
|
||||
end
|
||||
end
|
||||
|
||||
context 'when allow_possible_spam feature flag is true' do
|
||||
it 'does not mark as spam' do
|
||||
response = subject
|
||||
|
||||
expect(response.message).to match(expected_service_check_response_message)
|
||||
expect(issue).not_to be_spam
|
||||
end
|
||||
expect(response.message).to match(expected_service_check_response_message)
|
||||
expect(issue).to be_spam
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -226,37 +200,42 @@ RSpec.describe Spam::SpamActionService do
|
|||
allow(fake_verdict_service).to receive(:execute).and_return(CONDITIONAL_ALLOW)
|
||||
end
|
||||
|
||||
context 'when allow_possible_spam feature flag is false' do
|
||||
before do
|
||||
stub_feature_flags(allow_possible_spam: false)
|
||||
end
|
||||
it_behaves_like 'creates a spam log'
|
||||
|
||||
it_behaves_like 'creates a spam log'
|
||||
it 'does not mark as spam' do
|
||||
response = subject
|
||||
|
||||
it 'does not mark as spam' do
|
||||
response = subject
|
||||
|
||||
expect(response.message).to match(expected_service_check_response_message)
|
||||
expect(issue).not_to be_spam
|
||||
end
|
||||
|
||||
it 'marks as needing reCAPTCHA' do
|
||||
response = subject
|
||||
|
||||
expect(response.message).to match(expected_service_check_response_message)
|
||||
expect(issue).to be_needs_recaptcha
|
||||
end
|
||||
expect(response.message).to match(expected_service_check_response_message)
|
||||
expect(issue).not_to be_spam
|
||||
end
|
||||
|
||||
context 'when allow_possible_spam feature flag is true' do
|
||||
it_behaves_like 'creates a spam log'
|
||||
it 'marks as needing reCAPTCHA' do
|
||||
response = subject
|
||||
|
||||
it 'does not mark as needing reCAPTCHA' do
|
||||
response = subject
|
||||
expect(response.message).to match(expected_service_check_response_message)
|
||||
expect(issue).to be_needs_recaptcha
|
||||
end
|
||||
end
|
||||
|
||||
expect(response.message).to match(expected_service_check_response_message)
|
||||
expect(issue.needs_recaptcha).to be_falsey
|
||||
end
|
||||
context 'when spam verdict service returns OVERRIDE_VIA_ALLOW_POSSIBLE_SPAM' do
|
||||
before do
|
||||
allow(fake_verdict_service).to receive(:execute).and_return(OVERRIDE_VIA_ALLOW_POSSIBLE_SPAM)
|
||||
end
|
||||
|
||||
it_behaves_like 'creates a spam log'
|
||||
|
||||
it 'does not mark as spam' do
|
||||
response = subject
|
||||
|
||||
expect(response.message).to match(expected_service_check_response_message)
|
||||
expect(issue).not_to be_spam
|
||||
end
|
||||
|
||||
it 'does not mark as needing CAPTCHA' do
|
||||
response = subject
|
||||
|
||||
expect(response.message).to match(expected_service_check_response_message)
|
||||
expect(issue).not_to be_needs_recaptcha
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -27,6 +27,10 @@ RSpec.describe Spam::SpamVerdictService do
|
|||
extra_attributes
|
||||
end
|
||||
|
||||
before do
|
||||
stub_feature_flags(allow_possible_spam: false)
|
||||
end
|
||||
|
||||
describe '#execute' do
|
||||
subject { service.execute }
|
||||
|
||||
|
@ -114,6 +118,32 @@ RSpec.describe Spam::SpamVerdictService do
|
|||
end
|
||||
end
|
||||
|
||||
context 'if allow_possible_spam flag is true' do
|
||||
before do
|
||||
stub_feature_flags(allow_possible_spam: true)
|
||||
end
|
||||
|
||||
context 'and a service returns a verdict that should be overridden' do
|
||||
before do
|
||||
allow(service).to receive(:spamcheck_verdict).and_return([BLOCK_USER, attribs])
|
||||
end
|
||||
|
||||
it 'overrides and renders the override verdict' do
|
||||
expect(subject).to eq OVERRIDE_VIA_ALLOW_POSSIBLE_SPAM
|
||||
end
|
||||
end
|
||||
|
||||
context 'and a service returns a verdict that does not need to be overridden' do
|
||||
before do
|
||||
allow(service).to receive(:spamcheck_verdict).and_return([ALLOW, attribs])
|
||||
end
|
||||
|
||||
it 'does not override and renders the original verdict' do
|
||||
expect(subject).to eq ALLOW
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'records metrics' do
|
||||
let(:histogram) { instance_double(Prometheus::Client::Histogram) }
|
||||
|
||||
|
|
|
@ -2,10 +2,11 @@
|
|||
|
||||
RSpec.shared_context 'includes Spam constants' do
|
||||
before do
|
||||
stub_const('CONDITIONAL_ALLOW', Spam::SpamConstants::CONDITIONAL_ALLOW)
|
||||
stub_const('DISALLOW', Spam::SpamConstants::DISALLOW)
|
||||
stub_const('ALLOW', Spam::SpamConstants::ALLOW)
|
||||
stub_const('BLOCK_USER', Spam::SpamConstants::BLOCK_USER)
|
||||
stub_const('DISALLOW', Spam::SpamConstants::DISALLOW)
|
||||
stub_const('CONDITIONAL_ALLOW', Spam::SpamConstants::CONDITIONAL_ALLOW)
|
||||
stub_const('OVERRIDE_VIA_ALLOW_POSSIBLE_SPAM', Spam::SpamConstants::OVERRIDE_VIA_ALLOW_POSSIBLE_SPAM)
|
||||
stub_const('ALLOW', Spam::SpamConstants::ALLOW)
|
||||
stub_const('NOOP', Spam::SpamConstants::NOOP)
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue