Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
a08f8baa63
commit
ff06f859cd
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1 +1 @@
|
|||
9cde939eef5182b062f9aa04a3a3f78d8264af4b
|
||||
c6ba78e87bed03254417fe4cd52d377d88ac0b60
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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?
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Allow to apply group labels with service desk templates
|
||||
merge_request: 46492
|
||||
author:
|
||||
type: fixed
|
|
@ -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
|
|
@ -0,0 +1 @@
|
|||
e17da7eebb6d054a711368369d2b4fa684e96344f845bb7c6b3c89a9b4c4e067
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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+.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) }
|
||||
|
||||
|
|
|
@ -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 })
|
||||
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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) }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue