Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
042cd704b8
commit
039b920db3
43 changed files with 495 additions and 73 deletions
|
@ -72,8 +72,6 @@ Rails/SaveBang:
|
|||
- 'ee/spec/models/visible_approvable_spec.rb'
|
||||
- 'ee/spec/models/vulnerabilities/feedback_spec.rb'
|
||||
- 'ee/spec/models/vulnerabilities/issue_link_spec.rb'
|
||||
- 'ee/spec/services/approval_rules/finalize_service_spec.rb'
|
||||
- 'ee/spec/services/approval_rules/update_service_spec.rb'
|
||||
- 'ee/spec/services/ee/boards/issues/create_service_spec.rb'
|
||||
- 'ee/spec/services/ee/boards/issues/list_service_spec.rb'
|
||||
- 'ee/spec/services/ee/boards/lists/list_service_spec.rb'
|
||||
|
|
|
@ -22,6 +22,7 @@ class SessionsController < Devise::SessionsController
|
|||
prepend_before_action :check_captcha, only: [:create]
|
||||
prepend_before_action :store_redirect_uri, only: [:new]
|
||||
prepend_before_action :require_no_authentication_without_flash, only: [:new, :create]
|
||||
prepend_before_action :check_forbidden_password_based_login, if: -> { action_name == 'create' && password_based_login? }
|
||||
prepend_before_action :ensure_password_authentication_enabled!, if: -> { action_name == 'create' && password_based_login? }
|
||||
|
||||
before_action :auto_sign_in_with_provider, only: [:new]
|
||||
|
@ -313,6 +314,13 @@ class SessionsController < Devise::SessionsController
|
|||
def set_invite_params
|
||||
@invite_email = ActionController::Base.helpers.sanitize(params[:invite_email])
|
||||
end
|
||||
|
||||
def check_forbidden_password_based_login
|
||||
if find_user&.password_based_login_forbidden?
|
||||
flash[:alert] = _('You are not allowed to log in using password')
|
||||
redirect_to new_user_session_path
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
SessionsController.prepend_mod_with('SessionsController')
|
||||
|
|
|
@ -19,5 +19,9 @@ module Types
|
|||
field :branch_names, [GraphQL::STRING_TYPE], null: true, calls_gitaly: true,
|
||||
complexity: 170, description: 'Names of branches available in this repository that match the search pattern.',
|
||||
resolver: Resolvers::RepositoryBranchNamesResolver
|
||||
field :disk_path, GraphQL::STRING_TYPE,
|
||||
description: 'Shows a disk path of the repository.',
|
||||
null: true,
|
||||
authorize: :read_storage_disk_path
|
||||
end
|
||||
end
|
||||
|
|
|
@ -161,7 +161,7 @@ module CommitsHelper
|
|||
ref,
|
||||
{
|
||||
merge_request: merge_request,
|
||||
pipeline_status: hashed_pipeline_status(commit, ref),
|
||||
pipeline_status: commit.status_for(ref),
|
||||
xhr: request.xhr?,
|
||||
controller: controller.controller_path,
|
||||
path: @path # referred to in #link_to_browse_code
|
||||
|
@ -242,14 +242,4 @@ module CommitsHelper
|
|||
project_commit_path(project, commit)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def hashed_pipeline_status(commit, ref)
|
||||
status = commit.status_for(ref)
|
||||
|
||||
return if status.nil?
|
||||
|
||||
Digest::SHA1.hexdigest(status.to_s)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -14,6 +14,7 @@ module Ci
|
|||
belongs_to :project
|
||||
|
||||
scope :by_project_and_keys, -> (project, keys) { where(project_id: project.id, key_hash: keys) }
|
||||
scope :deletable, -> { where('NOT EXISTS (?)', Ci::UnitTestFailure.select(1).where("#{Ci::UnitTestFailure.table_name}.unit_test_id = #{table_name}.id")) }
|
||||
|
||||
class << self
|
||||
def find_or_create_by_batch(project, unit_test_attrs)
|
||||
|
|
|
@ -11,6 +11,8 @@ module Ci
|
|||
belongs_to :unit_test, class_name: "Ci::UnitTest", foreign_key: :unit_test_id
|
||||
belongs_to :build, class_name: "Ci::Build", foreign_key: :build_id
|
||||
|
||||
scope :deletable, -> { where('failed_at < ?', REPORT_WINDOW.ago) }
|
||||
|
||||
def self.recent_failures_count(project:, unit_test_keys:, date_range: REPORT_WINDOW.ago..Time.current)
|
||||
joins(:unit_test)
|
||||
.where(
|
||||
|
|
|
@ -649,7 +649,7 @@ class Group < Namespace
|
|||
end
|
||||
|
||||
def access_request_approvers_to_be_notified
|
||||
members.owners.order_recent_sign_in.limit(ACCESS_REQUEST_APPROVERS_TO_BE_NOTIFIED_LIMIT)
|
||||
members.owners.connected_to_user.order_recent_sign_in.limit(ACCESS_REQUEST_APPROVERS_TO_BE_NOTIFIED_LIMIT)
|
||||
end
|
||||
|
||||
def supports_events?
|
||||
|
|
|
@ -92,11 +92,13 @@ class Member < ApplicationRecord
|
|||
.reorder(nil)
|
||||
end
|
||||
|
||||
scope :connected_to_user, -> { where.not(user_id: nil) }
|
||||
|
||||
# This scope is exclusively used to get the members
|
||||
# that can possibly have project_authorization records
|
||||
# to projects/groups.
|
||||
scope :authorizable, -> do
|
||||
where.not(user_id: nil)
|
||||
connected_to_user
|
||||
.non_request
|
||||
.non_minimal_access
|
||||
end
|
||||
|
|
|
@ -2454,7 +2454,7 @@ class Project < ApplicationRecord
|
|||
end
|
||||
|
||||
def access_request_approvers_to_be_notified
|
||||
members.maintainers.order_recent_sign_in.limit(ACCESS_REQUEST_APPROVERS_TO_BE_NOTIFIED_LIMIT)
|
||||
members.maintainers.connected_to_user.order_recent_sign_in.limit(ACCESS_REQUEST_APPROVERS_TO_BE_NOTIFIED_LIMIT)
|
||||
end
|
||||
|
||||
def pages_lookup_path(trim_prefix: nil, domain: nil)
|
||||
|
|
|
@ -1121,6 +1121,11 @@ class User < ApplicationRecord
|
|||
Gitlab::CurrentSettings.password_authentication_enabled_for_git? && !password_based_omniauth_user?
|
||||
end
|
||||
|
||||
# method overriden in EE
|
||||
def password_based_login_forbidden?
|
||||
false
|
||||
end
|
||||
|
||||
def can_change_username?
|
||||
gitlab_config.username_changing_enabled
|
||||
end
|
||||
|
|
|
@ -171,6 +171,7 @@ class ProjectPolicy < BasePolicy
|
|||
rule { guest | admin }.enable :read_project_for_iids
|
||||
|
||||
rule { admin }.enable :update_max_artifacts_size
|
||||
rule { admin }.enable :read_storage_disk_path
|
||||
rule { can?(:read_all_resources) }.enable :read_confidential_issues
|
||||
|
||||
rule { guest }.enable :guest_access
|
||||
|
|
|
@ -31,11 +31,11 @@ module Projects
|
|||
def group_members
|
||||
return [] unless current_user.can?(:admin_group, project.group)
|
||||
|
||||
# We need `.where.not(user_id: nil)` here otherwise when a group has an
|
||||
# We need `.connected_to_user` here otherwise when a group has an
|
||||
# invitee, it would make the following query return 0 rows since a NULL
|
||||
# user_id would be present in the subquery
|
||||
# See http://stackoverflow.com/questions/129077/not-in-clause-and-null-values
|
||||
non_null_user_ids = project.project_members.where.not(user_id: nil).select(:user_id)
|
||||
non_null_user_ids = project.project_members.connected_to_user.select(:user_id)
|
||||
GroupMembersFinder.new(project.group).execute.where.not(user_id: non_null_user_ids)
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
|
|
37
app/services/ci/delete_unit_tests_service.rb
Normal file
37
app/services/ci/delete_unit_tests_service.rb
Normal file
|
@ -0,0 +1,37 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Ci
|
||||
class DeleteUnitTestsService
|
||||
include EachBatch
|
||||
|
||||
BATCH_SIZE = 100
|
||||
|
||||
def execute
|
||||
purge_data!(Ci::UnitTestFailure)
|
||||
purge_data!(Ci::UnitTest)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def purge_data!(klass)
|
||||
loop do
|
||||
break unless delete_batch!(klass)
|
||||
end
|
||||
end
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def delete_batch!(klass)
|
||||
deleted = 0
|
||||
|
||||
ActiveRecord::Base.transaction do
|
||||
ids = klass.deletable.lock('FOR UPDATE SKIP LOCKED').limit(BATCH_SIZE).pluck(:id)
|
||||
break if ids.empty?
|
||||
|
||||
deleted = klass.where(id: ids).delete_all
|
||||
end
|
||||
|
||||
deleted > 0
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
end
|
||||
end
|
|
@ -182,6 +182,15 @@
|
|||
:weight: 1
|
||||
:idempotent:
|
||||
:tags: []
|
||||
- :name: cronjob:ci_delete_unit_tests
|
||||
:worker_name: Ci::DeleteUnitTestsWorker
|
||||
:feature_category: :continuous_integration
|
||||
:has_external_dependencies:
|
||||
:urgency: :low
|
||||
:resource_boundary: :unknown
|
||||
:weight: 1
|
||||
:idempotent: true
|
||||
:tags: []
|
||||
- :name: cronjob:ci_pipeline_artifacts_expire_artifacts
|
||||
:worker_name: Ci::PipelineArtifacts::ExpireArtifactsWorker
|
||||
:feature_category: :continuous_integration
|
||||
|
|
18
app/workers/ci/delete_unit_tests_worker.rb
Normal file
18
app/workers/ci/delete_unit_tests_worker.rb
Normal file
|
@ -0,0 +1,18 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Ci
|
||||
class DeleteUnitTestsWorker
|
||||
include ApplicationWorker
|
||||
# rubocop:disable Scalability/CronWorkerContext
|
||||
# This worker does not perform work scoped to a context
|
||||
include CronjobQueue
|
||||
# rubocop:enable Scalability/CronWorkerContext
|
||||
|
||||
feature_category :continuous_integration
|
||||
idempotent!
|
||||
|
||||
def perform
|
||||
Ci::DeleteUnitTestsService.new.execute
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add repository diskPath parameter to GraphQL API
|
||||
merge_request: 61725
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Filter out unconnected-to-user members from receiving on access request emails
|
||||
merge_request: 61819
|
||||
author:
|
||||
type: fixed
|
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
title: Add duplicated gin trigram index on notes table (note) to replace existing
|
||||
merge_request: 61430
|
||||
author:
|
||||
type: performance
|
5
changelogs/unreleased/eb-clean-up-unit-test-tables.yml
Normal file
5
changelogs/unreleased/eb-clean-up-unit-test-tables.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add cron worker for cleaning up unit test tables
|
||||
merge_request: 61463
|
||||
author:
|
||||
type: added
|
|
@ -572,6 +572,9 @@ Settings.cron_jobs['ssh_keys_expiring_soon_notification_worker']['job_class'] =
|
|||
Settings.cron_jobs['users_deactivate_dormant_users_worker'] ||= Settingslogic.new({})
|
||||
Settings.cron_jobs['users_deactivate_dormant_users_worker']['cron'] ||= '21,42 0-4 * * *'
|
||||
Settings.cron_jobs['users_deactivate_dormant_users_worker']['job_class'] = 'Users::DeactivateDormantUsersWorker'
|
||||
Settings.cron_jobs['ci_delete_unit_tests_worker'] ||= Settingslogic.new({})
|
||||
Settings.cron_jobs['ci_delete_unit_tests_worker']['cron'] ||= '0 0 * * *'
|
||||
Settings.cron_jobs['ci_delete_unit_tests_worker']['job_class'] = 'Ci::DeleteUnitTestsWorker'
|
||||
|
||||
Gitlab.com do
|
||||
Settings.cron_jobs['batched_background_migrations_worker'] ||= Settingslogic.new({})
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# See https://docs.gitlab.com/ee/development/migration_style_guide.html
|
||||
# for more information on how to write migrations for GitLab.
|
||||
|
||||
class CreateIndexOnNotesNote < ActiveRecord::Migration[6.0]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
DUPLICATE_INDEX_NAME = 'index_notes_on_note_gin_trigram'
|
||||
CURRENT_INDEX_NAME = 'index_notes_on_note_trigram'
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
# https://gitlab.com/gitlab-org/gitlab/-/issues/218410#note_565624409
|
||||
# We are having troubles with the index, and some inserts are taking a long time
|
||||
# so in this migration we are recreating the index
|
||||
def up
|
||||
add_concurrent_index :notes, :note, name: DUPLICATE_INDEX_NAME, using: :gin, opclass: :gin_trgm_ops
|
||||
remove_concurrent_index_by_name :notes, CURRENT_INDEX_NAME
|
||||
end
|
||||
|
||||
def down
|
||||
add_concurrent_index :notes, :note, name: CURRENT_INDEX_NAME, using: :gin, opclass: :gin_trgm_ops
|
||||
remove_concurrent_index_by_name :notes, DUPLICATE_INDEX_NAME
|
||||
end
|
||||
end
|
|
@ -1 +0,0 @@
|
|||
2d11da499f49964f37cc0a2c541cf58182416ae7b6b0e762a135b327099de1a4
|
|
@ -23674,7 +23674,7 @@ CREATE INDEX index_notes_on_discussion_id ON notes USING btree (discussion_id);
|
|||
|
||||
CREATE INDEX index_notes_on_line_code ON notes USING btree (line_code);
|
||||
|
||||
CREATE INDEX index_notes_on_note_gin_trigram ON notes USING gin (note gin_trgm_ops);
|
||||
CREATE INDEX index_notes_on_note_trigram ON notes USING gin (note gin_trgm_ops);
|
||||
|
||||
CREATE INDEX index_notes_on_noteable_id_and_noteable_type_and_system ON notes USING btree (noteable_id, noteable_type, system);
|
||||
|
||||
|
|
|
@ -11926,6 +11926,7 @@ Represents the source code attached to a release in a particular format.
|
|||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="repositorydiskpath"></a>`diskPath` | [`String`](#string) | Shows a disk path of the repository. |
|
||||
| <a id="repositoryempty"></a>`empty` | [`Boolean!`](#boolean) | Indicates repository has no visible content. |
|
||||
| <a id="repositoryexists"></a>`exists` | [`Boolean!`](#boolean) | Indicates a corresponding Git repository exists on disk. |
|
||||
| <a id="repositoryrootref"></a>`rootRef` | [`String`](#string) | Default branch of the repository. |
|
||||
|
|
|
@ -59,6 +59,13 @@ If you have other target branches, include them in your regex. (See [Enabling pu
|
|||
The default branch also defaults to being a [protected branch](../user/project/protected_branches.md),
|
||||
which already limits users from pushing directly.
|
||||
|
||||
Some example regular expressions you can use in push rules:
|
||||
|
||||
- `^JIRA-` Branches must start with `JIRA-`.
|
||||
- `-JIRA$` Branches must end with `-JIRA`.
|
||||
- `^[a-z0-9\\-]{4,15}$` Branches must be between `4` and `15` characters long,
|
||||
accepting only lowercase letters, numbers and dashes.
|
||||
|
||||
#### Default restricted branch names
|
||||
|
||||
> Introduced in GitLab 12.10.
|
||||
|
|
|
@ -1160,6 +1160,56 @@ The API Fuzzing engine outputs an error message when it cannot establish a conne
|
|||
- Remove the `FUZZAPI_API` variable from the `.gitlab-ci.yml` file. The value will be inherited from the API Fuzzing CI/CD template. We recommend this method instead of manually setting a value.
|
||||
- If removing the variable is not possible, check to see if this value has changed in the latest version of the [API Fuzzing CI/CD template](https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Security/API-Fuzzing.gitlab-ci.yml). If so, update the value in the `.gitlab-ci.yml` file.
|
||||
|
||||
### Application cannot determine the base URL for the target API
|
||||
|
||||
The API Fuzzing analyzer outputs an error message when it cannot determine the target API after inspecting the OpenAPI document. This error message is shown when the target API has not been set in the `.gitlab-ci.yml`file, it is not available in the `environment_url.txt` file, and it could not be computed using the OpenAPI document.
|
||||
|
||||
There is an order of precedence in which the API Fuzzing analyzer tries to get the target API when checking the different sources. First, it will try to use the `FUZZAPI_TARGET_URL`. If the environment variable has not been set, then the API Fuzzing analyzer will attempt to use the `environment_url.txt` file. If there is no file `environment_url.txt`, the API Fuzzing analyzer will then use the OpenAPI document contents and the URL provided in `FUZZAPI_OPENAPI` (if a URL is provided) to try to compute the target API.
|
||||
|
||||
The best-suited solution will depend on whether or not your target API changes for each deployment. In static environments, the target API is the same for each deployment, in this case please refer to the [static environment solution](#static-environment-solution). If the target API changes for each deployment a [dynamic environment solution](#dynamic-environment-solutions) should be applied.
|
||||
|
||||
#### Static environment solution
|
||||
|
||||
This solution is for pipelines in which the target API URL doesn't change (is static).
|
||||
|
||||
**Add environmental variable**
|
||||
|
||||
For environments where the target API remains the same, we recommend you specify the target URL by using the `FUZZAPI_TARGET_URL` environment variable. In your `.gitlab-ci.yml` file, add a variable `FUZZAPI_TARGET_URL`. The variable must be set to the base URL of API testing target. For example:
|
||||
|
||||
```yaml
|
||||
include:
|
||||
- template: API-Fuzzing.gitlab-ci.yml
|
||||
|
||||
variables:
|
||||
FUZZAPI_TARGET_URL: http://test-deployment/
|
||||
FUZZAPI_OPENAPI: test-api-specification.json
|
||||
```
|
||||
|
||||
#### Dynamic environment solutions
|
||||
|
||||
In a dynamic environment your target API changes for each different deployment. In this case, there is more than one possible solution, we recommend to use the `environment_url.txt` file when dealing with dynamic environments.
|
||||
|
||||
**Use environment_url.txt**
|
||||
|
||||
To support dynamic environments in which the target API URL changes during each pipeline, API Fuzzing supports the use of an `environment_url.txt` file that contains the URL to use. This file is not checked into the repository, instead it's created during the pipeline by the job that deploys the test target and collected as an artifact that can be used by later jobs in the pipeline. The job that creates the `environment_url.txt` file must run before the API Fuzzing job.
|
||||
|
||||
1. Modify the test target deployment job adding the base URL in an `environment_url.txt` file at the root of your project.
|
||||
1. Modify the test target deployment job collecting the `environment_url.txt` as an artifact.
|
||||
|
||||
Example:
|
||||
|
||||
```yaml
|
||||
deploy-test-target:
|
||||
script:
|
||||
# Perform deployment steps
|
||||
# Create environment_url.txt (example)
|
||||
- echo http://${CI_PROJECT_ID}-${CI_ENVIRONMENT_SLUG}.example.org > environment_url.txt
|
||||
|
||||
artifacts:
|
||||
paths:
|
||||
- environment_url.txt
|
||||
```
|
||||
|
||||
<!--
|
||||
### Target Container
|
||||
|
||||
|
|
|
@ -1076,6 +1076,56 @@ The DAST API engine outputs an error message when it cannot establish a connecti
|
|||
- Remove the `DAST_API_API` variable from the `.gitlab-ci.yml` file. The value will be inherited from the DAST API CI/CD template. We recommend this method instead of manually setting a value.
|
||||
- If removing the variable is not possible, check to see if this value has changed in the latest version of the [DAST API CI/CD template](https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Security/DAST-API.gitlab-ci.yml). If so, update the value in the `.gitlab-ci.yml` file.
|
||||
|
||||
### Application cannot determine the base URL for the target API
|
||||
|
||||
The DAST API engine outputs an error message when it cannot determine the target API after inspecting the OpenAPI document. This error message is shown when the target API has not been set in the `.gitlab-ci.yml` file, it is not available in the `environment_url.txt` file, and it could not be computed using the OpenAPI document.
|
||||
|
||||
There is a order of precedence in which the DAST API engine tries to get the target API when checking the different sources. First, it will try to use the `DAST_API_TARGET_URL`. If the environment variable has not been set, then the DAST API engine will attempt to use the `environment_url.txt` file. If there is no file `environment_url.txt`, then the DAST API engine will use the OpenAPI document contents and the URL provided in `DAST_API_OPENAPI` (if a URL is provided) to try to compute the target API.
|
||||
|
||||
The best-suited solution will depend on whether or not your target API changes for each deployment. In static environments, the target API is the same for each deployment, in this case please refer to the [static environment solution](#static-environment-solution). If the target API changes for each deployment a [dynamic environment solution](#dynamic-environment-solutions) should be applied.
|
||||
|
||||
#### Static environment solution
|
||||
|
||||
This solution is for pipelines in which the target API URL doesn't change (is static).
|
||||
|
||||
**Add environmental variable**
|
||||
|
||||
For environments where the target API remains the same, we recommend you specify the target URL by using the `DAST_API_TARGET_URL` environment variable. In your `.gitlab-ci.yml`, add a variable `DAST_API_TARGET_URL`. The variable must be set to the base URL of API testing target. For example:
|
||||
|
||||
```yaml
|
||||
include:
|
||||
- template: DAST-API.gitlab-ci.yml
|
||||
|
||||
variables:
|
||||
DAST_API_TARGET_URL: http://test-deployment/
|
||||
DAST_API_OPENAPI: test-api-specification.json
|
||||
```
|
||||
|
||||
#### Dynamic environment solutions
|
||||
|
||||
In a dynamic environment your target API changes for each different deployment. In this case, there is more than one possible solution, we recommend you use the `environment_url.txt` file when dealing with dynamic environments.
|
||||
|
||||
**Use environment_url.txt**
|
||||
|
||||
To support dynamic environments in which the target API URL changes during each pipeline, DAST API engine supports the use of an `environment_url.txt` file that contains the URL to use. This file is not checked into the repository, instead it's created during the pipeline by the job that deploys the test target and collected as an artifact that can be used by later jobs in the pipeline. The job that creates the `environment_url.txt` file must run before the DAST API engine job.
|
||||
|
||||
1. Modify the test target deployment job adding the base URL in an `environment_url.txt` file at the root of your project.
|
||||
1. Modify the test target deployment job collecting the `environment_url.txt` as an artifact.
|
||||
|
||||
Example:
|
||||
|
||||
```yaml
|
||||
deploy-test-target:
|
||||
script:
|
||||
# Perform deployment steps
|
||||
# Create environment_url.txt (example)
|
||||
- echo http://${CI_PROJECT_ID}-${CI_ENVIRONMENT_SLUG}.example.org > environment_url.txt
|
||||
|
||||
artifacts:
|
||||
paths:
|
||||
- environment_url.txt
|
||||
```
|
||||
|
||||
## Glossary
|
||||
|
||||
- Assert: Assertions are detection modules used by checks to trigger a vulnerability. Many assertions have
|
||||
|
|
|
@ -68,8 +68,8 @@ time as pushing changes:
|
|||
| `merge_request.description="<description>"` | Set the description of the merge request. Ex: `git push -o merge_request.description="The description I want"`. | [12.2](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/64320) |
|
||||
| `merge_request.label="<label>"` | Add labels to the merge request. If the label does not exist, it is created. For example, for two labels: `git push -o merge_request.label="label1" -o merge_request.label="label2"`. | [12.3](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/31831) |
|
||||
| `merge_request.unlabel="<label>"` | Remove labels from the merge request. For example, for two labels: `git push -o merge_request.unlabel="label1" -o merge_request.unlabel="label2"`. | [12.3](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/31831) |
|
||||
| `merge_request.assign="<user>"` | Assign users to the merge request. For example, for two users: `git push -o merge_request.assign="user1" -o merge_request.assign="user2"`. | [12.9](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/XXXXX) |
|
||||
| `merge_request.unassign="<user>"` | Remove assigned users from the merge request. For example, for two users: `git push -o merge_request.unassign="user1" -o merge_request.unassign="user2"`. | [12.9](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/XXXXX) |
|
||||
| `merge_request.assign="<user>"` | Assign users to the merge request. For example, for two users: `git push -o merge_request.assign="user1" -o merge_request.assign="user2"`. | [13.10](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/25904) |
|
||||
| `merge_request.unassign="<user>"` | Remove assigned users from the merge request. For example, for two users: `git push -o merge_request.unassign="user1" -o merge_request.unassign="user2"`. | [13.10](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/25904) |
|
||||
|
||||
If you use a push option that requires text with spaces in it, you need to enclose it
|
||||
in quotes (`"`). You can omit the quotes if there are no spaces. Some examples:
|
||||
|
|
|
@ -18,7 +18,7 @@ module API
|
|||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def retrieve_members(source, params:, deep: false)
|
||||
members = deep ? find_all_members(source) : source_members(source).where.not(user_id: nil)
|
||||
members = deep ? find_all_members(source) : source_members(source).connected_to_user
|
||||
members = members.includes(:user)
|
||||
members = members.references(:user).merge(User.search(params[:query])) if params[:query].present?
|
||||
members = members.where(user_id: params[:user_ids]) if params[:user_ids].present?
|
||||
|
|
|
@ -9,6 +9,7 @@ module Gitlab
|
|||
class Authentication < Gitlab::Auth::OAuth::Authentication
|
||||
def login(login, password)
|
||||
return false unless Gitlab::CurrentSettings.password_authentication_enabled_for_git?
|
||||
return false if user.password_based_login_forbidden?
|
||||
|
||||
return user if user&.valid_password?(password)
|
||||
end
|
||||
|
|
|
@ -11,6 +11,8 @@ module Gitlab
|
|||
|
||||
attr_reader :subject, :user
|
||||
|
||||
delegate :cache_key, to: :subject
|
||||
|
||||
def initialize(subject, user)
|
||||
@subject = subject
|
||||
@user = user
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
# Read more about the feature here: https://docs.gitlab.com/ee/user/project/merge_requests/browser_performance_testing.html
|
||||
|
||||
stages:
|
||||
- build
|
||||
- test
|
||||
- deploy
|
||||
- performance
|
||||
|
||||
browser_performance:
|
||||
stage: performance
|
||||
image: docker:git
|
||||
variables:
|
||||
URL: ''
|
||||
SITESPEED_IMAGE: sitespeedio/sitespeed.io
|
||||
SITESPEED_VERSION: 14.1.0
|
||||
SITESPEED_OPTIONS: ''
|
||||
services:
|
||||
- docker:stable-dind
|
||||
script:
|
||||
- mkdir gitlab-exporter
|
||||
# Busybox wget does not support proxied HTTPS, get the real thing.
|
||||
# See https://gitlab.com/gitlab-org/gitlab/-/issues/287611.
|
||||
- (env | grep -i _proxy= 2>&1 >/dev/null) && apk --no-cache add wget
|
||||
- wget -O ./gitlab-exporter/index.js https://gitlab.com/gitlab-org/gl-performance/raw/1.1.0/index.js
|
||||
- mkdir sitespeed-results
|
||||
- |
|
||||
function propagate_env_vars() {
|
||||
CURRENT_ENV=$(printenv)
|
||||
|
||||
for VAR_NAME; do
|
||||
echo $CURRENT_ENV | grep "${VAR_NAME}=" > /dev/null && echo "--env $VAR_NAME "
|
||||
done
|
||||
}
|
||||
- |
|
||||
docker run \
|
||||
$(propagate_env_vars \
|
||||
auto_proxy \
|
||||
https_proxy \
|
||||
http_proxy \
|
||||
no_proxy \
|
||||
AUTO_PROXY \
|
||||
HTTPS_PROXY \
|
||||
HTTP_PROXY \
|
||||
NO_PROXY \
|
||||
) \
|
||||
--shm-size=1g --rm -v "$(pwd)":/sitespeed.io $SITESPEED_IMAGE:$SITESPEED_VERSION --plugins.add ./gitlab-exporter --cpu --outputFolder sitespeed-results $URL $SITESPEED_OPTIONS
|
||||
- mv sitespeed-results/data/performance.json browser-performance.json
|
||||
artifacts:
|
||||
paths:
|
||||
- sitespeed-results/
|
||||
reports:
|
||||
browser_performance: browser-performance.json
|
|
@ -36988,6 +36988,9 @@ msgstr ""
|
|||
msgid "You are not allowed to approve a user"
|
||||
msgstr ""
|
||||
|
||||
msgid "You are not allowed to log in using password"
|
||||
msgstr ""
|
||||
|
||||
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -16,4 +16,6 @@ RSpec.describe GitlabSchema.types['Repository'] do
|
|||
specify { expect(described_class).to have_graphql_field(:blobs) }
|
||||
|
||||
specify { expect(described_class).to have_graphql_field(:branch_names, calls_gitaly?: true, complexity: 170) }
|
||||
|
||||
specify { expect(described_class).to have_graphql_field(:disk_path) }
|
||||
end
|
||||
|
|
|
@ -291,10 +291,11 @@ RSpec.describe CommitsHelper do
|
|||
end
|
||||
|
||||
describe "#commit_partial_cache_key" do
|
||||
subject { helper.commit_partial_cache_key(commit, ref: ref, merge_request: merge_request, request: request) }
|
||||
subject(:cache_key) { helper.commit_partial_cache_key(commit, ref: ref, merge_request: merge_request, request: request) }
|
||||
|
||||
let(:commit) { create(:commit).present(current_user: user) }
|
||||
let(:commit_status) { create(:commit_status) }
|
||||
let(:commit_status) { Gitlab::Ci::Status::Running.new(pipeline, user) }
|
||||
let(:pipeline) { create(:ci_pipeline, :running) }
|
||||
let(:user) { create(:user) }
|
||||
let(:ref) { "master" }
|
||||
let(:merge_request) { nil }
|
||||
|
@ -315,12 +316,19 @@ RSpec.describe CommitsHelper do
|
|||
is_expected.to include(
|
||||
{
|
||||
merge_request: merge_request,
|
||||
pipeline_status: Digest::SHA1.hexdigest(commit_status.to_s),
|
||||
pipeline_status: commit_status,
|
||||
xhr: true,
|
||||
controller: "commits",
|
||||
path: current_path
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
describe "final cache key output" do
|
||||
subject { ActiveSupport::Cache.expand_cache_key(cache_key) }
|
||||
|
||||
it { is_expected.to include(commit.cache_key) }
|
||||
it { is_expected.to include(pipeline.cache_key) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
17
spec/lib/gitlab/ci/status/core_spec.rb
Normal file
17
spec/lib/gitlab/ci/status/core_spec.rb
Normal file
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "spec_helper"
|
||||
|
||||
RSpec.describe Gitlab::Ci::Status::Core do
|
||||
let(:subj) { double("subject", cache_key: "foo") }
|
||||
|
||||
subject(:status) do
|
||||
described_class.new(subj, double("user"))
|
||||
end
|
||||
|
||||
describe "#cache_key" do
|
||||
it "uses the subject's cache key" do
|
||||
expect(status.cache_key).to eq(subj.cache_key)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -2245,22 +2245,31 @@ RSpec.describe Group do
|
|||
end
|
||||
|
||||
describe '#access_request_approvers_to_be_notified' do
|
||||
it 'returns a maximum of ten, active, non_requested owners of the group in recent_sign_in descending order' do
|
||||
group = create(:group, :public)
|
||||
let_it_be(:group) { create(:group, :public) }
|
||||
|
||||
it 'returns a maximum of ten owners of the group in recent_sign_in descending order' do
|
||||
users = create_list(:user, 12, :with_sign_ins)
|
||||
active_owners = users.map do |user|
|
||||
create(:group_member, :owner, group: group, user: user)
|
||||
end
|
||||
|
||||
create(:group_member, :owner, :blocked, group: group)
|
||||
create(:group_member, :maintainer, group: group)
|
||||
create(:group_member, :access_request, :owner, group: group)
|
||||
|
||||
active_owners_in_recent_sign_in_desc_order = group.members_and_requesters.where(id: active_owners).order_recent_sign_in.limit(10)
|
||||
active_owners_in_recent_sign_in_desc_order = group.members_and_requesters
|
||||
.id_in(active_owners)
|
||||
.order_recent_sign_in.limit(10)
|
||||
|
||||
expect(group.access_request_approvers_to_be_notified).to eq(active_owners_in_recent_sign_in_desc_order)
|
||||
end
|
||||
|
||||
it 'returns active, non_invited, non_requested owners of the group' do
|
||||
owner = create(:group_member, :owner, source: group)
|
||||
|
||||
create(:group_member, :maintainer, group: group)
|
||||
create(:group_member, :owner, :invited, group: group)
|
||||
create(:group_member, :owner, :access_request, group: group)
|
||||
create(:group_member, :owner, :blocked, group: group)
|
||||
|
||||
expect(group.access_request_approvers_to_be_notified.to_a).to eq([owner])
|
||||
end
|
||||
end
|
||||
|
||||
describe '.groups_including_descendants_by' do
|
||||
|
|
|
@ -408,6 +408,20 @@ RSpec.describe Member do
|
|||
it { is_expected.not_to include @member_with_minimal_access }
|
||||
end
|
||||
|
||||
describe '.connected_to_user' do
|
||||
subject { described_class.connected_to_user.to_a }
|
||||
|
||||
it { is_expected.to include @owner }
|
||||
it { is_expected.to include @maintainer }
|
||||
it { is_expected.to include @accepted_invite_member }
|
||||
it { is_expected.to include @accepted_request_member }
|
||||
it { is_expected.to include @blocked_maintainer }
|
||||
it { is_expected.to include @blocked_developer }
|
||||
it { is_expected.to include @requested_member }
|
||||
it { is_expected.to include @member_with_minimal_access }
|
||||
it { is_expected.not_to include @invited_member }
|
||||
end
|
||||
|
||||
describe '.authorizable' do
|
||||
subject { described_class.authorizable.to_a }
|
||||
|
||||
|
|
|
@ -6304,23 +6304,31 @@ RSpec.describe Project, factory_default: :keep do
|
|||
end
|
||||
|
||||
describe '#access_request_approvers_to_be_notified' do
|
||||
it 'returns a maximum of ten, active, non_requested maintainers of the project in recent_sign_in descending order' do
|
||||
group = create(:group, :public)
|
||||
project = create(:project, group: group)
|
||||
let_it_be(:project) { create(:project, group: create(:group, :public)) }
|
||||
|
||||
it 'returns a maximum of ten maintainers of the project in recent_sign_in descending order' do
|
||||
users = create_list(:user, 12, :with_sign_ins)
|
||||
active_maintainers = users.map do |user|
|
||||
create(:project_member, :maintainer, user: user)
|
||||
create(:project_member, :maintainer, user: user, project: project)
|
||||
end
|
||||
|
||||
create(:project_member, :maintainer, :blocked, project: project)
|
||||
create(:project_member, :developer, project: project)
|
||||
create(:project_member, :access_request, :maintainer, project: project)
|
||||
|
||||
active_maintainers_in_recent_sign_in_desc_order = project.members_and_requesters.where(id: active_maintainers).order_recent_sign_in.limit(10)
|
||||
active_maintainers_in_recent_sign_in_desc_order = project.members_and_requesters
|
||||
.id_in(active_maintainers)
|
||||
.order_recent_sign_in.limit(10)
|
||||
|
||||
expect(project.access_request_approvers_to_be_notified).to eq(active_maintainers_in_recent_sign_in_desc_order)
|
||||
end
|
||||
|
||||
it 'returns active, non_invited, non_requested maintainers of the project' do
|
||||
maintainer = create(:project_member, :maintainer, source: project)
|
||||
|
||||
create(:project_member, :developer, project: project)
|
||||
create(:project_member, :maintainer, :invited, project: project)
|
||||
create(:project_member, :maintainer, :access_request, project: project)
|
||||
create(:project_member, :maintainer, :blocked, project: project)
|
||||
|
||||
expect(project.access_request_approvers_to_be_notified.to_a).to eq([maintainer])
|
||||
end
|
||||
end
|
||||
|
||||
describe '#pages_lookup_path' do
|
||||
|
|
|
@ -393,6 +393,34 @@ RSpec.describe ProjectPolicy do
|
|||
end
|
||||
end
|
||||
|
||||
describe 'read_storage_disk_path' do
|
||||
context 'when no user' do
|
||||
let(:current_user) { anonymous }
|
||||
|
||||
it { expect_disallowed(:read_storage_disk_path) }
|
||||
end
|
||||
|
||||
context 'admin' do
|
||||
let(:current_user) { admin }
|
||||
|
||||
context 'when admin mode is enabled', :enable_admin_mode do
|
||||
it { expect_allowed(:read_storage_disk_path) }
|
||||
end
|
||||
|
||||
context 'when admin mode is disabled' do
|
||||
it { expect_disallowed(:read_storage_disk_path) }
|
||||
end
|
||||
end
|
||||
|
||||
%w(guest reporter developer maintainer owner).each do |role|
|
||||
context role do
|
||||
let(:current_user) { send(role) }
|
||||
|
||||
it { expect_disallowed(:read_storage_disk_path) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'alert bot' do
|
||||
let(:current_user) { User.alert_bot }
|
||||
|
||||
|
|
|
@ -36,6 +36,30 @@ RSpec.describe 'getting a repository in a project' do
|
|||
end
|
||||
end
|
||||
|
||||
context 'as a non-admin' do
|
||||
let(:current_user) { create(:user) }
|
||||
|
||||
before do
|
||||
project.add_role(current_user, :developer)
|
||||
end
|
||||
|
||||
it 'does not return diskPath' do
|
||||
post_graphql(query, current_user: current_user)
|
||||
|
||||
expect(graphql_data['project']['repository']).not_to be_nil
|
||||
expect(graphql_data['project']['repository']['diskPath']).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'as an admin' do
|
||||
it 'returns diskPath' do
|
||||
post_graphql(query, current_user: create(:admin))
|
||||
|
||||
expect(graphql_data['project']['repository']).not_to be_nil
|
||||
expect(graphql_data['project']['repository']['diskPath']).to eq project.disk_path
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the repository is only accessible to members' do
|
||||
let(:project) do
|
||||
create(:project, :public, :repository, repository_access_level: ProjectFeature::PRIVATE)
|
||||
|
|
52
spec/services/ci/delete_unit_tests_service_spec.rb
Normal file
52
spec/services/ci/delete_unit_tests_service_spec.rb
Normal file
|
@ -0,0 +1,52 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Ci::DeleteUnitTestsService do
|
||||
describe '#execute' do
|
||||
let!(:unit_test_1) { create(:ci_unit_test) }
|
||||
let!(:unit_test_2) { create(:ci_unit_test) }
|
||||
let!(:unit_test_3) { create(:ci_unit_test) }
|
||||
let!(:unit_test_4) { create(:ci_unit_test) }
|
||||
let!(:unit_test_1_recent_failure) { create(:ci_unit_test_failure, unit_test: unit_test_1) }
|
||||
let!(:unit_test_1_old_failure) { create(:ci_unit_test_failure, unit_test: unit_test_1, failed_at: 15.days.ago) }
|
||||
let!(:unit_test_2_old_failure) { create(:ci_unit_test_failure, unit_test: unit_test_2, failed_at: 15.days.ago) }
|
||||
let!(:unit_test_3_old_failure) { create(:ci_unit_test_failure, unit_test: unit_test_3, failed_at: 15.days.ago) }
|
||||
let!(:unit_test_4_old_failure) { create(:ci_unit_test_failure, unit_test: unit_test_4, failed_at: 15.days.ago) }
|
||||
|
||||
before do
|
||||
stub_const("#{described_class.name}::BATCH_SIZE", 2)
|
||||
|
||||
described_class.new.execute
|
||||
end
|
||||
|
||||
it 'does not delete unit test failures not older than 14 days' do
|
||||
expect(unit_test_1_recent_failure.reload).to be_persisted
|
||||
end
|
||||
|
||||
it 'deletes unit test failures older than 14 days' do
|
||||
ids = [
|
||||
unit_test_1_old_failure,
|
||||
unit_test_2_old_failure,
|
||||
unit_test_3_old_failure,
|
||||
unit_test_4_old_failure
|
||||
].map(&:id)
|
||||
|
||||
result = Ci::UnitTestFailure.where(id: ids)
|
||||
|
||||
expect(result).to be_empty
|
||||
end
|
||||
|
||||
it 'deletes unit tests that have no more associated unit test failures' do
|
||||
ids = [
|
||||
unit_test_2,
|
||||
unit_test_3,
|
||||
unit_test_4
|
||||
].map(&:id)
|
||||
|
||||
result = Ci::UnitTest.where(id: ids)
|
||||
|
||||
expect(result).to be_empty
|
||||
end
|
||||
end
|
||||
end
|
33
spec/workers/ci/delete_unit_tests_worker_spec.rb
Normal file
33
spec/workers/ci/delete_unit_tests_worker_spec.rb
Normal file
|
@ -0,0 +1,33 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Ci::DeleteUnitTestsWorker do
|
||||
let(:worker) { described_class.new }
|
||||
|
||||
describe '#perform' do
|
||||
it 'executes a service' do
|
||||
expect_next_instance_of(Ci::DeleteUnitTestsService) do |instance|
|
||||
expect(instance).to receive(:execute)
|
||||
end
|
||||
|
||||
worker.perform
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'an idempotent worker' do
|
||||
let!(:unit_test_1) { create(:ci_unit_test) }
|
||||
let!(:unit_test_2) { create(:ci_unit_test) }
|
||||
let!(:unit_test_1_recent_failure) { create(:ci_unit_test_failure, unit_test: unit_test_1) }
|
||||
let!(:unit_test_2_old_failure) { create(:ci_unit_test_failure, unit_test: unit_test_2, failed_at: 15.days.ago) }
|
||||
|
||||
it 'only deletes old unit tests and their failures' do
|
||||
subject
|
||||
|
||||
expect(unit_test_1.reload).to be_persisted
|
||||
expect(unit_test_1_recent_failure.reload).to be_persisted
|
||||
expect(Ci::UnitTest.find_by(id: unit_test_2.id)).to be_nil
|
||||
expect(Ci::UnitTestFailure.find_by(id: unit_test_2_old_failure.id)).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue