Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2023-03-14 00:08:23 +00:00
parent 5699348c82
commit 6031d32432
28 changed files with 463 additions and 73 deletions

View File

@ -96,7 +96,7 @@ class Ability
# Hook call right before ability check.
def before_check(policy, ability, user, subject, opts)
# See Support::AbilityCheck.
# See Support::AbilityCheck and Support::PermissionsCheck.
end
def policy_for(user, subject = :global)

View File

@ -156,6 +156,9 @@ class MergeRequest < ApplicationRecord
# when creating new merge request
attr_accessor :can_be_created, :compare_commits, :diff_options, :compare
# Flag to skip triggering mergeRequestMergeStatusUpdated GraphQL subscription.
attr_accessor :skip_merge_status_trigger
participant :reviewers
# Keep states definition to be evaluated before the state_machine block to avoid spec failures.
@ -252,6 +255,8 @@ class MergeRequest < ApplicationRecord
end
after_transition any => [:unchecked, :cannot_be_merged_recheck, :checking, :cannot_be_merged_rechecking, :can_be_merged, :cannot_be_merged] do |merge_request, transition|
next if merge_request.skip_merge_status_trigger
merge_request.run_after_commit do
GraphqlTriggers.merge_request_merge_status_updated(merge_request)
end

View File

@ -127,16 +127,23 @@ module MergeRequests
merge_requests_array = merge_requests.to_a + merge_requests_from_forks.to_a
filter_merge_requests(merge_requests_array).each do |merge_request|
skip_merge_status_trigger = true
if branch_and_project_match?(merge_request) || @push.force_push?
merge_request.reload_diff(current_user)
# Clear existing merge error if the push were directed at the
# source branch. Clearing the error when the target branch
# changes will hide the error from the user.
merge_request.merge_error = nil
# Don't skip trigger since we to update the MR's merge status in real-time
# when the push if for the MR's source branch and project.
skip_merge_status_trigger = false
elsif merge_request.merge_request_diff.includes_any_commits?(push_commit_ids)
merge_request.reload_diff(current_user)
end
merge_request.skip_merge_status_trigger = skip_merge_status_trigger
merge_request.mark_as_unchecked
end

View File

@ -1,10 +1,9 @@
- group = local_assigns.fetch(:group)
%li.group-row.gl-py-3.gl-align-items-center{ class: 'gl-display-flex!', data: { qa_selector: 'group_row_content' } }
.avatar-container.rect-avatar.s40.gl-flex-shrink-0
= group_icon(group, class: "avatar s40")
= render Pajamas::AvatarComponent.new(group, size: 32, alt: '')
.gl-min-w-0.gl-flex-grow-1
.gl-min-w-0.gl-flex-grow-1.gl-ml-3
.title
= link_to [:admin, group], class: 'group-name', data: { qa_selector: 'group_name_link' } do
= group.full_name

View File

@ -20,8 +20,7 @@
- c.body do
%ul.content-list.content-list-items-padding
%li
.avatar-container.rect-avatar.s60
= group_icon(@group, class: "avatar s60")
= render Pajamas::AvatarComponent.new(@group, size: 64, alt: '')
%li
%span.light= _('Name:')
%strong

View File

@ -3,9 +3,8 @@
%ul.content-list
- @projects.each do |project|
%li.project-row.gl-align-items-center{ class: 'gl-display-flex!' }
.avatar-container.rect-avatar.s40.gl-flex-shrink-0
= project_icon(project, alt: '', class: 'avatar project-avatar s40', width: 40, height: 40)
.gl-min-w-0.gl-flex-grow-1
= render Pajamas::AvatarComponent.new(project, size: 32, alt: '')
.gl-min-w-0.gl-flex-grow-1.gl-ml-3
.title
= link_to(admin_project_path(project)) do
%span.project-full-name

View File

