Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab/CODEOWNERS1
-rw-r--r--.gitlab/ci/rules.gitlab-ci.yml3
-rw-r--r--app/assets/javascripts/issues/list/constants.js2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue3
-rw-r--r--app/assets/javascripts/work_items/constants.js2
-rw-r--r--app/controllers/concerns/lfs_request.rb7
-rw-r--r--app/finders/users_finder.rb6
-rw-r--r--app/helpers/search_helper.rb20
-rw-r--r--app/models/work_items/type.rb27
-rw-r--r--db/fixtures/development/50_create_work_item_hierarchy_restrictions.rb5
-rw-r--r--db/fixtures/production/020_create_work_item_hierarchy_restrictions.rb5
-rw-r--r--db/post_migrate/20221116143854_add_okr_hierarchy_restrictions.rb48
-rw-r--r--db/schema_migrations/202211161438541
-rw-r--r--doc/administration/feature_flags.md3
-rw-r--r--doc/api/graphql/reference/index.md2
-rw-r--r--doc/api/merge_request_approvals.md2
-rw-r--r--doc/api/packages/pypi.md6
-rw-r--r--doc/user/search/index.md1
-rw-r--r--lib/gitlab/ci/config/entry/default.rb26
-rw-r--r--lib/gitlab/ci/config/entry/hooks.rb2
-rw-r--r--lib/gitlab/ci/config/entry/job.rb2
-rw-r--r--lib/gitlab/ci/pipeline/logger.rb8
-rw-r--r--lib/gitlab/database_importers/work_items/hierarchy_restrictions_importer.rb37
-rw-r--r--lib/gitlab/slash_commands/application_help.rb11
-rw-r--r--lib/gitlab/slash_commands/command.rb6
-rw-r--r--scripts/api/create_issue_discussion.rb32
-rwxr-xr-xscripts/create-pipeline-failure-incident.rb20
-rwxr-xr-xscripts/review_apps/k8s-resources-count-checks.sh3
-rwxr-xr-xscripts/used-feature-flags3
-rw-r--r--spec/controllers/search_controller_spec.rb6
-rw-r--r--spec/db/development/create_work_item_hierarchy_restrictions_spec.rb9
-rw-r--r--spec/db/production/create_work_item_hierarchy_restrictions_spec.rb9
-rw-r--r--spec/finders/users_finder_spec.rb52
-rw-r--r--spec/helpers/search_helper_spec.rb28
-rw-r--r--spec/lib/gitlab/ci/config/entry/bridge_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/config/entry/default_spec.rb5
-rw-r--r--spec/lib/gitlab/ci/pipeline/logger_spec.rb15
-rw-r--r--spec/lib/gitlab/ci/yaml_processor_spec.rb31
-rw-r--r--spec/lib/gitlab/database_importers/work_items/hierarchy_restrictions_importer_spec.rb10
-rw-r--r--spec/lib/gitlab/slash_commands/application_help_spec.rb4
-rw-r--r--spec/migrations/add_okr_hierarchy_restrictions_spec.rb35
-rw-r--r--spec/models/work_items/type_spec.rb26
-rw-r--r--spec/requests/lfs_http_spec.rb52
-rw-r--r--spec/services/ci/create_pipeline_service/scripts_spec.rb68
-rw-r--r--spec/support/shared_examples/work_item_hierarchy_restrictions_importer.rb53
-rw-r--r--tooling/config/CODEOWNERS.yml1
46 files changed, 616 insertions, 84 deletions
diff --git a/.gitlab/CODEOWNERS b/.gitlab/CODEOWNERS
index 8587312bb08..3199739a33f 100644
--- a/.gitlab/CODEOWNERS
+++ b/.gitlab/CODEOWNERS
@@ -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
diff --git a/.gitlab/ci/rules.gitlab-ci.yml b/.gitlab/ci/rules.gitlab-ci.yml
index 830fbf64d38..c40f5744686 100644
--- a/.gitlab/ci/rules.gitlab-ci.yml
+++ b/.gitlab/ci/rules.gitlab-ci.yml
@@ -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
diff --git a/app/assets/javascripts/issues/list/constants.js b/app/assets/javascripts/issues/list/constants.js
index fd7d53e7a36..16d28e8404c 100644
--- a/app/assets/javascripts/issues/list/constants.js
+++ b/app/assets/javascripts/issues/list/constants.js
@@ -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',
};
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue
index a15bf6fadd8..6b9a8f59aa8 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue
@@ -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">
diff --git a/app/assets/javascripts/work_items/constants.js b/app/assets/javascripts/work_items/constants.js
index cdb69586969..aef488739ee 100644
--- a/app/assets/javascripts/work_items/constants.js
+++ b/app/assets/javascripts/work_items/constants.js
@@ -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]: {
diff --git a/app/controllers/concerns/lfs_request.rb b/app/controllers/concerns/lfs_request.rb
index 8acbba0621b..2ebc1d67ec7 100644
--- a/app/controllers/concerns/lfs_request.rb
+++ b/app/controllers/concerns/lfs_request.rb
@@ -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
diff --git a/app/finders/users_finder.rb b/app/finders/users_finder.rb
index 9c2462b42a6..11e3c341c1f 100644
--- a/app/finders/users_finder.rb
+++ b/app/finders/users_finder.rb
@@ -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
diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb
index b8ac2afa7d6..54700e634b6 100644
--- a/app/helpers/search_helper.rb
+++ b/app/helpers/search_helper.rb
@@ -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
diff --git a/app/models/work_items/type.rb b/app/models/work_items/type.rb
index dc30899d24f..51ec5676ae6 100644
--- a/app/models/work_items/type.rb
+++ b/app/models/work_items/type.rb
@@ -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
diff --git a/db/fixtures/development/50_create_work_item_hierarchy_restrictions.rb b/db/fixtures/development/50_create_work_item_hierarchy_restrictions.rb
new file mode 100644
index 00000000000..b5c5d0cacdd
--- /dev/null
+++ b/db/fixtures/development/50_create_work_item_hierarchy_restrictions.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+Gitlab::Seeder.quiet do
+ Gitlab::DatabaseImporters::WorkItems::HierarchyRestrictionsImporter.upsert_restrictions
+end
diff --git a/db/fixtures/production/020_create_work_item_hierarchy_restrictions.rb b/db/fixtures/production/020_create_work_item_hierarchy_restrictions.rb
new file mode 100644
index 00000000000..b5c5d0cacdd
--- /dev/null
+++ b/db/fixtures/production/020_create_work_item_hierarchy_restrictions.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+Gitlab::Seeder.quiet do
+ Gitlab::DatabaseImporters::WorkItems::HierarchyRestrictionsImporter.upsert_restrictions
+end
diff --git a/db/post_migrate/20221116143854_add_okr_hierarchy_restrictions.rb b/db/post_migrate/20221116143854_add_okr_hierarchy_restrictions.rb
new file mode 100644
index 00000000000..658ce0287f8
--- /dev/null
+++ b/db/post_migrate/20221116143854_add_okr_hierarchy_restrictions.rb
@@ -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
diff --git a/db/schema_migrations/20221116143854 b/db/schema_migrations/20221116143854
new file mode 100644
index 00000000000..9f0b0815c79
--- /dev/null
+++ b/db/schema_migrations/20221116143854
@@ -0,0 +1 @@
+a6caf06dd18f096219d5ce0752c956ef099a92df71899c1b9164d3a16f6ef0ba \ No newline at end of file
diff --git a/doc/administration/feature_flags.md b/doc/administration/feature_flags.md
index f2a40b60536..f7237b167e5 100644
--- a/doc/administration/feature_flags.md
+++ b/doc/administration/feature_flags.md
@@ -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)
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index e1fe6a0c1bf..1b606cc50bf 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -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. |
diff --git a/doc/api/merge_request_approvals.md b/doc/api/merge_request_approvals.md
index 0476035784a..4d52bf36f20 100644
--- a/doc/api/merge_request_approvals.md
+++ b/doc/api/merge_request_approvals.md
@@ -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
diff --git a/doc/api/packages/pypi.md b/doc/api/packages/pypi.md
index 4e4c060d99b..e9546f50e36 100644
--- a/doc/api/packages/pypi.md
+++ b/doc/api/packages/pypi.md
@@ -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
diff --git a/doc/user/search/index.md b/doc/user/search/index.md
index 9bff2a91ec8..e27ce075e0a 100644
--- a/doc/user/search/index.md
+++ b/doc/user/search/index.md
@@ -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**)
diff --git a/lib/gitlab/ci/config/entry/default.rb b/lib/gitlab/ci/config/entry/default.rb
index 12d68b755b3..e996b6b1312 100644
--- a/lib/gitlab/ci/config/entry/default.rb
+++ b/lib/gitlab/ci/config/entry/default.rb
@@ -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,22 +24,27 @@ module Gitlab
description: 'Script that will be executed before each job.',
inherit: true
- entry :image, Entry::Image,
- description: 'Docker image that will be used to execute jobs.',
- inherit: true
-
- entry :services, Entry::Services,
- 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 :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
+
+ entry :services, Entry::Services,
+ description: 'Docker images that will be linked to the container.',
+ inherit: true
+
entry :interruptible, ::Gitlab::Config::Entry::Boolean,
description: 'Set jobs interruptible default value.',
inherit: false
diff --git a/lib/gitlab/ci/config/entry/hooks.rb b/lib/gitlab/ci/config/entry/hooks.rb
index d979dd497b2..28bc2e4e7ce 100644
--- a/lib/gitlab/ci/config/entry/hooks.rb
+++ b/lib/gitlab/ci/config/entry/hooks.rb
@@ -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
diff --git a/lib/gitlab/ci/config/entry/job.rb b/lib/gitlab/ci/config/entry/job.rb
index 29335c4679c..7c49b59a7f0 100644
--- a/lib/gitlab/ci/config/entry/job.rb
+++ b/lib/gitlab/ci/config/entry/job.rb
@@ -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.',
diff --git a/lib/gitlab/ci/pipeline/logger.rb b/lib/gitlab/ci/pipeline/logger.rb
index 9659cec4889..08e731148c6 100644
--- a/lib/gitlab/ci/pipeline/logger.rb
+++ b/lib/gitlab/ci/pipeline/logger.rb
@@ -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)
diff --git a/lib/gitlab/database_importers/work_items/hierarchy_restrictions_importer.rb b/lib/gitlab/database_importers/work_items/hierarchy_restrictions_importer.rb
new file mode 100644
index 00000000000..1181c259a5c
--- /dev/null
+++ b/lib/gitlab/database_importers/work_items/hierarchy_restrictions_importer.rb
@@ -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
diff --git a/lib/gitlab/slash_commands/application_help.rb b/lib/gitlab/slash_commands/application_help.rb
index bfdb65a816d..94abc8b4508 100644
--- a/lib/gitlab/slash_commands/application_help.rb
+++ b/lib/gitlab/slash_commands/application_help.rb
@@ -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
diff --git a/lib/gitlab/slash_commands/command.rb b/lib/gitlab/slash_commands/command.rb
index 265eda46489..f8b55f1a91d 100644
--- a/lib/gitlab/slash_commands/command.rb
+++ b/lib/gitlab/slash_commands/command.rb
@@ -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
diff --git a/scripts/api/create_issue_discussion.rb b/scripts/api/create_issue_discussion.rb
new file mode 100644
index 00000000000..74a9f3ae378
--- /dev/null
+++ b/scripts/api/create_issue_discussion.rb
@@ -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
diff --git a/scripts/create-pipeline-failure-incident.rb b/scripts/create-pipeline-failure-incident.rb
index c38f80699e6..bbabfb7dfce 100755
--- a/scripts/create-pipeline-failure-incident.rb
+++ b/scripts/create-pipeline-failure-incident.rb
@@ -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
diff --git a/scripts/review_apps/k8s-resources-count-checks.sh b/scripts/review_apps/k8s-resources-count-checks.sh
index ae4c8e163e5..b63fa043065 100755
--- a/scripts/review_apps/k8s-resources-count-checks.sh
+++ b/scripts/review_apps/k8s-resources-count-checks.sh
@@ -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
diff --git a/scripts/used-feature-flags b/scripts/used-feature-flags
index eb7e85be229..74180d02a91 100755
--- a/scripts/used-feature-flags
+++ b/scripts/used-feature-flags
@@ -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
diff --git a/spec/controllers/search_controller_spec.rb b/spec/controllers/search_controller_spec.rb
index 21df53fb074..37fc5a033ba 100644
--- a/spec/controllers/search_controller_spec.rb
+++ b/spec/controllers/search_controller_spec.rb
@@ -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
diff --git a/spec/db/development/create_work_item_hierarchy_restrictions_spec.rb b/spec/db/development/create_work_item_hierarchy_restrictions_spec.rb
new file mode 100644
index 00000000000..0e60ecd08c0
--- /dev/null
+++ b/spec/db/development/create_work_item_hierarchy_restrictions_spec.rb
@@ -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
diff --git a/spec/db/production/create_work_item_hierarchy_restrictions_spec.rb b/spec/db/production/create_work_item_hierarchy_restrictions_spec.rb
new file mode 100644
index 00000000000..5b47d88d71a
--- /dev/null
+++ b/spec/db/production/create_work_item_hierarchy_restrictions_spec.rb
@@ -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
diff --git a/spec/finders/users_finder_spec.rb b/spec/finders/users_finder_spec.rb
index 271dce44db7..5cf845a87b2 100644
--- a/spec/finders/users_finder_spec.rb
+++ b/spec/finders/users_finder_spec.rb
@@ -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
diff --git a/spec/helpers/search_helper_spec.rb b/spec/helpers/search_helper_spec.rb
index 192dfaa9caf..74a59aa37ce 100644
--- a/spec/helpers/search_helper_spec.rb
+++ b/spec/helpers/search_helper_spec.rb
@@ -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
diff --git a/spec/lib/gitlab/ci/config/entry/bridge_spec.rb b/spec/lib/gitlab/ci/config/entry/bridge_spec.rb
index 8da46561b73..736c184a289 100644
--- a/spec/lib/gitlab/ci/config/entry/bridge_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/bridge_spec.rb
@@ -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
diff --git a/spec/lib/gitlab/ci/config/entry/default_spec.rb b/spec/lib/gitlab/ci/config/entry/default_spec.rb
index 5613b0f09d1..46e96843ee3 100644
--- a/spec/lib/gitlab/ci/config/entry/default_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/default_spec.rb
@@ -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
diff --git a/spec/lib/gitlab/ci/pipeline/logger_spec.rb b/spec/lib/gitlab/ci/pipeline/logger_spec.rb
index 4492ca716ae..60cc3486414 100644
--- a/spec/lib/gitlab/ci/pipeline/logger_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/logger_spec.rb
@@ -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
diff --git a/spec/lib/gitlab/ci/yaml_processor_spec.rb b/spec/lib/gitlab/ci/yaml_processor_spec.rb
index fee3731c662..0d1deb863b1 100644
--- a/spec/lib/gitlab/ci/yaml_processor_spec.rb
+++ b/spec/lib/gitlab/ci/yaml_processor_spec.rb
@@ -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
diff --git a/spec/lib/gitlab/database_importers/work_items/hierarchy_restrictions_importer_spec.rb b/spec/lib/gitlab/database_importers/work_items/hierarchy_restrictions_importer_spec.rb
new file mode 100644
index 00000000000..d8173794b3f
--- /dev/null
+++ b/spec/lib/gitlab/database_importers/work_items/hierarchy_restrictions_importer_spec.rb
@@ -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
diff --git a/spec/lib/gitlab/slash_commands/application_help_spec.rb b/spec/lib/gitlab/slash_commands/application_help_spec.rb
index b182c0e5cc6..d0cefdf4895 100644
--- a/spec/lib/gitlab/slash_commands/application_help_spec.rb
+++ b/spec/lib/gitlab/slash_commands/application_help_spec.rb
@@ -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
diff --git a/spec/migrations/add_okr_hierarchy_restrictions_spec.rb b/spec/migrations/add_okr_hierarchy_restrictions_spec.rb
new file mode 100644
index 00000000000..9923795925d
--- /dev/null
+++ b/spec/migrations/add_okr_hierarchy_restrictions_spec.rb
@@ -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
diff --git a/spec/models/work_items/type_spec.rb b/spec/models/work_items/type_spec.rb
index 6685720778a..1d8c5e79bf2 100644
--- a/spec/models/work_items/type_spec.rb
+++ b/spec/models/work_items/type_spec.rb
@@ -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? }
diff --git a/spec/requests/lfs_http_spec.rb b/spec/requests/lfs_http_spec.rb
index c072e6d48db..a3fa85dee73 100644
--- a/spec/requests/lfs_http_spec.rb
+++ b/spec/requests/lfs_http_spec.rb
@@ -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
diff --git a/spec/services/ci/create_pipeline_service/scripts_spec.rb b/spec/services/ci/create_pipeline_service/scripts_spec.rb
index 493e341395b..50b558e505a 100644
--- a/spec/services/ci/create_pipeline_service/scripts_spec.rb
+++ b/spec/services/ci/create_pipeline_service/scripts_spec.rb
@@ -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
diff --git a/spec/support/shared_examples/work_item_hierarchy_restrictions_importer.rb b/spec/support/shared_examples/work_item_hierarchy_restrictions_importer.rb
new file mode 100644
index 00000000000..a1bccb7b7a3
--- /dev/null
+++ b/spec/support/shared_examples/work_item_hierarchy_restrictions_importer.rb
@@ -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
diff --git a/tooling/config/CODEOWNERS.yml b/tooling/config/CODEOWNERS.yml
index d729ae5d532..07fddde056c 100644
--- a/tooling/config/CODEOWNERS.yml
+++ b/tooling/config/CODEOWNERS.yml
@@ -60,6 +60,7 @@
- 'config/audit_events/'
- 'runner_token_expiration/'
- '*metadata_id_tokens*'
+ - '/app/assets/javascripts/invite_members/'
patterns:
- '%{keyword}'