Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-10-19 15:12:08 +00:00
parent 6b19945915
commit 59b0e2f45d
22 changed files with 289 additions and 35 deletions

View File

@ -7,7 +7,8 @@ class CustomerRelations::Contact < ApplicationRecord
belongs_to :group, -> { where(type: Group.sti_name) }, foreign_key: 'group_id'
belongs_to :organization, optional: true
has_and_belongs_to_many :issues, join_table: :issue_customer_relations_contacts # rubocop: disable Rails/HasAndBelongsToMany
has_many :issue_contacts, inverse_of: :contact
has_many :issues, through: :issue_contacts, inverse_of: :customer_relations_contacts
strip_attributes! :phone, :first_name, :last_name

View File

@ -0,0 +1,20 @@
# frozen_string_literal: true
class CustomerRelations::IssueContact < ApplicationRecord
self.table_name = "issue_customer_relations_contacts"
belongs_to :issue, optional: false, inverse_of: :customer_relations_contacts
belongs_to :contact, optional: false, inverse_of: :issue_contacts
validate :contact_belongs_to_issue_group
private
def contact_belongs_to_issue_group
return unless contact&.group_id
return unless issue&.project&.namespace_id
return if contact.group_id == issue.project.namespace_id
errors.add(:base, _('The contact does not belong to the same group as the issue.'))
end
end

View File

@ -81,7 +81,8 @@ class Issue < ApplicationRecord
has_and_belongs_to_many :self_managed_prometheus_alert_events, join_table: :issues_self_managed_prometheus_alert_events # rubocop: disable Rails/HasAndBelongsToMany
has_and_belongs_to_many :prometheus_alert_events, join_table: :issues_prometheus_alert_events # rubocop: disable Rails/HasAndBelongsToMany
has_many :prometheus_alerts, through: :prometheus_alert_events
has_and_belongs_to_many :customer_relations_contacts, join_table: :issue_customer_relations_contacts, class_name: 'CustomerRelations::Contact' # rubocop: disable Rails/HasAndBelongsToMany
has_many :issue_customer_relations_contacts, class_name: 'CustomerRelations::IssueContact', inverse_of: :issue
has_many :customer_relations_contacts, through: :issue_customer_relations_contacts, source: :contact, class_name: 'CustomerRelations::Contact', inverse_of: :issues
accepts_nested_attributes_for :issuable_severity, update_only: true
accepts_nested_attributes_for :sentry_issue

View File

@ -23,7 +23,7 @@
= html_escape(_('From %{code_open}%{source_title}%{code_close} into')) % { source_title: source_title, code_open: '<code>'.html_safe, code_close: '</code>'.html_safe }
- if issuable.new_record?
%code#js-target-branch-title= target_title
%code#js-target-branch-title{ data: { branch_name: @merge_request.target_branch } }= target_title
&nbsp;
= link_to _('Change branches'), mr_change_branches_path(issuable)
- elsif issuable.for_fork?

View File