@ -2,10 +2,9 @@
- title = topic.title || topic.name
%li.topic-row.gl-py-3.gl-align-items-center{ class: 'gl-display-flex!', data: { qa_selector: 'topic_row_content' } }
.avatar-container.rect-avatar.s40.gl-flex-shrink-0
= topic_icon(topic, class: "avatar s40")
= render Pajamas::AvatarComponent.new(topic, size: 32, alt: '')
.gl-min-w-0.gl-flex-grow-1
.gl-min-w-0.gl-flex-grow-1.gl-ml-3
.title
= link_to title, topic_explore_projects_path(topic_name: topic.name)
%div

View File

@ -16,7 +16,7 @@
- if current_action?(:new) || current_action?(:create)
%span.float-left.gl-mr-3
\/
= text_field_tag 'file_name', params[:file_name], placeholder: "File name", data: { qa_selector: 'file_name_field' },
= text_field_tag 'file_name', params[:file_name], placeholder: "Filename", data: { qa_selector: 'file_name_field' },
required: true, class: 'form-control gl-form-input new-file-name js-file-path-name-input', value: params[:file_name] || (should_suggest_gitlab_ci_yml? ? '.gitlab-ci.yml' : '')
= render 'template_selectors'
- if should_suggest_gitlab_ci_yml?

View File

