Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-03-31 18:09:19 +00:00
parent 5facc34f44
commit 676109e1b3
75 changed files with 564 additions and 162 deletions

View File

@ -234,7 +234,15 @@ danger-review:
- run_timed_command "bundle install --jobs=$(nproc) --path=vendor --retry=3 --quiet --with danger"
- run_timed_command "retry yarn install --frozen-lockfile"
script:
- run_timed_command "bundle exec danger --fail-on-errors=true --verbose"
- >
if [ -z "$DANGER_GITLAB_API_TOKEN" ]; then
# Force danger to skip CI source GitLab and fallback to "local only git repo".
unset GITLAB_CI
# We need to base SHA to help danger determine the base commit for this shallow clone.
run_timed_command "bundle exec danger dry_run --fail-on-errors=true --verbose --base='$CI_MERGE_REQUEST_DIFF_BASE_SHA'"
else
run_timed_command "bundle exec danger --fail-on-errors=true --verbose"
fi
update-danger-review-cache:
extends:

View File

@ -1110,7 +1110,7 @@
.review:rules:danger:
rules:
- if: '$DANGER_GITLAB_API_TOKEN && $CI_MERGE_REQUEST_IID'
- if: '$CI_MERGE_REQUEST_IID'
###############
# Setup rules #

View File

@ -927,13 +927,6 @@ Style/RedundantRegexpEscape:
Style/RedundantSelf:
Enabled: false
# Offense count: 2
# Cop supports --auto-correct.
Style/RedundantSelfAssignment:
Exclude:
- 'app/models/concerns/issuable.rb'
- 'spec/db/schema_spec.rb'
# Offense count: 213
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, AllowInnerSlashes.

View File

@ -2,6 +2,28 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
## 13.10.1 (2021-03-31)
### Security (6 changes)
- Leave pool repository on fork unlinking.
- Fixed XSS in merge requests sidebar.
- Fix arbitrary read/write in AsciiDoctor and Kroki gems.
- Prevent infinite loop when checking if collaboration is allowed.
- Disable arbitrary URI and file reads in JSON validator.
- Require POST request to trigger system hooks.
### Removed (1 change)
- Make HipChat project service do nothing. !57434
### Other (3 changes)
- Remove direct mimemagic dependency. !57387
- Refactor MimeMagic calls to new MimeType class. !57421
- Switch to using a fake mimemagic gem. !57443
## 13.10.0 (2021-03-22)
### Security (3 changes)
@ -529,6 +551,28 @@ entry.
- Convert mattermost alert to pajamas. !56556
## 13.9.5 (2021-03-31)
### Security (6 changes)
- Leave pool repository on fork unlinking.
- Fixed XSS in merge requests sidebar.
- Fix arbitrary read/write in AsciiDoctor and Kroki gems.
- Prevent infinite loop when checking if collaboration is allowed.
- Disable arbitrary URI and file reads in JSON validator.
- Require POST request to trigger system hooks.
### Removed (1 change)
- Make HipChat project service do nothing. !57434
### Other (3 changes)
- Remove direct mimemagic dependency. !57387
- Refactor MimeMagic calls to new MimeType class. !57421
- Switch to using a fake mimemagic gem. !57443
## 13.9.4 (2021-03-17)
### Security (1 change)
@ -1144,6 +1188,27 @@ entry.
- Apply new GitLab UI for buttons in pipeline schedules.
## 13.8.7 (2021-03-31)
### Security (5 changes)
- Fixed XSS in merge requests sidebar.
- Leave pool repository on fork unlinking.
- Fix arbitrary read/write in AsciiDoctor and Kroki gems.
- Prevent infinite loop when checking if collaboration is allowed.
- Require POST request to trigger system hooks.
### Removed (1 change)
- Make HipChat project service do nothing. !57434
### Other (3 changes)
- Remove direct mimemagic dependency. !57387
- Refactor MimeMagic calls to new MimeType class. !57421
- Switch to using a fake mimemagic gem. !57443
## 13.8.6 (2021-03-17)
### Security (1 change)

View File

@ -274,7 +274,7 @@ gem 'licensee', '~> 9.14.1'
gem 'charlock_holmes', '~> 0.7.7'
# Detect mime content type from content
gem 'ruby-magic-static', '~> 0.3.4'
gem 'ruby-magic-static', '~> 0.3.5'
# Fake version of the gem to trick bundler
gem 'mimemagic', '0.3.7', path: 'vendor/shims/mimemagic', require: false

View File

@ -1113,7 +1113,8 @@ GEM
i18n
ruby-fogbugz (0.2.1)
crack (~> 0.4)
ruby-magic-static (0.3.4)
ruby-magic-static (0.3.5)
mini_portile2 (~> 2.5.0)
ruby-prof (1.3.1)
ruby-progressbar (1.11.0)
ruby-saml (1.7.2)
@ -1559,7 +1560,7 @@ DEPENDENCIES
rspec_junit_formatter
rspec_profiling (~> 0.0.6)
ruby-fogbugz (~> 0.2.1)
ruby-magic-static (~> 0.3.4)
ruby-magic-static (~> 0.3.5)
ruby-prof (~> 1.3.0)
ruby-progressbar (~> 1.10)
ruby_parser (~> 3.15)

View File

