Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2020-11-10 15:09:14 +00:00
parent a08f8baa63
commit ff06f859cd
43 changed files with 564 additions and 241 deletions

View File

@ -97,32 +97,41 @@ update-yarn-cache:
- .rails-cache
- .use-pg11
stage: fixtures
needs: ["setup-test-env", "compile-test-assets"]
needs: ["setup-test-env", "retrieve-tests-metadata", "compile-test-assets"]
variables:
SETUP_DB: "true"
WEBPACK_VENDOR_DLL: "true"
script:
- run_timed_command "gem install knapsack --no-document"
- run_timed_command "scripts/gitaly-test-build"
- run_timed_command "scripts/gitaly-test-spawn"
- run_timed_command "bin/rake frontend:fixtures"
- source ./scripts/rspec_helpers.sh
- rspec_paralellized_job "--tag frontend_fixture"
artifacts:
name: frontend-fixtures
expire_in: 31d
when: always
paths:
- tmp/tests/frontend/
- knapsack/
frontend-fixtures:
rspec frontend_fixture:
extends:
- .frontend-fixtures-base
- .frontend:rules:default-frontend-jobs
frontend-fixtures-as-if-foss:
rspec frontend_fixture as-if-foss:
extends:
- .frontend-fixtures-base
- .frontend:rules:default-frontend-jobs-as-if-foss
- .as-if-foss
rspec-ee frontend_fixture:
extends:
- .frontend-fixtures-base
- .frontend:rules:default-frontend-jobs
parallel: 2
.frontend-test-base:
extends:
- .frontend-base
@ -152,7 +161,8 @@ karma:
extends:
- .karma-base
- .frontend:rules:default-frontend-jobs
needs: ["frontend-fixtures"]
# Don't use `needs` since `rspec-ee frontend_fixture` doesn't exist in `gitlab-foss` pipelines.
dependencies: ["rspec frontend_fixture", "rspec-ee frontend_fixture"]
coverage: '/^Statements *: (\d+\.\d+%)/'
artifacts:
name: coverage-javascript
@ -171,7 +181,7 @@ karma-as-if-foss:
- .karma-base
- .frontend:rules:default-frontend-jobs-as-if-foss
- .as-if-foss
needs: ["frontend-fixtures-as-if-foss"]
needs: ["rspec frontend_fixture as-if-foss"]
.jest-base:
extends: .frontend-test-base
@ -183,7 +193,8 @@ jest:
extends:
- .jest-base
- .frontend:rules:default-frontend-jobs
needs: ["frontend-fixtures"]
# Don't use `needs` since `rspec-ee frontend_fixture` doesn't exist in `gitlab-foss` pipelines.
dependencies: ["rspec frontend_fixture", "rspec-ee frontend_fixture"]
artifacts:
name: coverage-frontend
expire_in: 31d
@ -203,14 +214,15 @@ jest-integration:
script:
- *yarn-install
- run_timed_command "yarn jest:integration --ci"
needs: ["frontend-fixtures"]
# Don't use `needs` since `rspec-ee frontend_fixture` doesn't exist in `gitlab-foss` pipelines.
dependencies: ["rspec frontend_fixture", "rspec-ee frontend_fixture"]
jest-as-if-foss:
extends:
- .jest-base
- .frontend:rules:default-frontend-jobs-as-if-foss
- .as-if-foss
needs: ["frontend-fixtures-as-if-foss"]
needs: ["rspec frontend_fixture as-if-foss"]
parallel: 2
coverage-frontend:

View File

@ -28,6 +28,8 @@ update-tests-metadata:
dependencies:
- setup-test-env
- rspec migration pg11
- rspec frontend_fixture
- rspec-ee frontend_fixture
- rspec unit pg11
- rspec integration pg11
- rspec system pg11

View File

@ -1 +1 @@
9cde939eef5182b062f9aa04a3a3f78d8264af4b
c6ba78e87bed03254417fe4cd52d377d88ac0b60

View File