@ -15,18 +15,13 @@ module Gitlab
# client - An instance of Gitlab::GithubImport::Client.
# project - An instance of Project.
def import(client, project)
info(project.id, message: "starting importer", importer: 'Importer::CollaboratorsImporter')
waiter = Importer::CollaboratorsImporter
.new(project, client)
.execute
info(project.id, message: 'starting importer', importer: 'Importer::CollaboratorsImporter')
return skip_to_next_stage(project) unless has_push_access?(client, project.import_source)
waiter = Importer::CollaboratorsImporter.new(project, client).execute
project.import_state.refresh_jid_expiration
AdvanceStageWorker.perform_async(
project.id,
{ waiter.key => waiter.jobs_remaining },
:pull_requests_merged_by
)
move_to_next_stage(project, { waiter.key => waiter.jobs_remaining })
rescue StandardError => e
Gitlab::Import::ImportFailureService.track(
project_id: project.id,
@ -41,6 +36,27 @@ module Gitlab
private
def has_push_access?(client, repo)
client.repository(repo).dig(:permissions, :push)
end
def skip_to_next_stage(project)
Gitlab::GithubImport::Logger.warn(
log_attributes(
project.id,
message: 'no push access rights to fetch collaborators',
importer: 'Importer::CollaboratorsImporter'
)
)
move_to_next_stage(project, {})
end
def move_to_next_stage(project, waiters = {})
AdvanceStageWorker.perform_async(
project.id, waiters, :pull_requests_merged_by
)
end
def abort_on_failure
true
end

View File

@ -0,0 +1,132 @@
---
stage: Create
group: Source Code
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
# Code Owners development guidelines
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/219916) in GitLab 15.10.
This document was created to help contributors understand the code design of
[Code Owners](../../user/project/code_owners.md). You should read this
document before making changes to the code for this feature.
This document is intentionally limited to an overview of how the code is
designed, as code can change often. To understand how a specific part of the
feature works, view the code and the specs. The details here explain how the
major components of the Code Owners feature work.
NOTE:
This document should be updated when parts of the codebase referenced in this
document are updated, removed, or new parts are added.
## Business logic
All of the business logic for code owners is located in the `Gitlab::CodeOwners`
namespace. Code Owners is an EE-only feature, so the files only exist in the `./ee` directory.
- `Gitlab::CodeOwners`: the main module used to interact with the code owner rules.
- Defined in `./ee/lib/gitlab/code_owners.rb`.
- `Gitlab::CodeOwners::Loader`: finds the correct `CODEOWNER` file and loads the
content into a `Gitlab::CodeOwners::File` instance.
- Defined in `./ee/lib/gitlab/code_owners/loader.rb`.
- `Gitlab::CodeOwners::ReferenceExtractor`: extracts `CODEOWNER` user, group,
and email references from texts.
- Defined in `./ee/lib/gitlab/code_owners/reference_extractor.rb`.
- `Gitlab::CodeOwners::UsersLoader`: the correct `CODEOWNER` file and loads the
content into a `Gitlab::CodeOwners::File` instance.
- Defined in `./ee/lib/gitlab/code_owners/users_loader.rb`.
- `Gitlab::CodeOwners::GroupsLoader`: finds the correct `CODEOWNER` file and loads
the content into a `Gitlab::CodeOwners::File` instance.
- Defined in `./ee/lib/gitlab/code_owners/groups_loader.rb`.
- `Gitlab::CodeOwners::File`: wraps a `CODEOWNERS` file and exposes the data through
the class' public methods.
- Defined in `./ee/lib/gitlab/code_owners/file.rb`.
- `Gitlab::CodeOwners::Entry`: wraps an entry (a pattern and owners line) in a
`CODEOWNERS` file and exposes the data through the class' public methods.
- Defined in `./ee/lib/gitlab/code_owners/entry.rb`.
- `Gitlab::CodeOwners::Validator`: validates no files in the `CODEOWNERS` entries
have been changed when a user pushes to a protected branch with `require_code_owner_approval` enabled.
- Defined in `./ee/lib/gitlab/code_owners/validator.rb`.
## Related models
### `ProtectedBranch`
The `ProtectedBranch` model is defined in `app/models/protected_branch.rb` and
extended in `ee/app/ee/models/protected_branch.rb`. The EE version includes a column
named `require_code_owner_approval` which prevents changes from being pushed directly
to the branch being protected if the file is listed in `CODEOWNERS`.
### `ApprovalMergeRequestRule`
The `ApprovalMergeRequestRule` model is defined in `ee/app/models/approval_merge_request_rule.rb`.
The model stores approval rules for a merge request. We use multiple rule types,
including a `code_owner` type rule.
## Controllers and Services
The following controllers and services below are being used for the approval
rules feature to work:
### `Api::Internal::Base`
This `/internal/allowed` endpoint is called when pushing to GitLab to ensure the
user is allowed to push. The `/internal/allowed` endpoint performs a `Gitlab::Checks::DiffCheck`.
In EE, this includes code owner checks.
Defined in `lib/api/internal/base.rb`.
### `Repositories::GitHttpController`
When changes are pushed to GitLab over HTTP, the controller performs an access check
to ensure the user is allowed to push. The checks perform a `Gitlab::Checks::DiffCheck`.
In EE, this includes Code Owner checks.
Defined in `app/controllers/repositories/git_http_controller.rb`.
### `EE::Gitlab::Checks::DiffCheck`
This module extends the CE `Gitlab::Checks::DiffChecks` class and adds code owner
validation. It uses the `Gitlab::CodeOwner::Validator` class to verify users are
not pushing files listed in `CODEOWNER` directly to a protected branch while the
branch requires code owner approval.
### `MergeRequests::SyncCodeOwnerApprovalRules`
This service is defined in `services/merge_requests/sync_code_owner_approval_rules.rb` and used for:
- Deleting outdated code owner approval rules when new changes are pushed to a merge request.
- Creating code owner approval rules for each changed file in a merge request that is also listed in the `CODEOWNER` file.
## Flow
These flowcharts should help explain the flow from the controllers down to the
models for different features.
### Push changes to a protected branch with `require_code_owner_approval` enabled
```mermaid
graph TD
Api::Internal::Base --> Gitlab::GitAccess
Gitlab::GitAccess --> Gitlab::Checks::DiffCheck
Gitlab::Checks::DiffCheck --> Gitlab::CodeOwners::Validator
Gitlab::CodeOwners::Validator --> ProtectedBranch
Gitlab::CodeOwners::Validator --> Gitlab::CodeOwners::Loader
Gitlab::CodeOwners::Loader --> Gitlab::CodeOwners::Entry
```
### Sync code owner rules to merge request approval rules
```mermaid
graph TD
EE::ProtectedBranches::CreateService --> MergeRequest::SyncCodeOwnerApprovalRules
EE::MergeRequestRefreshService --> MergeRequest::SyncCodeOwnerApprovalRules
EE::MergeRequests::ReloadMergeHeadDiffService --> MergeRequest::SyncCodeOwnerApprovalRules
EE::MergeRequests::CreateService --> MergeRequests::SyncCodeOwnerApprovalRulesWorker
EE::MergeRequests::UpdateService --> MergeRequests::SyncCodeOwnerApprovalRulesWorker
MergeRequests::SyncCodeOwnerApprovalRulesWorker --> MergeRequest::SyncCodeOwnerApprovalRules
MergeRequest::SyncCodeOwnerApprovalRules --> id1{delete outdated code owner rules}
MergeRequest::SyncCodeOwnerApprovalRules --> id2{create rule for each code owner entry}
```

View File

@ -969,6 +969,12 @@ Use lowercase for **personal access token**.
Do not use **please**. For details, see the [Microsoft style guide](https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/p/please).
## prerequisites
Use **prerequisites** when documenting the steps before a task. Do not use **requirements**.
For more information, see [the task topic type](../topic_types/task.md).
## press
Use **press** when talking about keyboard keys. For example:
@ -1031,6 +1037,12 @@ Do not use **Reporter permissions**. A user who is assigned the Reporter role ha
Use title case for **Repository Mirroring**.
## requirements
Use **prerequisites** when documenting the steps before a task. Do not use **requirements**.
For more information, see [the task topic type](../topic_types/task.md).
## respectively
Avoid **respectively** and be more precise instead.

View File

@ -198,3 +198,124 @@ Say, for example, we extract the whole endpoint into a service. The `can?` check
- If the finder doesn't accept `current_user`, and therefore doesn't check permissions, then probably no.
- If the finder accepts `current_user`, and doesn't check permissions, then it would be a good idea to double check other usages of the finder, and we might consider adding authorization.
- If the finder accepts `current_user`, and already checks permissions, then either we need to add our case, or the existing checks are appropriate.
### Refactoring permissions
#### Finding existing permissions checks
As mentioned [above](#where-should-permissions-be-checked), permissions are
often checked in multiple locations for a single endpoint or web request. As a
result, finding the list of authorization checks that are run for a given endpoint
can be challenging.
To assist with this, you can locally set `GITLAB_DEBUG_POLICIES=true`.
This outputs information about which abilities are checked in the requests
made in any specs that you run. The output also includes the line of code where the
authorization check was made. Caller information is especially helpful in cases
where there is metaprogramming used because those cases are difficult to find by
grepping for ability name strings.
Example:
```shell
# example spec run
GITLAB_DEBUG_POLICIES=true bundle exec rspec spec/controllers/groups_controller_spec.rb:162
# permissions debug output when spec is run; if multiple policy checks are run they will all be in the debug output.
POLICY CHECK DEBUG -> policy: GlobalPolicy, ability: create_group, called_from: ["/gitlab/app/controllers/application_controller.rb:245:in `can?'", "/gitlab/app/controllers/groups_controller.rb:255:in `authorize_create_group!'"]
```
This flag is meant to help learn more about authorization checks while
refactoring and should not remain enabled for any specs on the default branch.
#### Understanding logic for individual abilities
References to an ability may appear in a `DeclarativePolicy` class many times
and depend on conditions and rules which reference other abilities. As a result,
it can be challenging to know exactly which conditions apply to a particular
ability.
`DeclarativePolicy` provides a `ability_map` for each Policy class, which
pulls all Rules for an ability into an array.
Example:
```ruby
> GroupPolicy.ability_map.map.select { |k,v| k == :read_group_member }
=> {:read_group_member=>[[:enable, #<Rule can?(:read_group)>], [:prevent, #<Rule ~can_read_group_member>]]}
> GroupPolicy.ability_map.map.select { |k,v| k == :read_group }
=> {:read_group=>
[[:enable, #<Rule public_group>],
[:enable, #<Rule logged_in_viewable>],
[:enable, #<Rule guest>],
[:enable, #<Rule admin>],
[:enable, #<Rule has_projects>],
[:enable, #<Rule read_package_registry_deploy_token>],
[:enable, #<Rule write_package_registry_deploy_token>],
[:prevent, #<Rule all?(~public_group, ~admin, user_banned_from_group)>],
[:enable, #<Rule auditor>],
[:prevent, #<Rule needs_new_sso_session>],
[:prevent, #<Rule all?(ip_enforcement_prevents_access, ~owner, ~auditor)>]]}
```
`DeclarativePolicy` also provides a `debug` method that can be used to
understand the logic tree for a specific object and actor. The output is similar
to the list of rules from `ability_map`. But, `DeclarativePolicy` stops
evaluating rules once one `prevent`s an ability, so it is possible that
not all conditions are called.
Example:
```ruby
policy = GroupPolicy.new(User.last, Group.last)
policy.debug(:read_group)
- [0] enable when public_group ((@custom_guest_user1 : Group/139))
- [0] enable when logged_in_viewable ((@custom_guest_user1 : Group/139))
- [0] enable when admin ((@custom_guest_user1 : Group/139))
- [0] enable when auditor ((@custom_guest_user1 : Group/139))
- [14] prevent when all?(~public_group, ~admin, user_banned_from_group) ((@custom_guest_user1 : Group/139))
- [14] prevent when needs_new_sso_session ((@custom_guest_user1 : Group/139))
- [16] enable when guest ((@custom_guest_user1 : Group/139))
- [16] enable when has_projects ((@custom_guest_user1 : Group/139))
- [16] enable when read_package_registry_deploy_token ((@custom_guest_user1 : Group/139))
- [16] enable when write_package_registry_deploy_token ((@custom_guest_user1 : Group/139))
[21] prevent when all?(ip_enforcement_prevents_access, ~owner, ~auditor) ((@custom_guest_user1 : Group/139))
=> #<DeclarativePolicy::Runner::State:0x000000015c665050
@called_conditions=
#<Set: {
"/dp/condition/GroupPolicy/public_group/Group:139",
"/dp/condition/GroupPolicy/logged_in_viewable/User:83,Group:139",
"/dp/condition/BasePolicy/admin/User:83",
"/dp/condition/BasePolicy/auditor/User:83",
"/dp/condition/GroupPolicy/user_banned_from_group/User:83,Group:139",
"/dp/condition/GroupPolicy/needs_new_sso_session/User:83,Group:139",
"/dp/condition/GroupPolicy/guest/User:83,Group:139",
"/dp/condition/GroupPolicy/has_projects/User:83,Group:139",
"/dp/condition/GroupPolicy/read_package_registry_deploy_token/User:83,Group:139",
"/dp/condition/GroupPolicy/write_package_registry_deploy_token/User:83,Group:139"}>,
@enabled=false,
@prevented=true>
```
#### Testing that individual policies are equivalent
You can use the `'equivalent project policy abilities'` shared example to ensure
that 2 project policy abilities are equivalent for all project visibility levels
and access levels.
Example:
```ruby
context 'when refactoring read_pipeline_schedule and read_pipeline' do
let(:old_policy) { :read_pipeline_schedule }
let(:new_policy) { :read_pipeline }
it_behaves_like 'equivalent policies'
end
```

View File

@ -149,19 +149,19 @@ Depending on your version of GitLab, the Chain of Custody report is either sent
#### Generate commit-specific Chain of Custody report
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/267629) in GitLab 13.6.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/267629) in GitLab 13.6.
> - Support for including all commits instead of only merge commits [added](https://gitlab.com/gitlab-org/gitlab/-/issues/393446) in GitLab 15.10.
You can generate a commit-specific Chain of Custody report for a given merge commit SHA. This report provides only the
details for the provided merge commit SHA. Issue [393446](https://gitlab.com/gitlab-org/gitlab/-/issues/393446) proposes
to extend the commit SHA filtering to work with all commits instead of only merge commits.
You can generate a commit-specific Chain of Custody report for a given commit SHA. This report provides only the
details for the provided commit SHA.
To generate a commit-specific Chain of Custody report:
1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Security and Compliance > Compliance report**.
1. At the top of the compliance report, to the right of **List of all merge commits**, select the down arrow
1. At the top of the compliance report, to the right of **List of all commits**, select the down arrow
(**{chevron-lg-down}**).
1. Enter the merge commit SHA, and then select **Export commit custody report**.
1. Enter the commit SHA, and then select **Export commit custody report**.
Depending on your version of GitLab, the Chain of Custody report is either sent through email or available for download.

View File

@ -284,6 +284,8 @@ GitHub Enterprise Cloud has
[custom repository roles](https://docs.github.com/en/enterprise-cloud@latest/organizations/managing-peoples-access-to-your-organization-with-roles/about-custom-repository-roles).
These roles aren't supported and cause partial imports.
To import GitHub collaborators, you must have at least the Write role on the GitHub project. Otherwise collaborators import is skipped.
## Import from GitHub Enterprise on an internal network
If your GitHub Enterprise instance is on a internal network that is inaccessible to the internet, you can use a reverse proxy

View File

@ -26,11 +26,32 @@ for any change you commit through the Web Editor.
To create a text file in the Web Editor:
1. On the top bar, select **Main menu > Projects** and find your project.
1. From the project dashboard or repository, next to the branch name, select the plus icon (**{plus}**).
1. From the project dashboard or repository, next to the branch name,
select the plus icon (**{plus}**).
1. From the dropdown list, select **New file**.
1. Complete the fields. To create a merge request with the new file, ensure the **Start a new merge request with these changes** checkbox is selected.
1. Complete the fields.
1. To create a merge request with the new file, ensure the **Start a new merge request with these changes** checkbox is selected, if you had chosen a **Target branch** other than the [default branch (such as `main`)](../../../user/project/repository/branches/default.md).
1. Select **Commit changes**.
### Create a file from a template
1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Repository > Files**.
1. Next to the project name, select the plus icon (**{plus}**) to display a
dropdown list, then select **New file** from the list.
1. For **Filename**, provide one of the filenames that GitLab provides a template for:
- `.gitignore`
- `.gitlab-ci.yml`
- `LICENSE`
- `Dockerfile`
1. Select **Apply a template**, then select the template you want to apply.
1. Make your changes to the file.
1. Provide a **Commit message**.
1. Enter a **Target branch** to merge into. To create a new merge request with
your changes, enter a branch name that is not your repository's
[default branch](../../../user/project/repository/branches/default.md),
1. Select **Commit changes** to add the commit to your branch.
## Edit a file
To edit a text file in the Web Editor:

View File

@ -12,7 +12,7 @@ module Gitlab
def load(string)
return unless string
object = YAML.safe_load(string, [Symbol])
object = YAML.safe_load(string, permitted_classes: [Symbol])
object.map do |variable|
variable.symbolize_keys.tap do |variable|

View File

@ -26847,9 +26847,6 @@ msgstr ""
msgid "Merge blocked: pipeline must succeed. It's waiting for a manual job to continue."
msgstr ""
msgid "Merge commit SHA"
msgstr ""
msgid "Merge commit message"
msgstr ""

View File

@ -100,8 +100,8 @@ RSpec.describe Gitlab::Ci::Lint, feature_category: :pipeline_composition do
end
it 'sets merged_config' do
root_config = YAML.safe_load(content, [Symbol])
included_config = YAML.safe_load(included_content, [Symbol])
root_config = YAML.safe_load(content, permitted_classes: [Symbol])
included_config = YAML.safe_load(included_content, permitted_classes: [Symbol])
expected_config = included_config.merge(root_config).except(:include).deep_stringify_keys
expect(subject.merged_yaml).to eq(expected_config.to_yaml)

View File

@ -47,8 +47,8 @@ module Gitlab
end
it 'returns expanded yaml config' do
expanded_config = YAML.safe_load(config_metadata[:merged_yaml], [Symbol])
included_config = YAML.safe_load(included_yml, [Symbol])
expanded_config = YAML.safe_load(config_metadata[:merged_yaml], permitted_classes: [Symbol])
included_config = YAML.safe_load(included_yml, permitted_classes: [Symbol])
expect(expanded_config).to include(*included_config.keys)
end

View File

@ -84,7 +84,7 @@ RSpec.describe Gitlab::ImportExport::Config, feature_category: :importers do
EOF
end
let(:config_hash) { YAML.safe_load(config, [Symbol]) }
let(:config_hash) { YAML.safe_load(config, permitted_classes: [Symbol]) }
before do
allow_any_instance_of(described_class).to receive(:parse_yaml) do

View File

@ -4321,6 +4321,14 @@ RSpec.describe MergeRequest, factory_default: :keep, feature_category: :code_rev
transition!
end
context 'when skip_merge_status_trigger is set to true' do
before do
subject.skip_merge_status_trigger = true
end
it_behaves_like 'transition not triggering mergeRequestMergeStatusUpdated GraphQL subscription'
end
context 'when transaction is not committed' do
it_behaves_like 'transition not triggering mergeRequestMergeStatusUpdated GraphQL subscription' do
def transition!

View File

@ -245,8 +245,8 @@ RSpec.describe API::Lint, feature_category: :pipeline_composition do
it 'passes validation' do
ci_lint
included_config = YAML.safe_load(included_content, [Symbol])
root_config = YAML.safe_load(yaml_content, [Symbol])
included_config = YAML.safe_load(included_content, permitted_classes: [Symbol])
root_config = YAML.safe_load(yaml_content, permitted_classes: [Symbol])
expected_yaml = included_config.merge(root_config).except(:include).deep_stringify_keys.to_yaml
expect(response).to have_gitlab_http_status(:ok)
@ -535,8 +535,8 @@ RSpec.describe API::Lint, feature_category: :pipeline_composition do
it 'passes validation' do
ci_lint
included_config = YAML.safe_load(included_content, [Symbol])
root_config = YAML.safe_load(yaml_content, [Symbol])
included_config = YAML.safe_load(included_content, permitted_classes: [Symbol])
root_config = YAML.safe_load(yaml_content, permitted_classes: [Symbol])
expected_yaml = included_config.merge(root_config).except(:include).deep_stringify_keys.to_yaml
expect(response).to have_gitlab_http_status(:ok)

View File

@ -109,6 +109,14 @@ RSpec.describe MergeRequests::RefreshService, feature_category: :code_review_wor
expect(@fork_build_failed_todo).to be_done
end
it 'triggers mergeRequestMergeStatusUpdated GraphQL subscription conditionally' do
expect(GraphqlTriggers).to receive(:merge_request_merge_status_updated).with(@merge_request)
expect(GraphqlTriggers).to receive(:merge_request_merge_status_updated).with(@another_merge_request)
expect(GraphqlTriggers).not_to receive(:merge_request_merge_status_updated).with(@fork_merge_request)
refresh_service.execute(@oldrev, @newrev, 'refs/heads/master')
end
context 'when a merge error exists' do
let(:error_message) { 'This is a merge error' }

View File

@ -546,6 +546,7 @@ end
# Disabled because it's causing N+1 queries.
# See https://gitlab.com/gitlab-org/gitlab/-/issues/396352.
# Support::AbilityCheck.inject(Ability.singleton_class)
Support::PermissionsCheck.inject(Ability.singleton_class)
ActiveRecord::Migration.maintain_test_schema!

View File

@ -0,0 +1,18 @@
# frozen_string_literal: true
module Support
module PermissionsCheck
def self.inject(mod)
mod.prepend PermissionsExtension if Gitlab::Utils.to_boolean(ENV['GITLAB_DEBUG_POLICIES'])
end
module PermissionsExtension
def before_check(policy, ability, _user, _subject, _opts)
puts(
"POLICY CHECK DEBUG -> " \
"policy: #{policy.class.name}, ability: #{ability}, called_from: #{caller_locations(2, 5)}"
)
end
end
end
end

View File

@ -401,3 +401,24 @@ RSpec.shared_examples 'package access with repository disabled' do
it { is_expected.to be_allowed(:read_package) }
end
RSpec.shared_examples 'equivalent project policy abilities' do
where(:project_visibility, :user_role_on_project) do
project_visibilities = [:public, :internal, :private]
user_role_on_project = [:anonymous, :non_member, :guest, :reporter, :developer, :maintainer, :owner, :admin]
project_visibilities.product(user_role_on_project)
end
with_them do
it 'evaluates the same' do
project = public_send("#{project_visibility}_project")
current_user = public_send(user_role_on_project)
enable_admin_mode!(current_user) if user_role_on_project == :admin
policy = ProjectPolicy.new(current_user, project)
old_permissions = policy.allowed?(old_policy)
new_permissions = policy.allowed?(new_policy)
expect(old_permissions).to eq new_permissions
end
end
end

View File

@ -11,40 +11,63 @@ RSpec.describe Gitlab::GithubImport::Stage::ImportCollaboratorsWorker, feature_c
let(:client) { instance_double(Gitlab::GithubImport::Client) }
describe '#import' do
it 'imports all the pull requests' do
waiter = Gitlab::JobWaiter.new(2, '123')
let(:push_rights_granted) { true }
expect(Gitlab::GithubImport::Importer::CollaboratorsImporter)
.to receive(:new)
.with(project, client)
.and_return(importer)
expect(importer).to receive(:execute).and_return(waiter)
expect(import_state).to receive(:refresh_jid_expiration)
expect(Gitlab::GithubImport::AdvanceStageWorker)
.to receive(:perform_async)
.with(project.id, { '123' => 2 }, :pull_requests_merged_by)
worker.import(client, project)
before do
allow(client).to receive(:repository).with(project.import_source)
.and_return({ permissions: { push: push_rights_granted } })
end
end
it 'raises an error' do
exception = StandardError.new('_some_error_')
context 'when user has push access for this repo' do
it 'imports all the pull requests' do
waiter = Gitlab::JobWaiter.new(2, '123')
expect_next_instance_of(Gitlab::GithubImport::Importer::CollaboratorsImporter) do |importer|
expect(importer).to receive(:execute).and_raise(exception)
expect(Gitlab::GithubImport::Importer::CollaboratorsImporter)
.to receive(:new)
.with(project, client)
.and_return(importer)
expect(importer).to receive(:execute).and_return(waiter)
expect(import_state).to receive(:refresh_jid_expiration)
expect(Gitlab::GithubImport::AdvanceStageWorker)
.to receive(:perform_async)
.with(project.id, { '123' => 2 }, :pull_requests_merged_by)
worker.import(client, project)
end
end
expect(Gitlab::Import::ImportFailureService).to receive(:track)
.with(
project_id: project.id,
exception: exception,
error_source: described_class.name,
fail_import: true,
metrics: true
).and_call_original
expect { worker.import(client, project) }.to raise_error(StandardError)
context 'when user do not have push access for this repo' do
let(:push_rights_granted) { false }
it 'skips stage' do
expect(Gitlab::GithubImport::Importer::CollaboratorsImporter).not_to receive(:new)
expect(Gitlab::GithubImport::AdvanceStageWorker)
.to receive(:perform_async)
.with(project.id, {}, :pull_requests_merged_by)
worker.import(client, project)
end
end
it 'raises an error' do
exception = StandardError.new('_some_error_')
expect_next_instance_of(Gitlab::GithubImport::Importer::CollaboratorsImporter) do |importer|
expect(importer).to receive(:execute).and_raise(exception)
end
expect(Gitlab::Import::ImportFailureService).to receive(:track)
.with(
project_id: project.id,
exception: exception,
error_source: described_class.name,
fail_import: true,
metrics: true
).and_call_original
expect { worker.import(client, project) }.to raise_error(StandardError)
end
end
end

View File

@ -12,7 +12,9 @@ module Tooling
def parse(yaml_files)
Array(yaml_files).each do |yaml_file|
data = File.read(yaml_file)
metadata, example_groups = data.split("---\n").reject(&:empty?).map { |yml| YAML.safe_load(yml, [Symbol]) }
metadata, example_groups = data.split("---\n").reject(&:empty?).map do |yml|
YAML.safe_load(yml, permitted_classes: [Symbol])
end
if example_groups.nil?
puts "No examples in #{yaml_file}! Metadata: #{metadata}"