@ -119,8 +119,8 @@ export default {
<gl-button @click="onCancel">{{ s__('Cancel') }}</gl-button>
<gl-button
:disabled="!canSubmit"
category="primary"
variant="warning"
category="secondary"
variant="danger"
@click="onSecondaryAction"
>
{{ secondaryAction }}

View File

@ -55,6 +55,7 @@ class CommitStatus < ApplicationRecord
scope :for_ref, -> (ref) { where(ref: ref) }
scope :by_name, -> (name) { where(name: name) }
scope :in_pipelines, ->(pipelines) { where(pipeline: pipelines) }
scope :eager_load_pipeline, -> { eager_load(:pipeline, project: { namespace: :route }) }
scope :for_project_paths, -> (paths) do
where(project: Project.where_full_path_in(Array(paths)))

View File

@ -324,7 +324,7 @@ module Issuable
# This prevents errors when ignored columns are present in the database.
issuable_columns = with_cte ? issue_grouping_columns(use_cte: with_cte) : "#{table_name}.*"
extra_select_columns = extra_select_columns.unshift("(#{highest_priority}) AS highest_priority")
extra_select_columns.unshift("(#{highest_priority}) AS highest_priority")
select(issuable_columns)
.select(extra_select_columns)

View File

@ -7,7 +7,7 @@ class GroupMember < Member
SOURCE_TYPE = 'Namespace'
belongs_to :group, foreign_key: 'source_id'
alias_attribute :namespace_id, :source_id
delegate :update_two_factor_requirement, to: :user
# Make sure group member points only to group as it source

View File

@ -5,6 +5,8 @@ class ProjectMember < Member
belongs_to :project, foreign_key: 'source_id'
delegate :namespace_id, to: :project
# Make sure project member points only to project as it source
default_value_for :source_type, SOURCE_TYPE
validates :source_type, format: { with: /\AProject\z/ }

View File

@ -1350,8 +1350,8 @@ class MergeRequest < ApplicationRecord
has_no_commits? || branch_missing? || cannot_be_merged?
end
def can_be_merged_by?(user)
access = ::Gitlab::UserAccess.new(user, container: project)
def can_be_merged_by?(user, skip_collaboration_check: false)
access = ::Gitlab::UserAccess.new(user, container: project, skip_collaboration_check: skip_collaboration_check)
access.can_update_branch?(target_branch)
end

View File

@ -2711,7 +2711,7 @@ class Project < ApplicationRecord
# Issue for N+1: https://gitlab.com/gitlab-org/gitlab-foss/issues/49322
Gitlab::GitalyClient.allow_n_plus_1_calls do
merge_requests_allowing_collaboration(branch_name).any? do |merge_request|
merge_request.can_be_merged_by?(user)
merge_request.can_be_merged_by?(user, skip_collaboration_check: true)
end
end
end

View File

@ -93,10 +93,6 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
filename_path(repository.license_blob&.name)
end
def ci_configuration_path
filename_path(repository.gitlab_ci_yml&.name)
end
def contribution_guide_path
if project && contribution_guide = repository.contribution_guide
project_blob_path(
@ -131,10 +127,6 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
ide_edit_path(project, default_branch_or_master, 'CONTRIBUTING.md')
end
def add_ci_yml_path
add_special_file_path(file_name: ci_config_path_or_default)
end
def add_readme_path
add_special_file_path(file_name: 'README.md')
end
@ -384,11 +376,11 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
if cicd_missing?
AnchorData.new(false,
statistic_icon + _('Set up CI/CD'),
add_ci_yml_path)
project_ci_pipeline_editor_path(project))
elsif repository.gitlab_ci_yml.present?
AnchorData.new(false,
statistic_icon('doc-text') + _('CI/CD configuration'),
ci_configuration_path,
project_ci_pipeline_editor_path(project),
'btn-default')
end
end

View File

@ -10,13 +10,14 @@ module Members
@errors = {}
@emails = params[:email]&.split(',')&.uniq&.flatten
@source = params[:source]
end
def execute(source)
@source = source
def execute
validate_emails!
emails.each(&method(:process_email))
enqueue_onboarding_progress_action
result
rescue BlankEmailsError, TooManyEmailsError => e
error(e.message)
@ -24,7 +25,7 @@ module Members
private
attr_reader :source, :errors, :emails
attr_reader :source, :errors, :emails, :member_created_namespace_id
def validate_emails!
raise BlankEmailsError, s_('AddMember|Email cannot be blank') if emails.blank?
@ -88,6 +89,7 @@ module Members
errors[email] = new_member.errors.full_messages.to_sentence
else
after_execute(member: new_member)
@member_created_namespace_id ||= new_member.namespace_id
end
end
@ -98,6 +100,12 @@ module Members
success
end
end
def enqueue_onboarding_progress_action
return unless member_created_namespace_id
Namespaces::OnboardingUserAddedWorker.perform_async(member_created_namespace_id)
end
end
end

View File

@ -32,6 +32,8 @@ module Projects
if fork_network = @project.root_of_fork_network
fork_network.update(root_project: nil, deleted_root_project_name: @project.full_name)
end
@project.leave_pool_repository
end
# rubocop: disable Cop/InBatches

View File

@ -12,7 +12,7 @@
= s_('Jobs|Use jobs to automate your tasks')
%p
= s_('Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project.')
= link_to s_('Jobs|Create CI/CD configuration file'), @project.present(current_user: current_user).add_ci_yml_path, class: 'btn gl-button btn-info js-empty-state-button'
= link_to s_('Jobs|Create CI/CD configuration file'), project_ci_pipeline_editor_path(project), class: 'btn gl-button btn-info js-empty-state-button'
- else
.nothing-here-block= s_('Jobs|No jobs to show')
- else

View File

@ -1,7 +1,7 @@
- project = local_assigns.fetch(:project)
- model = local_assigns.fetch(:model)
- form = local_assigns.fetch(:form)
- placeholder = model.is_a?(MergeRequest) ? _('Describe the goal of the changes and what reviewers should be aware of.') : _('Write a comment or drag your files here…')
- placeholder = model.is_a?(MergeRequest) ? _('Describe the goal of the changes and what reviewers should be aware of.') : _('Write a description or drag your files here…')
- supports_quick_actions = true
- preview_url = preview_markdown_path(project, target_type: model.class.name)

View File

@ -138,7 +138,7 @@
= clipboard_button(text: source_branch, title: _('Copy branch name'), placement: "left", boundary: 'viewport')
.gl-display-flex.gl-align-items-center.gl-justify-content-space-between.gl-mb-2.hide-collapsed
%span.gl-overflow-hidden.gl-text-overflow-ellipsis.gl-white-space-nowrap
= _('Source branch: %{source_branch_open}%{source_branch}%{source_branch_close}').html_safe % { source_branch_open: "<span class='gl-font-monospace' title='#{source_branch}'>".html_safe, source_branch_close: "</span>".html_safe, source_branch: source_branch }
= _('Source branch: %{source_branch_open}%{source_branch}%{source_branch_close}').html_safe % { source_branch_open: "<span class='gl-font-monospace' data-testid='ref-name' title='#{html_escape(source_branch)}'>".html_safe, source_branch_close: "</span>".html_safe, source_branch: html_escape(source_branch) }
= clipboard_button(text: source_branch, title: _('Copy branch name'), placement: "left", boundary: 'viewport')
- if show_forwarding_email

View File

@ -10,7 +10,7 @@ class ExpireJobCacheWorker
# rubocop: disable CodeReuse/ActiveRecord
def perform(job_id)
job = CommitStatus.joins(:pipeline, :project).find_by(id: job_id)
job = CommitStatus.eager_load_pipeline.find_by(id: job_id)
return unless job
pipeline = job.pipeline

View File

@ -0,0 +1,5 @@
---
title: Update New Issue form description copy from 'wite a comment' to 'wite a description'
merge_request: 58068
author:
type: changed

View File

@ -0,0 +1,5 @@
---
title: Add enqueueing of Onboarding Progress to the Invite Service
merge_request: 57372
author:
type: other

View File

@ -0,0 +1,5 @@
---
title: Deprecate btn-warning on admin area delete user modal
merge_request: 57761
author:
type: changed

View File

@ -0,0 +1,5 @@
---
title: 'Migration: Add cloud column to licenses'
merge_request: 57781
author:
type: added

View File

@ -0,0 +1,5 @@
---
title: Redirect deprecated pipeline routes
merge_request: 53990
author:
type: removed

View File

@ -1,5 +0,0 @@
---
title: Switch to using a fake mimemagic gem
merge_request: 57443
author:
type: other

View File

@ -0,0 +1,5 @@
---
title: Fixes rubocop offenses Style/RedundantSelfAssignment
merge_request: 57920
author: Shubham Kumar (@imskr)
type: fixed

View File

@ -0,0 +1,5 @@
---
title: Reduce query count for popular worker ExpireJobCacheWorker
merge_request: 57773
author:
type: performance

View File

@ -0,0 +1,5 @@
---
title: Redirect to the pipeline editor when clicking on CI/CD quick links
merge_request: 57085
author:
type: changed

View File

@ -1,5 +0,0 @@
---
title: Refactor MimeMagic calls to new MimeType class
merge_request: 57421
author:
type: other

View File

@ -1,5 +0,0 @@
---
title: Remove direct mimemagic dependency
merge_request: 57387
author:
type: other

View File

@ -1,5 +0,0 @@
---
title: Make HipChat project service do nothing
merge_request: 57434
author:
type: removed

View File

@ -0,0 +1,5 @@
---
title: Update ruby-magic-static to v0.3.5
merge_request: 57984
author:
type: changed

View File

@ -0,0 +1,20 @@
# frozen_string_literal: true
# Ensure that locked attributes can not be changed using a counter.
# TODO: this can be removed once `asciidoctor` gem is > 2.0.12
# and https://github.com/asciidoctor/asciidoctor/issues/3939 is merged
module Asciidoctor
module DocumentPatch
def counter(name, seed = nil)
return @parent_document.counter(name, seed) if @parent_document # rubocop: disable Gitlab/ModuleWithInstanceVariables
unless attribute_locked? name
super
end
end
end
end
class Asciidoctor::Document
prepend Asciidoctor::DocumentPatch
end

View File

@ -553,7 +553,6 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
# Deprecated unscoped routing.
scope as: 'deprecated' do
# Issue https://gitlab.com/gitlab-org/gitlab/issues/118849
draw :pipelines
draw :repository
# Issue https://gitlab.com/gitlab-org/gitlab/-/issues/29572
@ -576,7 +575,8 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
:environments, :protected_environments, :error_tracking, :alert_management,
:tracing,
:serverless, :clusters, :audit_events, :wikis, :merge_requests,
:vulnerability_feedback, :security, :dependencies, :issues)
:vulnerability_feedback, :security, :dependencies, :issues,
:pipelines, :pipeline_schedules)
end
# rubocop: disable Cop/PutProjectRoutesUnderScope