@ -0,0 +1,59 @@
# frozen_string_literal: true
class FixDoubleEntriesInPostgresIndexView < Gitlab::Database::Migration[1.0]
def up
execute(<<~SQL)
DROP VIEW IF EXISTS postgres_indexes;
CREATE VIEW postgres_indexes AS
SELECT (pg_namespace.nspname::text || '.'::text) || i.relname::text AS identifier,
pg_index.indexrelid,
pg_namespace.nspname AS schema,
i.relname AS name,
pg_indexes.tablename,
a.amname AS type,
pg_index.indisunique AS "unique",
pg_index.indisvalid AS valid_index,
i.relispartition AS partitioned,
pg_index.indisexclusion AS exclusion,
pg_index.indexprs IS NOT NULL AS expression,
pg_index.indpred IS NOT NULL AS partial,
pg_indexes.indexdef AS definition,
pg_relation_size(i.oid::regclass) AS ondisk_size_bytes
FROM pg_index
JOIN pg_class i ON i.oid = pg_index.indexrelid
JOIN pg_namespace ON i.relnamespace = pg_namespace.oid
JOIN pg_indexes ON i.relname = pg_indexes.indexname AND pg_namespace.nspname = pg_indexes.schemaname
JOIN pg_am a ON i.relam = a.oid
WHERE pg_namespace.nspname <> 'pg_catalog'::name AND (pg_namespace.nspname = ANY (ARRAY["current_schema"(), 'gitlab_partitions_dynamic'::name, 'gitlab_partitions_static'::name]));
SQL
end
def down
execute(<<~SQL)
DROP VIEW IF EXISTS postgres_indexes;
CREATE VIEW postgres_indexes AS
SELECT (pg_namespace.nspname::text || '.'::text) || i.relname::text AS identifier,
pg_index.indexrelid,
pg_namespace.nspname AS schema,
i.relname AS name,
pg_indexes.tablename,
a.amname AS type,
pg_index.indisunique AS "unique",
pg_index.indisvalid AS valid_index,
i.relispartition AS partitioned,
pg_index.indisexclusion AS exclusion,
pg_index.indexprs IS NOT NULL AS expression,
pg_index.indpred IS NOT NULL AS partial,
pg_indexes.indexdef AS definition,
pg_relation_size(i.oid::regclass) AS ondisk_size_bytes
FROM pg_index
JOIN pg_class i ON i.oid = pg_index.indexrelid
JOIN pg_namespace ON i.relnamespace = pg_namespace.oid
JOIN pg_indexes ON i.relname = pg_indexes.indexname
JOIN pg_am a ON i.relam = a.oid
WHERE pg_namespace.nspname <> 'pg_catalog'::name AND (pg_namespace.nspname = ANY (ARRAY["current_schema"(), 'gitlab_partitions_dynamic'::name, 'gitlab_partitions_static'::name]));
SQL
end
end

View File

@ -0,0 +1 @@
168b383c4a85de35ade8a26e442ca49a40342ba05fb23fab4f0444814d976f65

View File

@ -17661,7 +17661,7 @@ CREATE VIEW postgres_indexes AS
FROM ((((pg_index
JOIN pg_class i ON ((i.oid = pg_index.indexrelid)))
JOIN pg_namespace ON ((i.relnamespace = pg_namespace.oid)))
JOIN pg_indexes ON ((i.relname = pg_indexes.indexname)))
JOIN pg_indexes ON (((i.relname = pg_indexes.indexname) AND (pg_namespace.nspname = pg_indexes.schemaname))))
JOIN pg_am a ON ((i.relam = a.oid)))
WHERE ((pg_namespace.nspname <> 'pg_catalog'::name) AND (pg_namespace.nspname = ANY (ARRAY["current_schema"(), 'gitlab_partitions_dynamic'::name, 'gitlab_partitions_static'::name])));

View File