@ -167,7 +167,7 @@ export const updateOrCreateNotes = ({ commit, state, getters, dispatch }, notes)
if (discussion) {
commit(types.ADD_NEW_REPLY_TO_DISCUSSION, note);
} else if (note.type === constants.DIFF_NOTE) {
} else if (note.type === constants.DIFF_NOTE && !note.base_discussion) {
debouncedFetchDiscussions(state.currentlyFetchingDiscussions);
} else {
commit(types.ADD_NEW_NOTE, note);

View File

@ -11,24 +11,30 @@ export default {
const isDiscussion = type === constants.DISCUSSION_NOTE || type === constants.DIFF_NOTE;
if (!exists) {
const noteData = {
expanded: true,
id: discussion_id,
individual_note: !isDiscussion,
notes: [note],
reply_id: discussion_id,
};
let discussion = data.discussion || note.base_discussion;
if (isDiscussion && isInMRPage()) {
noteData.resolvable = note.resolvable;
noteData.resolved = false;
noteData.active = true;
noteData.resolve_path = note.resolve_path;
noteData.resolve_with_issue_path = note.resolve_with_issue_path;
noteData.diff_discussion = false;
if (!discussion) {
discussion = {
expanded: true,
id: discussion_id,
individual_note: !isDiscussion,
reply_id: discussion_id,
};
if (isDiscussion && isInMRPage()) {
discussion.resolvable = note.resolvable;
discussion.resolved = false;
discussion.active = true;
discussion.resolve_path = note.resolve_path;
discussion.resolve_with_issue_path = note.resolve_with_issue_path;
discussion.diff_discussion = false;
}
}
state.discussions.push(noteData);
note.base_discussion = undefined; // No point keeping a reference to this
discussion.notes = [note];
state.discussions.push(discussion);
}
},

View File

@ -1,5 +1,5 @@
import axios from '~/lib/utils/axios_utils';
import { deprecatedCreateFlash as createFlash } from '~/flash';
import createFlash from '~/flash';
import Api from '~/api';
import * as types from './mutation_types';
import {
@ -38,7 +38,7 @@ export const requestImagesList = (
dispatch('receiveImagesListSuccess', { data, headers });
})
.catch(() => {
createFlash(FETCH_IMAGES_LIST_ERROR_MESSAGE);
createFlash({ message: FETCH_IMAGES_LIST_ERROR_MESSAGE });
})
.finally(() => {
commit(types.SET_MAIN_LOADING, false);

View File

@ -41,6 +41,7 @@ class Admin::GroupsController < Admin::ApplicationController
if @group.save
@group.add_owner(current_user)
@group.create_namespace_settings
redirect_to [:admin, @group], notice: _('Group %{group_name} was successfully created.') % { group_name: @group.name }
else
render "new"

View File

@ -53,7 +53,7 @@ class AutocompleteController < ApplicationController
end
def deploy_keys_with_owners
deploy_keys = DeployKeys::CollectKeysService.new(project, current_user).execute
deploy_keys = DeployKey.with_write_access_for_project(project)
render json: DeployKeySerializer.new.represent(deploy_keys, { with_owner: true, user: current_user })
end

View File

@ -60,7 +60,7 @@ class Projects::NotesController < Projects::ApplicationController
def render_json_with_notes_serializer
prepare_notes_for_rendering([note])
render json: note_serializer.represent(note)
render json: note_serializer.represent(note, render_truncated_diff_lines: true)
end
def note

View File

@ -7,8 +7,9 @@ class DeployKey < Key
has_many :deploy_keys_projects, inverse_of: :deploy_key, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
has_many :projects, through: :deploy_keys_projects
scope :in_projects, ->(projects) { joins(:deploy_keys_projects).where('deploy_keys_projects.project_id in (?)', projects) }
scope :are_public, -> { where(public: true) }
scope :in_projects, ->(projects) { joins(:deploy_keys_projects).where(deploy_keys_projects: { project_id: projects }) }
scope :with_write_access, -> { joins(:deploy_keys_projects).merge(DeployKeysProject.with_write_access) }
scope :are_public, -> { where(public: true) }
scope :with_projects, -> { includes(deploy_keys_projects: { project: [:route, namespace: :route] }) }
ignore_column :can_push, remove_after: '2019-12-15', remove_with: '12.6'
@ -54,4 +55,11 @@ class DeployKey < Key
def projects_with_write_access
Project.with_route.where(id: deploy_keys_projects.with_write_access.select(:project_id))
end
def self.with_write_access_for_project(project, deploy_key: nil)
query = in_projects(project).with_write_access
query = query.where(id: deploy_key) if deploy_key
query
end
end

View File

@ -6,7 +6,6 @@ class DeployKeysProject < ApplicationRecord
scope :without_project_deleted, -> { joins(:project).where(projects: { pending_delete: false }) }
scope :in_project, ->(project) { where(project: project) }
scope :with_write_access, -> { where(can_push: true) }
scope :with_deploy_keys, -> { includes(:deploy_key) }
accepts_nested_attributes_for :deploy_key

View File

@ -584,6 +584,10 @@ class Group < Namespace
ancestor_settings.allow_mfa_for_subgroups
end
def has_project_with_service_desk_enabled?
Gitlab::ServiceDesk.supported? && all_projects.service_desk_enabled.exists?
end
private
def update_two_factor_requirement

View File

@ -59,6 +59,9 @@ class GroupPolicy < BasePolicy
with_scope :subject
condition(:resource_access_token_available) { resource_access_token_available? }
with_scope :subject
condition(:has_project_with_service_desk_enabled) { @subject.has_project_with_service_desk_enabled? }
rule { design_management_enabled }.policy do
enable :read_design_activity
end
@ -194,6 +197,10 @@ class GroupPolicy < BasePolicy
enable :admin_resource_access_tokens
end
rule { support_bot & has_project_with_service_desk_enabled }.policy do
enable :read_label
end
def access_level
return GroupMember::NO_ACCESS if @user.nil?
return GroupMember::NO_ACCESS unless user_is_user?

View File

@ -0,0 +1,55 @@
# frozen_string_literal: true
class BaseDiscussionEntity < Grape::Entity
include RequestAwareEntity
include NotesHelper
expose :id
expose :reply_id
expose :project_id
expose :commit_id
expose :confidential?, as: :confidential
expose :diff_discussion?, as: :diff_discussion
expose :expanded?, as: :expanded
expose :for_commit?, as: :for_commit
expose :individual_note?, as: :individual_note
expose :resolvable?, as: :resolvable
expose :truncated_diff_lines, using: DiffLineEntity, if: -> (d, _) { d.diff_discussion? && d.on_text? && (d.expanded? || render_truncated_diff_lines?) }
with_options if: -> (d, _) { d.diff_discussion? } do
expose :active?, as: :active
expose :line_code
expose :diff_file, using: DiscussionDiffFileEntity
end
with_options if: -> (d, _) { d.diff_discussion? && !d.legacy_diff_discussion? } do
expose :position
expose :original_position
end
expose :discussion_path do |discussion|
discussion_path(discussion)
end
with_options if: -> (d, _) { d.resolvable? } do
expose :resolve_path do |discussion|
resolve_project_merge_request_discussion_path(discussion.project, discussion.noteable, discussion.id)
end
expose :resolve_with_issue_path do |discussion|
new_project_issue_path(discussion.project, merge_request_to_resolve_discussions_of: discussion.noteable.iid, discussion_to_resolve: discussion.id)
end
end
expose :truncated_diff_lines_path, if: -> (d, _) { !d.expanded? && !render_truncated_diff_lines? } do |discussion|
project_merge_request_discussion_path(discussion.project, discussion.noteable, discussion)
end
private
def render_truncated_diff_lines?
options.fetch(:render_truncated_diff_lines, false)
end
end

View File

@ -1,23 +1,8 @@
# frozen_string_literal: true
class DiscussionEntity < Grape::Entity
include RequestAwareEntity
include NotesHelper
expose :id, :reply_id
expose :position, if: -> (d, _) { d.diff_discussion? && !d.legacy_diff_discussion? }
expose :original_position, if: -> (d, _) { d.diff_discussion? && !d.legacy_diff_discussion? }
expose :line_code, if: -> (d, _) { d.diff_discussion? }
expose :expanded?, as: :expanded
expose :active?, as: :active, if: -> (d, _) { d.diff_discussion? }
expose :project_id
class DiscussionEntity < BaseDiscussionEntity
expose :notes do |discussion, opts|
request.note_entity.represent(discussion.notes, opts)
end
expose :discussion_path do |discussion|
discussion_path(discussion)
request.note_entity.represent(discussion.notes, opts.merge(with_base_discussion: false))
end
expose :positions, if: -> (d, _) { display_merge_ref_discussions?(d) } do |discussion|
@ -28,42 +13,13 @@ class DiscussionEntity < Grape::Entity
discussion.diff_note_positions.map(&:line_code)
end
expose :individual_note?, as: :individual_note
expose :resolvable do |discussion|
discussion.resolvable?
end
expose :resolved?, as: :resolved
expose :resolved_by_push?, as: :resolved_by_push
expose :resolved_by, using: NoteUserEntity
expose :resolved_at
expose :resolve_path, if: -> (d, _) { d.resolvable? } do |discussion|
resolve_project_merge_request_discussion_path(discussion.project, discussion.noteable, discussion.id)
end
expose :resolve_with_issue_path, if: -> (d, _) { d.resolvable? } do |discussion|
new_project_issue_path(discussion.project, merge_request_to_resolve_discussions_of: discussion.noteable.iid, discussion_to_resolve: discussion.id)
end
expose :diff_file, using: DiscussionDiffFileEntity, if: -> (d, _) { d.diff_discussion? }
expose :diff_discussion?, as: :diff_discussion
expose :truncated_diff_lines_path, if: -> (d, _) { !d.expanded? && !render_truncated_diff_lines? } do |discussion|
project_merge_request_discussion_path(discussion.project, discussion.noteable, discussion)
end
expose :truncated_diff_lines, using: DiffLineEntity, if: -> (d, _) { d.diff_discussion? && d.on_text? && (d.expanded? || render_truncated_diff_lines?) }
expose :for_commit?, as: :for_commit
expose :commit_id
expose :confidential?, as: :confidential
private
def render_truncated_diff_lines?
options[:render_truncated_diff_lines]
end
def current_user
request.current_user
end

View File

@ -81,11 +81,25 @@ class NoteEntity < API::Entities::Note
expose :cached_markdown_version
# Correctly rendering a note requires some background information about any
# discussion it is part of. This is essential for the notes endpoint, but
# optional for the discussions endpoint, which will include the discussion
# along with the note
expose :discussion, as: :base_discussion, using: BaseDiscussionEntity, if: -> (_, _) { with_base_discussion? }
private
def discussion
@discussion ||= object.to_discussion(request.noteable)
end
def current_user
request.current_user
end
def with_base_discussion?
options.fetch(:with_base_discussion, true)
end
end
NoteEntity.prepend_if_ee('EE::NoteEntity')

View File

@ -1,27 +0,0 @@
# frozen_string_literal: true
module DeployKeys
class CollectKeysService
def initialize(project, current_user)
@project = project
@current_user = current_user
end
def execute
return [] unless current_user && project && user_can_read_project
project.deploy_keys_projects
.with_deploy_keys
.with_write_access
.map(&:deploy_key)
end
private
def user_can_read_project
Ability.allowed?(current_user, :read_project, project)
end
attr_reader :project, :current_user
end
end

View File

@ -0,0 +1,5 @@
---
title: Don't refresh all discussions for a new diff note on a merge request
merge_request: 43015
author:
type: performance

View File

@ -0,0 +1,6 @@
---
title: Resolve problem when namespace_settings were not created for groups created
via admin panel
merge_request: 46875
author:
type: fixed

View File

@ -0,0 +1,5 @@
---
title: Allow to apply group labels with service desk templates
merge_request: 46492
author:
type: fixed

View File

@ -0,0 +1,40 @@
# frozen_string_literal: true
class EnsureNamespaceSettingsCreation < ActiveRecord::Migration[5.2]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
BATCH_SIZE = 10000
MIGRATION = 'BackfillNamespaceSettings'
DELAY_INTERVAL = 2.minutes.to_i
disable_ddl_transaction!
class Namespace < ActiveRecord::Base
include EachBatch
self.table_name = 'namespaces'
end
def up
ensure_data_migration
end
def down
# no-op
end
private
def ensure_data_migration
Namespace.each_batch(of: BATCH_SIZE) do |query, index|
missing_count = query.where("NOT EXISTS (SELECT 1 FROM namespace_settings WHERE namespace_settings.namespace_id=namespaces.id)").limit(1).size
if missing_count > 0
ids_range = query.pluck("MIN(id), MAX(id)").flatten
migrate_in(index * DELAY_INTERVAL, MIGRATION, ids_range)
end
end
end
end

View File

@ -0,0 +1 @@
e17da7eebb6d054a711368369d2b4fa684e96344f845bb7c6b3c89a9b4c4e067

View File

@ -32,10 +32,7 @@ upgrade to GitLab 13.4 or later.
## Updating to GitLab 13.2
In GitLab 13.2, promoting a secondary node to a primary while the secondary is
paused fails. Do not pause replication before promoting a secondary. If the
node is paused, be sure to resume before promoting. To avoid this issue,
upgrade to GitLab 13.4 or later.
In GitLab 13.2, promoting a secondary node to a primary while the secondary is paused fails. **Do not pause replication before promoting a secondary.** If the node is paused, please resume before promoting. To avoid this issue, upgrade to GitLab 13.4 or later.
## Updating to GitLab 13.0

View File

@ -56,24 +56,11 @@ test:
> Introduced in GitLab Runner 8.9.
By default, GitLab is configured to always prefer the `GIT_STRATEGY: fetch` strategy.
The `GIT_STRATEGY: fetch` strategy will re-use existing worktrees if found
on disk. This is different to the `GIT_STRATEGY: clone` strategy
as in case of clones, if a worktree is found, it is removed before clone.
Usage of `fetch` is preferred because it reduces the amount of data to transfer and
By default, GitLab is configured to use the [`fetch` Git strategy](../runners/README.md#git-strategy),
which is recommended for large repositories.
This strategy reduces the amount of data to transfer and
does not really impact the operations that you might do on a repository from CI.
However, `fetch` does require access to the previous worktree. This works
well when using the `shell` or `docker` executor because these
try to preserve worktrees and try to re-use them by default.
This does not work today for `kubernetes` executor and has limitations when using
`docker+machine`. `kubernetes` executor today always clones into ephemeral directory.
GitLab also offers the `GIT_STRATEGY: none` strategy. This disables any `fetch` and `checkout` commands
done by GitLab, requiring you to do them.
## Git clone path
> Introduced in GitLab Runner 11.10.

View File

@ -26,10 +26,11 @@ There are two options. Using:
- `git clone`, which is slower since it clones the repository from scratch
for every job, ensuring that the local working copy is always pristine.
- `git fetch`, which is faster as it re-uses the local working copy (falling
- `git fetch`, which is GitLab's default and faster as it re-uses the local working copy (falling
back to clone if it doesn't exist).
This is recommended, especially for [large repositories](../large_repositories/index.md#git-strategy).
The default Git strategy can be overridden by the [GIT_STRATEGY variable](../runners/README.md#git-strategy)
The configured Git strategy can be overridden by the [`GIT_STRATEGY` variable](../runners/README.md#git-strategy)
in `.gitlab-ci.yml`.
## Git shallow clone

View File

@ -478,47 +478,44 @@ You can also use variables to configure how many times a runner
> - Introduced in GitLab 8.9 as an experimental feature.
> - `GIT_STRATEGY=none` requires GitLab Runner v1.7+.
You can set the `GIT_STRATEGY` used for getting recent application code, either
globally or per-job in the [`variables`](../yaml/README.md#variables) section. If left
unspecified, the default from the project settings is used.
There are three possible values: `clone`, `fetch`, and `none`.
`clone` is the slowest option. It clones the repository from scratch for every
job, ensuring that the local working copy is always pristine.
You can set the `GIT_STRATEGY` used to fetch the repository content, either
globally or per-job in the [`variables`](../yaml/README.md#variables) section:
```yaml
variables:
GIT_STRATEGY: clone
```
There are three possible values: `clone`, `fetch`, and `none`. If left unspecified,
jobs use the [project's pipeline setting](../pipelines/settings.md#git-strategy).
`clone` is the slowest option. It clones the repository from scratch for every
job, ensuring that the local working copy is always pristine.
If an existing worktree is found, it is removed before cloning.
`fetch` is faster as it re-uses the local working copy (falling back to `clone`
if it does not exist). `git clean` is used to undo any changes made by the last
job, and `git fetch` is used to retrieve commits made since the last job ran.
job, and `git fetch` is used to retrieve commits made after the last job ran.
```yaml
variables:
GIT_STRATEGY: fetch
```
However, `fetch` does require access to the previous worktree. This works
well when using the `shell` or `docker` executor because these
try to preserve worktrees and try to re-use them by default.
`none` also re-uses the local working copy. However, it skips all Git operations,
including GitLab Runner's pre-clone script, if present.
This has limitations when using the [Docker Machine executor](https://docs.gitlab.com/runner/executors/docker_machine.html).
It's useful for jobs that operate exclusively on artifacts, like a deployment job.
Git repository data may be present, but it's likely out-of-date. You should only
It does not work for [the `kubernetes` executor](https://docs.gitlab.com/runner/executors/kubernetes.html),
but a [feature proposal](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/3847) exists.
The `kubernetes` executor always clones into an temporary directory.
A Git strategy of `none` also re-uses the local working copy, but skips all Git
operations normally done by GitLab. GitLab Runner pre-clone scripts are also skipped,
if present. This strategy could mean you need to add `fetch` and `checkout` commands
to [your `.gitlab-ci.yml` script](../yaml/README.md#script).
It can be used for jobs that operate exclusively on artifacts, like a deployment job.
Git repository data may be present, but it's likely out of date. You should only
rely on files brought into the local working copy from cache or artifacts.
```yaml
variables:
GIT_STRATEGY: none
```
NOTE: **Note:**
`GIT_STRATEGY` is not supported for
[Kubernetes executor](https://docs.gitlab.com/runner/executors/kubernetes.html),
but may be in the future. See the [support Git strategy with Kubernetes executor feature proposal](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/3847)
for updates.
### Git submodule strategy
> Requires GitLab Runner v1.10+.

View File

@ -90,9 +90,13 @@ module Gitlab
def can_collaborate?(ref)
assert_project!
can_push? || branch_allows_collaboration_for?(ref)
end
def branch_allows_collaboration_for?(ref)
# Checking for an internal project or group to prevent an infinite loop:
# https://gitlab.com/gitlab-org/gitlab/issues/36805
can_push? || (!project.internal? && project.branch_allows_collaboration?(user, ref))
(!project.internal? && project.branch_allows_collaboration?(user, ref))
end
def permission_cache

View File

@ -12,6 +12,9 @@ module Quality
lib/gitlab/background_migration
lib/ee/gitlab/background_migration
],
frontend_fixture: %w[
frontend/fixtures
],
unit: %w[
bin
channels
@ -63,7 +66,7 @@ module Quality
end
def pattern(level)
@patterns[level] ||= "#{prefix}spec/#{folders_pattern(level)}{,/**/}*_spec.rb"
@patterns[level] ||= "#{prefix}spec/#{folders_pattern(level)}{,/**/}*#{suffix(level)}"
end
def regexp(level)
@ -76,6 +79,9 @@ module Quality
# spec/lib/gitlab/background_migration and tests under spec/lib are unit by default
when regexp(:migration), regexp(:background_migration)
:migration
# Detect frontend fixture before matching other unit tests
when regexp(:frontend_fixture)
:frontend_fixture
when regexp(:unit)
:unit
when regexp(:integration)
@ -93,6 +99,15 @@ module Quality
private
def suffix(level)
case level
when :frontend_fixture
".rb"
else
"_spec.rb"
end
end
def migration_and_background_migration_folders
TEST_LEVEL_FOLDERS.fetch(:migration) + TEST_LEVEL_FOLDERS.fetch(:background_migration)
end

View File

@ -2,9 +2,9 @@
require_relative '../../qa'
# This script deletes all subgroups of a group specified by ENV['GROUP_NAME_OR_PATH']
# This script deletes all subgroups of a group specified by ENV['TOP_LEVEL_GROUP_NAME']
# Required environment variables: GITLAB_QA_ACCESS_TOKEN and GITLAB_ADDRESS
# Optional environment variable: GROUP_NAME_OR_PATH (defaults to 'gitlab-qa-sandbox-group')
# Optional environment variable: TOP_LEVEL_GROUP_NAME (defaults to 'gitlab-qa-sandbox-group')
# Run `rake delete_subgroups`
module QA
@ -47,8 +47,9 @@ module QA
end
def fetch_group_id
group_search_response = get Runtime::API::Request.new(@api_client, "/groups", search: ENV['GROUP_NAME_OR_PATH'] || 'gitlab-qa-sandbox-group').url
JSON.parse(group_search_response.body).first["id"]
group_name = ENV['TOP_LEVEL_GROUP_NAME'] || 'gitlab-qa-sandbox-group'
group_search_response = get Runtime::API::Request.new(@api_client, "/groups/#{group_name}" ).url
JSON.parse(group_search_response.body)["id"]
end
def fetch_subgroup_ids(group_id, group_pages)

View File

@ -25,6 +25,20 @@ RSpec.describe Admin::GroupsController do
end
end
describe 'POST #create' do
it 'creates group' do
expect do
post :create, params: { group: { path: 'test', name: 'test' } }
end.to change { Group.count }.by(1)
end
it 'creates namespace_settings for group' do
expect do
post :create, params: { group: { path: 'test', name: 'test' } }
end.to change { NamespaceSetting.count }.by(1)
end
end
describe 'PUT #members_update' do
let(:group_user) { create(:user) }

View File

@ -382,6 +382,17 @@ RSpec.describe AutocompleteController do
sign_in(user)
end
context 'and they cannot read the project' do
it 'returns a not found response' do
allow(Ability).to receive(:allowed?).and_call_original
allow(Ability).to receive(:allowed?).with(user, :read_project, project).and_return(false)
get(:deploy_keys_with_owners, params: { project_id: project.id })
expect(response).to have_gitlab_http_status(:not_found)
end
end
it 'renders the deploy key in a json payload, with its owner' do
get(:deploy_keys_with_owners, params: { project_id: project.id })

View File

@ -1,7 +1,7 @@
import MockAdapter from 'axios-mock-adapter';
import testAction from 'helpers/vuex_action_helper';
import { TEST_HOST } from 'helpers/test_constants';
import { deprecatedCreateFlash as createFlash } from '~/flash';
import createFlash from '~/flash';
import Api from '~/api';
import axios from '~/lib/utils/axios_utils';
import * as actions from '~/registry/explorer/stores/actions';

View File

@ -11,13 +11,13 @@ RSpec.describe Gitlab::Email::Handler::ServiceDeskHandler do
end
let(:email_raw) { email_fixture('emails/service_desk.eml') }
let_it_be(:namespace) { create(:namespace, name: "email") }
let_it_be(:group) { create(:group, :private, name: "email") }
let(:expected_description) do
"Service desk stuff!\n\n```\na = b\n```\n\n`/label ~label1`\n`/assign @user1`\n`/close`\n![image](uploads/image.png)"
end
context 'service desk is enabled for the project' do
let_it_be(:project) { create(:project, :repository, :public, namespace: namespace, path: 'test', service_desk_enabled: true) }
let_it_be(:project) { create(:project, :repository, :private, group: group, path: 'test', service_desk_enabled: true) }
before do
allow(Gitlab::ServiceDesk).to receive(:supported?).and_return(true)
@ -101,6 +101,18 @@ RSpec.describe Gitlab::Email::Handler::ServiceDeskHandler do
expect(issue.milestone).to eq(milestone)
end
it 'applies group labels using quick actions' do
group_label = create(:group_label, group: project.group, title: 'label2')
file_content = %(Text from template \n/label ~#{group_label.title}"")
set_template_file('with_group_labels', file_content)
receiver.execute
issue = Issue.last
expect(issue.description).to include('Text from template')
expect(issue.label_ids).to include(group_label.id)
end
it 'redacts quick actions present on user email body' do
set_template_file('service_desk1', 'text from template')
@ -289,7 +301,8 @@ RSpec.describe Gitlab::Email::Handler::ServiceDeskHandler do
end
context 'service desk is disabled for the project' do
let(:project) { create(:project, :public, namespace: namespace, path: 'test', service_desk_enabled: false) }
let(:group) { create(:group)}
let(:project) { create(:project, :public, group: group, path: 'test', service_desk_enabled: false) }
it 'bounces the email' do
expect { receiver.execute }.to raise_error(Gitlab::Email::ProcessingError)

View File

@ -18,6 +18,13 @@ RSpec.describe Quality::TestLevel do
end
end
context 'when level is frontend_fixture' do
it 'returns a pattern' do
expect(subject.pattern(:frontend_fixture))
.to eq("spec/{frontend/fixtures}{,/**/}*.rb")
end
end
context 'when level is unit' do
it 'returns a pattern' do
expect(subject.pattern(:unit))
@ -86,6 +93,13 @@ RSpec.describe Quality::TestLevel do
end
end
context 'when level is frontend_fixture' do
it 'returns a regexp' do
expect(subject.regexp(:frontend_fixture))
.to eq(%r{spec/(frontend/fixtures)})
end
end
context 'when level is unit' do
it 'returns a regexp' do
expect(subject.regexp(:unit))
@ -144,6 +158,10 @@ RSpec.describe Quality::TestLevel do
expect(subject.level_for('spec/models/abuse_report_spec.rb')).to eq(:unit)
end
it 'returns the correct level for a frontend fixture test' do
expect(subject.level_for('spec/frontend/fixtures/pipelines.rb')).to eq(:frontend_fixture)
end
it 'returns the correct level for a tooling test' do
expect(subject.level_for('spec/tooling/lib/tooling/test_file_finder_spec.rb')).to eq(:unit)
end

View File

@ -0,0 +1,44 @@
# frozen_string_literal: true
require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20201104124300_ensure_namespace_settings_creation.rb')
RSpec.describe EnsureNamespaceSettingsCreation do
context 'when there are namespaces without namespace settings' do
let(:namespaces) { table(:namespaces) }
let(:namespace_settings) { table(:namespace_settings) }
let!(:namespace) { namespaces.create!(name: 'gitlab', path: 'gitlab-org') }
let!(:namespace_2) { namespaces.create!(name: 'gitlab', path: 'gitlab-org2') }
it 'migrates namespaces without namespace_settings' do
stub_const("#{described_class.name}::BATCH_SIZE", 2)
Sidekiq::Testing.fake! do
freeze_time do
migrate!
expect(described_class::MIGRATION)
.to be_scheduled_delayed_migration(2.minutes.to_i, namespace.id, namespace_2.id)
end
end
end
it 'schedules migrations in batches ' do
stub_const("#{described_class.name}::BATCH_SIZE", 2)
namespace_3 = namespaces.create!(name: 'gitlab', path: 'gitlab-org3')
namespace_4 = namespaces.create!(name: 'gitlab', path: 'gitlab-org4')
Sidekiq::Testing.fake! do
freeze_time do
migrate!
expect(described_class::MIGRATION)
.to be_scheduled_delayed_migration(2.minutes.to_i, namespace.id, namespace_2.id)
expect(described_class::MIGRATION)
.to be_scheduled_delayed_migration(4.minutes.to_i, namespace_3.id, namespace_4.id)
end
end
end
end
end

View File

@ -40,4 +40,56 @@ RSpec.describe DeployKey, :mailer do
end
end
end
describe '.with_write_access_for_project' do
let_it_be(:project) { create(:project, :private) }
subject { described_class.with_write_access_for_project(project) }
context 'when no project is passed in' do
let(:project) { nil }
it { is_expected.to be_empty }
end
context 'when a project is passed in' do
let_it_be(:deploy_keys_project) { create(:deploy_keys_project, :write_access, project: project) }
let_it_be(:deploy_key) { deploy_keys_project.deploy_key }
it 'only returns deploy keys with write access' do
create(:deploy_keys_project, project: project)
is_expected.to contain_exactly(deploy_key)
end
it 'returns deploy keys only for this project' do
other_project = create(:project)
create(:deploy_keys_project, :write_access, project: other_project)
is_expected.to contain_exactly(deploy_key)
end
context 'and a specific deploy key is passed in' do
subject { described_class.with_write_access_for_project(project, deploy_key: specific_deploy_key) }
context 'and this deploy key is not linked to the project' do
let(:specific_deploy_key) { create(:deploy_key) }
it { is_expected.to be_empty }
end
context 'and this deploy key has not write access to the project' do
let(:specific_deploy_key) { create(:deploy_key, deploy_keys_projects: [create(:deploy_keys_project, project: project)]) }
it { is_expected.to be_empty }
end
context 'and this deploy key has write access to the project' do
let(:specific_deploy_key) { create(:deploy_key, deploy_keys_projects: [create(:deploy_keys_project, :write_access, project: project)]) }
it { is_expected.to contain_exactly(specific_deploy_key) }
end
end
end
end
end

View File

@ -13,21 +13,6 @@ RSpec.describe DeployKeysProject do
it { is_expected.to validate_presence_of(:deploy_key) }
end
describe '.with_deploy_keys' do
subject(:scoped_query) { described_class.with_deploy_keys.last }
it 'includes deploy_keys in query' do
project = create(:project)
create(:deploy_keys_project, project: project, deploy_key: create(:deploy_key))
includes_query_count = ActiveRecord::QueryRecorder.new { scoped_query }.count
deploy_key_query_count = ActiveRecord::QueryRecorder.new { scoped_query.deploy_key }.count
expect(includes_query_count).to eq(2)
expect(deploy_key_query_count).to eq(0)
end
end
describe "Destroying" do
let(:project) { create(:project) }
subject { create(:deploy_keys_project, project: project) }

View File

@ -1663,4 +1663,47 @@ RSpec.describe Group do
end
end
end
describe 'has_project_with_service_desk_enabled?' do
let_it_be(:group) { create(:group, :private) }
subject { group.has_project_with_service_desk_enabled? }
before do
allow(Gitlab::ServiceDesk).to receive(:supported?).and_return(true)
end
context 'when service desk is enabled' do
context 'for top level group' do
let_it_be(:project) { create(:project, group: group, service_desk_enabled: true) }
it { is_expected.to eq(true) }
context 'when service desk is not supported' do
before do
allow(Gitlab::ServiceDesk).to receive(:supported?).and_return(false)
end
it { is_expected.to eq(false) }
end
end
context 'for subgroup project' do
let_it_be(:subgroup) { create(:group, :private, parent: group)}
let_it_be(:project) { create(:project, group: subgroup, service_desk_enabled: true) }
it { is_expected.to eq(true) }
end
end
context 'when none of group child projects has service desk enabled' do
let_it_be(:project) { create(:project, group: group, service_desk_enabled: false) }
before do
project.update(service_desk_enabled: false)
end
it { is_expected.to eq(false) }
end
end
end

View File

@ -882,4 +882,23 @@ RSpec.describe GroupPolicy do
end
it_behaves_like 'Self-managed Core resource access tokens'
context 'support bot' do
let_it_be(:group) { create(:group, :private) }
let_it_be(:current_user) { User.support_bot }
before do
allow(Gitlab::ServiceDesk).to receive(:supported?).and_return(true)
end
it { expect_disallowed(:read_label) }
context 'when group hierarchy has a project with service desk enabled' do
let_it_be(:subgroup) { create(:group, :private, parent: group)}
let_it_be(:project) { create(:project, group: subgroup, service_desk_enabled: true) }
it { expect_allowed(:read_label) }
it { expect(described_class.new(current_user, subgroup)).to be_allowed(:read_label) }
end
end
end

View File

@ -0,0 +1,69 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe BaseDiscussionEntity do
let_it_be(:user) { create(:user) }
let_it_be(:note) { create(:discussion_note_on_merge_request) }
let(:request) { double('request', note_entity: ProjectNoteEntity) }
let(:controller) { double('controller') }
let(:entity) { described_class.new(discussion, request: request, context: controller) }
let(:discussion) { note.discussion }
subject { entity.as_json }
before do
allow(controller).to receive(:render_to_string)
allow(request).to receive(:current_user).and_return(user)
allow(request).to receive(:noteable).and_return(note.noteable)
end
it 'exposes correct attributes' do
expect(subject.keys.sort).to include(
:commit_id,
:confidential,
:diff_discussion,
:discussion_path,
:expanded,
:for_commit,
:id,
:individual_note,
:resolvable,
:resolve_path,
:resolve_with_issue_path
)
end
context 'when is LegacyDiffDiscussion' do
let(:project) { create(:project) }
let(:merge_request) { create(:merge_request, source_project: project) }
let(:discussion) { create(:legacy_diff_note_on_merge_request, noteable: merge_request, project: project).to_discussion }
it 'exposes correct attributes' do
expect(subject.keys.sort).to include(
:commit_id,
:diff_discussion,
:discussion_path,
:expanded,
:for_commit,
:id,
:individual_note
)
end
end
context 'when diff file is present' do
let(:note) { create(:diff_note_on_merge_request) }
it 'exposes diff file attributes' do
expect(subject.keys.sort).to include(
:active,
:diff_file,
:line_code,
:position,
:truncated_diff_lines
)
end
end
end

View File

@ -39,6 +39,10 @@ RSpec.describe DiscussionEntity do
)
end
it 'does not include base discussion in the notes' do
expect(subject[:notes].first.keys).not_to include(:base_discussion)
end
it 'resolved_by matches note_user_entity schema' do
Notes::ResolveService.new(note.project, user).execute(note)

View File

@ -1,58 +0,0 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe DeployKeys::CollectKeysService do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :private) }
subject { DeployKeys::CollectKeysService.new(project, user) }
before do
project&.add_developer(user)
end
context 'when no project is passed in' do
let(:project) { nil }
it 'returns an empty Array' do
expect(subject.execute).to be_empty
end
end
context 'when no user is passed in' do
let(:user) { nil }
it 'returns an empty Array' do
expect(subject.execute).to be_empty
end
end
context 'when a project is passed in' do
let_it_be(:deploy_keys_project) { create(:deploy_keys_project, :write_access, project: project) }
let_it_be(:deploy_key) { deploy_keys_project.deploy_key }
it 'only returns deploy keys with write access' do
create(:deploy_keys_project, project: project)
expect(subject.execute).to contain_exactly(deploy_key)
end
it 'returns deploy keys only for this project' do
other_project = create(:project)
create(:deploy_keys_project, :write_access, project: other_project)
expect(subject.execute).to contain_exactly(deploy_key)
end
end
context 'when the user cannot read the project' do
before do
project.members.delete_all
end
it 'returns an empty Array' do
expect(subject.execute).to be_empty
end
end
end

View File

@ -5,8 +5,21 @@ RSpec.shared_examples 'note entity' do
context 'basic note' do
it 'exposes correct elements' do
expect(subject).to include(:type, :author, :note, :note_html, :current_user, :discussion_id,
:emoji_awardable, :award_emoji, :report_abuse_path, :attachment, :noteable_note_url, :resolvable)
expect(subject).to include(
:attachment,
:author,
:award_emoji,
:base_discussion,
:current_user,
:discussion_id,
:emoji_awardable,
:note,
:note_html,
:noteable_note_url,
:report_abuse_path,
:resolvable,
:type
)
end
it 'does not expose elements for specific notes cases' do