Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
3a25b40d55
commit
953180403c
|
|
@ -1043,7 +1043,6 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
|
|||
/app/assets/javascripts/alerts_settings/graphql/mutations/reset_http_token.mutation.graphql @gitlab-org/manage/authentication-and-authorization/approvers
|
||||
/app/assets/javascripts/authentication/ @gitlab-org/manage/authentication-and-authorization/approvers
|
||||
/app/assets/javascripts/ide/components/shared/tokened_input.vue @gitlab-org/manage/authentication-and-authorization/approvers
|
||||
/app/assets/javascripts/invite_members/components/members_token_select.vue @gitlab-org/manage/authentication-and-authorization/approvers
|
||||
/app/assets/javascripts/packages_and_registries/package_registry/components/list/tokens/ @gitlab-org/manage/authentication-and-authorization/approvers
|
||||
/app/assets/javascripts/pages/admin/impersonation_tokens/ @gitlab-org/manage/authentication-and-authorization/approvers
|
||||
/app/assets/javascripts/pages/groups/settings/access_tokens/ @gitlab-org/manage/authentication-and-authorization/approvers
|
||||
|
|
|
|||
|
|
@ -1903,14 +1903,17 @@
|
|||
- <<: *if-dot-com-gitlab-org-merge-request
|
||||
changes: *controllers-patterns
|
||||
variables: *review-change-pattern
|
||||
when: manual
|
||||
allow_failure: true
|
||||
- <<: *if-dot-com-gitlab-org-merge-request
|
||||
changes: *models-patterns
|
||||
variables: *review-change-pattern
|
||||
when: manual
|
||||
allow_failure: true
|
||||
- <<: *if-dot-com-gitlab-org-merge-request
|
||||
changes: *lib-gitlab-patterns
|
||||
variables: *review-change-pattern
|
||||
when: manual
|
||||
allow_failure: true
|
||||
- <<: *if-dot-com-gitlab-org-merge-request
|
||||
changes: *qa-patterns
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ export const specialFilterValues = [
|
|||
|
||||
export const TYPE_TOKEN_TASK_OPTION = { icon: 'issue-type-task', title: 'task', value: 'task' };
|
||||
export const TYPE_TOKEN_OBJECTIVE_OPTION = {
|
||||
icon: 'issue-type-issue',
|
||||
icon: 'issue-type-objective',
|
||||
title: 'objective',
|
||||
value: 'objective',
|
||||
};
|
||||
|
|
|
|||
|
|
@ -467,8 +467,9 @@ export default {
|
|||
|
||||
<template>
|
||||
<div
|
||||
:class="{ 'gl-bg-gray-10': mr.state !== 'closed' && mr.state !== 'merged' }"
|
||||
data-testid="ready_to_merge_state"
|
||||
class="gl-border-t-1 gl-border-t-solid gl-border-gray-100 gl-bg-gray-10 gl-pl-7"
|
||||
class="gl-border-t-1 gl-border-t-solid gl-border-gray-100 gl-pl-7"
|
||||
>
|
||||
<div v-if="loading" class="mr-widget-body">
|
||||
<div class="gl-w-full mr-ready-to-merge-loader">
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ export const WORK_ITEMS_TYPE_MAP = {
|
|||
name: s__('WorkItem|Requirements'),
|
||||
},
|
||||
[WORK_ITEM_TYPE_ENUM_OBJECTIVE]: {
|
||||
icon: `issue-type-issue`,
|
||||
icon: `issue-type-objective`,
|
||||
name: s__('WorkItem|Objective'),
|
||||
},
|
||||
[WORK_ITEM_TYPE_ENUM_KEY_RESULT]: {
|
||||
|
|
|
|||
|
|
@ -94,10 +94,15 @@ module LfsRequest
|
|||
next false unless has_authentication_ability?(:push_code)
|
||||
next false if limit_exceeded?
|
||||
|
||||
lfs_deploy_token? || can?(user, :push_code, project) || can?(deploy_token, :push_code, project)
|
||||
lfs_deploy_token? || can?(user, :push_code,
|
||||
project) || can?(deploy_token, :push_code, project) || any_branch_allows_collaboration?
|
||||
end
|
||||
end
|
||||
|
||||
def any_branch_allows_collaboration?
|
||||
project.merge_requests_allowing_push_to_user(user).any?
|
||||
end
|
||||
|
||||
def lfs_deploy_token?
|
||||
authentication_result.lfs_deploy_token?(project)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ class UsersFinder
|
|||
private
|
||||
|
||||
def base_scope
|
||||
scope = current_user&.admin? ? User.all : User.without_forbidden_states
|
||||
scope = current_user&.can_admin_all_resources? ? User.all : User.without_forbidden_states
|
||||
scope.order_id_desc
|
||||
end
|
||||
|
||||
|
|
@ -80,7 +80,7 @@ class UsersFinder
|
|||
def by_search(users)
|
||||
return users unless params[:search].present?
|
||||
|
||||
users.search(params[:search], with_private_emails: current_user&.admin?)
|
||||
users.search(params[:search], with_private_emails: current_user&.can_admin_all_resources?)
|
||||
end
|
||||
|
||||
def by_blocked(users)
|
||||
|
|
@ -97,7 +97,7 @@ class UsersFinder
|
|||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def by_external_identity(users)
|
||||
return users unless current_user&.admin? && params[:extern_uid] && params[:provider]
|
||||
return users unless current_user&.can_admin_all_resources? && params[:extern_uid] && params[:provider]
|
||||
|
||||
users.joins(:identities).merge(Identity.with_extern_uid(params[:provider], params[:extern_uid]))
|
||||
end
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ module SearchHelper
|
|||
[
|
||||
groups_autocomplete(term),
|
||||
projects_autocomplete(term),
|
||||
users_autocomplete(term),
|
||||
issue_autocomplete(term)
|
||||
].flatten
|
||||
end
|
||||
|
|
@ -351,6 +352,25 @@ module SearchHelper
|
|||
end
|
||||
end
|
||||
|
||||
def users_autocomplete(term, limit = 5)
|
||||
return [] unless current_user && Ability.allowed?(current_user, :read_users_list)
|
||||
|
||||
SearchService
|
||||
.new(current_user, { scope: 'users', search: term })
|
||||
.search_objects
|
||||
.limit(limit)
|
||||
.map do |user|
|
||||
{
|
||||
category: "Users",
|
||||
id: user.id,
|
||||
value: "#{search_result_sanitize(user.name)}",
|
||||
label: "#{search_result_sanitize(user.username)}",
|
||||
url: user_path(user),
|
||||
avatar_url: user.avatar_url || ''
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def recent_merge_requests_autocomplete(term)
|
||||
return [] unless current_user
|
||||
|
||||
|
|
|
|||
|
|
@ -10,17 +10,29 @@ module WorkItems
|
|||
|
||||
include CacheMarkdownField
|
||||
|
||||
# type name is used in restrictions DB seeder to assure restrictions for
|
||||
# default types are pre-filled
|
||||
TYPE_NAMES = {
|
||||
issue: 'Issue',
|
||||
incident: 'Incident',
|
||||
test_case: 'Test Case',
|
||||
requirement: 'Requirement',
|
||||
task: 'Task',
|
||||
objective: 'Objective',
|
||||
key_result: 'Key Result'
|
||||
}.freeze
|
||||
|
||||
# Base types need to exist on the DB on app startup
|
||||
# This constant is used by the DB seeder
|
||||
# TODO - where to add new icon names created?
|
||||
BASE_TYPES = {
|
||||
issue: { name: 'Issue', icon_name: 'issue-type-issue', enum_value: 0 },
|
||||
incident: { name: 'Incident', icon_name: 'issue-type-incident', enum_value: 1 },
|
||||
test_case: { name: 'Test Case', icon_name: 'issue-type-test-case', enum_value: 2 }, ## EE-only
|
||||
requirement: { name: 'Requirement', icon_name: 'issue-type-requirements', enum_value: 3 }, ## EE-only
|
||||
task: { name: 'Task', icon_name: 'issue-type-task', enum_value: 4 },
|
||||
objective: { name: 'Objective', icon_name: 'issue-type-objective', enum_value: 5 }, ## EE-only
|
||||
key_result: { name: 'Key Result', icon_name: 'issue-type-keyresult', enum_value: 6 } ## EE-only
|
||||
issue: { name: TYPE_NAMES[:issue], icon_name: 'issue-type-issue', enum_value: 0 },
|
||||
incident: { name: TYPE_NAMES[:incident], icon_name: 'issue-type-incident', enum_value: 1 },
|
||||
test_case: { name: TYPE_NAMES[:test_case], icon_name: 'issue-type-test-case', enum_value: 2 }, ## EE-only
|
||||
requirement: { name: TYPE_NAMES[:requirement], icon_name: 'issue-type-requirements', enum_value: 3 }, ## EE-only
|
||||
task: { name: TYPE_NAMES[:task], icon_name: 'issue-type-task', enum_value: 4 },
|
||||
objective: { name: TYPE_NAMES[:objective], icon_name: 'issue-type-objective', enum_value: 5 }, ## EE-only
|
||||
key_result: { name: TYPE_NAMES[:key_result], icon_name: 'issue-type-keyresult', enum_value: 6 } ## EE-only
|
||||
}.freeze
|
||||
|
||||
WIDGETS_FOR_TYPE = {
|
||||
|
|
@ -66,6 +78,7 @@ module WorkItems
|
|||
return found_type if found_type
|
||||
|
||||
Gitlab::DatabaseImporters::WorkItems::BaseTypeImporter.upsert_types
|
||||
Gitlab::DatabaseImporters::WorkItems::HierarchyRestrictionsImporter.upsert_restrictions
|
||||
find_by(namespace_id: nil, base_type: type)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
Gitlab::Seeder.quiet do
|
||||
Gitlab::DatabaseImporters::WorkItems::HierarchyRestrictionsImporter.upsert_restrictions
|
||||
end
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
Gitlab::Seeder.quiet do
|
||||
Gitlab::DatabaseImporters::WorkItems::HierarchyRestrictionsImporter.upsert_restrictions
|
||||
end
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddOkrHierarchyRestrictions < Gitlab::Database::Migration[2.0]
|
||||
class WorkItemType < MigrationRecord
|
||||
self.table_name = 'work_item_types'
|
||||
end
|
||||
|
||||
class HierarchyRestriction < MigrationRecord
|
||||
self.table_name = 'work_item_hierarchy_restrictions'
|
||||
end
|
||||
|
||||
restrict_gitlab_migration gitlab_schema: :gitlab_main
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
objective = WorkItemType.find_by_name_and_namespace_id('Objective', nil)
|
||||
key_result = WorkItemType.find_by_name_and_namespace_id('Key Result', nil)
|
||||
issue = WorkItemType.find_by_name_and_namespace_id('Issue', nil)
|
||||
task = WorkItemType.find_by_name_and_namespace_id('Task', nil)
|
||||
incident = WorkItemType.find_by_name_and_namespace_id('Incident', nil)
|
||||
|
||||
# work item default types should be filled, if this is not the case
|
||||
# then restrictions will be created together with work item types
|
||||
unless objective && key_result && issue && task && incident
|
||||
Gitlab::AppLogger.warn('default types are missing, not adding restrictions')
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
restrictions = [
|
||||
{ parent_type_id: objective.id, child_type_id: objective.id, maximum_depth: 9 },
|
||||
{ parent_type_id: objective.id, child_type_id: key_result.id, maximum_depth: 1 },
|
||||
{ parent_type_id: issue.id, child_type_id: task.id, maximum_depth: 1 },
|
||||
{ parent_type_id: incident.id, child_type_id: task.id, maximum_depth: 1 }
|
||||
]
|
||||
|
||||
HierarchyRestriction.upsert_all(
|
||||
restrictions,
|
||||
unique_by: :index_work_item_hierarchy_restrictions_on_parent_and_child
|
||||
)
|
||||
end
|
||||
|
||||
def down
|
||||
# so far restrictions table was empty so we can delete all records when
|
||||
# migrating down
|
||||
HierarchyRestriction.delete_all
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1 @@
|
|||
a6caf06dd18f096219d5ce0752c956ef099a92df71899c1b9164d3a16f6ef0ba
|
||||
|
|
@ -45,8 +45,7 @@ Features that are disabled by default may change or be removed without notice in
|
|||
|
||||
Data corruption, stability degradation, performance degradation, or security issues might occur if
|
||||
you enable a feature that's disabled by default. Problems caused by using a default
|
||||
disabled feature aren't covered by GitLab support, unless you were directed by GitLab
|
||||
to enable the feature.
|
||||
disabled feature aren't covered by GitLab Support.
|
||||
|
||||
Security issues found in features that are disabled by default are patched in regular releases
|
||||
and do not follow our regular [maintenance policy](../policy/maintenance.md#security-releases)
|
||||
|
|
|
|||
|
|
@ -5795,7 +5795,7 @@ Input type: `VulnerabilityDismissInput`
|
|||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="mutationvulnerabilitydismissclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
|
||||
| <a id="mutationvulnerabilitydismisscomment"></a>`comment` | [`String`](#string) | Comment why vulnerability should be dismissed. |
|
||||
| <a id="mutationvulnerabilitydismisscomment"></a>`comment` | [`String`](#string) | Comment why vulnerability should be dismissed (max. 50 000 characters). |
|
||||
| <a id="mutationvulnerabilitydismissdismissalreason"></a>`dismissalReason` | [`VulnerabilityDismissalReason`](#vulnerabilitydismissalreason) | Reason why vulnerability should be dismissed. |
|
||||
| <a id="mutationvulnerabilitydismissid"></a>`id` | [`VulnerabilityID!`](#vulnerabilityid) | ID of the vulnerability to be dismissed. |
|
||||
|
||||
|
|
|
|||
|
|
@ -433,6 +433,7 @@ Supported attributes:
|
|||
| `applies_to_all_protected_branches` | boolean | **{dotted-circle}** No | Whether the rule is applied to all protected branches. If set to `true`, the value of `protected_branch_ids` is ignored. Default is `false`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/335316) in GitLab 15.3. |
|
||||
| `group_ids` | Array | **{dotted-circle}** No | The IDs of groups as approvers. |
|
||||
| `protected_branch_ids` | Array | **{dotted-circle}** No | The IDs of protected branches to scope the rule by. To identify the ID, [use the API](protected_branches.md#list-protected-branches). |
|
||||
| `remove_hidden_groups` | boolean | **{dotted-circle}** No | Whether hidden groups should be removed. |
|
||||
| `user_ids` | Array | **{dotted-circle}** No | The IDs of users as approvers. |
|
||||
|
||||
```json
|
||||
|
|
@ -964,6 +965,7 @@ Supported attributes:
|
|||
| `merge_request_iid` | integer | **{check-circle}** Yes | The IID of a merge request. |
|
||||
| `name` | string | **{check-circle}** Yes | The name of the approval rule. |
|
||||
| `group_ids` | Array | **{dotted-circle}** No | The IDs of groups as approvers. |
|
||||
| `remove_hidden_groups` | boolean | **{dotted-circle}** No | Whether hidden groups should be removed. |
|
||||
| `user_ids` | Array | **{dotted-circle}** No | The IDs of users as approvers. |
|
||||
|
||||
```json
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ Download a PyPI package file. The [simple API](#group-level-simple-api-entry-poi
|
|||
normally supplies this URL.
|
||||
|
||||
```plaintext
|
||||
GET groups/:id/packages/pypi/files/:sha256/:file_identifier
|
||||
GET groups/:id/-/packages/pypi/files/:sha256/:file_identifier
|
||||
```
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
|
|
@ -42,13 +42,13 @@ GET groups/:id/packages/pypi/files/:sha256/:file_identifier
|
|||
| `file_identifier` | string | yes | The PyPI package file's name. |
|
||||
|
||||
```shell
|
||||
curl --user <username>:<personal_access_token> "https://gitlab.example.com/api/v4/groups/1/packages/pypi/files/5y57017232013c8ac80647f4ca153k3726f6cba62d055cd747844ed95b3c65ff/my.pypi.package-0.0.1.tar.gz"
|
||||
curl --user <username>:<personal_access_token> "https://gitlab.example.com/api/v4/groups/1/-/packages/pypi/files/5y57017232013c8ac80647f4ca153k3726f6cba62d055cd747844ed95b3c65ff/my.pypi.package-0.0.1.tar.gz"
|
||||
```
|
||||
|
||||
To write the output to a file:
|
||||
|
||||
```shell
|
||||
curl --user <username>:<personal_access_token> "https://gitlab.example.com/api/v4/groups/1/packages/pypi/files/5y57017232013c8ac80647f4ca153k3726f6cba62d055cd747844ed95b3c65ff/my.pypi.package-0.0.1.tar.gz" >> my.pypi.package-0.0.1.tar.gz
|
||||
curl --user <username>:<personal_access_token> "https://gitlab.example.com/api/v4/groups/1/-/packages/pypi/files/5y57017232013c8ac80647f4ca153k3726f6cba62d055cd747844ed95b3c65ff/my.pypi.package-0.0.1.tar.gz" >> my.pypi.package-0.0.1.tar.gz
|
||||
```
|
||||
|
||||
This writes the downloaded file to `my.pypi.package-0.0.1.tar.gz` in the current
|
||||
|
|
|
|||
|
|
@ -150,6 +150,7 @@ To delete filter tokens one at a time, the <kbd>⌥</kbd> (Mac) / <kbd>Control</
|
|||
In the search bar, you can view autocomplete suggestions for:
|
||||
|
||||
- Projects and groups
|
||||
- Users
|
||||
- Various help pages (try and type **API help**)
|
||||
- Project feature pages (try and type **milestones**)
|
||||
- Various settings pages (try and type **user settings**)
|
||||
|
|
|
|||
|
|
@ -13,9 +13,8 @@ module Gitlab
|
|||
include ::Gitlab::Config::Entry::Configurable
|
||||
include ::Gitlab::Config::Entry::Inheritable
|
||||
|
||||
ALLOWED_KEYS = %i[before_script image services
|
||||
after_script cache interruptible
|
||||
timeout retry tags artifacts].freeze
|
||||
ALLOWED_KEYS = %i[before_script after_script hooks cache image services
|
||||
interruptible timeout retry tags artifacts].freeze
|
||||
|
||||
validations do
|
||||
validates :config, allowed_keys: ALLOWED_KEYS
|
||||
|
|
@ -25,6 +24,19 @@ module Gitlab
|
|||
description: 'Script that will be executed before each job.',
|
||||
inherit: true
|
||||
|
||||
entry :after_script, Entry::Commands,
|
||||
description: 'Script that will be executed after each job.',
|
||||
inherit: true
|
||||
|
||||
entry :hooks, Entry::Hooks,
|
||||
description: 'Commands that will be executed on Runner before/after some events ' \
|
||||
'such as `clone` and `build-script`.',
|
||||
inherit: false
|
||||
|
||||
entry :cache, Entry::Caches,
|
||||
description: 'Configure caching between build jobs.',
|
||||
inherit: true
|
||||
|
||||
entry :image, Entry::Image,
|
||||
description: 'Docker image that will be used to execute jobs.',
|
||||
inherit: true
|
||||
|
|
@ -33,14 +45,6 @@ module Gitlab
|
|||
description: 'Docker images that will be linked to the container.',
|
||||
inherit: true
|
||||
|
||||
entry :after_script, Entry::Commands,
|
||||
description: 'Script that will be executed after each job.',
|
||||
inherit: true
|
||||
|
||||
entry :cache, Entry::Caches,
|
||||
description: 'Configure caching between build jobs.',
|
||||
inherit: true
|
||||
|
||||
entry :interruptible, ::Gitlab::Config::Entry::Boolean,
|
||||
description: 'Set jobs interruptible default value.',
|
||||
inherit: false
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ module Gitlab
|
|||
# `Configurable` alreadys adds `Validatable`
|
||||
include ::Gitlab::Config::Entry::Configurable
|
||||
|
||||
# NOTE: If a new hook is added, inheriting should be changed because a `job:hooks` overrides all
|
||||
# `default:hooks` now. We should implement merging; each hook must be overridden individually.
|
||||
ALLOWED_HOOKS = %i[pre_get_sources_script].freeze
|
||||
|
||||
validations do
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ module Gitlab
|
|||
|
||||
entry :hooks, Entry::Hooks,
|
||||
description: 'Commands that will be executed on Runner before/after some events; clone, build-script.',
|
||||
inherit: false # This will be true in next iterations
|
||||
inherit: true
|
||||
|
||||
entry :cache, Entry::Caches,
|
||||
description: 'Cache definition for this job.',
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ module Gitlab
|
|||
if once
|
||||
observations[operation.to_s] = value
|
||||
else
|
||||
observations[operation.to_s] ||= []
|
||||
observations[operation.to_s].push(value)
|
||||
end
|
||||
end
|
||||
|
|
@ -116,13 +117,12 @@ module Gitlab
|
|||
end
|
||||
|
||||
def enabled?
|
||||
strong_memoize(:enabled) do
|
||||
::Feature.enabled?(:ci_pipeline_creation_logger, project, type: :ops)
|
||||
end
|
||||
::Feature.enabled?(:ci_pipeline_creation_logger, project, type: :ops)
|
||||
end
|
||||
strong_memoize_attr :enabled?, :enabled
|
||||
|
||||
def observations
|
||||
@observations ||= Hash.new { |hash, key| hash[key] = [] }
|
||||
@observations ||= {}
|
||||
end
|
||||
|
||||
def observe_sql_counters(operation, start_db_counters, end_db_counters, once: false)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module DatabaseImporters
|
||||
module WorkItems
|
||||
module HierarchyRestrictionsImporter
|
||||
def self.upsert_restrictions
|
||||
objective = find_or_create_type(::WorkItems::Type::TYPE_NAMES[:objective])
|
||||
key_result = find_or_create_type(::WorkItems::Type::TYPE_NAMES[:key_result])
|
||||
issue = find_or_create_type(::WorkItems::Type::TYPE_NAMES[:issue])
|
||||
task = find_or_create_type(::WorkItems::Type::TYPE_NAMES[:task])
|
||||
incident = find_or_create_type(::WorkItems::Type::TYPE_NAMES[:incident])
|
||||
|
||||
restrictions = [
|
||||
{ parent_type_id: objective.id, child_type_id: objective.id, maximum_depth: 9 },
|
||||
{ parent_type_id: objective.id, child_type_id: key_result.id, maximum_depth: 1 },
|
||||
{ parent_type_id: issue.id, child_type_id: task.id, maximum_depth: 1 },
|
||||
{ parent_type_id: incident.id, child_type_id: task.id, maximum_depth: 1 }
|
||||
]
|
||||
|
||||
::WorkItems::HierarchyRestriction.upsert_all(
|
||||
restrictions,
|
||||
unique_by: :index_work_item_hierarchy_restrictions_on_parent_and_child
|
||||
)
|
||||
end
|
||||
|
||||
def self.find_or_create_type(name)
|
||||
type = ::WorkItems::Type.find_by_name_and_namespace_id(name, nil)
|
||||
return type if type
|
||||
|
||||
Gitlab::DatabaseImporters::WorkItems::BaseTypeImporter.upsert_types
|
||||
::WorkItems::Type.find_by_name_and_namespace_id(name, nil)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -3,6 +3,11 @@
|
|||
module Gitlab
|
||||
module SlashCommands
|
||||
class ApplicationHelp < BaseCommand
|
||||
def initialize(project, params)
|
||||
@project = project
|
||||
@params = params
|
||||
end
|
||||
|
||||
def execute
|
||||
Gitlab::SlashCommands::Presenters::Help
|
||||
.new(project, commands, params)
|
||||
|
|
@ -16,11 +21,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def commands
|
||||
Gitlab::SlashCommands::Command.new(
|
||||
project,
|
||||
chat_name,
|
||||
params
|
||||
).commands
|
||||
Gitlab::SlashCommands::Command.commands
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
module Gitlab
|
||||
module SlashCommands
|
||||
class Command < BaseCommand
|
||||
def commands
|
||||
def self.commands
|
||||
commands = [
|
||||
Gitlab::SlashCommands::IssueShow,
|
||||
Gitlab::SlashCommands::IssueNew,
|
||||
|
|
@ -15,7 +15,7 @@ module Gitlab
|
|||
Gitlab::SlashCommands::Run
|
||||
]
|
||||
|
||||
if Feature.enabled?(:incident_declare_slash_command, current_user)
|
||||
if Feature.enabled?(:incident_declare_slash_command)
|
||||
commands << Gitlab::SlashCommands::IncidentManagement::IncidentNew
|
||||
end
|
||||
|
||||
|
|
@ -50,7 +50,7 @@ module Gitlab
|
|||
private
|
||||
|
||||
def available_commands
|
||||
commands.keep_if do |klass|
|
||||
self.class.commands.keep_if do |klass|
|
||||
klass.available?(project)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'gitlab'
|
||||
require_relative 'default_options'
|
||||
|
||||
class CreateIssueDiscussion
|
||||
def initialize(options)
|
||||
@project = options.fetch(:project)
|
||||
|
||||
# Force the token to be a string so that if api_token is nil, it's set to '',
|
||||
# allowing unauthenticated requests (for forks).
|
||||
api_token = options.delete(:api_token).to_s
|
||||
|
||||
warn "No API token given." if api_token.empty?
|
||||
|
||||
@client = Gitlab.client(
|
||||
endpoint: options.delete(:endpoint) || API::DEFAULT_OPTIONS[:endpoint],
|
||||
private_token: api_token
|
||||
)
|
||||
end
|
||||
|
||||
def execute(discussion_data)
|
||||
client.post(
|
||||
"/projects/#{client.url_encode project}/issues/#{discussion_data.delete(:issue_iid)}/discussions",
|
||||
body: discussion_data
|
||||
)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :project, :client
|
||||
end
|
||||
|
|
@ -7,6 +7,7 @@ require 'json'
|
|||
|
||||
require_relative 'api/pipeline_failed_jobs'
|
||||
require_relative 'api/create_issue'
|
||||
require_relative 'api/create_issue_discussion'
|
||||
|
||||
class CreatePipelineFailureIncident
|
||||
DEFAULT_OPTIONS = {
|
||||
|
|
@ -28,7 +29,12 @@ class CreatePipelineFailureIncident
|
|||
labels: incident_labels
|
||||
}
|
||||
|
||||
CreateIssue.new(project: project, api_token: api_token).execute(payload)
|
||||
CreateIssue.new(project: project, api_token: api_token).execute(payload).tap do |incident|
|
||||
CreateIssueDiscussion.new(project: project, api_token: api_token)
|
||||
.execute(issue_iid: incident.iid, body: "## Root Cause Analysis")
|
||||
CreateIssueDiscussion.new(project: project, api_token: api_token)
|
||||
.execute(issue_iid: incident.iid, body: "## Investigation Steps")
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
|
@ -44,8 +50,16 @@ class CreatePipelineFailureIncident
|
|||
end
|
||||
|
||||
def title
|
||||
"#{now.strftime('%A %F %R UTC')} - `#{ENV['CI_PROJECT_PATH']}` broken `#{ENV['CI_COMMIT_REF_NAME']}` " \
|
||||
"with #{failed_jobs.size} failed jobs"
|
||||
@title ||= begin
|
||||
full_title = "#{now.strftime('%A %F %R UTC')} - `#{ENV['CI_PROJECT_PATH']}` " \
|
||||
"broken `#{ENV['CI_COMMIT_REF_NAME']}` with #{failed_jobs.map(&:name).join(', ')}"
|
||||
|
||||
if full_title.size >= 255
|
||||
"#{full_title[...252]}..." # max title length is 255, and we add an elipsis
|
||||
else
|
||||
full_title
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def description
|
||||
|
|
|
|||
|
|
@ -56,8 +56,6 @@ cat > k8s-resources-count.out <<COMMANDS
|
|||
$(k8s_resource_count daemonsets.apps) daemonsets.apps
|
||||
$(k8s_resource_count deployments.apps) deployments.apps
|
||||
$(k8s_resource_count endpoints) endpoints
|
||||
$(k8s_resource_count endpointslices.discovery.k8s.io) endpointslices.discovery.k8s.io
|
||||
$(k8s_resource_count events) events
|
||||
$(k8s_resource_count frontendconfigs.networking.gke.io) frontendconfigs.networking.gke.io
|
||||
$(k8s_resource_count horizontalpodautoscalers.autoscaling) horizontalpodautoscalers.autoscaling
|
||||
$(k8s_resource_count ingressclasses) ingressclasses
|
||||
|
|
@ -71,7 +69,6 @@ cat > k8s-resources-count.out <<COMMANDS
|
|||
$(k8s_resource_count orders.acme.cert-manager.io) orders.acme.cert-manager.io
|
||||
$(k8s_resource_count persistentvolumeclaims) persistentvolumeclaims
|
||||
$(k8s_resource_count poddisruptionbudgets.policy) poddisruptionbudgets.policy
|
||||
$(k8s_resource_count pods.metrics.k8s.io) pods.metrics.k8s.io
|
||||
$(k8s_resource_count pods) pods
|
||||
$(k8s_resource_count podtemplates) podtemplates
|
||||
$(k8s_resource_count replicasets.apps) replicasets.apps
|
||||
|
|
|
|||
|
|
@ -114,6 +114,9 @@ if unused_flags.count > 0
|
|||
puts
|
||||
puts "If they are really no longer needed REMOVE their .yml definition".red
|
||||
puts "If they are needed you need to ENSURE that their usage is covered with specs to continue.".red
|
||||
puts "Feature flag usage is detected via Rubocop, which is unable to resolve dynamic feature flag usage,".red.bold
|
||||
puts "interpolated strings however are optimistically matched. For more details consult test suite:".red
|
||||
puts "https://gitlab.com/gitlab-org/gitlab/-/blob/69cb5d36db95881b495966c95655672cfb816f62/spec/rubocop/cop/gitlab/mark_used_feature_flags_spec.rb".red
|
||||
puts
|
||||
unused_flags.keys.sort.each do |name|
|
||||
puts "- #{name}".yellow
|
||||
|
|
|
|||
|
|
@ -421,6 +421,12 @@ RSpec.describe SearchController do
|
|||
expect(json_response.count).to eq(1)
|
||||
expect(json_response.first['label']).to match(/User settings/)
|
||||
end
|
||||
|
||||
it 'makes a call to search_autocomplete_opts' do
|
||||
expect(controller).to receive(:search_autocomplete_opts).once
|
||||
|
||||
get :autocomplete, params: { term: 'setting', filter: 'generic' }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#append_info_to_payload' do
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe 'Create work item hierarchy restrictions in development', feature_category: :portfolio_management do
|
||||
subject { load Rails.root.join('db/fixtures/development/50_create_work_item_hierarchy_restrictions.rb') }
|
||||
|
||||
it_behaves_like 'work item hierarchy restrictions importer'
|
||||
end
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe 'Create work item hierarchy restrictions in production', feature_category: :portfolio_management do
|
||||
subject { load Rails.root.join('db/fixtures/production/020_create_work_item_hierarchy_restrictions.rb') }
|
||||
|
||||
it_behaves_like 'work item hierarchy restrictions importer'
|
||||
end
|
||||
|
|
@ -8,9 +8,7 @@ RSpec.describe UsersFinder do
|
|||
|
||||
let_it_be(:project_bot) { create(:user, :project_bot) }
|
||||
|
||||
context 'with a normal user' do
|
||||
let_it_be(:user) { create(:user) }
|
||||
|
||||
shared_examples 'executes users finder as normal user' do
|
||||
it 'returns searchable users' do
|
||||
users = described_class.new(user).execute
|
||||
|
||||
|
|
@ -97,37 +95,35 @@ RSpec.describe UsersFinder do
|
|||
end
|
||||
end
|
||||
|
||||
context 'with an admin user', :enable_admin_mode do
|
||||
let_it_be(:admin) { create(:admin) }
|
||||
|
||||
shared_examples 'executes users finder as admin' do
|
||||
it 'filters by external users' do
|
||||
users = described_class.new(admin, external: true).execute
|
||||
users = described_class.new(user, external: true).execute
|
||||
|
||||
expect(users).to contain_exactly(external_user)
|
||||
end
|
||||
|
||||
it 'returns all users' do
|
||||
users = described_class.new(admin).execute
|
||||
users = described_class.new(user).execute
|
||||
|
||||
expect(users).to contain_exactly(admin, normal_user, blocked_user, unconfirmed_user, banned_user, external_user, omniauth_user, internal_user, admin_user, project_bot)
|
||||
expect(users).to contain_exactly(user, normal_user, blocked_user, unconfirmed_user, banned_user, external_user, omniauth_user, internal_user, admin_user, project_bot)
|
||||
end
|
||||
|
||||
it 'filters by blocked users' do
|
||||
users = described_class.new(admin, blocked: true).execute
|
||||
users = described_class.new(user, blocked: true).execute
|
||||
|
||||
expect(users).to contain_exactly(blocked_user)
|
||||
end
|
||||
|
||||
it 'filters by active users' do
|
||||
users = described_class.new(admin, active: true).execute
|
||||
users = described_class.new(user, active: true).execute
|
||||
|
||||
expect(users).to contain_exactly(admin, normal_user, unconfirmed_user, external_user, omniauth_user, admin_user, project_bot)
|
||||
expect(users).to contain_exactly(user, normal_user, unconfirmed_user, external_user, omniauth_user, admin_user, project_bot)
|
||||
end
|
||||
|
||||
it 'returns only admins' do
|
||||
users = described_class.new(admin, admins: true).execute
|
||||
users = described_class.new(user, admins: true).execute
|
||||
|
||||
expect(users).to contain_exactly(admin, admin_user)
|
||||
expect(users).to contain_exactly(user, admin_user)
|
||||
end
|
||||
|
||||
it 'filters by custom attributes' do
|
||||
|
|
@ -137,7 +133,7 @@ RSpec.describe UsersFinder do
|
|||
create :user_custom_attribute, user: internal_user, key: 'foo', value: 'foo'
|
||||
|
||||
users = described_class.new(
|
||||
admin,
|
||||
user,
|
||||
custom_attributes: { foo: 'foo', bar: 'bar' }
|
||||
).execute
|
||||
|
||||
|
|
@ -145,10 +141,34 @@ RSpec.describe UsersFinder do
|
|||
end
|
||||
|
||||
it 'filters by private emails search' do
|
||||
users = described_class.new(admin, search: normal_user.email).execute
|
||||
users = described_class.new(user, search: normal_user.email).execute
|
||||
|
||||
expect(users).to contain_exactly(normal_user)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a normal user' do
|
||||
let_it_be(:user) { create(:user) }
|
||||
|
||||
it_behaves_like 'executes users finder as normal user'
|
||||
end
|
||||
|
||||
context 'with an admin user' do
|
||||
let_it_be(:user) { create(:admin) }
|
||||
|
||||
context 'when admin mode setting is disabled', :do_not_mock_admin_mode_setting do
|
||||
it_behaves_like 'executes users finder as admin'
|
||||
end
|
||||
|
||||
context 'when admin mode setting is enabled' do
|
||||
context 'when in admin mode', :enable_admin_mode do
|
||||
it_behaves_like 'executes users finder as admin'
|
||||
end
|
||||
|
||||
context 'when not in admin mode' do
|
||||
it_behaves_like 'executes users finder as normal user'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -60,6 +60,34 @@ RSpec.describe SearchHelper do
|
|||
expect(search_autocomplete_opts(project.name).size).to eq(1)
|
||||
end
|
||||
|
||||
context 'for users' do
|
||||
let_it_be(:another_user) { create(:user, name: 'Jane Doe') }
|
||||
let(:term) { 'jane' }
|
||||
|
||||
it 'makes a call to SearchService' do
|
||||
expect(SearchService).to receive(:new).with(current_user, { search: term, scope: 'users' }).and_call_original
|
||||
|
||||
search_autocomplete_opts(term)
|
||||
end
|
||||
|
||||
it 'returns users matching the term' do
|
||||
result = search_autocomplete_opts(term)
|
||||
expect(result.size).to eq(1)
|
||||
expect(result.first[:id]).to eq(another_user.id)
|
||||
end
|
||||
|
||||
context 'when current_user cannot read_users_list' do
|
||||
before do
|
||||
allow(Ability).to receive(:allowed?).and_return(true)
|
||||
allow(Ability).to receive(:allowed?).with(current_user, :read_users_list).and_return(false)
|
||||
end
|
||||
|
||||
it 'returns an empty array' do
|
||||
expect(search_autocomplete_opts(term)).to eq([])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "includes the required project attrs" do
|
||||
project = create(:project, namespace: create(:namespace, owner: user))
|
||||
result = search_autocomplete_opts(project.name).first
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Bridge do
|
|||
# that we know that we don't want to inherit
|
||||
# as they do not have sense in context of Bridge
|
||||
let(:ignored_inheritable_columns) do
|
||||
%i[before_script after_script image services cache interruptible timeout
|
||||
%i[before_script after_script hooks image services cache interruptible timeout
|
||||
retry tags artifacts]
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -26,9 +26,8 @@ RSpec.describe Gitlab::Ci::Config::Entry::Default do
|
|||
context 'when filtering all the entry/node names' do
|
||||
it 'contains the expected node names' do
|
||||
expect(described_class.nodes.keys)
|
||||
.to match_array(%i[before_script image services
|
||||
after_script cache interruptible
|
||||
timeout retry tags artifacts])
|
||||
.to match_array(%i[before_script after_script hooks cache image services
|
||||
interruptible timeout retry tags artifacts])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -213,6 +213,21 @@ RSpec.describe ::Gitlab::Ci::Pipeline::Logger do
|
|||
|
||||
expect(commit).to be_truthy
|
||||
end
|
||||
|
||||
context 'with unexistent observations in condition' do
|
||||
it 'does not commit the log' do
|
||||
logger.log_when do |observations|
|
||||
value = observations['non_existent_value']
|
||||
next false unless value
|
||||
|
||||
value > 0
|
||||
end
|
||||
|
||||
expect(Gitlab::AppJsonLogger).not_to receive(:info)
|
||||
|
||||
expect(commit).to be_falsey
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when project is not passed and pipeline is not persisted' do
|
||||
|
|
|
|||
|
|
@ -901,6 +901,37 @@ module Gitlab
|
|||
)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when receiving from the default' do
|
||||
let(:config) do
|
||||
{
|
||||
default: { hooks: { pre_get_sources_script: ["echo 1", "echo 2", "pwd"] } },
|
||||
test: { script: ["script"] }
|
||||
}
|
||||
end
|
||||
|
||||
it "inherits hooks" do
|
||||
expect(subject[:options][:hooks]).to eq(
|
||||
{ pre_get_sources_script: ["echo 1", "echo 2", "pwd"] }
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when overriding the default' do
|
||||
let(:config) do
|
||||
{
|
||||
default: { hooks: { pre_get_sources_script: ["echo 1", "echo 2", "pwd"] } },
|
||||
test: { script: ["script"],
|
||||
hooks: { pre_get_sources_script: ["echo 3", "echo 4", "pwd"] } }
|
||||
}
|
||||
end
|
||||
|
||||
it "overrides hooks" do
|
||||
expect(subject[:options][:hooks]).to eq(
|
||||
{ pre_get_sources_script: ["echo 3", "echo 4", "pwd"] }
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::DatabaseImporters::WorkItems::HierarchyRestrictionsImporter,
|
||||
feature_category: :portfolio_management do
|
||||
subject { described_class.upsert_restrictions }
|
||||
|
||||
it_behaves_like 'work item hierarchy restrictions importer'
|
||||
end
|
||||
|
|
@ -4,13 +4,11 @@ require 'spec_helper'
|
|||
|
||||
RSpec.describe Gitlab::SlashCommands::ApplicationHelp do
|
||||
let(:params) { { command: '/gitlab', text: 'help' } }
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:chat_user) { create(:chat_name, user: user) }
|
||||
let(:project) { build(:project) }
|
||||
|
||||
describe '#execute' do
|
||||
subject do
|
||||
described_class.new(project, chat_user, params).execute
|
||||
described_class.new(project, params).execute
|
||||
end
|
||||
|
||||
it 'displays the help section' do
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
require_migration!
|
||||
|
||||
RSpec.describe AddOkrHierarchyRestrictions, :migration, feature_category: :portfolio_management do
|
||||
include MigrationHelpers::WorkItemTypesHelper
|
||||
|
||||
let_it_be(:restrictions) { table(:work_item_hierarchy_restrictions) }
|
||||
let_it_be(:work_item_types) { table(:work_item_types) }
|
||||
|
||||
it 'creates default restrictions' do
|
||||
restrictions.delete_all
|
||||
|
||||
reversible_migration do |migration|
|
||||
migration.before -> {
|
||||
expect(restrictions.count).to eq(0)
|
||||
}
|
||||
|
||||
migration.after -> {
|
||||
expect(restrictions.count).to eq(4)
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
context 'when work items are missing' do
|
||||
before do
|
||||
work_item_types.delete_all
|
||||
end
|
||||
|
||||
it 'does nothing' do
|
||||
expect { migrate! }.not_to change { restrictions.count }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -75,6 +75,32 @@ RSpec.describe WorkItems::Type do
|
|||
end
|
||||
end
|
||||
|
||||
describe '.default_by_type' do
|
||||
let(:default_issue_type) { described_class.find_by(namespace_id: nil, base_type: :issue) }
|
||||
|
||||
subject { described_class.default_by_type(:issue) }
|
||||
|
||||
it 'returns default work item type by base type without calling importer' do
|
||||
expect(Gitlab::DatabaseImporters::WorkItems::BaseTypeImporter).not_to receive(:upsert_types)
|
||||
expect(Gitlab::DatabaseImporters::WorkItems::HierarchyRestrictionsImporter).not_to receive(:upsert_restrictions)
|
||||
|
||||
expect(subject).to eq(default_issue_type)
|
||||
end
|
||||
|
||||
context 'when default types are missing' do
|
||||
before do
|
||||
described_class.delete_all
|
||||
end
|
||||
|
||||
it 'creates types and restrictions and returns default work item type by base type' do
|
||||
expect(Gitlab::DatabaseImporters::WorkItems::BaseTypeImporter).to receive(:upsert_types)
|
||||
expect(Gitlab::DatabaseImporters::WorkItems::HierarchyRestrictionsImporter).to receive(:upsert_restrictions)
|
||||
|
||||
expect(subject).to eq(default_issue_type)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#default?' do
|
||||
subject { build(:work_item_type, namespace: namespace).default? }
|
||||
|
||||
|
|
|
|||
|
|
@ -1031,7 +1031,7 @@ RSpec.describe 'Git LFS API and storage' do
|
|||
end
|
||||
|
||||
describe 'to a forked project' do
|
||||
let_it_be(:upstream_project) { create(:project, :public) }
|
||||
let_it_be_with_reload(:upstream_project) { create(:project, :public) }
|
||||
let_it_be(:project_owner) { create(:user) }
|
||||
|
||||
let(:project) { fork_project(upstream_project, project_owner) }
|
||||
|
|
@ -1069,6 +1069,56 @@ RSpec.describe 'Git LFS API and storage' do
|
|||
end
|
||||
end
|
||||
|
||||
describe 'when user has push access to upstream project' do
|
||||
before do
|
||||
upstream_project.add_maintainer(user)
|
||||
end
|
||||
|
||||
context 'an MR exists on target forked project' do
|
||||
let(:allow_collaboration) { true }
|
||||
let(:merge_request) do
|
||||
create(:merge_request,
|
||||
target_project: upstream_project,
|
||||
source_project: project,
|
||||
allow_collaboration: allow_collaboration)
|
||||
end
|
||||
|
||||
before do
|
||||
merge_request
|
||||
end
|
||||
|
||||
context 'with allow_collaboration option set to true' do
|
||||
context 'and request is sent by gitlab-workhorse to authorize the request' do
|
||||
before do
|
||||
put_authorize
|
||||
end
|
||||
|
||||
it_behaves_like 'LFS http 200 workhorse response'
|
||||
end
|
||||
|
||||
context 'and request is sent by gitlab-workhorse to finalize the upload' do
|
||||
before do
|
||||
put_finalize
|
||||
end
|
||||
|
||||
it_behaves_like 'LFS http 200 response'
|
||||
end
|
||||
end
|
||||
|
||||
context 'with allow_collaboration option set to false' do
|
||||
context 'request is sent by gitlab-workhorse to authorize the request' do
|
||||
let(:allow_collaboration) { false }
|
||||
|
||||
before do
|
||||
put_authorize
|
||||
end
|
||||
|
||||
it_behaves_like 'forbidden'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'and user does not have push access' do
|
||||
it_behaves_like 'forbidden'
|
||||
end
|
||||
|
|
|
|||
|
|
@ -39,24 +39,74 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes
|
|||
end
|
||||
end
|
||||
|
||||
context 'when job has hooks' do
|
||||
context 'when job has hooks and default hooks' do
|
||||
let(:config) do
|
||||
<<-CI_CONFIG
|
||||
job:
|
||||
default:
|
||||
hooks:
|
||||
pre_get_sources_script: echo 'hello job pre_get_sources_script'
|
||||
script: echo 'hello job script'
|
||||
pre_get_sources_script:
|
||||
- echo 'hello default pre_get_sources_script'
|
||||
|
||||
job1:
|
||||
hooks:
|
||||
pre_get_sources_script:
|
||||
- echo 'hello job1 pre_get_sources_script'
|
||||
script: echo 'hello job1 script'
|
||||
|
||||
job2:
|
||||
script: echo 'hello job2 script'
|
||||
|
||||
job3:
|
||||
inherit:
|
||||
default: false
|
||||
script: echo 'hello job3 script'
|
||||
CI_CONFIG
|
||||
end
|
||||
|
||||
it 'creates a job with script data' do
|
||||
it 'creates jobs with hook data' do
|
||||
expect(pipeline).to be_created_successfully
|
||||
expect(pipeline.builds.first).to have_attributes(
|
||||
name: 'job',
|
||||
expect(pipeline.builds.find_by(name: 'job1')).to have_attributes(
|
||||
name: 'job1',
|
||||
stage: 'test',
|
||||
options: { script: ["echo 'hello job script'"],
|
||||
hooks: { pre_get_sources_script: ["echo 'hello job pre_get_sources_script'"] } }
|
||||
options: { script: ["echo 'hello job1 script'"],
|
||||
hooks: { pre_get_sources_script: ["echo 'hello job1 pre_get_sources_script'"] } }
|
||||
)
|
||||
expect(pipeline.builds.find_by(name: 'job2')).to have_attributes(
|
||||
name: 'job2',
|
||||
stage: 'test',
|
||||
options: { script: ["echo 'hello job2 script'"],
|
||||
hooks: { pre_get_sources_script: ["echo 'hello default pre_get_sources_script'"] } }
|
||||
)
|
||||
expect(pipeline.builds.find_by(name: 'job3')).to have_attributes(
|
||||
name: 'job3',
|
||||
stage: 'test',
|
||||
options: { script: ["echo 'hello job3 script'"] }
|
||||
)
|
||||
end
|
||||
|
||||
context 'when the FF ci_hooks_pre_get_sources_script is disabled' do
|
||||
before do
|
||||
stub_feature_flags(ci_hooks_pre_get_sources_script: false)
|
||||
end
|
||||
|
||||
it 'creates jobs without hook data' do
|
||||
expect(pipeline).to be_created_successfully
|
||||
expect(pipeline.builds.find_by(name: 'job1')).to have_attributes(
|
||||
name: 'job1',
|
||||
stage: 'test',
|
||||
options: { script: ["echo 'hello job1 script'"] }
|
||||
)
|
||||
expect(pipeline.builds.find_by(name: 'job2')).to have_attributes(
|
||||
name: 'job2',
|
||||
stage: 'test',
|
||||
options: { script: ["echo 'hello job2 script'"] }
|
||||
)
|
||||
expect(pipeline.builds.find_by(name: 'job3')).to have_attributes(
|
||||
name: 'job3',
|
||||
stage: 'test',
|
||||
options: { script: ["echo 'hello job3 script'"] }
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,53 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.shared_examples 'work item hierarchy restrictions importer' do
|
||||
shared_examples_for 'adds restrictions' do
|
||||
it "adds all restrictions if they don't exist" do
|
||||
expect { subject }.to change { WorkItems::HierarchyRestriction.count }.from(0).to(4)
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'adds restrictions'
|
||||
|
||||
context 'when base types are missing' do
|
||||
before do
|
||||
WorkItems::Type.delete_all
|
||||
end
|
||||
|
||||
it_behaves_like 'adds restrictions'
|
||||
end
|
||||
|
||||
context 'when restrictions already exist' do
|
||||
before do
|
||||
Gitlab::DatabaseImporters::WorkItems::HierarchyRestrictionsImporter.upsert_restrictions
|
||||
end
|
||||
|
||||
it 'upserts restrictions' do
|
||||
restriction = WorkItems::HierarchyRestriction.first
|
||||
depth = restriction.maximum_depth
|
||||
|
||||
restriction.update!(maximum_depth: depth + 1)
|
||||
|
||||
expect do
|
||||
subject
|
||||
restriction.reload
|
||||
end.to not_change { WorkItems::HierarchyRestriction.count }.and(
|
||||
change { restriction.maximum_depth }.from(depth + 1).to(depth)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when some restrictions are missing' do
|
||||
before do
|
||||
Gitlab::DatabaseImporters::WorkItems::HierarchyRestrictionsImporter.upsert_restrictions
|
||||
WorkItems::HierarchyRestriction.limit(1).delete_all
|
||||
end
|
||||
|
||||
it 'inserts missing restrictions and does nothing if some already existed' do
|
||||
expect { subject }.to make_queries_matching(/INSERT/, 1).and(
|
||||
change { WorkItems::HierarchyRestriction.count }.by(1)
|
||||
)
|
||||
expect(WorkItems::HierarchyRestriction.count).to eq(4)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -60,6 +60,7 @@
|
|||
- 'config/audit_events/'
|
||||
- 'runner_token_expiration/'
|
||||
- '*metadata_id_tokens*'
|
||||
- '/app/assets/javascripts/invite_members/'
|
||||
patterns:
|
||||
- '%{keyword}'
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue