diff options
67 files changed, 743 insertions, 361 deletions
@@ -420,7 +420,7 @@ end gem 'octokit', '~> 4.15' # https://gitlab.com/gitlab-org/gitlab/issues/207207 -gem 'gitlab-mail_room', '~> 0.0.2', require: 'mail_room' +gem 'gitlab-mail_room', '~> 0.0.3', require: 'mail_room' gem 'email_reply_trimmer', '~> 0.1' gem 'html2text' diff --git a/Gemfile.lock b/Gemfile.lock index 38de70b41ef..b11f0c5a5db 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -388,7 +388,7 @@ GEM opentracing (~> 0.4) redis (> 3.0.0, < 5.0.0) gitlab-license (1.0.0) - gitlab-mail_room (0.0.2) + gitlab-mail_room (0.0.3) gitlab-markup (1.7.0) gitlab-net-dns (0.9.1) gitlab-puma (4.3.1.gitlab.2) @@ -1235,7 +1235,7 @@ DEPENDENCIES gitlab-chronic (~> 0.10.5) gitlab-labkit (= 0.10.0) gitlab-license (~> 1.0) - gitlab-mail_room (~> 0.0.2) + gitlab-mail_room (~> 0.0.3) gitlab-markup (~> 1.7.0) gitlab-net-dns (~> 0.9.1) gitlab-puma (~> 4.3.1.gitlab.2) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 7cb629dee21..5a2eb2337aa 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -34,6 +34,7 @@ class ApplicationController < ActionController::Base before_action :check_impersonation_availability before_action :required_signup_info + around_action :sessionless_bypass_admin_mode!, if: :sessionless_user? around_action :set_current_context around_action :set_locale around_action :set_session_storage diff --git a/app/controllers/concerns/sessionless_authentication.rb b/app/controllers/concerns/sessionless_authentication.rb index d5c26fca957..a9ef33bf3b9 100644 --- a/app/controllers/concerns/sessionless_authentication.rb +++ b/app/controllers/concerns/sessionless_authentication.rb @@ -5,12 +5,6 @@ # Controller concern to handle PAT, RSS, and static objects token authentication methods # module SessionlessAuthentication - extend ActiveSupport::Concern - - included do - before_action :enable_admin_mode!, if: :sessionless_user? - end - # This filter handles personal access tokens, atom requests with rss tokens, and static object tokens def authenticate_sessionless_user!(request_format) user = Gitlab::Auth::RequestAuthenticator.new(request).find_sessionless_user(request_format) @@ -32,9 +26,9 @@ module SessionlessAuthentication end end - def enable_admin_mode! - return unless Feature.enabled?(:user_mode_in_session) + def sessionless_bypass_admin_mode!(&block) + return yield unless Feature.enabled?(:user_mode_in_session) - current_user_mode.enable_sessionless_admin_mode! + Gitlab::Auth::CurrentUserMode.bypass_session!(current_user.id, &block) end end diff --git a/app/controllers/graphql_controller.rb b/app/controllers/graphql_controller.rb index d7ff2ded5ae..522d171b5bf 100644 --- a/app/controllers/graphql_controller.rb +++ b/app/controllers/graphql_controller.rb @@ -15,6 +15,11 @@ class GraphqlController < ApplicationController before_action :authorize_access_api! before_action(only: [:execute]) { authenticate_sessionless_user!(:api) } + # Since we deactivate authentication from the main ApplicationController and + # defer it to :authorize_access_api!, we need to override the bypass session + # callback execution order here + around_action :sessionless_bypass_admin_mode!, if: :sessionless_user? + def execute result = multiplex? ? execute_multiplex : execute_query diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 80c7a803392..7175eefcde7 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -195,7 +195,8 @@ class GroupsController < Groups::ApplicationController :require_two_factor_authentication, :two_factor_grace_period, :project_creation_level, - :subgroup_creation_level + :subgroup_creation_level, + :default_branch_protection ] end diff --git a/app/models/deployment.rb b/app/models/deployment.rb index fbb59173a3c..b118404b916 100644 --- a/app/models/deployment.rb +++ b/app/models/deployment.rb @@ -135,7 +135,7 @@ class Deployment < ApplicationRecord end def create_ref - project.repository.create_ref(ref, ref_path) + project.repository.create_ref(sha, ref_path) end def invalidate_cache @@ -280,12 +280,12 @@ class Deployment < ApplicationRecord errors.add(:ref, _('The branch or tag does not exist')) end - private - def ref_path File.join(environment.ref_path, 'deployments', iid.to_s) end + private + def legacy_finished_at self.created_at if success? && !read_attribute(:finished_at) end diff --git a/app/models/environment.rb b/app/models/environment.rb index 4224a32a6d7..0e2962b893a 100644 --- a/app/models/environment.rb +++ b/app/models/environment.rb @@ -193,15 +193,6 @@ class Environment < ApplicationRecord folder_name == "production" end - def first_deployment_for(commit_sha) - ref = project.repository.ref_name_for_sha(ref_path, commit_sha) - - return unless ref - - deployment_iid = ref.split('/').last - deployments.find_by(iid: deployment_iid) - end - def ref_path "refs/#{Repository::REF_ENVIRONMENTS}/#{slug}" end diff --git a/app/models/namespace.rb b/app/models/namespace.rb index 99212d09b8e..f06e9da3b2a 100644 --- a/app/models/namespace.rb +++ b/app/models/namespace.rb @@ -139,6 +139,10 @@ class Namespace < ApplicationRecord end end + def default_branch_protection + super || Gitlab::CurrentSettings.default_branch_protection + end + def visibility_level_field :visibility_level end diff --git a/app/models/project.rb b/app/models/project.rb index f72e777c004..fdf7452d143 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -2359,6 +2359,12 @@ class Project < ApplicationRecord Gitlab::Routing.url_helpers.revoke_project_deploy_token_path(self, token) end + def default_branch_protected? + branch_protection = Gitlab::Access::BranchProtection.new(self.namespace.default_branch_protection) + + branch_protection.fully_protected? || branch_protection.developer_can_merge? + end + private def closest_namespace_setting(name) diff --git a/app/models/protected_branch.rb b/app/models/protected_branch.rb index 94c3b83564f..594c822c18f 100644 --- a/app/models/protected_branch.rb +++ b/app/models/protected_branch.rb @@ -11,7 +11,8 @@ class ProtectedBranch < ApplicationRecord def self.protected_ref_accessible_to?(ref, user, project:, action:, protected_refs: nil) # Maintainers, owners and admins are allowed to create the default branch - if default_branch_protected? && project.empty_repo? + + if project.empty_repo? && project.default_branch_protected? return true if user.admin? || project.team.max_member_access(user.id) > Gitlab::Access::DEVELOPER end @@ -20,7 +21,7 @@ class ProtectedBranch < ApplicationRecord # Check if branch name is marked as protected in the system def self.protected?(project, ref_name) - return true if project.empty_repo? && default_branch_protected? + return true if project.empty_repo? && project.default_branch_protected? self.matching(ref_name, protected_refs: protected_refs(project)).present? end @@ -33,11 +34,6 @@ class ProtectedBranch < ApplicationRecord end end - def self.default_branch_protected? - Gitlab::CurrentSettings.default_branch_protection == Gitlab::Access::PROTECTION_FULL || - Gitlab::CurrentSettings.default_branch_protection == Gitlab::Access::PROTECTION_DEV_CAN_MERGE - end - def self.protected_refs(project) project.protected_branches end diff --git a/app/services/issues/import_csv_service.rb b/app/services/issues/import_csv_service.rb index ef08fafa7cc..c01db5fcfe6 100644 --- a/app/services/issues/import_csv_service.rb +++ b/app/services/issues/import_csv_service.rb @@ -21,8 +21,19 @@ module Issues def process_csv csv_data = @csv_io.open(&:read).force_encoding(Encoding::UTF_8) - CSV.new(csv_data, col_sep: detect_col_sep(csv_data.lines.first), headers: true).each.with_index(2) do |row, line_no| - issue = Issues::CreateService.new(@project, @user, title: row[0], description: row[1]).execute + csv_parsing_params = { + col_sep: detect_col_sep(csv_data.lines.first), + headers: true, + header_converters: :symbol + } + + CSV.new(csv_data, csv_parsing_params).each.with_index(2) do |row, line_no| + issue_attributes = { + title: row[:title], + description: row[:description] + } + + issue = Issues::CreateService.new(@project, @user, issue_attributes).execute if issue.persisted? @results[:success] += 1 diff --git a/app/services/projects/protect_default_branch_service.rb b/app/services/projects/protect_default_branch_service.rb index 245490791bf..1d3fb523448 100644 --- a/app/services/projects/protect_default_branch_service.rb +++ b/app/services/projects/protect_default_branch_service.rb @@ -11,7 +11,7 @@ module Projects @project = project @default_branch_protection = Gitlab::Access::BranchProtection - .new(Gitlab::CurrentSettings.default_branch_protection) + .new(project.namespace.default_branch_protection) end def execute diff --git a/app/views/admin/application_settings/_visibility_and_access.html.haml b/app/views/admin/application_settings/_visibility_and_access.html.haml index ae90ffd9efc..a4acbe6c885 100644 --- a/app/views/admin/application_settings/_visibility_and_access.html.haml +++ b/app/views/admin/application_settings/_visibility_and_access.html.haml @@ -2,9 +2,8 @@ = form_errors(@application_setting) %fieldset - .form-group - = f.label :default_branch_protection, class: 'label-bold' - = f.select :default_branch_protection, options_for_select(Gitlab::Access.protection_options, @application_setting.default_branch_protection), {}, class: 'form-control' + = render 'shared/default_branch_protection', f: f, selected_level: @application_setting.default_branch_protection + .form-group = f.label s_('ProjectCreationLevel|Default project creation protection'), class: 'label-bold' = f.select :default_project_creation, options_for_select(Gitlab::Access.project_creation_options, @application_setting.default_project_creation), {}, class: 'form-control' diff --git a/app/views/groups/settings/_permissions.html.haml b/app/views/groups/settings/_permissions.html.haml index 618cfe57be4..016a9c8e054 100644 --- a/app/views/groups/settings/_permissions.html.haml +++ b/app/views/groups/settings/_permissions.html.haml @@ -33,6 +33,7 @@ = render_if_exists 'groups/settings/ip_restriction', f: f, group: @group = render_if_exists 'groups/settings/allowed_email_domain', f: f, group: @group = render 'groups/settings/lfs', f: f + = render 'shared/default_branch_protection', f: f, selected_level: @group.default_branch_protection = render 'groups/settings/project_creation_level', f: f, group: @group = render 'groups/settings/subgroup_creation_level', f: f, group: @group = render 'groups/settings/two_factor_auth', f: f diff --git a/app/views/shared/_default_branch_protection.html.haml b/app/views/shared/_default_branch_protection.html.haml new file mode 100644 index 00000000000..d7ae21debd8 --- /dev/null +++ b/app/views/shared/_default_branch_protection.html.haml @@ -0,0 +1,3 @@ +.form-group + = f.label :default_branch_protection, class: 'label-bold' + = f.select :default_branch_protection, options_for_select(Gitlab::Access.protection_options, selected_level), {}, class: 'form-control' diff --git a/changelogs/unreleased/193170-fix-deployment-ref-validation.yml b/changelogs/unreleased/193170-fix-deployment-ref-validation.yml new file mode 100644 index 00000000000..921cf96b7ba --- /dev/null +++ b/changelogs/unreleased/193170-fix-deployment-ref-validation.yml @@ -0,0 +1,5 @@ +--- +title: Use of sha instead of ref when creating a new ref on deployment creation. +merge_request: 23170 +author: +type: fixed diff --git a/changelogs/unreleased/207927-validate-actor-user-against-codeowners.yml b/changelogs/unreleased/207927-validate-actor-user-against-codeowners.yml new file mode 100644 index 00000000000..cbdfaa48c44 --- /dev/null +++ b/changelogs/unreleased/207927-validate-actor-user-against-codeowners.yml @@ -0,0 +1,5 @@ +--- +title: Validate actor against CODEOWNERS entries +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/208151-code-review-analytics-shows-no-data-for-mrs-in-review-for-less-tha.yml b/changelogs/unreleased/208151-code-review-analytics-shows-no-data-for-mrs-in-review-for-less-tha.yml new file mode 100644 index 00000000000..a891fe33277 --- /dev/null +++ b/changelogs/unreleased/208151-code-review-analytics-shows-no-data-for-mrs-in-review-for-less-tha.yml @@ -0,0 +1,5 @@ +--- +title: 'Code Review Analytics: Fix review time display' +merge_request: 26057 +author: +type: fixed diff --git a/changelogs/unreleased/7583-developer-cannot-push-to-projects-they-create-in-groups.yml b/changelogs/unreleased/7583-developer-cannot-push-to-projects-they-create-in-groups.yml new file mode 100644 index 00000000000..2055abc4551 --- /dev/null +++ b/changelogs/unreleased/7583-developer-cannot-push-to-projects-they-create-in-groups.yml @@ -0,0 +1,5 @@ +--- +title: Introduce default branch protection at the group level +merge_request: 24426 +author: +type: added diff --git a/changelogs/unreleased/jhyson-issue-import-export-consistency.yml b/changelogs/unreleased/jhyson-issue-import-export-consistency.yml new file mode 100644 index 00000000000..0d2b8167d94 --- /dev/null +++ b/changelogs/unreleased/jhyson-issue-import-export-consistency.yml @@ -0,0 +1,5 @@ +--- +title: Fix issue importer so it matches issue export format +merge_request: 25896 +author: +type: fixed diff --git a/changelogs/unreleased/refactor-bypass-session-admin-mode.yml b/changelogs/unreleased/refactor-bypass-session-admin-mode.yml new file mode 100644 index 00000000000..902f3239fd0 --- /dev/null +++ b/changelogs/unreleased/refactor-bypass-session-admin-mode.yml @@ -0,0 +1,5 @@ +--- +title: Sessionless and API endpoints bypass session for admin mode +merge_request: 25056 +author: Diego Louzán +type: changed diff --git a/config/application.rb b/config/application.rb index 2f4d271322f..d0bb411c3cc 100644 --- a/config/application.rb +++ b/config/application.rb @@ -292,6 +292,13 @@ module Gitlab initializer :move_initializers, before: :load_config_initializers, after: :let_zeitwerk_take_over do end + # We need this for initializers that need to be run before Zeitwerk is loaded + initializer :before_zeitwerk, before: :let_zeitwerk_take_over, after: :prepend_helpers_path do + Dir[Rails.root.join('config/initializers_before_autoloader/*.rb')].sort.each do |initializer| + load_config_initializer(initializer) + end + end + config.after_initialize do Rails.application.reload_routes! diff --git a/config/initializers/0_inflections.rb b/config/initializers_before_autoloader/000_inflections.rb index 1fabce9a57e..1fabce9a57e 100644 --- a/config/initializers/0_inflections.rb +++ b/config/initializers_before_autoloader/000_inflections.rb diff --git a/db/migrate/20200207062728_add_default_branch_protection_to_namespaces.rb b/db/migrate/20200207062728_add_default_branch_protection_to_namespaces.rb new file mode 100644 index 00000000000..c21cac307f9 --- /dev/null +++ b/db/migrate/20200207062728_add_default_branch_protection_to_namespaces.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +class AddDefaultBranchProtectionToNamespaces < ActiveRecord::Migration[6.0] + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + def change + with_lock_retries do + add_column :namespaces, :default_branch_protection, :integer, limit: 2 + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 149582f94bf..fa251f65741 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -2762,6 +2762,7 @@ ActiveRecord::Schema.define(version: 2020_02_26_162723) do t.integer "max_pages_size" t.integer "max_artifacts_size" t.boolean "mentions_disabled" + t.integer "default_branch_protection", limit: 2 t.index ["created_at"], name: "index_namespaces_on_created_at" t.index ["custom_project_templates_group_id", "type"], name: "index_namespaces_on_custom_project_templates_group_id_and_type", where: "(custom_project_templates_group_id IS NOT NULL)" t.index ["file_template_project_id"], name: "index_namespaces_on_file_template_project_id" diff --git a/doc/api/groups.md b/doc/api/groups.md index 3a488ca6546..e48cb78b2cc 100644 --- a/doc/api/groups.md +++ b/doc/api/groups.md @@ -42,6 +42,7 @@ GET /groups "emails_disabled": null, "mentions_disabled": null, "lfs_enabled": true, + "default_branch_protection": 2, "avatar_url": "http://localhost:3000/uploads/group/avatar/1/foo.jpg", "web_url": "http://localhost:3000/groups/foo-bar", "request_access_enabled": false, @@ -76,6 +77,7 @@ GET /groups?statistics=true "emails_disabled": null, "mentions_disabled": null, "lfs_enabled": true, + "default_branch_protection": 2, "avatar_url": "http://localhost:3000/uploads/group/avatar/1/foo.jpg", "web_url": "http://localhost:3000/groups/foo-bar", "request_access_enabled": false, @@ -148,6 +150,7 @@ GET /groups/:id/subgroups "emails_disabled": null, "mentions_disabled": null, "lfs_enabled": true, + "default_branch_protection": 2, "avatar_url": "http://gitlab.example.com/uploads/group/avatar/1/foo.jpg", "web_url": "http://gitlab.example.com/groups/foo-bar", "request_access_enabled": false, @@ -493,9 +496,20 @@ Parameters: | `lfs_enabled` | boolean | no | Enable/disable Large File Storage (LFS) for the projects in this group. | | `request_access_enabled` | boolean | no | Allow users to request member access. | | `parent_id` | integer | no | The parent group ID for creating nested group. | +| `default_branch_protection` | integer | no | See [Options for `default_branch_protection`](#options-for-default_branch_protection). Default to the global level default branch protection setting. | | `shared_runners_minutes_limit` | integer | no | **(STARTER ONLY)** Pipeline minutes quota for this group. | | `extra_shared_runners_minutes_limit` | integer | no | **(STARTER ONLY)** Extra pipeline minutes quota for this group. | +### Options for `default_branch_protection` + +The `default_branch_protection` attribute determines whether developers and maintainers can push to the applicable master branch, as described in the following table: + +| Value | Description | +|-------|-------------------------------------------------------------------------------------------------------------| +| `0` | No protection. Developers and maintainers can: <br>- Push new commits<br>- Force push changes<br>- Delete the branch | +| `1` | Partial protection. Developers and maintainers can: <br>- Push new commits | +| `2` | Full protection. Only maintainers can: <br>- Push new commits | + ## Transfer project to group Transfer a project to the Group namespace. Available only to instance administrators, although an [alternative API endpoint](projects.md#transfer-a-project-to-a-new-namespace) is available which does not require instance administrator access. Transferring projects may fail when tagged packages exist in the project's repository. @@ -542,6 +556,7 @@ PUT /groups/:id | `mentions_disabled` | boolean | no | Disable the capability of a group from getting mentioned | | `lfs_enabled` (optional) | boolean | no | Enable/disable Large File Storage (LFS) for the projects in this group. | | `request_access_enabled` | boolean | no | Allow users to request member access. | +| `default_branch_protection` | integer | no | See [Options for `default_branch_protection`](#options-for-default_branch_protection). | | `file_template_project_id` | integer | no | **(PREMIUM)** The ID of a project to load custom file templates from. | | `shared_runners_minutes_limit` | integer | no | **(STARTER ONLY)** Pipeline minutes quota for this group. | | `extra_shared_runners_minutes_limit` | integer | no | **(STARTER ONLY)** Extra pipeline minutes quota for this group. | diff --git a/doc/development/README.md b/doc/development/README.md index 01dff0c2017..6121ddf3ed6 100644 --- a/doc/development/README.md +++ b/doc/development/README.md @@ -97,6 +97,7 @@ Complementary reads: - [Issue types vs first-class types](issue_types.md) - [Application limits](application_limits.md) - [Redis guidelines](redis.md) +- [Rails initializers](rails_initializers.md) ## Performance guides diff --git a/doc/development/database_review.md b/doc/development/database_review.md index 77e5060720b..4e97e5961b6 100644 --- a/doc/development/database_review.md +++ b/doc/development/database_review.md @@ -187,4 +187,4 @@ NOTE: **Note:** Keep in mind that all runtimes should be measured against GitLab |----|----|---| | Regular migrations on `db/migrate` | `3 minutes` | A valid exception are index creation as this can take a long time. | | Post migrations on `db/post_migrate` | `10 minutes` | | -| Background migrations | --- | Since these are suitable for larger tables, it's not possible to set a precise timing guideline, however, any query must stay well below `10s` of execution time. | +| Background migrations | --- | Since these are suitable for larger tables, it's not possible to set a precise timing guideline, however, any single query must stay below `1 second` execution time with cold caches. | diff --git a/doc/development/migration_style_guide.md b/doc/development/migration_style_guide.md index f3f46baa54f..3f202115b4c 100644 --- a/doc/development/migration_style_guide.md +++ b/doc/development/migration_style_guide.md @@ -432,10 +432,6 @@ default values if absolutely necessary. There is a RuboCop cop that will fail if this method is used on some tables that are very large on GitLab.com, which would cause other issues. -For a small table (such as an empty one or one with less than `1,000` records), -use `add_column` and `change_column_default` in a single-transaction migration, -combining it with other operations that don't require `disable_ddl_transaction!`. - ## Changing the column default One might think that changing a default column with `change_column_default` is an diff --git a/doc/development/rails_initializers.md b/doc/development/rails_initializers.md new file mode 100644 index 00000000000..6473baf58d4 --- /dev/null +++ b/doc/development/rails_initializers.md @@ -0,0 +1,16 @@ +# Rails initializers + +By default, Rails loads Zeitwerk after the initializers in `config/initializers` are loaded. +Autoloading before Zeitwerk is loaded is now deprecated but because we use a lot of autoloaded +constants in our initializers, we had to move the loading of Zeitwerk earlier than these +initializers. + +A side-effect of this is that in the initializers, `config.autoload_paths` is already frozen. + +To run an initializer before Zeitwerk is loaded, you need put them in `config/initializers_before_autoloader`. +Ruby files in this folder are loaded in alphabetical order just like the default Rails initializers. + +Some examples where you would need to do this are: + +1. Modifying Rails' `config.autoload_paths` +1. Changing configuration that Zeitwerk uses, e.g. inflections diff --git a/doc/user/admin_area/settings/visibility_and_access_controls.md b/doc/user/admin_area/settings/visibility_and_access_controls.md index 8f64e9207b5..704dd89ede2 100644 --- a/doc/user/admin_area/settings/visibility_and_access_controls.md +++ b/doc/user/admin_area/settings/visibility_and_access_controls.md @@ -26,6 +26,8 @@ To change the default branch protection: For more details, see [Protected branches](../../project/protected_branches.md). +To change this setting for a specific group, see [Default branch protection for groups](../../group/index.md#changing-the-default-branch-protection-of-a-group) + ## Default project creation protection Project creation protection specifies which roles can create projects. diff --git a/doc/user/group/index.md b/doc/user/group/index.md index 6b76e070c41..cca82f6a4fb 100644 --- a/doc/user/group/index.md +++ b/doc/user/group/index.md @@ -181,6 +181,21 @@ of a group: 1. Give a different member **Owner** permissions. 1. Have the new owner sign in and remove **Owner** permissions from you. +## Changing the default branch protection of a group + +> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/7583) in GitLab 12.9. + +By default, every group inherits the branch protection set at the global level. + +To change this setting for a specific group: + +1. Go to the group's **{settings}** **Settings > General** page. +1. Expand the **Permissions, LFS, 2FA** section. +1. Select the desired option in the **Default branch protection** dropdown list. +1. Click **Save changes**. + +To change this setting globally, see [Default branch protection](../admin_area/settings/visibility_and_access_controls.md#default-branch-protection). + ## Add projects to a group There are two different ways to add a new project to a group: diff --git a/doc/user/project/issues/csv_import.md b/doc/user/project/issues/csv_import.md index 56643dcee53..d67b135186f 100644 --- a/doc/user/project/issues/csv_import.md +++ b/doc/user/project/issues/csv_import.md @@ -3,7 +3,7 @@ > [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/23532) in GitLab 11.7. Issues can be imported to a project by uploading a CSV file with the columns -`title` and `description`, in that order. +`title` and `description`. The user uploading the CSV file will be set as the author of the imported issues. @@ -31,9 +31,9 @@ to you once the import is complete. When importing issues from a CSV file, it must be formatted in a certain way: -- **header row:** CSV files must contain a header row where the first column header - is `title` and the second is `description`. If additional columns are present, they - will be ignored. +- **header row:** CSV files must include the following headers: +`title` and `description`. The case of the headers does not matter. +- **columns:** Data from columns beyond `title` and `description` are not imported. - **separators:** The column separator is automatically detected from the header row. Supported separator characters are: commas (`,`), semicolons (`;`), and tabs (`\t`). The row separator can be either `CRLF` or `LF`. diff --git a/lib/api/api_guard.rb b/lib/api/api_guard.rb index 0769e464d26..5cab13f001e 100644 --- a/lib/api/api_guard.rb +++ b/lib/api/api_guard.rb @@ -50,17 +50,13 @@ module API user = find_user_from_sources return unless user + # Sessions are enforced to be unavailable for API calls, so ignore them for admin mode + Gitlab::Auth::CurrentUserMode.bypass_session!(user.id) if Feature.enabled?(:user_mode_in_session) + unless api_access_allowed?(user) forbidden!(api_access_denied_message(user)) end - # Set admin mode for API requests (if admin) - if Feature.enabled?(:user_mode_in_session) - current_user_mode = Gitlab::Auth::CurrentUserMode.new(user) - - current_user_mode.enable_sessionless_admin_mode! - end - user end @@ -154,19 +150,13 @@ module API end class AdminModeMiddleware < ::Grape::Middleware::Base - def initialize(app, **options) - super - end + def after + # Use a Grape middleware since the Grape `after` blocks might run + # before we are finished rendering the `Grape::Entity` classes + Gitlab::Auth::CurrentUserMode.reset_bypass_session! if Feature.enabled?(:user_mode_in_session) - def call(env) - if Feature.enabled?(:user_mode_in_session) - session = {} - Gitlab::Session.with_session(session) do - app.call(env) - end - else - app.call(env) - end + # Explicit nil is needed or the api call return value will be overwritten + nil end end end diff --git a/lib/api/entities/group.rb b/lib/api/entities/group.rb index ae5ee4784ed..10e10e52d9f 100644 --- a/lib/api/entities/group.rb +++ b/lib/api/entities/group.rb @@ -13,6 +13,7 @@ module API expose :emails_disabled expose :mentions_disabled expose :lfs_enabled?, as: :lfs_enabled + expose :default_branch_protection expose :avatar_url do |group, options| group.avatar_url(only_path: false) end diff --git a/lib/api/helpers/groups_helpers.rb b/lib/api/helpers/groups_helpers.rb index e0fea4c7c96..25701ff683d 100644 --- a/lib/api/helpers/groups_helpers.rb +++ b/lib/api/helpers/groups_helpers.rb @@ -21,6 +21,7 @@ module API optional :mentions_disabled, type: Boolean, desc: 'Disable a group from getting mentioned' optional :lfs_enabled, type: Boolean, desc: 'Enable/disable LFS for the projects in this group' optional :request_access_enabled, type: Boolean, desc: 'Allow users to request member access' + optional :default_branch_protection, type: Integer, values: ::Gitlab::Access.protection_values, desc: 'Determine if developers can push to master' end params :optional_params_ee do diff --git a/lib/gitlab/access/branch_protection.rb b/lib/gitlab/access/branch_protection.rb index f039e5c011f..339a99eb068 100644 --- a/lib/gitlab/access/branch_protection.rb +++ b/lib/gitlab/access/branch_protection.rb @@ -37,6 +37,10 @@ module Gitlab def developer_can_merge? level == PROTECTION_DEV_CAN_MERGE end + + def fully_protected? + level == PROTECTION_FULL + end end end end diff --git a/lib/gitlab/auth/current_user_mode.rb b/lib/gitlab/auth/current_user_mode.rb index 1ef95c03cfc..06ae4d81870 100644 --- a/lib/gitlab/auth/current_user_mode.rb +++ b/lib/gitlab/auth/current_user_mode.rb @@ -23,15 +23,26 @@ module Gitlab class << self # Admin mode activation requires storing a flag in the user session. Using this - # method when scheduling jobs in Sidekiq will bypass the session check for a - # user that was already in admin mode + # method when scheduling jobs in sessionless environments (e.g. Sidekiq, API) + # will bypass the session check for a user that was already in admin mode + # + # If passed a block, it will surround the block execution and reset the session + # bypass at the end; otherwise use manually '.reset_bypass_session!' def bypass_session!(admin_id) Gitlab::SafeRequestStore[CURRENT_REQUEST_BYPASS_SESSION_ADMIN_ID_RS_KEY] = admin_id Gitlab::AppLogger.debug("Bypassing session in admin mode for: #{admin_id}") - yield - ensure + if block_given? + begin + yield + ensure + reset_bypass_session! + end + end + end + + def reset_bypass_session! Gitlab::SafeRequestStore.delete(CURRENT_REQUEST_BYPASS_SESSION_ADMIN_ID_RS_KEY) end @@ -90,10 +101,6 @@ module Gitlab current_session_data[ADMIN_MODE_START_TIME_KEY] = Time.now end - def enable_sessionless_admin_mode! - request_admin_mode! && enable_admin_mode!(skip_password_validation: true) - end - def disable_admin_mode! return unless user&.admin? diff --git a/lib/gitlab/graphql/connections.rb b/lib/gitlab/graphql/connections.rb index 08d5cd0b72e..0c0bfe5a458 100644 --- a/lib/gitlab/graphql/connections.rb +++ b/lib/gitlab/graphql/connections.rb @@ -16,6 +16,10 @@ module Gitlab Gitlab::Graphql::ExternallyPaginatedArray, Gitlab::Graphql::Connections::ExternallyPaginatedArrayConnection ) + GraphQL::Relay::BaseConnection.register_connection_implementation( + Gitlab::Graphql::Pagination::Relations::OffsetActiveRecordRelation, + Gitlab::Graphql::Pagination::OffsetActiveRecordRelationConnection + ) end end end diff --git a/lib/gitlab/graphql/pagination/offset_active_record_relation_connection.rb b/lib/gitlab/graphql/pagination/offset_active_record_relation_connection.rb new file mode 100644 index 00000000000..c852fbf0ab8 --- /dev/null +++ b/lib/gitlab/graphql/pagination/offset_active_record_relation_connection.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +# We use the Keyset / Stable cursor connection by default for ActiveRecord::Relation. +# However, there are times when that may not be powerful enough (yet), and we +# want to use standard offset pagination. +module Gitlab + module Graphql + module Pagination + class OffsetActiveRecordRelationConnection < GraphQL::Relay::RelationConnection + end + end + end +end diff --git a/lib/gitlab/graphql/pagination/relations/offset_active_record_relation.rb b/lib/gitlab/graphql/pagination/relations/offset_active_record_relation.rb new file mode 100644 index 00000000000..2e5a0d66d4e --- /dev/null +++ b/lib/gitlab/graphql/pagination/relations/offset_active_record_relation.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +module Gitlab + module Graphql + module Pagination + module Relations + class OffsetActiveRecordRelation < ::ActiveRecord::Relation + end + end + end + end +end diff --git a/package.json b/package.json index 852f9cfcc87..645f687186d 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "@babel/preset-env": "^7.8.4", "@gitlab/at.js": "^1.5.5", "@gitlab/svgs": "^1.104.0", - "@gitlab/ui": "^9.18.0", + "@gitlab/ui": "^9.18.2", "@gitlab/visual-review-tools": "1.5.1", "@sentry/browser": "^5.10.2", "@sourcegraph/code-host-integration": "0.0.30", diff --git a/scripts/review_apps/base-config.yaml b/scripts/review_apps/base-config.yaml index 95b0295622a..a54dadb8d85 100644 --- a/scripts/review_apps/base-config.yaml +++ b/scripts/review_apps/base-config.yaml @@ -17,10 +17,10 @@ gitlab: resources: requests: cpu: 1200m - memory: 240M + memory: 245M limits: cpu: 1800m - memory: 360M + memory: 367M persistence: size: 10G gitlab-exporter: @@ -51,11 +51,11 @@ gitlab: sidekiq: resources: requests: - cpu: 650m - memory: 1018M + cpu: 855m + memory: 1071M limits: - cpu: 975m - memory: 1527M + cpu: 1282m + memory: 1606M hpa: targetAverageValue: 650m task-runner: @@ -69,11 +69,11 @@ gitlab: unicorn: resources: requests: - cpu: 525m - memory: 1711M + cpu: 746m + memory: 1873M limits: - cpu: 787m - memory: 2566M + cpu: 1119m + memory: 2809M deployment: readinessProbe: initialDelaySeconds: 5 # Default is 0 @@ -140,10 +140,10 @@ postgresql: enabled: false resources: requests: - cpu: 300m + cpu: 347m memory: 250M limits: - cpu: 450m + cpu: 520m memory: 375M prometheus: install: false diff --git a/spec/controllers/groups_controller_spec.rb b/spec/controllers/groups_controller_spec.rb index 1c58c2b5c97..11c70d3aeca 100644 --- a/spec/controllers/groups_controller_spec.rb +++ b/spec/controllers/groups_controller_spec.rb @@ -411,6 +411,13 @@ describe GroupsController do expect(group.reload.project_creation_level).to eq(::Gitlab::Access::MAINTAINER_PROJECT_ACCESS) end + it 'updates the default_branch_protection successfully' do + post :update, params: { id: group.to_param, group: { default_branch_protection: ::Gitlab::Access::PROTECTION_DEV_CAN_MERGE } } + + expect(response).to have_gitlab_http_status(:found) + expect(group.reload.default_branch_protection).to eq(::Gitlab::Access::PROTECTION_DEV_CAN_MERGE) + end + context 'when a project inside the group has container repositories' do before do stub_container_registry_config(enabled: true) diff --git a/spec/fixtures/csv_gitlab_export.csv b/spec/fixtures/csv_gitlab_export.csv new file mode 100644 index 00000000000..65422509eef --- /dev/null +++ b/spec/fixtures/csv_gitlab_export.csv @@ -0,0 +1,5 @@ +Issue ID,URL,Title,State,Description,Author,Author Username,Assignee,Assignee Username,Confidential,Locked,Due Date,Created At (UTC),Updated At (UTC),Closed At (UTC),Milestone,Weight,Labels,Time Estimate,Time Spent,Epic ID,Epic Title +1,http://localhost:3000/jashkenas/underscore/issues/1,Title,Open,,Elva Jerde,jamel,Tierra Effertz,aurora_hahn,No,No,,2020-01-17 10:36:26,2020-02-19 10:36:26,,v1.0,,"Brene,Cutlass,Escort,GVM",0,0,, +3,http://localhost:3000/jashkenas/underscore/issues/3,Nihil impedit neque quos totam ut aut enim cupiditate doloribus molestiae.,Open,Omnis aliquid sint laudantium quam.,Marybeth Goodwin,rocio.blanda,Annemarie Von,reynalda_howe,No,No,,2020-01-23 10:36:26,2020-02-19 10:36:27,,v1.0,,"Brene,Cutlass,Escort,GVM",0,0,, +34,http://localhost:3000/jashkenas/underscore/issues/34,Dismiss Cipher with no integrity,Open,,Marybeth Goodwin,rocio.blanda,"","",No,No,,2020-02-19 10:38:49,2020-02-19 10:38:49,,,,,0,0,, +35,http://localhost:3000/jashkenas/underscore/issues/35,Test Title,Open,Test Description,Marybeth Goodwin,rocio.blanda,"","",No,No,,2020-02-19 10:38:49,2020-02-19 10:38:49,,,,,0,0,, diff --git a/spec/graphql/gitlab_schema_spec.rb b/spec/graphql/gitlab_schema_spec.rb index 2ec477fc494..d0eb0475879 100644 --- a/spec/graphql/gitlab_schema_spec.rb +++ b/spec/graphql/gitlab_schema_spec.rb @@ -3,6 +3,7 @@ require 'spec_helper' describe GitlabSchema do + let_it_be(:implementations) { GraphQL::Relay::BaseConnection::CONNECTION_IMPLEMENTATIONS } let(:user) { build :user } it 'uses batch loading' do @@ -33,12 +34,30 @@ describe GitlabSchema do expect(described_class.query).to eq(::Types::QueryType.to_graphql) end - it 'paginates active record relations using `Gitlab::Graphql::Connections::KeysetConnection`' do - connection = GraphQL::Relay::BaseConnection::CONNECTION_IMPLEMENTATIONS[ActiveRecord::Relation.name] + it 'paginates active record relations using `Connections::Keyset::Connection`' do + connection = implementations[ActiveRecord::Relation.name] expect(connection).to eq(Gitlab::Graphql::Connections::Keyset::Connection) end + it 'paginates ExternallyPaginatedArray using `Connections::ExternallyPaginatedArrayConnection`' do + connection = implementations[Gitlab::Graphql::ExternallyPaginatedArray.name] + + expect(connection).to eq(Gitlab::Graphql::Connections::ExternallyPaginatedArrayConnection) + end + + it 'paginates FilterableArray using `Connections::FilterableArrayConnection`' do + connection = implementations[Gitlab::Graphql::FilterableArray.name] + + expect(connection).to eq(Gitlab::Graphql::Connections::FilterableArrayConnection) + end + + it 'paginates OffsetActiveRecordRelation using `Pagination::OffsetActiveRecordRelationConnection`' do + connection = implementations[Gitlab::Graphql::Pagination::Relations::OffsetActiveRecordRelation.name] + + expect(connection).to eq(Gitlab::Graphql::Pagination::OffsetActiveRecordRelationConnection) + end + describe '.execute' do context 'for different types of users' do context 'when no context' do diff --git a/spec/lib/gitlab/access/branch_protection_spec.rb b/spec/lib/gitlab/access/branch_protection_spec.rb index 7f2979e8e28..e4b763357c4 100644 --- a/spec/lib/gitlab/access/branch_protection_spec.rb +++ b/spec/lib/gitlab/access/branch_protection_spec.rb @@ -51,4 +51,21 @@ describe Gitlab::Access::BranchProtection do end end end + + describe '#fully_protected?' do + using RSpec::Parameterized::TableSyntax + + where(:level, :result) do + Gitlab::Access::PROTECTION_NONE | false + Gitlab::Access::PROTECTION_DEV_CAN_PUSH | false + Gitlab::Access::PROTECTION_DEV_CAN_MERGE | false + Gitlab::Access::PROTECTION_FULL | true + end + + with_them do + it do + expect(described_class.new(level).fully_protected?).to eq(result) + end + end + end end diff --git a/spec/lib/gitlab/auth/current_user_mode_spec.rb b/spec/lib/gitlab/auth/current_user_mode_spec.rb index 7c2fdac6c25..2b910fac155 100644 --- a/spec/lib/gitlab/auth/current_user_mode_spec.rb +++ b/spec/lib/gitlab/auth/current_user_mode_spec.rb @@ -3,294 +3,330 @@ require 'spec_helper' describe Gitlab::Auth::CurrentUserMode, :do_not_mock_admin_mode, :request_store do - include_context 'custom session' - let(:user) { build_stubbed(:user) } subject { described_class.new(user) } - before do - allow(ActiveSession).to receive(:list_sessions).with(user).and_return([session]) - end - - shared_examples 'admin mode cannot be enabled' do - it 'is false by default' do - expect(subject.admin_mode?).to be(false) - end - - it 'cannot be enabled with a valid password' do - subject.enable_admin_mode!(password: user.password) - - expect(subject.admin_mode?).to be(false) - end - - it 'cannot be enabled with an invalid password' do - subject.enable_admin_mode!(password: nil) - - expect(subject.admin_mode?).to be(false) - end - - it 'cannot be enabled with empty params' do - subject.enable_admin_mode! + context 'when session is available' do + include_context 'custom session' - expect(subject.admin_mode?).to be(false) + before do + allow(ActiveSession).to receive(:list_sessions).with(user).and_return([session]) end - it 'disable has no effect' do - subject.enable_admin_mode! - subject.disable_admin_mode! - - expect(subject.admin_mode?).to be(false) - end + shared_examples 'admin mode cannot be enabled' do + it 'is false by default' do + expect(subject.admin_mode?).to be(false) + end - context 'skipping password validation' do it 'cannot be enabled with a valid password' do - subject.enable_admin_mode!(password: user.password, skip_password_validation: true) + subject.enable_admin_mode!(password: user.password) expect(subject.admin_mode?).to be(false) end it 'cannot be enabled with an invalid password' do - subject.enable_admin_mode!(skip_password_validation: true) + subject.enable_admin_mode!(password: nil) expect(subject.admin_mode?).to be(false) end - end - end - describe '#admin_mode?' do - context 'when the user is a regular user' do - it_behaves_like 'admin mode cannot be enabled' + it 'cannot be enabled with empty params' do + subject.enable_admin_mode! - context 'bypassing session' do - it_behaves_like 'admin mode cannot be enabled' do - around do |example| - described_class.bypass_session!(user.id) { example.run } - end - end + expect(subject.admin_mode?).to be(false) end - end - - context 'when the user is an admin' do - let(:user) { build_stubbed(:user, :admin) } - context 'when admin mode not requested' do - it 'is false by default' do - expect(subject.admin_mode?).to be(false) - end - - it 'raises exception if we try to enable it' do - expect do - subject.enable_admin_mode!(password: user.password) - end.to raise_error(::Gitlab::Auth::CurrentUserMode::NotRequestedError) + it 'disable has no effect' do + subject.enable_admin_mode! + subject.disable_admin_mode! - expect(subject.admin_mode?).to be(false) - end + expect(subject.admin_mode?).to be(false) end - context 'when admin mode requested first' do - before do - subject.request_admin_mode! - end + context 'skipping password validation' do + it 'cannot be enabled with a valid password' do + subject.enable_admin_mode!(password: user.password, skip_password_validation: true) - it 'is false by default' do expect(subject.admin_mode?).to be(false) end it 'cannot be enabled with an invalid password' do - subject.enable_admin_mode!(password: nil) + subject.enable_admin_mode!(skip_password_validation: true) expect(subject.admin_mode?).to be(false) end + end + end - it 'can be enabled with a valid password' do - subject.enable_admin_mode!(password: user.password) + describe '#admin_mode?' do + context 'when the user is a regular user' do + it_behaves_like 'admin mode cannot be enabled' - expect(subject.admin_mode?).to be(true) + context 'bypassing session' do + it_behaves_like 'admin mode cannot be enabled' do + around do |example| + described_class.bypass_session!(user.id) { example.run } + end + end end + end - it 'can be disabled' do - subject.enable_admin_mode!(password: user.password) - subject.disable_admin_mode! + context 'when the user is an admin' do + let(:user) { build_stubbed(:user, :admin) } - expect(subject.admin_mode?).to be(false) + context 'when admin mode not requested' do + it 'is false by default' do + expect(subject.admin_mode?).to be(false) + end + + it 'raises exception if we try to enable it' do + expect do + subject.enable_admin_mode!(password: user.password) + end.to raise_error(::Gitlab::Auth::CurrentUserMode::NotRequestedError) + + expect(subject.admin_mode?).to be(false) + end end - it 'will expire in the future' do - subject.enable_admin_mode!(password: user.password) - expect(subject.admin_mode?).to be(true), 'admin mode is not active in the present' + context 'when admin mode requested first' do + before do + subject.request_admin_mode! + end - Timecop.freeze(Gitlab::Auth::CurrentUserMode::MAX_ADMIN_MODE_TIME.from_now) do - # in the future this will be a new request, simulate by clearing the RequestStore - Gitlab::SafeRequestStore.clear! + it 'is false by default' do + expect(subject.admin_mode?).to be(false) + end + + it 'cannot be enabled with an invalid password' do + subject.enable_admin_mode!(password: nil) - expect(subject.admin_mode?).to be(false), 'admin mode did not expire in the future' + expect(subject.admin_mode?).to be(false) end - end - context 'skipping password validation' do it 'can be enabled with a valid password' do - subject.enable_admin_mode!(password: user.password, skip_password_validation: true) + subject.enable_admin_mode!(password: user.password) expect(subject.admin_mode?).to be(true) end - it 'can be enabled with an invalid password' do - subject.enable_admin_mode!(skip_password_validation: true) + it 'can be disabled' do + subject.enable_admin_mode!(password: user.password) + subject.disable_admin_mode! - expect(subject.admin_mode?).to be(true) + expect(subject.admin_mode?).to be(false) end - end - context 'with two independent sessions' do - let(:another_session) { {} } - let(:another_subject) { described_class.new(user) } + it 'will expire in the future' do + subject.enable_admin_mode!(password: user.password) + expect(subject.admin_mode?).to be(true), 'admin mode is not active in the present' - before do - allow(ActiveSession).to receive(:list_sessions).with(user).and_return([session, another_session]) + Timecop.freeze(Gitlab::Auth::CurrentUserMode::MAX_ADMIN_MODE_TIME.from_now) do + # in the future this will be a new request, simulate by clearing the RequestStore + Gitlab::SafeRequestStore.clear! + + expect(subject.admin_mode?).to be(false), 'admin mode did not expire in the future' + end end - it 'can be enabled in one and seen in the other' do - Gitlab::Session.with_session(another_session) do - another_subject.request_admin_mode! - another_subject.enable_admin_mode!(password: user.password) + context 'skipping password validation' do + it 'can be enabled with a valid password' do + subject.enable_admin_mode!(password: user.password, skip_password_validation: true) + + expect(subject.admin_mode?).to be(true) end - expect(subject.admin_mode?).to be(true) + it 'can be enabled with an invalid password' do + subject.enable_admin_mode!(skip_password_validation: true) + + expect(subject.admin_mode?).to be(true) + end end - end - end - context 'bypassing session' do - it 'is active by default' do - described_class.bypass_session!(user.id) do - expect(subject.admin_mode?).to be(true) + context 'with two independent sessions' do + let(:another_session) { {} } + let(:another_subject) { described_class.new(user) } + + before do + allow(ActiveSession).to receive(:list_sessions).with(user).and_return([session, another_session]) + end + + it 'can be enabled in one and seen in the other' do + Gitlab::Session.with_session(another_session) do + another_subject.request_admin_mode! + another_subject.enable_admin_mode!(password: user.password) + end + + expect(subject.admin_mode?).to be(true) + end end end - it 'enable has no effect' do - described_class.bypass_session!(user.id) do - subject.request_admin_mode! - subject.enable_admin_mode!(password: user.password) + context 'bypassing session' do + it 'is active by default' do + described_class.bypass_session!(user.id) do + expect(subject.admin_mode?).to be(true) + end + end - expect(subject.admin_mode?).to be(true) + it 'enable has no effect' do + described_class.bypass_session!(user.id) do + subject.request_admin_mode! + subject.enable_admin_mode!(password: user.password) + + expect(subject.admin_mode?).to be(true) + end end - end - it 'disable has no effect' do - described_class.bypass_session!(user.id) do - subject.disable_admin_mode! + it 'disable has no effect' do + described_class.bypass_session!(user.id) do + subject.disable_admin_mode! - expect(subject.admin_mode?).to be(true) + expect(subject.admin_mode?).to be(true) + end end end end end - end - describe '#enable_admin_mode!' do - let(:user) { build_stubbed(:user, :admin) } + describe '#enable_admin_mode!' do + let(:user) { build_stubbed(:user, :admin) } - it 'creates a timestamp in the session' do - subject.request_admin_mode! - subject.enable_admin_mode!(password: user.password) + it 'creates a timestamp in the session' do + subject.request_admin_mode! + subject.enable_admin_mode!(password: user.password) - expect(session).to include(expected_session_entry(be_within(1.second).of Time.now)) + expect(session).to include(expected_session_entry(be_within(1.second).of Time.now)) + end end - end - describe '#enable_sessionless_admin_mode!' do - let(:user) { build_stubbed(:user, :admin) } + describe '#disable_admin_mode!' do + let(:user) { build_stubbed(:user, :admin) } - it 'enabled admin mode without password' do - subject.enable_sessionless_admin_mode! + it 'sets the session timestamp to nil' do + subject.request_admin_mode! + subject.disable_admin_mode! - expect(subject.admin_mode?).to be(true) + expect(session).to include(expected_session_entry(be_nil)) + end end - end - describe '#disable_admin_mode!' do - let(:user) { build_stubbed(:user, :admin) } + describe '.with_current_request_admin_mode' do + context 'with a regular user' do + it 'user is not available inside nor outside the yielded block' do + described_class.with_current_admin(user) do + expect(described_class.current_admin).to be_nil + end - it 'sets the session timestamp to nil' do - subject.request_admin_mode! - subject.disable_admin_mode! + expect(described_class.bypass_session_admin_id).to be_nil + end + end - expect(session).to include(expected_session_entry(be_nil)) - end - end + context 'with an admin user' do + let(:user) { build_stubbed(:user, :admin) } - describe '.bypass_session!' do - context 'with a regular user' do - it 'admin mode is false' do - described_class.bypass_session!(user.id) do - expect(subject.admin_mode?).to be(false) - expect(described_class.bypass_session_admin_id).to be(user.id) + context 'admin mode is disabled' do + it 'user is not available inside nor outside the yielded block' do + described_class.with_current_admin(user) do + expect(described_class.current_admin).to be_nil + end + + expect(described_class.bypass_session_admin_id).to be_nil + end end - expect(described_class.bypass_session_admin_id).to be_nil - end - end + context 'admin mode is enabled' do + before do + subject.request_admin_mode! + subject.enable_admin_mode!(password: user.password) + end - context 'with an admin user' do - let(:user) { build_stubbed(:user, :admin) } + it 'user is available only inside the yielded block' do + described_class.with_current_admin(user) do + expect(described_class.current_admin).to be(user) + end - it 'admin mode is true' do - described_class.bypass_session!(user.id) do - expect(subject.admin_mode?).to be(true) - expect(described_class.bypass_session_admin_id).to be(user.id) + expect(described_class.current_admin).to be_nil + end end - - expect(described_class.bypass_session_admin_id).to be_nil end end - end - describe '.with_current_request_admin_mode' do - context 'with a regular user' do - it 'user is not available inside nor outside the yielded block' do - described_class.with_current_admin(user) do - expect(described_class.current_admin).to be_nil - end + def expected_session_entry(value_matcher) + { + Gitlab::Auth::CurrentUserMode::SESSION_STORE_KEY => a_hash_including( + Gitlab::Auth::CurrentUserMode::ADMIN_MODE_START_TIME_KEY => value_matcher) + } + end + end - expect(described_class.bypass_session_admin_id).to be_nil + context 'when no session available' do + around do |example| + Gitlab::Session.with_session(nil) do + example.run end end - context 'with an admin user' do - let(:user) { build_stubbed(:user, :admin) } + describe '.bypass_session!' do + context 'when providing a block' do + context 'with a regular user' do + it 'admin mode is false' do + described_class.bypass_session!(user.id) do + expect(Gitlab::Session.current).to be_nil + expect(subject.admin_mode?).to be(false) + expect(described_class.bypass_session_admin_id).to be(user.id) + end - context 'admin mode is disabled' do - it 'user is not available inside nor outside the yielded block' do - described_class.with_current_admin(user) do - expect(described_class.current_admin).to be_nil + expect(described_class.bypass_session_admin_id).to be_nil end + end - expect(described_class.bypass_session_admin_id).to be_nil + context 'with an admin user' do + let(:user) { build_stubbed(:user, :admin) } + + it 'admin mode is true' do + described_class.bypass_session!(user.id) do + expect(Gitlab::Session.current).to be_nil + expect(subject.admin_mode?).to be(true) + expect(described_class.bypass_session_admin_id).to be(user.id) + end + + expect(described_class.bypass_session_admin_id).to be_nil + end end end - context 'admin mode is enabled' do - before do - subject.request_admin_mode! - subject.enable_admin_mode!(password: user.password) - end + context 'when not providing a block' do + context 'with a regular user' do + it 'admin mode is false' do + described_class.bypass_session!(user.id) - it 'user is available only inside the yielded block' do - described_class.with_current_admin(user) do - expect(described_class.current_admin).to be(user) + expect(Gitlab::Session.current).to be_nil + expect(subject.admin_mode?).to be(false) + expect(described_class.bypass_session_admin_id).to be(user.id) + + described_class.reset_bypass_session! + + expect(described_class.bypass_session_admin_id).to be_nil end + end - expect(described_class.current_admin).to be_nil + context 'with an admin user' do + let(:user) { build_stubbed(:user, :admin) } + + it 'admin mode is true' do + described_class.bypass_session!(user.id) + + expect(Gitlab::Session.current).to be_nil + expect(subject.admin_mode?).to be(true) + expect(described_class.bypass_session_admin_id).to be(user.id) + + described_class.reset_bypass_session! + + expect(described_class.bypass_session_admin_id).to be_nil + end end end end end - - def expected_session_entry(value_matcher) - { - Gitlab::Auth::CurrentUserMode::SESSION_STORE_KEY => a_hash_including( - Gitlab::Auth::CurrentUserMode::ADMIN_MODE_START_TIME_KEY => value_matcher) - } - end end diff --git a/spec/lib/gitlab/graphql/pagination/offset_active_record_relation_connection_spec.rb b/spec/lib/gitlab/graphql/pagination/offset_active_record_relation_connection_spec.rb new file mode 100644 index 00000000000..2269b4def82 --- /dev/null +++ b/spec/lib/gitlab/graphql/pagination/offset_active_record_relation_connection_spec.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::Graphql::Pagination::OffsetActiveRecordRelationConnection do + it 'subclasses from GraphQL::Relay::RelationConnection' do + expect(described_class.superclass).to eq GraphQL::Relay::RelationConnection + end +end diff --git a/spec/lib/gitlab/user_access_spec.rb b/spec/lib/gitlab/user_access_spec.rb index 2f4ab2e71db..8d13f377677 100644 --- a/spec/lib/gitlab/user_access_spec.rb +++ b/spec/lib/gitlab/user_access_spec.rb @@ -46,32 +46,27 @@ describe Gitlab::UserAccess do expect(project_access.can_push_to_branch?('master')).to be_truthy end - it 'returns false if user is developer and project is fully protected' do - empty_project.add_developer(user) - stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_FULL) - - expect(project_access.can_push_to_branch?('master')).to be_falsey - end - - it 'returns false if user is developer and it is not allowed to push new commits but can merge into branch' do - empty_project.add_developer(user) - stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_DEV_CAN_MERGE) - - expect(project_access.can_push_to_branch?('master')).to be_falsey - end - - it 'returns true if user is developer and project is unprotected' do - empty_project.add_developer(user) - stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_NONE) - - expect(project_access.can_push_to_branch?('master')).to be_truthy - end - - it 'returns true if user is developer and project grants developers permission' do - empty_project.add_developer(user) - stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_DEV_CAN_PUSH) - - expect(project_access.can_push_to_branch?('master')).to be_truthy + context 'when the user is a developer' do + using RSpec::Parameterized::TableSyntax + + before do + empty_project.add_developer(user) + end + + where(:default_branch_protection_level, :result) do + Gitlab::Access::PROTECTION_NONE | true + Gitlab::Access::PROTECTION_DEV_CAN_PUSH | true + Gitlab::Access::PROTECTION_DEV_CAN_MERGE | false + Gitlab::Access::PROTECTION_FULL | false + end + + with_them do + it do + expect(empty_project.namespace).to receive(:default_branch_protection).and_return(default_branch_protection_level).at_least(:once) + + expect(project_access.can_push_to_branch?('master')).to eq(result) + end + end end end diff --git a/spec/models/deployment_spec.rb b/spec/models/deployment_spec.rb index ab7e12cd43c..86dfa586862 100644 --- a/spec/models/deployment_spec.rb +++ b/spec/models/deployment_spec.rb @@ -520,6 +520,21 @@ describe Deployment do end end + describe '#create_ref' do + let(:deployment) { build(:deployment) } + + subject { deployment.create_ref } + + it 'creates a ref using the sha' do + expect(deployment.project.repository).to receive(:create_ref).with( + deployment.sha, + "refs/environments/#{deployment.environment.name}/deployments/#{deployment.iid}" + ) + + subject + end + end + describe '#playable_build' do subject { deployment.playable_build } diff --git a/spec/models/environment_spec.rb b/spec/models/environment_spec.rb index 48cabd4301c..03aef7aea5c 100644 --- a/spec/models/environment_spec.rb +++ b/spec/models/environment_spec.rb @@ -325,26 +325,6 @@ describe Environment, :use_clean_rails_memory_store_caching do end end - describe '#first_deployment_for' do - let(:project) { create(:project, :repository) } - let!(:deployment) { create(:deployment, :succeed, environment: environment, ref: commit.parent.id) } - let!(:deployment1) { create(:deployment, :succeed, environment: environment, ref: commit.id) } - let(:head_commit) { project.commit } - let(:commit) { project.commit.parent } - - it 'returns deployment id for the environment', :sidekiq_might_not_need_inline do - expect(environment.first_deployment_for(commit.id)).to eq deployment1 - end - - it 'return nil when no deployment is found' do - expect(environment.first_deployment_for(head_commit.id)).to eq nil - end - - it 'returns a UTF-8 ref', :sidekiq_might_not_need_inline do - expect(environment.first_deployment_for(commit.id).ref).to be_utf8 - end - end - describe '#environment_type' do subject { environment.environment_type } diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb index 54e54366c5a..276fbc2cb54 100644 --- a/spec/models/namespace_spec.rb +++ b/spec/models/namespace_spec.rb @@ -531,6 +531,41 @@ describe Namespace do end end + describe "#default_branch_protection" do + let(:namespace) { create(:namespace) } + let(:default_branch_protection) { nil } + let(:group) { create(:group, default_branch_protection: default_branch_protection) } + + before do + stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_DEV_CAN_MERGE) + end + + context 'for a namespace' do + # Unlike a group, the settings of a namespace cannot be altered + # via the UI or the API. + + it 'returns the instance level setting' do + expect(namespace.default_branch_protection).to eq(Gitlab::Access::PROTECTION_DEV_CAN_MERGE) + end + end + + context 'for a group' do + context 'that has not altered the default value' do + it 'returns the instance level setting' do + expect(group.default_branch_protection).to eq(Gitlab::Access::PROTECTION_DEV_CAN_MERGE) + end + end + + context 'that has altered the default value' do + let(:default_branch_protection) { Gitlab::Access::PROTECTION_FULL } + + it 'returns the group level setting' do + expect(group.default_branch_protection).to eq(default_branch_protection) + end + end + end + end + describe '#self_and_hierarchy' do let!(:group) { create(:group, path: 'git_lab') } let!(:nested_group) { create(:group, parent: group) } diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 2b4a832634f..4885d4b63ff 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -1623,6 +1623,29 @@ describe Project do end end + describe '#default_branch_protected?' do + using RSpec::Parameterized::TableSyntax + + let_it_be(:project) { create(:project) } + + subject { project.default_branch_protected? } + + where(:default_branch_protection_level, :result) do + Gitlab::Access::PROTECTION_NONE | false + Gitlab::Access::PROTECTION_DEV_CAN_PUSH | false + Gitlab::Access::PROTECTION_DEV_CAN_MERGE | true + Gitlab::Access::PROTECTION_FULL | true + end + + with_them do + before do + expect(project.namespace).to receive(:default_branch_protection).and_return(default_branch_protection_level) + end + + it { is_expected.to eq(result) } + end + end + describe '#pages_url' do let(:group) { create(:group, name: group_name) } let(:project) { create(:project, namespace: group, name: project_name) } @@ -4576,7 +4599,7 @@ describe Project do end it 'does not protect when branch protection is disabled' do - stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_NONE) + expect(project.namespace).to receive(:default_branch_protection).and_return(Gitlab::Access::PROTECTION_NONE) project.after_import @@ -4584,7 +4607,7 @@ describe Project do end it "gives developer access to push when branch protection is set to 'developers can push'" do - stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_DEV_CAN_PUSH) + expect(project.namespace).to receive(:default_branch_protection).and_return(Gitlab::Access::PROTECTION_DEV_CAN_PUSH) project.after_import @@ -4594,7 +4617,7 @@ describe Project do end it "gives developer access to merge when branch protection is set to 'developers can merge'" do - stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_DEV_CAN_MERGE) + expect(project.namespace).to receive(:default_branch_protection).and_return(Gitlab::Access::PROTECTION_DEV_CAN_MERGE) project.after_import diff --git a/spec/models/protected_branch_spec.rb b/spec/models/protected_branch_spec.rb index 7f8a60dafa8..30fce1cd5c4 100644 --- a/spec/models/protected_branch_spec.rb +++ b/spec/models/protected_branch_spec.rb @@ -164,31 +164,45 @@ describe ProtectedBranch do end end - context "new project" do - let(:project) { create(:project) } + context 'new project' do + using RSpec::Parameterized::TableSyntax - it 'returns false when default_protected_branch is unprotected' do - stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_NONE) + let(:project) { create(:project) } - expect(described_class.protected?(project, 'master')).to be false - end + context 'when the group has set their own default_branch_protection level' do + where(:default_branch_protection_level, :result) do + Gitlab::Access::PROTECTION_NONE | false + Gitlab::Access::PROTECTION_DEV_CAN_PUSH | false + Gitlab::Access::PROTECTION_DEV_CAN_MERGE | true + Gitlab::Access::PROTECTION_FULL | true + end - it 'returns false when default_protected_branch lets developers push' do - stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_DEV_CAN_PUSH) + with_them do + it 'protects the default branch based on the default branch protection setting of the group' do + expect(project.namespace).to receive(:default_branch_protection).and_return(default_branch_protection_level) - expect(described_class.protected?(project, 'master')).to be false + expect(described_class.protected?(project, 'master')).to eq(result) + end + end end - it 'returns true when default_branch_protection does not let developers push but let developer merge branches' do - stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_DEV_CAN_MERGE) - - expect(described_class.protected?(project, 'master')).to be true - end + context 'when the group has not set their own default_branch_protection level' do + where(:default_branch_protection_level, :result) do + Gitlab::Access::PROTECTION_NONE | false + Gitlab::Access::PROTECTION_DEV_CAN_PUSH | false + Gitlab::Access::PROTECTION_DEV_CAN_MERGE | true + Gitlab::Access::PROTECTION_FULL | true + end - it 'returns true when default_branch_protection is in full protection' do - stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_FULL) + with_them do + before do + stub_application_setting(default_branch_protection: default_branch_protection_level) + end - expect(described_class.protected?(project, 'master')).to be true + it 'protects the default branch based on the instance level default branch protection setting' do + expect(described_class.protected?(project, 'master')).to eq(result) + end + end end end end diff --git a/spec/requests/api/api_guard/admin_mode_middleware_spec.rb b/spec/requests/api/api_guard/admin_mode_middleware_spec.rb new file mode 100644 index 00000000000..8973afe6570 --- /dev/null +++ b/spec/requests/api/api_guard/admin_mode_middleware_spec.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe API::APIGuard::AdminModeMiddleware, :do_not_mock_admin_mode, :request_store do + let(:user) { create(:admin) } + + it 'is loaded' do + expect(API::API.middleware).to include([:use, described_class]) + end + + context 'when there is an exception in the api call' do + let(:app) do + Class.new(API::API) do + get 'willfail' do + raise StandardError.new('oh noes!') + end + end + end + + it 'resets admin mode' do + Gitlab::Auth::CurrentUserMode.bypass_session!(user.id) + + expect(Gitlab::Auth::CurrentUserMode.bypass_session_admin_id).to be(user.id) + expect(Gitlab::Auth::CurrentUserMode).to receive(:reset_bypass_session!).and_call_original + + get api('/willfail') + + expect(response.status).to eq(500) + expect(response.body).to include('oh noes!') + + expect(Gitlab::Auth::CurrentUserMode.bypass_session_admin_id).to be_nil + end + end +end diff --git a/spec/requests/api/graphql/group_query_spec.rb b/spec/requests/api/graphql/group_query_spec.rb index 6e2663fb090..a38d1857076 100644 --- a/spec/requests/api/graphql/group_query_spec.rb +++ b/spec/requests/api/graphql/group_query_spec.rb @@ -4,7 +4,7 @@ require 'spec_helper' # Based on spec/requests/api/groups_spec.rb # Should follow closely in order to ensure all situations are covered -describe 'getting group information' do +describe 'getting group information', :do_not_mock_admin_mode do include GraphqlHelpers include UploadHelpers diff --git a/spec/requests/api/graphql/mutations/snippets/mark_as_spam_spec.rb b/spec/requests/api/graphql/mutations/snippets/mark_as_spam_spec.rb index f80a3401134..05e3f7e6806 100644 --- a/spec/requests/api/graphql/mutations/snippets/mark_as_spam_spec.rb +++ b/spec/requests/api/graphql/mutations/snippets/mark_as_spam_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe 'Mark snippet as spam' do +describe 'Mark snippet as spam', :do_not_mock_admin_mode do include GraphqlHelpers let_it_be(:admin) { create(:admin) } diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb index e8499246840..fb564bb398b 100644 --- a/spec/requests/api/groups_spec.rb +++ b/spec/requests/api/groups_spec.rb @@ -545,7 +545,8 @@ describe API::Groups do name: new_group_name, request_access_enabled: true, project_creation_level: "noone", - subgroup_creation_level: "maintainer" + subgroup_creation_level: "maintainer", + default_branch_protection: ::Gitlab::Access::MAINTAINER_PROJECT_ACCESS } expect(response).to have_gitlab_http_status(:ok) @@ -566,6 +567,7 @@ describe API::Groups do expect(json_response['projects'].length).to eq(2) expect(json_response['shared_projects']).to be_an Array expect(json_response['shared_projects'].length).to eq(0) + expect(json_response['default_branch_protection']).to eq(::Gitlab::Access::MAINTAINER_PROJECT_ACCESS) end it 'returns 404 for a non existing group' do diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb index 12ac601c013..5a302f0528e 100644 --- a/spec/requests/api/users_spec.rb +++ b/spec/requests/api/users_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe API::Users do +describe API::Users, :do_not_mock_admin_mode do let(:user) { create(:user, username: 'user.with.dot') } let(:admin) { create(:admin) } let(:key) { create(:key, user: user) } diff --git a/spec/services/deployments/after_create_service_spec.rb b/spec/services/deployments/after_create_service_spec.rb index 51c6de2c0b9..605700c73b2 100644 --- a/spec/services/deployments/after_create_service_spec.rb +++ b/spec/services/deployments/after_create_service_spec.rb @@ -49,7 +49,7 @@ describe Deployments::AfterCreateService do it 'creates ref' do expect_any_instance_of(Repository) .to receive(:create_ref) - .with(deployment.ref, deployment.send(:ref_path)) + .with(deployment.sha, deployment.send(:ref_path)) service.execute end diff --git a/spec/services/git/branch_push_service_spec.rb b/spec/services/git/branch_push_service_spec.rb index 8b4f45010ed..d7357cf4d0b 100644 --- a/spec/services/git/branch_push_service_spec.rb +++ b/spec/services/git/branch_push_service_spec.rb @@ -186,7 +186,7 @@ describe Git::BranchPushService, services: true do end it "when pushing a branch for the first time with default branch protection disabled" do - stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_NONE) + expect(project.namespace).to receive(:default_branch_protection).and_return(Gitlab::Access::PROTECTION_NONE) expect(project).to receive(:execute_hooks) expect(project.default_branch).to eq("master") @@ -195,7 +195,7 @@ describe Git::BranchPushService, services: true do end it "when pushing a branch for the first time with default branch protection set to 'developers can push'" do - stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_DEV_CAN_PUSH) + expect(project.namespace).to receive(:default_branch_protection).and_return(Gitlab::Access::PROTECTION_DEV_CAN_PUSH) expect(project).to receive(:execute_hooks) expect(project.default_branch).to eq("master") @@ -208,7 +208,7 @@ describe Git::BranchPushService, services: true do end it "when pushing a branch for the first time with an existing branch permission configured" do - stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_DEV_CAN_PUSH) + expect(project.namespace).to receive(:default_branch_protection).and_return(Gitlab::Access::PROTECTION_DEV_CAN_PUSH) create(:protected_branch, :no_one_can_push, :developers_can_merge, project: project, name: 'master') expect(project).to receive(:execute_hooks) @@ -223,7 +223,7 @@ describe Git::BranchPushService, services: true do end it "when pushing a branch for the first time with default branch protection set to 'developers can merge'" do - stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_DEV_CAN_MERGE) + expect(project.namespace).to receive(:default_branch_protection).and_return(Gitlab::Access::PROTECTION_DEV_CAN_MERGE) expect(project).to receive(:execute_hooks) expect(project.default_branch).to eq("master") diff --git a/spec/services/issues/import_csv_service_spec.rb b/spec/services/issues/import_csv_service_spec.rb index e7370407d4c..aa43892a36d 100644 --- a/spec/services/issues/import_csv_service_spec.rb +++ b/spec/services/issues/import_csv_service_spec.rb @@ -27,6 +27,29 @@ describe Issues::ImportCsvService do end end + context 'with a file generated by Gitlab CSV export' do + let(:file) { fixture_file_upload('spec/fixtures/csv_gitlab_export.csv') } + + it 'imports the CSV without errors' do + expect_next_instance_of(Notify) do |instance| + expect(instance).to receive(:import_issues_csv_email) + end + + expect(subject[:success]).to eq(4) + expect(subject[:error_lines]).to eq([]) + expect(subject[:parse_error]).to eq(false) + end + + it 'correctly sets the issue attributes' do + expect { subject }.to change { project.issues.count }.by 4 + + expect(project.issues.reload.last).to have_attributes( + title: 'Test Title', + description: 'Test Description' + ) + end + end + context 'comma delimited file' do let(:file) { fixture_file_upload('spec/fixtures/csv_comma.csv') } @@ -39,6 +62,15 @@ describe Issues::ImportCsvService do expect(subject[:error_lines]).to eq([]) expect(subject[:parse_error]).to eq(false) end + + it 'correctly sets the issue attributes' do + expect { subject }.to change { project.issues.count }.by 3 + + expect(project.issues.reload.last).to have_attributes( + title: 'Title with quote"', + description: 'Description' + ) + end end context 'tab delimited file with error row' do @@ -53,6 +85,15 @@ describe Issues::ImportCsvService do expect(subject[:error_lines]).to eq([3]) expect(subject[:parse_error]).to eq(false) end + + it 'correctly sets the issue attributes' do + expect { subject }.to change { project.issues.count }.by 2 + + expect(project.issues.reload.last).to have_attributes( + title: 'Hello', + description: 'World' + ) + end end context 'semicolon delimited file with CRLF' do @@ -67,6 +108,15 @@ describe Issues::ImportCsvService do expect(subject[:error_lines]).to eq([4]) expect(subject[:parse_error]).to eq(false) end + + it 'correctly sets the issue attributes' do + expect { subject }.to change { project.issues.count }.by 3 + + expect(project.issues.reload.last).to have_attributes( + title: 'Hello', + description: 'World' + ) + end end end end diff --git a/spec/services/projects/protect_default_branch_service_spec.rb b/spec/services/projects/protect_default_branch_service_spec.rb index c145b2c06c6..c0b819ab17b 100644 --- a/spec/services/projects/protect_default_branch_service_spec.rb +++ b/spec/services/projects/protect_default_branch_service_spec.rb @@ -4,7 +4,7 @@ require 'spec_helper' describe Projects::ProtectDefaultBranchService do let(:service) { described_class.new(project) } - let(:project) { instance_spy(Project) } + let(:project) { create(:project) } describe '#execute' do before do @@ -147,7 +147,7 @@ describe Projects::ProtectDefaultBranchService do describe '#protect_branch?' do context 'when default branch protection is disabled' do it 'returns false' do - allow(Gitlab::CurrentSettings) + allow(project.namespace) .to receive(:default_branch_protection) .and_return(Gitlab::Access::PROTECTION_NONE) @@ -157,7 +157,7 @@ describe Projects::ProtectDefaultBranchService do context 'when default branch protection is enabled' do before do - allow(Gitlab::CurrentSettings) + allow(project.namespace) .to receive(:default_branch_protection) .and_return(Gitlab::Access::PROTECTION_DEV_CAN_MERGE) @@ -199,7 +199,7 @@ describe Projects::ProtectDefaultBranchService do describe '#push_access_level' do context 'when developers can push' do it 'returns the DEVELOPER access level' do - allow(Gitlab::CurrentSettings) + allow(project.namespace) .to receive(:default_branch_protection) .and_return(Gitlab::Access::PROTECTION_DEV_CAN_PUSH) @@ -209,7 +209,7 @@ describe Projects::ProtectDefaultBranchService do context 'when developers can not push' do it 'returns the MAINTAINER access level' do - allow(Gitlab::CurrentSettings) + allow(project.namespace) .to receive(:default_branch_protection) .and_return(Gitlab::Access::PROTECTION_DEV_CAN_MERGE) @@ -221,7 +221,7 @@ describe Projects::ProtectDefaultBranchService do describe '#merge_access_level' do context 'when developers can merge' do it 'returns the DEVELOPER access level' do - allow(Gitlab::CurrentSettings) + allow(project.namespace) .to receive(:default_branch_protection) .and_return(Gitlab::Access::PROTECTION_DEV_CAN_MERGE) @@ -231,7 +231,7 @@ describe Projects::ProtectDefaultBranchService do context 'when developers can not merge' do it 'returns the MAINTAINER access level' do - allow(Gitlab::CurrentSettings) + allow(project.namespace) .to receive(:default_branch_protection) .and_return(Gitlab::Access::PROTECTION_DEV_CAN_PUSH) diff --git a/spec/support/helpers/wait_for_requests.rb b/spec/support/helpers/wait_for_requests.rb index d5483d0b0a7..52d1c59ab03 100644 --- a/spec/support/helpers/wait_for_requests.rb +++ b/spec/support/helpers/wait_for_requests.rb @@ -34,14 +34,14 @@ module WaitForRequests # Wait for active Rack requests and client-side AJAX requests def wait_for_all_requests wait_for('pending requests complete') do - finished_all_rack_reqiests? && + finished_all_rack_requests? && finished_all_js_requests? end end private - def finished_all_rack_reqiests? + def finished_all_rack_requests? Gitlab::Testing::RequestBlockerMiddleware.num_active_requests.zero? end diff --git a/yarn.lock b/yarn.lock index 48e42c00eba..3a98c0e795e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -801,10 +801,10 @@ resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.104.0.tgz#ebbf99788d74b7224f116f1c0040fa0c90034d99" integrity sha512-lWg/EzxFdbx4YIdDWB2p5ag6Cna78AYGET8nXQYXYwd21/U3wKXKL7vsGR4kOxe1goA9ZAYG9eY+MK7cf+X2cA== -"@gitlab/ui@^9.18.0": - version "9.18.0" - resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-9.18.0.tgz#3f4f09e8b7791d0bd01f90920ac6e4477e977ab9" - integrity sha512-GdQFuH4fPU+/wvX+UL5wSYN6VB2EuIXHBjjrdLHeUhEWuXrtrbBBmtoVEfmii0XcX5iUccgw55orz8Dmq3DlGg== +"@gitlab/ui@^9.18.2": + version "9.18.2" + resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-9.18.2.tgz#b82387a8c7bde1db069670596d5ef05d4a16400a" + integrity sha512-nBJMIdTX+LYYVGWiF3tdhQ5/tPaYIW80rz8DBzxfLaZYw9pJPolXZ8Vj7xlJWRHbqiM+HghvJPPvgcBXIfKzPw== dependencies: "@babel/standalone" "^7.0.0" "@gitlab/vue-toasted" "^1.3.0" |