View File

@ -0,0 +1,9 @@
# frozen_string_literal: true
class AddCloudToLicenses < ActiveRecord::Migration[6.0]
DOWNTIME = false
def change
add_column :licenses, :cloud, :boolean, default: false
end
end

View File

@ -0,0 +1 @@
a435a211d7e8b9a972323769299fc6e537fdeaa127f8db6ab53031901a51ec36

View File

@ -14182,7 +14182,8 @@ CREATE TABLE licenses (
id integer NOT NULL,
data text NOT NULL,
created_at timestamp without time zone,
updated_at timestamp without time zone
updated_at timestamp without time zone,
cloud boolean DEFAULT false
);
CREATE SEQUENCE licenses_id_seq

View File

@ -35,7 +35,7 @@ This section is for links to information elsewhere in the GitLab documentation.
- Storing data in another location.
- Destructively reseeding the GitLab database.
- Guidance around updating packaged PostgreSQL, including how to stop it
happening automatically.
from happening automatically.
- [Information about external PostgreSQL](../postgresql/external.md).

View File

@ -88,7 +88,7 @@ Example response:
## Test system hook
```plaintext
GET /hooks/:id
POST /hooks/:id
```
| Attribute | Type | Required | Description |
@ -98,7 +98,7 @@ GET /hooks/:id
Example request:
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/hooks/2"
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/hooks/1"
```
Example response:

View File

@ -150,16 +150,13 @@ at GitLab so far:
## Limitations
- Danger output is not added to a merge request comment if working on
a fork. This happens because the secret variable from the canonical
project is not shared to forks.
To work around this, you can add an [environment
variable](../ci/variables/README.md) called
`DANGER_GITLAB_API_TOKEN` with a personal API token to your
fork. That way the danger comments are made from CI using that
API token instead.
Making the variable
[masked](../ci/variables/README.md#mask-a-custom-variable) makes sure
it doesn't show up in the job logs. The variable cannot be
[protected](../ci/variables/README.md#protect-a-custom-variable),
as it needs to be present for all feature branches.
Danger is run but its output is not added to a merge request comment if working
on a fork. This happens because the secret variable from the canonical project
is not shared to forks. To work around this, you can add an [environment
variable](../ci/variables/README.md) called `DANGER_GITLAB_API_TOKEN` with a
personal API token to your fork. That way the danger comments are made from CI
using that API token instead. Making the variable
[masked](../ci/variables/README.md#mask-a-custom-variable) makes sure it
doesn't show up in the job logs. The variable cannot be
[protected](../ci/variables/README.md#protect-a-custom-variable), as it needs
to be present for all feature branches.

View File

@ -896,18 +896,56 @@ On GitLab.com, we have DangerBot setup to monitor Product Intelligence related f
On GitLab.com, the Product Intelligence team regularly monitors Usage Ping. They may alert you that your metrics need further optimization to run quicker and with greater success. You may also use the [Usage Ping QA dashboard](https://app.periscopedata.com/app/gitlab/632033/Usage-Ping-QA) to check how well your metric performs. The dashboard allows filtering by GitLab version, by "Self-managed" & "SaaS" and shows you how many failures have occurred for each metric. Whenever you notice a high failure rate, you may re-optimize your metric.
### Optional: Test Prometheus based Usage Ping
### Usage Ping local setup
If the data submitted includes metrics [queried from Prometheus](#prometheus-queries) that you would like to inspect and verify,
then you need to ensure that a Prometheus server is running locally, and that furthermore the respective GitLab components
are exporting metrics to it. If you do not need to test data coming from Prometheus, no further action
To set up Usage Ping locally, you must:
1. [Set up local repositories]#(set-up-local-repositories)
1. [Test local setup](#test-local-setup)
1. (Optional) [Test Prometheus-based usage ping](#test-prometheus-based-usage-ping)
#### Set up local repositories
1. Clone and start [GitLab](https://gitlab.com/gitlab-org/gitlab-development-kit).
1. Clone and start [Versions Application](https://gitlab.com/gitlab-services/version-gitlab-com).
Make sure to run `docker-compose up` to start a PostgreSQL and Redis instance.
1. Point GitLab to the Versions Application endpoint instead of the default endpoint:
1. Open [submit_usage_ping_service.rb](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/services/submit_usage_ping_service.rb#L4) in your local and modified `PRODUCTION_URL`.
1. Set it to the local Versions Application URL `http://localhost:3000/usage_data`.
#### Test local setup
1. Using the `gitlab` Rails console, manually trigger a usage ping:
```ruby
SubmitUsagePingService.new.execute
```
1. Use the `versions` Rails console to check the usage ping was successfully received,
parsed, and stored in the Versions database:
```ruby
UsageData.last
```
### Test Prometheus-based usage ping
If the data submitted includes metrics [queried from Prometheus](#prometheus-queries)
you want to inspect and verify, you must:
- Ensure that a Prometheus server is running locally.
- Ensure the respective GitLab components are exporting metrics to the Prometheus server.
If you do not need to test data coming from Prometheus, no further action
is necessary. Usage Ping should degrade gracefully in the absence of a running Prometheus server.
There are three kinds of components that may export data to Prometheus, and which are included in Usage Ping:
Three kinds of components may export data to Prometheus, and are included in Usage Ping:
- [`node_exporter`](https://github.com/prometheus/node_exporter) - Exports node metrics from the host machine
- [`gitlab-exporter`](https://gitlab.com/gitlab-org/gitlab-exporter) - Exports process metrics from various GitLab components
- various GitLab services such as Sidekiq and the Rails server that export their own metrics
- [`node_exporter`](https://github.com/prometheus/node_exporter): Exports node metrics
from the host machine.
- [`gitlab-exporter`](https://gitlab.com/gitlab-org/gitlab-exporter): Exports process metrics
from various GitLab components.
- Other various GitLab services, such as Sidekiq and the Rails server, which export their own metrics.
#### Test with an Omnibus container

View File

@ -6,30 +6,18 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Jira integrations **(FREE)**
GitLab can be integrated with [Jira](https://www.atlassian.com/software/jira).
If your organization uses [Jira](https://www.atlassian.com/software/jira) issues,
you can [migrate](../../../user/project/import/jira.md) your issues from Jira and work
exclusively in GitLab.
[Issues](../issues/index.md) are a tool for discussing ideas, and planning and tracking work.
However, your organization may already use Jira for these purposes, with extensive, established data
and business processes they rely on.
However, if you'd like to continue to use Jira, you can integrate it with GitLab.
Although you can [migrate](../../../user/project/import/jira.md) your Jira issues and work
exclusively in GitLab, you can also continue to use Jira by using the GitLab Jira integrations.
There are two ways to use GitLab with Jira:
## Integration types
There are two different Jira integrations that allow different types of cross-referencing between
GitLab activity and Jira issues, with additional features:
- [Jira integration](jira.md), built in to GitLab. In a given GitLab project, it can be configured
to connect to any Jira instance, either hosted by you or hosted in
[Atlassian cloud](https://www.atlassian.com/cloud).
- [Jira development panel integration](../../../integration/jira/index.md). Connects all
GitLab projects under a specified group or personal namespace.
Jira development panel integration configuration depends on whether:
- You're using GitLab.com or a self-managed GitLab instance.
- You're using Jira on [Atlassian cloud](https://www.atlassian.com/cloud) or on your own server.
- [Jira integration](jira.md). Connect a GitLab project
to a Jira instance. The Jira instance can be hosted by you or in [Atlassian cloud](https://www.atlassian.com/cloud).
- [Jira Development panel integration](../../../integration/jira_development_panel.md).
Connect all GitLab projects under a group or personal namespace.
The integration you choose depends on the capabilities you require.
You can also install both at the same time.

View File

@ -25,11 +25,11 @@ module API
optional :expires_at, type: DateTime, desc: 'Date string in the format YEAR-MONTH-DAY'
end
post ":id/invitations" do
source = find_source(source_type, params[:id])
params[:source] = find_source(source_type, params[:id])
authorize_admin_source!(source_type, source)
authorize_admin_source!(source_type, params[:source])
::Members::InviteService.new(current_user, params).execute(source)
::Members::InviteService.new(current_user, params).execute
end
desc 'Get a list of group or project invitations viewable by the authenticated user' do

View File

@ -47,7 +47,7 @@ module API
params do
requires :id, type: Integer, desc: 'The ID of the system hook'
end
get ":id" do
post ":id" do
hook = SystemHook.find(params[:id])
data = {
event_name: "project_create",

View File

@ -69,8 +69,12 @@ module Gitlab
end
def validate_service_request
headers = {}
headers['X-Gitlab-Token'] = validation_service_token if validation_service_token
Gitlab::HTTP.post(
validation_service_url, timeout: validation_service_timeout,
headers: headers,
body: validation_service_payload.to_json
)
end
@ -86,6 +90,10 @@ module Gitlab
ENV['EXTERNAL_VALIDATION_SERVICE_URL']
end
def validation_service_token
ENV['EXTERNAL_VALIDATION_SERVICE_TOKEN']
end
def validation_service_payload
{
project: {

View File

@ -3,7 +3,7 @@
module Gitlab
module MarkdownCache
# Increment this number every time the renderer changes its output
CACHE_COMMONMARK_VERSION = 26
CACHE_COMMONMARK_VERSION = 27
CACHE_COMMONMARK_VERSION_START = 10
BaseError = Class.new(StandardError)

View File

@ -11,10 +11,11 @@ module Gitlab
attr_reader :user, :push_ability
attr_accessor :container
def initialize(user, container: nil, push_ability: :push_code)
def initialize(user, container: nil, push_ability: :push_code, skip_collaboration_check: false)
@user = user
@container = container
@push_ability = push_ability
@skip_collaboration_check = skip_collaboration_check
end
def can_do_action?(action)
@ -87,6 +88,8 @@ module Gitlab
private
attr_reader :skip_collaboration_check
def can_push?
user.can?(push_ability, container)
end
@ -98,6 +101,8 @@ module Gitlab
end
def branch_allows_collaboration_for?(ref)
return false if skip_collaboration_check
# Checking for an internal project or group to prevent an infinite loop:
# https://gitlab.com/gitlab-org/gitlab/issues/36805
(!project.internal? && project.branch_allows_collaboration?(user, ref))

View File

@ -34656,6 +34656,9 @@ msgstr ""
msgid "Write a comment…"
msgstr ""
msgid "Write a description or drag your files here…"
msgstr ""
msgid "Write milestone description..."
msgstr ""

View File

@ -166,6 +166,26 @@ module QA
end
end
# Method for selecting radios
def choose_element(name, click_by_js = false)
if find_element(name, visible: false).checked?
QA::Runtime::Logger.debug("#{name} is already selected")
return
end
retry_until(sleep_interval: 1) do
radio = find_element(name, visible: false)
# Some radio buttons are hidden by their labels and cannot be clicked directly
click_by_js ? page.execute_script("arguments[0].click();", radio) : radio.click
selected = find_element(name, visible: false).checked?
QA::Runtime::Logger.debug(selected ? "#{name} was selected" : "#{name} was not selected")
selected
end
end
# Use this to simulate moving the pointer to an element's coordinate
# and sending a click event.
# This is a helpful workaround when there is a transparent element overlapping

View File

@ -20,11 +20,11 @@ module QA
end
def click_save_changes
click_element :save_merge_request_changes_button
click_element(:save_merge_request_changes_button)
end
def enable_ff_only
click_element :merge_ff_radio_button
click_element(:merge_ff_radio_button)
click_save_changes
end

View File

@ -81,7 +81,7 @@ module QA
end
def fill_element(name, content)
masked_content = name.to_s.include?('password') ? '*****' : content
masked_content = name.to_s.match?(/token|key|password/) ? '*****' : content
log(%Q(filling :#{name} with "#{masked_content}"))

View File

@ -136,8 +136,10 @@ postgresql:
metrics:
enabled: false
resources:
requests:
cpu: 600m
memory: 1000M
limits:
cpu: 1300m
memory: 1500M
prometheus:

View File

@ -114,7 +114,7 @@ RSpec.describe 'Database schema' do
# postgres and mysql both automatically create an index on the primary
# key. Also, the rails connection.indexes() method does not return
# automatically generated indexes (like the primary key index).
first_indexed_column = first_indexed_column.push(primary_key_column)
first_indexed_column.push(primary_key_column)
expect(first_indexed_column.uniq).to include(*foreign_keys_columns)
end

View File

@ -6,7 +6,7 @@ FactoryBot.define do
state { :none }
before(:create) do |pool|
pool.source_project = create(:project, :repository)
pool.source_project ||= create(:project, :repository)
pool.source_project.update!(pool_repository: pool)
end

View File

@ -45,7 +45,7 @@ RSpec.describe "User creates issue" do
.and have_no_content("Milestone")
expect(page.find('#issue_title')['placeholder']).to eq 'Title'
expect(page.find('#issue_description')['placeholder']).to eq 'Write a comment or drag your files here…'
expect(page.find('#issue_description')['placeholder']).to eq 'Write a description or drag your files here…'
end
issue_title = "500 error on profile"

View File

@ -111,4 +111,21 @@ RSpec.describe 'User views an open merge request' do
end
end
end
context 'XSS source branch' do
let(:project) { create(:project, :public, :repository) }
let(:source_branch) { "&#39;&gt;&lt;iframe/srcdoc=&#39;&#39;&gt;&lt;/iframe&gt;" }
before do
project.repository.create_branch(source_branch, "master")
mr = create(:merge_request, source_project: project, target_project: project, source_branch: source_branch)
visit(merge_request_path(mr))
end
it 'encodes branch name' do
expect(find("[data-testid='ref-name']")[:title]).to eq(source_branch)
end
end
end

View File

@ -32,7 +32,7 @@ RSpec.describe 'Jobs', :clean_gitlab_redis_shared_state do
it 'shows the empty state page' do
expect(page).to have_content('Use jobs to automate your tasks')
expect(page).to have_link('Create CI/CD configuration file', href: project.present(current_user: user).add_ci_yml_path)
expect(page).to have_link('Create CI/CD configuration file', href: project_ci_pipeline_editor_path(project))
end
end

View File

@ -226,11 +226,11 @@ RSpec.describe 'Projects > Show > User sees setup shortcut buttons' do
expect(project.repository.gitlab_ci_yml).to be_nil
page.within('.project-buttons') do
expect(page).to have_link('Set up CI/CD', href: presenter.add_ci_yml_path)
expect(page).to have_link('Set up CI/CD', href: project_ci_pipeline_editor_path(project))
end
end
it 'no "Set up CI/CD" button if the project already has a .gitlab-ci.yml' do
it '"Set up CI/CD" button is renamed if the project already has a .gitlab-ci.yml' do
Files::CreateService.new(
project,
project.creator,
@ -247,6 +247,7 @@ RSpec.describe 'Projects > Show > User sees setup shortcut buttons' do
page.within('.project-buttons') do
expect(page).not_to have_link('Set up CI/CD')
expect(page).to have_link('CI/CD configuration')
end
end
end

View File

@ -50,11 +50,11 @@ exports[`User Operation confirmation modal renders modal with form included 1`]
<gl-button-stub
buttontextclasses=""
category="primary"
category="secondary"
disabled="true"
icon=""
size="medium"
variant="warning"
variant="danger"
>
secondaryAction

View File

@ -11,15 +11,15 @@ describe('User Operation confirmation modal', () => {
let wrapper;
let formSubmitSpy;
const findButton = (variant) =>
const findButton = (variant, category) =>
wrapper
.findAll(GlButton)
.filter((w) => w.attributes('variant') === variant)
.filter((w) => w.attributes('variant') === variant && w.attributes('category') === category)
.at(0);
const findForm = () => wrapper.find('form');
const findUsernameInput = () => wrapper.find(GlFormInput);
const findPrimaryButton = () => findButton('danger');
const findSecondaryButton = () => findButton('warning');
const findPrimaryButton = () => findButton('danger', 'primary');
const findSecondaryButton = () => findButton('danger', 'secondary');
const findAuthenticityToken = () => new FormData(findForm().element).get('authenticity_token');
const getUsername = () => findUsernameInput().attributes('value');
const getMethodParam = () => new FormData(findForm().element).get('_method');

View File

@ -92,6 +92,15 @@ module Gitlab
expect(render(data[:input], context)).to include(data[:output])
end
end
it 'does not allow locked attributes to be overridden' do
input = <<~ADOC
{counter:max-include-depth:1234}
<|-- {max-include-depth}
ADOC
expect(render(input, {})).not_to include('1234')
end
end
context "images" do
@ -543,6 +552,40 @@ module Gitlab
expect(render(input, context)).to include(output.strip)
end
it 'does not allow kroki-plantuml-include to be overridden' do
input = <<~ADOC
[plantuml, test="{counter:kroki-plantuml-include:/etc/passwd}", format="png"]
....
class BlockProcessor
BlockProcessor <|-- {counter:kroki-plantuml-include}
....
ADOC
output = <<~HTML
<div>
<div>
<a class=\"no-attachment-icon\" href=\"https://kroki.io/plantuml/png/eNpLzkksLlZwyslPzg4oyk9OLS7OL-LiQuUr2NTo6ipUJ-eX5pWkFlllF-VnZ-oW5CTmlZTm5uhm5iXnlKak1gIABQEb8A==\" target=\"_blank\" rel=\"noopener noreferrer\"><img src=\"data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"Diagram\" class=\"lazy\" data-src=\"https://kroki.io/plantuml/png/eNpLzkksLlZwyslPzg4oyk9OLS7OL-LiQuUr2NTo6ipUJ-eX5pWkFlllF-VnZ-oW5CTmlZTm5uhm5iXnlKak1gIABQEb8A==\"></a>
</div>
</div>
HTML
expect(render(input, {})).to include(output.strip)
end
it 'does not allow kroki-server-url to be overridden' do
input = <<~ADOC
[plantuml, test="{counter:kroki-server-url:evilsite}", format="png"]
....
class BlockProcessor
BlockProcessor
....
ADOC
expect(render(input, {})).not_to include('evilsite')
end
end
context 'with Kroki and BlockDiag (additional format) enabled' do

View File

@ -63,6 +63,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Validate::External do
expect(::Gitlab::HTTP).to receive(:post) do |_url, params|
expect(params[:body]).to match_schema('/external_validation')
expect(params[:timeout]).to eq(described_class::DEFAULT_VALIDATION_REQUEST_TIMEOUT)
expect(params[:headers]).to eq({})
end
perform!
@ -119,6 +120,20 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Validate::External do
end
end
context 'when EXTERNAL_VALIDATION_SERVICE_TOKEN is set' do
before do
stub_env('EXTERNAL_VALIDATION_SERVICE_TOKEN', '123')
end
it 'passes token in X-Gitlab-Token header' do
expect(::Gitlab::HTTP).to receive(:post) do |_url, params|
expect(params[:headers]).to eq({ 'X-Gitlab-Token' => '123' })
end
perform!
end
end
context 'when validation returns 200 OK' do
before do
stub_request(:post, validation_service_url).to_return(status: 200, body: "{}")

View File

@ -216,6 +216,15 @@ RSpec.describe Gitlab::UserAccess do
expect(access.can_merge_to_branch?(@branch.name)).to be_falsey
end
end
context 'when skip_collaboration_check is true' do
let(:access) { described_class.new(user, container: project, skip_collaboration_check: true) }
it 'does not call Project#branch_allows_collaboration?' do
expect(project).not_to receive(:branch_allows_collaboration?)
expect(access.can_push_to_branch?('master')).to be_falsey
end
end
end
describe '#can_create_tag?' do

View File

@ -66,6 +66,12 @@ RSpec.describe GroupMember do
it_behaves_like 'members notifications', :group
describe '#namespace_id' do
subject { build(:group_member, source_id: 1).namespace_id }
it { is_expected.to eq 1 }
end
describe '#real_source_type' do
subject { create(:group_member).real_source_type }

View File

@ -13,6 +13,10 @@ RSpec.describe ProjectMember do
it { is_expected.to validate_inclusion_of(:access_level).in_array(Gitlab::Access.values) }
end
describe 'delegations' do
it { is_expected.to delegate_method(:namespace_id).to(:project) }
end
describe '.access_level_roles' do
it 'returns Gitlab::Access.options' do
expect(described_class.access_level_roles).to eq(Gitlab::Access.options)

View File

@ -5319,6 +5319,64 @@ RSpec.describe Project, factory_default: :keep do
end
end
describe '#branch_allows_collaboration?' do
context 'when there are open merge requests that have their source/target branches point to each other' do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:developer) { create(:user) }
let_it_be(:reporter) { create(:user) }
let_it_be(:guest) { create(:user) }
before_all do
create(
:merge_request,
target_project: project,
target_branch: 'master',
source_project: project,
source_branch: 'merge-test',
allow_collaboration: true
)
create(
:merge_request,
target_project: project,
target_branch: 'merge-test',
source_project: project,
source_branch: 'master',
allow_collaboration: true
)
project.add_developer(developer)
project.add_reporter(reporter)
project.add_guest(guest)
end
shared_examples_for 'successful check' do
it 'does not go into an infinite loop' do
expect { project.branch_allows_collaboration?(user, 'master') }
.not_to raise_error
end
end
context 'when user is a developer' do
let(:user) { developer }
it_behaves_like 'successful check'
end
context 'when user is a reporter' do
let(:user) { reporter }
it_behaves_like 'successful check'
end
context 'when user is a guest' do
let(:user) { guest }
it_behaves_like 'successful check'
end
end
end
context 'with cross project merge requests' do
let(:user) { create(:user) }
let(:target_project) { create(:project, :repository) }

View File

@ -103,15 +103,15 @@ RSpec.describe API::SystemHooks do
end
end
describe "GET /hooks/:id" do
it "returns hook by id" do
get api("/hooks/#{hook.id}", admin)
expect(response).to have_gitlab_http_status(:ok)
describe 'POST /hooks/:id' do
it "returns and trigger hook by id" do
post api("/hooks/#{hook.id}", admin)
expect(response).to have_gitlab_http_status(:created)
expect(json_response['event_name']).to eq('project_create')
end
it "returns 404 on failure" do
get api("/hooks/404", admin)
post api("/hooks/404", admin)
expect(response).to have_gitlab_http_status(:not_found)
end
end

View File

@ -684,6 +684,26 @@ RSpec.describe 'project routing' do
end
end
describe Projects::PipelinesController, 'routing' do
it 'to #index' do
expect(get('/gitlab/gitlabhq/-/pipelines')).to route_to('projects/pipelines#index', namespace_id: 'gitlab', project_id: 'gitlabhq')
end
it 'to #show' do
expect(get('/gitlab/gitlabhq/-/pipelines/12')).to route_to('projects/pipelines#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '12')
end
it_behaves_like 'redirecting a legacy path', '/gitlab/gitlabhq/pipelines', '/gitlab/gitlabhq/-/pipelines'
end
describe Projects::PipelineSchedulesController, 'routing' do
it 'to #index' do
expect(get('/gitlab/gitlabhq/-/pipeline_schedules')).to route_to('projects/pipeline_schedules#index', namespace_id: 'gitlab', project_id: 'gitlabhq')
end
it_behaves_like 'redirecting a legacy path', '/gitlab/gitlabhq/pipeline_schedules', '/gitlab/gitlabhq/-/pipeline_schedules'
end
describe Projects::Settings::OperationsController, 'routing' do
it 'to #reset_alerting_token' do
expect(post('/gitlab/gitlabhq/-/settings/operations/reset_alerting_token')).to route_to('projects/settings/operations#reset_alerting_token', namespace_id: 'gitlab', project_id: 'gitlabhq')

View File

@ -2,29 +2,43 @@
require 'spec_helper'
RSpec.describe Members::InviteService, :aggregate_failures do
RSpec.describe Members::InviteService, :aggregate_failures, :clean_gitlab_redis_shared_state, :sidekiq_inline do
let_it_be(:project) { create(:project) }
let_it_be(:user) { project.owner }
let_it_be(:project_user) { create(:user) }
let_it_be(:namespace) { project.namespace }
let(:params) { {} }
let(:base_params) { { access_level: Gitlab::Access::GUEST } }
let(:base_params) { { access_level: Gitlab::Access::GUEST, source: project } }
subject(:result) { described_class.new(user, base_params.merge(params)).execute(project) }
subject(:result) { described_class.new(user, base_params.merge(params) ).execute }
context 'when email is previously unused by current members' do
context 'when there is a valid member invited' do
let(:params) { { email: 'email@example.org' } }
it 'successfully creates a member' do
expect { result }.to change(ProjectMember, :count).by(1)
expect_to_create_members(count: 1)
expect(result[:status]).to eq(:success)
end
it_behaves_like 'records an onboarding progress action', :user_added
end
context 'when email is not a valid email' do
let(:params) { { email: '_bogus_' } }
it 'returns an error' do
expect_not_to_create_members
expect(result[:message]['_bogus_']).to eq("Invite email is invalid")
end
it_behaves_like 'does not record an onboarding progress action'
end
context 'when emails are passed as an array' do
let(:params) { { email: %w[email@example.org email2@example.org] } }
it 'successfully creates members' do
expect { result }.to change(ProjectMember, :count).by(2)
expect_to_create_members(count: 2)
expect(result[:status]).to eq(:success)
end
end
@ -33,33 +47,23 @@ RSpec.describe Members::InviteService, :aggregate_failures do
let(:params) { { email: '' } }
it 'returns an error' do
expect(result[:status]).to eq(:error)
expect_not_to_create_members
expect(result[:message]).to eq('Email cannot be blank')
end
end
context 'when email param is not included' do
it 'returns an error' do
expect(result[:status]).to eq(:error)
expect_not_to_create_members
expect(result[:message]).to eq('Email cannot be blank')
end
end
context 'when email is not a valid email' do
let(:params) { { email: '_bogus_' } }
it 'returns an error' do
expect { result }.not_to change(ProjectMember, :count)
expect(result[:status]).to eq(:error)
expect(result[:message]['_bogus_']).to eq("Invite email is invalid")
end
end
context 'when duplicate email addresses are passed' do
let(:params) { { email: 'email@example.org,email@example.org' } }
it 'only creates one member per unique address' do
expect { result }.to change(ProjectMember, :count).by(1)
expect_to_create_members(count: 1)
expect(result[:status]).to eq(:success)
end
end
@ -71,8 +75,7 @@ RSpec.describe Members::InviteService, :aggregate_failures do
let(:params) { { email: emails } }
it 'limits the number of emails to 100' do
expect { result }.not_to change(ProjectMember, :count)
expect(result[:status]).to eq(:error)
expect_not_to_create_members
expect(result[:message]).to eq('Too many users specified (limit is 100)')
end
end
@ -81,8 +84,7 @@ RSpec.describe Members::InviteService, :aggregate_failures do
let(:params) { { email: 'email@example.org,email2@example.org', limit: 1 } }
it 'limits the number of emails to the limit supplied' do
expect { result }.not_to change(ProjectMember, :count)
expect(result[:status]).to eq(:error)
expect_not_to_create_members
expect(result[:message]).to eq('Too many users specified (limit is 1)')
end
end
@ -91,7 +93,7 @@ RSpec.describe Members::InviteService, :aggregate_failures do
let(:params) { { email: emails, limit: -1 } }
it 'does not limit number of emails' do
expect { result }.to change(ProjectMember, :count).by(101)
expect_to_create_members(count: 101)
expect(result[:status]).to eq(:success)
end
end
@ -101,7 +103,7 @@ RSpec.describe Members::InviteService, :aggregate_failures do
let(:params) { { email: project_user.email } }
it 'adds an existing user to members' do
expect { result }.to change(ProjectMember, :count).by(1)
expect_to_create_members(count: 1)
expect(result[:status]).to eq(:success)
expect(project.users).to include project_user
end
@ -111,8 +113,7 @@ RSpec.describe Members::InviteService, :aggregate_failures do
let(:params) { { email: project_user.email, access_level: -1 } }
it 'returns an error' do
expect { result }.not_to change(ProjectMember, :count)
expect(result[:status]).to eq(:error)
expect_not_to_create_members
expect(result[:message][project_user.email]).to eq("Access level is not included in the list")
end
end
@ -122,7 +123,7 @@ RSpec.describe Members::InviteService, :aggregate_failures do
let(:params) { { email: "#{invited_member.invite_email},#{project_user.email}" } }
it 'adds new email and returns an error for the already invited email' do
expect { result }.to change(ProjectMember, :count).by(1)
expect_to_create_members(count: 1)
expect(result[:status]).to eq(:error)
expect(result[:message][invited_member.invite_email]).to eq("Member already invited to #{project.name}")
expect(project.users).to include project_user
@ -134,7 +135,7 @@ RSpec.describe Members::InviteService, :aggregate_failures do
let(:params) { { email: "#{requested_member.user.email},#{project_user.email}" } }
it 'adds new email and returns an error for the already invited email' do
expect { result }.to change(ProjectMember, :count).by(1)
expect_to_create_members(count: 1)
expect(result[:status]).to eq(:error)
expect(result[:message][requested_member.user.email])
.to eq("Member cannot be invited because they already requested to join #{project.name}")
@ -147,10 +148,19 @@ RSpec.describe Members::InviteService, :aggregate_failures do
let(:params) { { email: "#{existing_member.user.email},#{project_user.email}" } }
it 'adds new email and returns an error for the already invited email' do
expect { result }.to change(ProjectMember, :count).by(1)
expect_to_create_members(count: 1)
expect(result[:status]).to eq(:error)
expect(result[:message][existing_member.user.email]).to eq("Already a member of #{project.name}")
expect(project.users).to include project_user
end
end
def expect_to_create_members(count:)
expect { result }.to change(ProjectMember, :count).by(count)
end
def expect_not_to_create_members
expect { result }.not_to change(ProjectMember, :count)
expect(result[:status]).to eq(:error)
end
end

View File

@ -403,7 +403,7 @@ RSpec.describe Projects::ForkService do
end
context 'when forking with object pools' do
let(:fork_from_project) { create(:project, :public) }
let(:fork_from_project) { create(:project, :repository, :public) }
let(:forker) { create(:user) }
context 'when no pool exists' do

View File

@ -207,6 +207,17 @@ RSpec.describe Projects::UnlinkForkService, :use_clean_rails_memory_store_cachin
end
end
context 'a project with pool repository' do
let(:project) { create(:project, :public, :repository) }
let!(:pool_repository) { create(:pool_repository, :ready, source_project: project) }
subject { described_class.new(project, user) }
it 'when unlinked leaves pool repository' do
expect { subject.execute }.to change { project.reload.has_pool_repository? }.from(true).to(false)
end
end
context 'when given project is not part of a fork network' do
let!(:project_without_forks) { create(:project, :public) }

View File

@ -8,7 +8,8 @@ RSpec.describe ExpireJobCacheWorker do
describe '#perform' do
context 'with a job in the pipeline' do
let(:job) { create(:ci_build, pipeline: pipeline) }
let_it_be(:job) { create(:ci_build, pipeline: pipeline) }
let(:job_args) { job.id }
include_examples 'an idempotent worker' do
@ -31,6 +32,24 @@ RSpec.describe ExpireJobCacheWorker do
subject
end
end
it 'does not perform extra queries', :aggregate_failures do
worker = described_class.new
recorder = ActiveRecord::QueryRecorder.new { worker.perform(job.id) }
occurences = recorder.data.values.flat_map {|v| v[:occurrences]}
project_queries = occurences.select {|s| s.include?('FROM "projects"')}
namespace_queries = occurences.select {|s| s.include?('FROM "namespaces"')}
route_queries = occurences.select {|s| s.include?('FROM "routes"')}
# This worker is run 1 million times an hour, so we need to save as much
# queries as possible.
expect(recorder.count).to be <= 1
expect(project_queries.size).to eq(0)
expect(namespace_queries.size).to eq(0)
expect(route_queries.size).to eq(0)
end
end
context 'when there is no job in the pipeline' do

0
vendor/gitignore/C++.gitignore vendored Normal file → Executable file
View File

0
vendor/gitignore/Java.gitignore vendored Normal file → Executable file
View File