Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-11-28 18:09:29 +00:00
parent 3a25b40d55
commit 953180403c
46 changed files with 616 additions and 84 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,5 @@
# frozen_string_literal: true
Gitlab::Seeder.quiet do
Gitlab::DatabaseImporters::WorkItems::HierarchyRestrictionsImporter.upsert_restrictions
end

View File

@ -0,0 +1,5 @@
# frozen_string_literal: true
Gitlab::Seeder.quiet do
Gitlab::DatabaseImporters::WorkItems::HierarchyRestrictionsImporter.upsert_restrictions
end

View File

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

View File

@ -0,0 +1 @@
a6caf06dd18f096219d5ce0752c956ef099a92df71899c1b9164d3a16f6ef0ba

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -60,6 +60,7 @@
- 'config/audit_events/'
- 'runner_token_expiration/'
- '*metadata_id_tokens*'
- '/app/assets/javascripts/invite_members/'
patterns:
- '%{keyword}'