@ -400,11 +400,12 @@ Retrieve the job that generated a job token.
GET /job
```
Examples
Examples (must run as part of the [`script`](../ci/yaml/index.md#script) section of a [CI/CD job](../ci/jobs/index.md)):
```shell
curl --header "JOB-TOKEN: <your_job_token>" "https://gitlab.example.com/api/v4/job"
curl "https://gitlab.example.com/api/v4/job?job_token=<your_job_token>"
curl --header "Authorization: Bearer $CI_JOB_TOKEN" "${CI_API_V4_URL}/job"
curl --header "JOB-TOKEN: $CI_JOB_TOKEN" "${CI_API_V4_URL}/job"
curl "${CI_API_V4_URL}/job?job_token=$CI_JOB_TOKEN"
```
Example of response

View File

@ -68,7 +68,7 @@ GitLab administrators can still update the default branch protection of a group.
## Define which roles can create projects
Instance-level protections for project creation define which roles can
[add projects to a group](../../group/index.md#specify-who-can-add-projects-to-a-group)]
[add projects to a group](../../group/index.md#specify-who-can-add-projects-to-a-group)
on the instance. To alter which roles have permission to create projects:
1. Sign in to GitLab as a user with [Administrator role](../../permissions.md).

View File

@ -784,6 +784,12 @@ module Gitlab
end
end
def list_refs
wrapped_gitaly_errors do
gitaly_ref_client.list_refs
end
end
# Refactoring aid; allows us to copy code from app/models/repository.rb
def commit(ref = 'HEAD')
Gitlab::Git::Commit.find(self, ref)

View File

@ -194,6 +194,16 @@ module Gitlab
raise ArgumentError, ex
end
def list_refs(patterns = [Gitlab::Git::BRANCH_REF_PREFIX])
request = Gitaly::ListRefsRequest.new(
repository: @gitaly_repo,
patterns: patterns
)
response = GitalyClient.call(@storage, :ref_service, :list_refs, request, timeout: GitalyClient.fast_timeout)
consume_list_refs_response(response)
end
def pack_refs
request = Gitaly::PackRefsRequest.new(repository: @gitaly_repo)
@ -206,6 +216,10 @@ module Gitlab
response.flat_map { |message| message.names.map { |name| yield(name) } }
end
def consume_list_refs_response(response)
response.flat_map(&:references)
end
def sort_local_branches_by_param(sort_by)
sort_by = 'name' if sort_by == 'name_asc'

View File

@ -30434,6 +30434,9 @@ msgstr ""
msgid "SecurityReports|Create issue"
msgstr ""
msgid "SecurityReports|Create policy"
msgstr ""
msgid "SecurityReports|Development vulnerabilities"
msgstr ""
@ -30491,6 +30494,15 @@ msgstr ""
msgid "SecurityReports|Manage and track vulnerabilities identified in projects within your group. Vulnerabilities in projects are shown here when security testing is configured."
msgstr ""
msgid "SecurityReports|Manage and track vulnerabilities identified in your Kubernetes clusters. Vulnerabilities appear here after you create a scan execution policy in any project in this group."
msgstr ""
msgid "SecurityReports|Manage and track vulnerabilities identified in your Kubernetes clusters. Vulnerabilities appear here after you create a scan execution policy in any project in this instance."
msgstr ""
msgid "SecurityReports|Manage and track vulnerabilities identified in your Kubernetes clusters. Vulnerabilities appear here after you create a scan execution policy in this project."
msgstr ""
msgid "SecurityReports|Manage and track vulnerabilities identified in your project. Vulnerabilities are shown here when security testing is configured."
msgstr ""
@ -30500,6 +30512,9 @@ msgstr ""
msgid "SecurityReports|Maximum selected projects limit reached"
msgstr ""
msgid "SecurityReports|Monitor vulnerabilities across clusters"
msgstr ""
msgid "SecurityReports|Monitor vulnerabilities in all of your projects"
msgstr ""
@ -33861,6 +33876,9 @@ msgstr ""
msgid "The connection will time out after %{timeout}. For repositories that take longer, use a clone/push combination."
msgstr ""
msgid "The contact does not belong to the same group as the issue."
msgstr ""
msgid "The content of this page is not encoded in UTF-8. Edits can only be made via the Git repository."
msgstr ""

View File

@ -0,0 +1,27 @@
# frozen_string_literal: true
FactoryBot.define do
factory :issue_customer_relations_contact, class: 'CustomerRelations::IssueContact' do
issue { association(:issue, project: project) }
contact { association(:contact, group: group) }
transient do
group { association(:group) }
project { association(:project, group: group) }
end
trait :for_contact do
issue { association(:issue, project: project) }
contact { raise ArgumentError, '`contact` is manadatory' }
transient do
project { association(:project, group: contact.group) }
end
end
trait :for_issue do
issue { raise ArgumentError, '`issue` is manadatory' }
contact { association(:contact, group: issue.project.group) }
end
end
end

View File

@ -22,6 +22,8 @@ RSpec.describe 'factories' do
[:debian_project_component_file, :object_storage],
[:debian_project_distribution, :object_storage],
[:debian_file_metadatum, :unknown],
[:issue_customer_relations_contact, :for_contact],
[:issue_customer_relations_contact, :for_issue],
[:package_file, :object_storage],
[:pages_domain, :without_certificate],
[:pages_domain, :without_key],
@ -72,6 +74,7 @@ RSpec.describe 'factories' do
fork_network_member
group_member
import_state
issue_customer_relations_contact
milestone_release
namespace
project_broken_repo

View File

@ -873,45 +873,65 @@ RSpec.describe Gitlab::Auth::AuthFinders do
end
describe '#find_user_from_job_token' do
let(:token) { job.token }
subject { find_user_from_job_token }
context 'when the token is in the headers' do
before do
set_header(described_class::JOB_TOKEN_HEADER, token)
shared_examples 'finds user when job token allowed' do
context 'when the token is in the headers' do
before do
set_header(described_class::JOB_TOKEN_HEADER, token)
end
it_behaves_like 'find user from job token'
end
it_behaves_like 'find user from job token'
end
context 'when the token is in the job_token param' do
before do
set_param(described_class::JOB_TOKEN_PARAM, token)
end
context 'when the token is in the job_token param' do
before do
set_param(described_class::JOB_TOKEN_PARAM, token)
it_behaves_like 'find user from job token'
end
it_behaves_like 'find user from job token'
end
context 'when the token is in the token param' do
before do
set_param(described_class::RUNNER_JOB_TOKEN_PARAM, token)
end
context 'when the token is in the token param' do
before do
set_param(described_class::RUNNER_JOB_TOKEN_PARAM, token)
it_behaves_like 'find user from job token'
end
it_behaves_like 'find user from job token'
end
context 'when the job token is provided via basic auth' do
context 'when route setting allows job_token' do
let(:route_authentication_setting) { { job_token_allowed: true } }
include_examples 'finds user when job token allowed'
end
context 'when route setting is basic auth' do
let(:route_authentication_setting) { { job_token_allowed: :basic_auth } }
let(:username) { ::Gitlab::Auth::CI_JOB_USER }
let(:token) { job.token }
before do
set_basic_auth_header(username, token)
context 'when the token is provided via basic auth' do
let(:username) { ::Gitlab::Auth::CI_JOB_USER }
before do
set_basic_auth_header(username, token)
end
it { is_expected.to eq(user) }
end
it { is_expected.to eq(user) }
include_examples 'finds user when job token allowed'
end
context 'credentials are provided but route setting is incorrect' do
let(:route_authentication_setting) { { job_token_allowed: :unknown } }
context 'when route setting job_token_allowed is invalid' do
let(:route_authentication_setting) { { job_token_allowed: false } }
context 'when the token is provided' do
before do
set_header(described_class::JOB_TOKEN_HEADER, token)
end
it { is_expected.to be_nil }
end

View File

@ -1888,6 +1888,18 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
end
end
describe '#list_refs' do
it 'returns a list of branches with their head commit' do
refs = repository.list_refs
reference = refs.first
expect(refs).to be_an(Enumerable)
expect(reference).to be_a(Gitaly::ListRefsResponse::Reference)
expect(reference.name).to be_a(String)
expect(reference.target).to be_a(String)
end
end
describe '#set_full_path' do
before do
repository_rugged.config["gitlab.fullpath"] = repository_path

View File

@ -252,6 +252,26 @@ RSpec.describe Gitlab::GitalyClient::RefService do
end
end
describe '#list_refs' do
it 'sends a list_refs message' do
expect_any_instance_of(Gitaly::RefService::Stub)
.to receive(:list_refs)
.with(gitaly_request_with_params(patterns: ['refs/heads/']), kind_of(Hash))
.and_call_original
client.list_refs
end
it 'accepts a patterns argument' do
expect_any_instance_of(Gitaly::RefService::Stub)
.to receive(:list_refs)
.with(gitaly_request_with_params(patterns: ['refs/tags/']), kind_of(Hash))
.and_call_original
client.list_refs([Gitlab::Git::TAG_REF_PREFIX])
end
end
describe '#pack_refs' do
it 'sends a pack_refs message' do
expect_any_instance_of(Gitaly::RefService::Stub)

View File

@ -60,6 +60,7 @@ issues:
- incident_management_issuable_escalation_status
- pending_escalations
- customer_relations_contacts
- issue_customer_relations_contacts
work_item_type:
- issues
events:

View File

@ -6,7 +6,8 @@ RSpec.describe CustomerRelations::Contact, type: :model do
describe 'associations' do
it { is_expected.to belong_to(:group) }
it { is_expected.to belong_to(:organization).optional }
it { is_expected.to have_and_belong_to_many(:issues) }
it { is_expected.to have_many(:issue_contacts) }
it { is_expected.to have_many(:issues) }
end
describe 'validations' do

View File

@ -0,0 +1,48 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe CustomerRelations::IssueContact do
let_it_be(:issue_contact, reload: true) { create(:issue_customer_relations_contact) }
subject { issue_contact }
it { expect(subject).to be_valid }
describe 'associations' do
it { is_expected.to belong_to(:issue).required }
it { is_expected.to belong_to(:contact).required }
end
describe 'factory' do
let(:built) { build(:issue_customer_relations_contact) }
let(:stubbed) { build_stubbed(:issue_customer_relations_contact) }
let(:created) { create(:issue_customer_relations_contact) }
let(:group) { build(:group) }
let(:project) { build(:project, group: group) }
let(:issue) { build(:issue, project: project) }
let(:contact) { build(:contact, group: group) }
let(:for_issue) { build(:issue_customer_relations_contact, :for_issue, issue: issue) }
let(:for_contact) { build(:issue_customer_relations_contact, :for_contact, contact: contact) }
it 'uses objects from the same group', :aggregate_failures do
expect(stubbed.contact.group).to eq(stubbed.issue.project.group)
expect(built.contact.group).to eq(built.issue.project.group)
expect(created.contact.group).to eq(created.issue.project.group)
end
it 'builds using the same group', :aggregate_failures do
expect(for_issue.contact.group).to eq(group)
expect(for_contact.issue.project.group).to eq(group)
end
end
describe 'validation' do
let(:built) { build(:issue_customer_relations_contact, issue: create(:issue), contact: create(:contact)) }
it 'fails when the contact group does not match the issue group' do
expect(built).not_to be_valid
end
end
end

View File

@ -34,7 +34,8 @@ RSpec.describe Issue do
it { is_expected.to have_many(:issue_email_participants) }
it { is_expected.to have_many(:timelogs).autosave(true) }
it { is_expected.to have_one(:incident_management_issuable_escalation_status) }
it { is_expected.to have_and_belong_to_many(:customer_relations_contacts) }
it { is_expected.to have_many(:issue_customer_relations_contacts) }
it { is_expected.to have_many(:customer_relations_contacts) }
describe 'versions.most_recent' do
it 'returns the most recent version' do

View File

@ -429,11 +429,11 @@ RSpec.describe 'getting an issue list for a project' do
end
it 'avoids N+1 queries' do
create(:contact, group_id: group.id, issues: [issue_a])
create(:issue_customer_relations_contact, :for_issue, issue: issue_a)
control = ActiveRecord::QueryRecorder.new(skip_cached: false) { clean_state_query }
create(:contact, group_id: group.id, issues: [issue_a])
create(:issue_customer_relations_contact, :for_issue, issue: issue_a)
expect { clean_state_query }.not_to exceed_all_query_limit(control)
end