Merge branch 'master-ce' into scheduled-manual-jobs
This commit is contained in:
commit
7542a5d102
|
|
@ -1 +1 @@
|
|||
1.1.0
|
||||
1.2.0
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ export const defaultAutocompleteConfig = {
|
|||
epics: true,
|
||||
milestones: true,
|
||||
labels: true,
|
||||
snippets: true,
|
||||
};
|
||||
|
||||
class GfmAutoComplete {
|
||||
|
|
@ -50,6 +51,7 @@ class GfmAutoComplete {
|
|||
if (this.enableMap.milestones) this.setupMilestones($input);
|
||||
if (this.enableMap.mergeRequests) this.setupMergeRequests($input);
|
||||
if (this.enableMap.labels) this.setupLabels($input);
|
||||
if (this.enableMap.snippets) this.setupSnippets($input);
|
||||
|
||||
// We don't instantiate the quick actions autocomplete for note and issue/MR edit forms
|
||||
$input.filter('[data-supports-quick-actions="true"]').atwho({
|
||||
|
|
@ -360,6 +362,39 @@ class GfmAutoComplete {
|
|||
});
|
||||
}
|
||||
|
||||
setupSnippets($input) {
|
||||
$input.atwho({
|
||||
at: '$',
|
||||
alias: 'snippets',
|
||||
searchKey: 'search',
|
||||
displayTpl(value) {
|
||||
let tmpl = GfmAutoComplete.Loading.template;
|
||||
if (value.title != null) {
|
||||
tmpl = GfmAutoComplete.Issues.template;
|
||||
}
|
||||
return tmpl;
|
||||
},
|
||||
data: GfmAutoComplete.defaultLoadingData,
|
||||
// eslint-disable-next-line no-template-curly-in-string
|
||||
insertTpl: '${atwho-at}${id}',
|
||||
callbacks: {
|
||||
...this.getDefaultCallbacks(),
|
||||
beforeSave(snippets) {
|
||||
return $.map(snippets, (m) => {
|
||||
if (m.title == null) {
|
||||
return m;
|
||||
}
|
||||
return {
|
||||
id: m.id,
|
||||
title: sanitize(m.title),
|
||||
search: `${m.id} ${m.title}`,
|
||||
};
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
getDefaultCallbacks() {
|
||||
const fetchData = this.fetchData.bind(this);
|
||||
|
||||
|
|
@ -470,7 +505,7 @@ class GfmAutoComplete {
|
|||
// The below is taken from At.js source
|
||||
// Tweaked to commands to start without a space only if char before is a non-word character
|
||||
// https://github.com/ichord/At.js
|
||||
const atSymbolsWithBar = Object.keys(controllers).join('|');
|
||||
const atSymbolsWithBar = Object.keys(controllers).join('|').replace(/[$]/, '\\$&');
|
||||
const atSymbolsWithoutBar = Object.keys(controllers).join('');
|
||||
const targetSubtext = subtext.split(GfmAutoComplete.regexSubtext).pop();
|
||||
const resultantFlag = flag.replace(/[-[\]/{}()*+?.\\^$|]/g, '\\$&');
|
||||
|
|
@ -497,6 +532,7 @@ GfmAutoComplete.atTypeMap = {
|
|||
'~': 'labels',
|
||||
'%': 'milestones',
|
||||
'/': 'commands',
|
||||
$: 'snippets',
|
||||
};
|
||||
|
||||
// Emoji
|
||||
|
|
@ -519,7 +555,7 @@ GfmAutoComplete.Labels = {
|
|||
// eslint-disable-next-line no-template-curly-in-string
|
||||
template: '<li><span class="dropdown-label-box" style="background: ${color}"></span> ${title}</li>',
|
||||
};
|
||||
// Issues and MergeRequests
|
||||
// Issues, MergeRequests and Snippets
|
||||
GfmAutoComplete.Issues = {
|
||||
// eslint-disable-next-line no-template-curly-in-string
|
||||
template: '<li><small>${id}</small> ${title}</li>',
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@
|
|||
<button
|
||||
:class="{ disabled: formState.updateLoading || !isSubmitEnabled }"
|
||||
:disabled="formState.updateLoading || !isSubmitEnabled"
|
||||
class="btn btn-success float-left"
|
||||
class="btn btn-success float-left qa-save-button"
|
||||
type="submit"
|
||||
@click.prevent="updateIssuable">
|
||||
Save changes
|
||||
|
|
@ -86,7 +86,7 @@
|
|||
v-if="shouldShowDeleteButton"
|
||||
:class="{ disabled: deleteLoading }"
|
||||
:disabled="deleteLoading"
|
||||
class="btn btn-danger float-right append-right-default"
|
||||
class="btn btn-danger float-right append-right-default qa-delete-button"
|
||||
type="button"
|
||||
@click="deleteIssuable">
|
||||
Delete
|
||||
|
|
|
|||
|
|
@ -61,7 +61,8 @@
|
|||
ref="textarea"
|
||||
slot="textarea"
|
||||
v-model="formState.description"
|
||||
class="note-textarea js-gfm-input js-autosize markdown-area"
|
||||
class="note-textarea js-gfm-input js-autosize markdown-area
|
||||
qa-description-textarea"
|
||||
data-supports-quick-actions="false"
|
||||
aria-label="Description"
|
||||
placeholder="Write a comment or drag your files here…"
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
<input
|
||||
id="issuable-title"
|
||||
v-model="formState.title"
|
||||
class="form-control"
|
||||
class="form-control qa-title-input"
|
||||
type="text"
|
||||
placeholder="Title"
|
||||
aria-label="Title"
|
||||
|
|
|
|||
|
|
@ -79,7 +79,8 @@ export default {
|
|||
v-if="showInlineEditButton && canUpdate"
|
||||
v-tooltip
|
||||
type="button"
|
||||
class="btn btn-default btn-edit btn-svg js-issuable-edit"
|
||||
class="btn btn-default btn-edit btn-svg js-issuable-edit
|
||||
qa-edit-button"
|
||||
title="Edit title and description"
|
||||
data-placement="bottom"
|
||||
data-container="body"
|
||||
|
|
|
|||
|
|
@ -7,7 +7,11 @@ import { __, sprintf } from '~/locale';
|
|||
import Flash from '../../flash';
|
||||
import Autosave from '../../autosave';
|
||||
import TaskList from '../../task_list';
|
||||
import { capitalizeFirstCharacter, convertToCamelCase, splitCamelCase } from '../../lib/utils/text_utility';
|
||||
import {
|
||||
capitalizeFirstCharacter,
|
||||
convertToCamelCase,
|
||||
splitCamelCase,
|
||||
} from '../../lib/utils/text_utility';
|
||||
import * as constants from '../constants';
|
||||
import eventHub from '../event_hub';
|
||||
import issueWarning from '../../vue_shared/components/issue/issue_warning.vue';
|
||||
|
|
@ -122,7 +126,9 @@ export default {
|
|||
return this.getNoteableData.create_note_path;
|
||||
},
|
||||
issuableTypeTitle() {
|
||||
return this.noteableType === constants.MERGE_REQUEST_NOTEABLE_TYPE ? 'merge request' : 'issue';
|
||||
return this.noteableType === constants.MERGE_REQUEST_NOTEABLE_TYPE
|
||||
? 'merge request'
|
||||
: 'issue';
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
|
|
@ -359,7 +365,7 @@ Please check your network connection and try again.`;
|
|||
:disabled="isSubmitting"
|
||||
name="note[note]"
|
||||
class="note-textarea js-vue-comment-form js-note-text
|
||||
js-gfm-input js-autosize markdown-area js-vue-textarea"
|
||||
js-gfm-input js-autosize markdown-area js-vue-textarea qa-comment-input"
|
||||
data-supports-quick-actions="true"
|
||||
aria-label="Description"
|
||||
placeholder="Write a comment or drag your files here…"
|
||||
|
|
@ -374,7 +380,8 @@ js-gfm-input js-autosize markdown-area js-vue-textarea"
|
|||
append-right-10 comment-type-dropdown js-comment-type-dropdown droplab-dropdown">
|
||||
<button
|
||||
:disabled="isSubmitButtonDisabled"
|
||||
class="btn btn-success comment-btn js-comment-button js-comment-submit-button"
|
||||
class="btn btn-create comment-btn js-comment-button js-comment-submit-button
|
||||
qa-comment-button"
|
||||
type="submit"
|
||||
@click.prevent="handleSave()">
|
||||
{{ __(commentButtonTitle) }}
|
||||
|
|
|
|||
|
|
@ -52,6 +52,21 @@
|
|||
required: false,
|
||||
default: '',
|
||||
},
|
||||
pagesAvailable: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
pagesAccessControlEnabled: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
pagesHelpPath: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
|
|
@ -64,6 +79,7 @@
|
|||
buildsAccessLevel: 20,
|
||||
wikiAccessLevel: 20,
|
||||
snippetsAccessLevel: 20,
|
||||
pagesAccessLevel: 20,
|
||||
containerRegistryEnabled: true,
|
||||
lfsEnabled: true,
|
||||
requestAccessEnabled: true,
|
||||
|
|
@ -90,6 +106,13 @@
|
|||
);
|
||||
},
|
||||
|
||||
pagesFeatureAccessLevelOptions() {
|
||||
if (this.visibilityLevel !== visibilityOptions.PUBLIC) {
|
||||
return this.featureAccessLevelOptions.concat([[30, 'Everyone']]);
|
||||
}
|
||||
return this.featureAccessLevelOptions;
|
||||
},
|
||||
|
||||
repositoryEnabled() {
|
||||
return this.repositoryAccessLevel > 0;
|
||||
},
|
||||
|
|
@ -109,6 +132,10 @@
|
|||
this.buildsAccessLevel = Math.min(10, this.buildsAccessLevel);
|
||||
this.wikiAccessLevel = Math.min(10, this.wikiAccessLevel);
|
||||
this.snippetsAccessLevel = Math.min(10, this.snippetsAccessLevel);
|
||||
if (this.pagesAccessLevel === 20) {
|
||||
// When from Internal->Private narrow access for only members
|
||||
this.pagesAccessLevel = 10;
|
||||
}
|
||||
this.highlightChanges();
|
||||
} else if (oldValue === visibilityOptions.PRIVATE) {
|
||||
// if changing away from private, make enabled features more permissive
|
||||
|
|
@ -118,6 +145,7 @@
|
|||
if (this.buildsAccessLevel > 0) this.buildsAccessLevel = 20;
|
||||
if (this.wikiAccessLevel > 0) this.wikiAccessLevel = 20;
|
||||
if (this.snippetsAccessLevel > 0) this.snippetsAccessLevel = 20;
|
||||
if (this.pagesAccessLevel === 10) this.pagesAccessLevel = 20;
|
||||
this.highlightChanges();
|
||||
}
|
||||
},
|
||||
|
|
@ -323,6 +351,18 @@
|
|||
name="project[project_feature_attributes][snippets_access_level]"
|
||||
/>
|
||||
</project-setting-row>
|
||||
<project-setting-row
|
||||
v-if="pagesAvailable && pagesAccessControlEnabled"
|
||||
:help-path="pagesHelpPath"
|
||||
label="Pages"
|
||||
help-text="Static website for the project."
|
||||
>
|
||||
<project-feature-setting
|
||||
v-model="pagesAccessLevel"
|
||||
:options="pagesFeatureAccessLevelOptions"
|
||||
name="project[project_feature_attributes][pages_access_level]"
|
||||
/>
|
||||
</project-setting-row>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ export default () => {
|
|||
epics: false,
|
||||
milestones: false,
|
||||
labels: false,
|
||||
snippets: false,
|
||||
});
|
||||
new ZenMode(); // eslint-disable-line no-new
|
||||
};
|
||||
|
|
|
|||
|
|
@ -15,5 +15,6 @@ export default (initGFM = true) => {
|
|||
epics: initGFM,
|
||||
milestones: initGFM,
|
||||
labels: initGFM,
|
||||
snippets: initGFM,
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@
|
|||
epics: this.enableAutocomplete,
|
||||
milestones: this.enableAutocomplete,
|
||||
labels: this.enableAutocomplete,
|
||||
snippets: this.enableAutocomplete,
|
||||
});
|
||||
},
|
||||
beforeDestroy() {
|
||||
|
|
|
|||
|
|
@ -27,6 +27,10 @@ class Projects::AutocompleteSourcesController < Projects::ApplicationController
|
|||
render json: @autocomplete_service.commands(target, params[:type])
|
||||
end
|
||||
|
||||
def snippets
|
||||
render json: @autocomplete_service.snippets
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_autocomplete_service
|
||||
|
|
|
|||
|
|
@ -366,6 +366,7 @@ class ProjectsController < Projects::ApplicationController
|
|||
repository_access_level
|
||||
snippets_access_level
|
||||
wiki_access_level
|
||||
pages_access_level
|
||||
]
|
||||
]
|
||||
end
|
||||
|
|
|
|||
|
|
@ -363,6 +363,7 @@ class IssuableFinder
|
|||
def use_cte_for_search?
|
||||
return false unless search
|
||||
return false unless Gitlab::Database.postgresql?
|
||||
return false unless Feature.enabled?(:use_cte_for_group_issues_search, default_enabled: true)
|
||||
|
||||
params[:use_cte_for_search]
|
||||
end
|
||||
|
|
@ -428,6 +429,10 @@ class IssuableFinder
|
|||
params[:milestone_title] == Milestone::Upcoming.name
|
||||
end
|
||||
|
||||
def filter_by_any_milestone?
|
||||
params[:milestone_title] == Milestone::Any.title
|
||||
end
|
||||
|
||||
def filter_by_started_milestone?
|
||||
params[:milestone_title] == Milestone::Started.name
|
||||
end
|
||||
|
|
@ -437,6 +442,8 @@ class IssuableFinder
|
|||
if milestones?
|
||||
if filter_by_no_milestone?
|
||||
items = items.left_joins_milestones.where(milestone_id: [-1, nil])
|
||||
elsif filter_by_any_milestone?
|
||||
items = items.any_milestone
|
||||
elsif filter_by_upcoming_milestone?
|
||||
upcoming_ids = Milestone.upcoming_ids_by_projects(projects(items))
|
||||
items = items.left_joins_milestones.where(milestone_id: upcoming_ids)
|
||||
|
|
|
|||
|
|
@ -120,9 +120,13 @@ class IssuesFinder < IssuableFinder
|
|||
return @user_can_see_all_confidential_issues = true if current_user.full_private_access?
|
||||
|
||||
@user_can_see_all_confidential_issues =
|
||||
project? &&
|
||||
project &&
|
||||
project.team.max_member_access(current_user.id) >= CONFIDENTIAL_ACCESS_LEVEL
|
||||
if project? && project
|
||||
project.team.max_member_access(current_user.id) >= CONFIDENTIAL_ACCESS_LEVEL
|
||||
elsif group
|
||||
group.max_member_access_for_user(current_user) >= CONFIDENTIAL_ACCESS_LEVEL
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def user_cannot_see_confidential_issues?
|
||||
|
|
|
|||
|
|
@ -129,7 +129,7 @@ class LabelsFinder < UnionFinder
|
|||
end
|
||||
|
||||
def project?
|
||||
params[:project_id].present?
|
||||
params[:project].present? || params[:project_id].present?
|
||||
end
|
||||
|
||||
def projects?
|
||||
|
|
@ -152,7 +152,7 @@ class LabelsFinder < UnionFinder
|
|||
return @project if defined?(@project)
|
||||
|
||||
if project?
|
||||
@project = Project.find(params[:project_id])
|
||||
@project = params[:project] || Project.find(params[:project_id])
|
||||
@project = nil unless authorized_to_read_labels?(@project)
|
||||
else
|
||||
@project = nil
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ module Types
|
|||
:create_deployment, :push_to_delete_protected_branch,
|
||||
:admin_wiki, :admin_project, :update_pages,
|
||||
:admin_remote_mirror, :create_label, :update_wiki, :destroy_wiki,
|
||||
:create_pages, :destroy_pages
|
||||
:create_pages, :destroy_pages, :read_pages_content
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -292,7 +292,8 @@ module ApplicationHelper
|
|||
mergeRequests: merge_requests_project_autocomplete_sources_path(object),
|
||||
labels: labels_project_autocomplete_sources_path(object, type: noteable_type, type_id: params[:id]),
|
||||
milestones: milestones_project_autocomplete_sources_path(object),
|
||||
commands: commands_project_autocomplete_sources_path(object, type: noteable_type, type_id: params[:id])
|
||||
commands: commands_project_autocomplete_sources_path(object, type: noteable_type, type_id: params[:id]),
|
||||
snippets: snippets_project_autocomplete_sources_path(object)
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -454,6 +454,7 @@ module ProjectsHelper
|
|||
buildsAccessLevel: feature.builds_access_level,
|
||||
wikiAccessLevel: feature.wiki_access_level,
|
||||
snippetsAccessLevel: feature.snippets_access_level,
|
||||
pagesAccessLevel: feature.pages_access_level,
|
||||
containerRegistryEnabled: !!project.container_registry_enabled,
|
||||
lfsEnabled: !!project.lfs_enabled
|
||||
}
|
||||
|
|
@ -468,7 +469,10 @@ module ProjectsHelper
|
|||
registryAvailable: Gitlab.config.registry.enabled,
|
||||
registryHelpPath: help_page_path('user/project/container_registry'),
|
||||
lfsAvailable: Gitlab.config.lfs.enabled,
|
||||
lfsHelpPath: help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs')
|
||||
lfsHelpPath: help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs'),
|
||||
pagesAvailable: Gitlab.config.pages.enabled,
|
||||
pagesAccessControlEnabled: Gitlab.config.pages.access_control,
|
||||
pagesHelpPath: help_page_path('user/project/pages/index.md')
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -633,6 +633,18 @@ module Ci
|
|||
end
|
||||
end
|
||||
|
||||
def branch_updated?
|
||||
strong_memoize(:branch_updated) do
|
||||
push_details.branch_updated?
|
||||
end
|
||||
end
|
||||
|
||||
def modified_paths
|
||||
strong_memoize(:modified_paths) do
|
||||
push_details.modified_paths
|
||||
end
|
||||
end
|
||||
|
||||
def default_branch?
|
||||
ref == project.default_branch
|
||||
end
|
||||
|
|
@ -660,6 +672,22 @@ module Ci
|
|||
Gitlab::DataBuilder::Pipeline.build(self)
|
||||
end
|
||||
|
||||
def push_details
|
||||
strong_memoize(:push_details) do
|
||||
Gitlab::Git::Push.new(project, before_sha, sha, push_ref)
|
||||
end
|
||||
end
|
||||
|
||||
def push_ref
|
||||
if branch?
|
||||
Gitlab::Git::BRANCH_REF_PREFIX + ref.to_s
|
||||
elsif tag?
|
||||
Gitlab::Git::TAG_REF_PREFIX + ref.to_s
|
||||
else
|
||||
raise ArgumentError, 'Invalid pipeline type!'
|
||||
end
|
||||
end
|
||||
|
||||
def latest_builds_status
|
||||
return 'failed' unless yaml_errors.blank?
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,81 @@
|
|||
# frozen_string_literal: true
|
||||
module DiffPositionableNote
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
before_validation :set_original_position, on: :create
|
||||
before_validation :update_position, on: :create, if: :on_text?
|
||||
|
||||
serialize :original_position, Gitlab::Diff::Position # rubocop:disable Cop/ActiveRecordSerialize
|
||||
serialize :position, Gitlab::Diff::Position # rubocop:disable Cop/ActiveRecordSerialize
|
||||
serialize :change_position, Gitlab::Diff::Position # rubocop:disable Cop/ActiveRecordSerialize
|
||||
end
|
||||
|
||||
%i(original_position position change_position).each do |meth|
|
||||
define_method "#{meth}=" do |new_position|
|
||||
if new_position.is_a?(String)
|
||||
new_position = JSON.parse(new_position) rescue nil
|
||||
end
|
||||
|
||||
if new_position.is_a?(Hash)
|
||||
new_position = new_position.with_indifferent_access
|
||||
new_position = Gitlab::Diff::Position.new(new_position)
|
||||
end
|
||||
|
||||
return if new_position == read_attribute(meth)
|
||||
|
||||
super(new_position)
|
||||
end
|
||||
end
|
||||
|
||||
def on_text?
|
||||
position&.position_type == "text"
|
||||
end
|
||||
|
||||
def on_image?
|
||||
position&.position_type == "image"
|
||||
end
|
||||
|
||||
def supported?
|
||||
for_commit? || self.noteable.has_complete_diff_refs?
|
||||
end
|
||||
|
||||
def active?(diff_refs = nil)
|
||||
return false unless supported?
|
||||
return true if for_commit?
|
||||
|
||||
diff_refs ||= noteable.diff_refs
|
||||
|
||||
self.position.diff_refs == diff_refs
|
||||
end
|
||||
|
||||
def set_original_position
|
||||
return unless position
|
||||
|
||||
self.original_position = self.position.dup unless self.original_position&.complete?
|
||||
end
|
||||
|
||||
def update_position
|
||||
return unless supported?
|
||||
return if for_commit?
|
||||
|
||||
return if active?
|
||||
return unless position
|
||||
|
||||
tracer = Gitlab::Diff::PositionTracer.new(
|
||||
project: self.project,
|
||||
old_diff_refs: self.position.diff_refs,
|
||||
new_diff_refs: self.noteable.diff_refs,
|
||||
paths: self.position.paths
|
||||
)
|
||||
|
||||
result = tracer.trace(self.position)
|
||||
return unless result
|
||||
|
||||
if result[:outdated]
|
||||
self.change_position = result[:position]
|
||||
else
|
||||
self.position = result[:position]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -76,6 +76,7 @@ module Issuable
|
|||
scope :recent, -> { reorder(id: :desc) }
|
||||
scope :of_projects, ->(ids) { where(project_id: ids) }
|
||||
scope :of_milestones, ->(ids) { where(milestone_id: ids) }
|
||||
scope :any_milestone, -> { where('milestone_id IS NOT NULL') }
|
||||
scope :with_milestone, ->(title) { left_joins_milestones.where(milestones: { title: title }) }
|
||||
scope :opened, -> { with_state(:opened) }
|
||||
scope :only_opened, -> { with_state(:opened) }
|
||||
|
|
|
|||
|
|
@ -5,14 +5,11 @@
|
|||
# A note of this type can be resolvable.
|
||||
class DiffNote < Note
|
||||
include NoteOnDiff
|
||||
include DiffPositionableNote
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
|
||||
NOTEABLE_TYPES = %w(MergeRequest Commit).freeze
|
||||
|
||||
serialize :original_position, Gitlab::Diff::Position # rubocop:disable Cop/ActiveRecordSerialize
|
||||
serialize :position, Gitlab::Diff::Position # rubocop:disable Cop/ActiveRecordSerialize
|
||||
serialize :change_position, Gitlab::Diff::Position # rubocop:disable Cop/ActiveRecordSerialize
|
||||
|
||||
validates :original_position, presence: true
|
||||
validates :position, presence: true
|
||||
validates :line_code, presence: true, line_code: true, if: :on_text?
|
||||
|
|
@ -21,8 +18,6 @@ class DiffNote < Note
|
|||
validate :verify_supported
|
||||
validate :diff_refs_match_commit, if: :for_commit?
|
||||
|
||||
before_validation :set_original_position, on: :create
|
||||
before_validation :update_position, on: :create, if: :on_text?
|
||||
before_validation :set_line_code, if: :on_text?
|
||||
after_save :keep_around_commits
|
||||
after_commit :create_diff_file, on: :create
|
||||
|
|
@ -31,31 +26,6 @@ class DiffNote < Note
|
|||
DiffDiscussion
|
||||
end
|
||||
|
||||
%i(original_position position change_position).each do |meth|
|
||||
define_method "#{meth}=" do |new_position|
|
||||
if new_position.is_a?(String)
|
||||
new_position = JSON.parse(new_position) rescue nil
|
||||
end
|
||||
|
||||
if new_position.is_a?(Hash)
|
||||
new_position = new_position.with_indifferent_access
|
||||
new_position = Gitlab::Diff::Position.new(new_position)
|
||||
end
|
||||
|
||||
return if new_position == read_attribute(meth)
|
||||
|
||||
super(new_position)
|
||||
end
|
||||
end
|
||||
|
||||
def on_text?
|
||||
position.position_type == "text"
|
||||
end
|
||||
|
||||
def on_image?
|
||||
position.position_type == "image"
|
||||
end
|
||||
|
||||
def create_diff_file
|
||||
return unless should_create_diff_file?
|
||||
|
||||
|
|
@ -87,15 +57,6 @@ class DiffNote < Note
|
|||
self.diff_file.line_code(self.diff_line)
|
||||
end
|
||||
|
||||
def active?(diff_refs = nil)
|
||||
return false unless supported?
|
||||
return true if for_commit?
|
||||
|
||||
diff_refs ||= noteable.diff_refs
|
||||
|
||||
self.position.diff_refs == diff_refs
|
||||
end
|
||||
|
||||
def created_at_diff?(diff_refs)
|
||||
return false unless supported?
|
||||
return true if for_commit?
|
||||
|
|
@ -141,37 +102,10 @@ class DiffNote < Note
|
|||
for_commit? || self.noteable.has_complete_diff_refs?
|
||||
end
|
||||
|
||||
def set_original_position
|
||||
self.original_position = self.position.dup unless self.original_position&.complete?
|
||||
end
|
||||
|
||||
def set_line_code
|
||||
self.line_code = self.position.line_code(self.project.repository)
|
||||
end
|
||||
|
||||
def update_position
|
||||
return unless supported?
|
||||
return if for_commit?
|
||||
|
||||
return if active?
|
||||
|
||||
tracer = Gitlab::Diff::PositionTracer.new(
|
||||
project: self.project,
|
||||
old_diff_refs: self.position.diff_refs,
|
||||
new_diff_refs: self.noteable.diff_refs,
|
||||
paths: self.position.paths
|
||||
)
|
||||
|
||||
result = tracer.trace(self.position)
|
||||
return unless result
|
||||
|
||||
if result[:outdated]
|
||||
self.change_position = result[:position]
|
||||
else
|
||||
self.position = result[:position]
|
||||
end
|
||||
end
|
||||
|
||||
def verify_supported
|
||||
return if supported?
|
||||
|
||||
|
|
|
|||
|
|
@ -55,8 +55,8 @@ class Project < ActiveRecord::Base
|
|||
cache_markdown_field :description, pipeline: :description
|
||||
|
||||
delegate :feature_available?, :builds_enabled?, :wiki_enabled?,
|
||||
:merge_requests_enabled?, :issues_enabled?, to: :project_feature,
|
||||
allow_nil: true
|
||||
:merge_requests_enabled?, :issues_enabled?, :pages_enabled?, :public_pages?,
|
||||
to: :project_feature, allow_nil: true
|
||||
|
||||
delegate :base_dir, :disk_path, :ensure_storage_path_exists, to: :storage
|
||||
|
||||
|
|
@ -356,7 +356,7 @@ class Project < ActiveRecord::Base
|
|||
# "enabled" here means "not disabled". It includes private features!
|
||||
scope :with_feature_enabled, ->(feature) {
|
||||
access_level_attribute = ProjectFeature.access_level_attribute(feature)
|
||||
with_project_feature.where(project_features: { access_level_attribute => [nil, ProjectFeature::PRIVATE, ProjectFeature::ENABLED] })
|
||||
with_project_feature.where(project_features: { access_level_attribute => [nil, ProjectFeature::PRIVATE, ProjectFeature::ENABLED, ProjectFeature::PUBLIC] })
|
||||
}
|
||||
|
||||
# Picks a feature where the level is exactly that given.
|
||||
|
|
@ -418,15 +418,15 @@ class Project < ActiveRecord::Base
|
|||
end
|
||||
end
|
||||
|
||||
# project features may be "disabled", "internal" or "enabled". If "internal",
|
||||
# project features may be "disabled", "internal", "enabled" or "public". If "internal",
|
||||
# they are only available to team members. This scope returns projects where
|
||||
# the feature is either enabled, or internal with permission for the user.
|
||||
# the feature is either public, enabled, or internal with permission for the user.
|
||||
#
|
||||
# This method uses an optimised version of `with_feature_access_level` for
|
||||
# logged in users to more efficiently get private projects with the given
|
||||
# feature.
|
||||
def self.with_feature_available_for_user(feature, user)
|
||||
visible = [nil, ProjectFeature::ENABLED]
|
||||
visible = [nil, ProjectFeature::ENABLED, ProjectFeature::PUBLIC]
|
||||
|
||||
if user&.admin?
|
||||
with_feature_enabled(feature)
|
||||
|
|
|
|||
|
|
@ -13,14 +13,16 @@ class ProjectFeature < ActiveRecord::Base
|
|||
# Disabled: not enabled for anyone
|
||||
# Private: enabled only for team members
|
||||
# Enabled: enabled for everyone able to access the project
|
||||
# Public: enabled for everyone (only allowed for pages)
|
||||
#
|
||||
|
||||
# Permission levels
|
||||
DISABLED = 0
|
||||
PRIVATE = 10
|
||||
ENABLED = 20
|
||||
PUBLIC = 30
|
||||
|
||||
FEATURES = %i(issues merge_requests wiki snippets builds repository).freeze
|
||||
FEATURES = %i(issues merge_requests wiki snippets builds repository pages).freeze
|
||||
|
||||
class << self
|
||||
def access_level_attribute(feature)
|
||||
|
|
@ -46,6 +48,7 @@ class ProjectFeature < ActiveRecord::Base
|
|||
validates :project, presence: true
|
||||
|
||||
validate :repository_children_level
|
||||
validate :allowed_access_levels
|
||||
|
||||
default_value_for :builds_access_level, value: ENABLED, allows_nil: false
|
||||
default_value_for :issues_access_level, value: ENABLED, allows_nil: false
|
||||
|
|
@ -55,6 +58,9 @@ class ProjectFeature < ActiveRecord::Base
|
|||
default_value_for :repository_access_level, value: ENABLED, allows_nil: false
|
||||
|
||||
def feature_available?(feature, user)
|
||||
# This feature might not be behind a feature flag at all, so default to true
|
||||
return false unless ::Feature.enabled?(feature, user, default_enabled: true)
|
||||
|
||||
get_permission(user, access_level(feature))
|
||||
end
|
||||
|
||||
|
|
@ -78,6 +84,16 @@ class ProjectFeature < ActiveRecord::Base
|
|||
issues_access_level > DISABLED
|
||||
end
|
||||
|
||||
def pages_enabled?
|
||||
pages_access_level > DISABLED
|
||||
end
|
||||
|
||||
def public_pages?
|
||||
return true unless Gitlab.config.pages.access_control
|
||||
|
||||
pages_access_level == PUBLIC || pages_access_level == ENABLED && project.public?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Validates builds and merge requests access level
|
||||
|
|
@ -92,6 +108,17 @@ class ProjectFeature < ActiveRecord::Base
|
|||
%i(merge_requests_access_level builds_access_level).each(&validator)
|
||||
end
|
||||
|
||||
# Validates access level for other than pages cannot be PUBLIC
|
||||
def allowed_access_levels
|
||||
validator = lambda do |field|
|
||||
level = public_send(field) || ProjectFeature::ENABLED # rubocop:disable GitlabSecurity/PublicSend
|
||||
not_allowed = level > ProjectFeature::ENABLED
|
||||
self.errors.add(field, "cannot have public visibility level") if not_allowed
|
||||
end
|
||||
|
||||
(FEATURES - %i(pages)).each {|f| validator.call("#{f}_access_level")}
|
||||
end
|
||||
|
||||
def get_permission(user, level)
|
||||
case level
|
||||
when DISABLED
|
||||
|
|
@ -100,6 +127,8 @@ class ProjectFeature < ActiveRecord::Base
|
|||
user && (project.team.member?(user) || user.full_private_access?)
|
||||
when ENABLED
|
||||
true
|
||||
when PUBLIC
|
||||
true
|
||||
else
|
||||
true
|
||||
end
|
||||
|
|
|
|||
|
|
@ -110,6 +110,7 @@ class ProjectPolicy < BasePolicy
|
|||
snippets
|
||||
wiki
|
||||
builds
|
||||
pages
|
||||
]
|
||||
|
||||
features.each do |f|
|
||||
|
|
@ -167,6 +168,7 @@ class ProjectPolicy < BasePolicy
|
|||
enable :upload_file
|
||||
enable :read_cycle_analytics
|
||||
enable :award_emoji
|
||||
enable :read_pages_content
|
||||
end
|
||||
|
||||
# These abilities are not allowed to admins that are not members of the project,
|
||||
|
|
@ -286,6 +288,8 @@ class ProjectPolicy < BasePolicy
|
|||
prevent(*create_read_update_admin_destroy(:merge_request))
|
||||
end
|
||||
|
||||
rule { pages_disabled }.prevent :read_pages_content
|
||||
|
||||
rule { issues_disabled & merge_requests_disabled }.policy do
|
||||
prevent(*create_read_update_admin_destroy(:label))
|
||||
prevent(*create_read_update_admin_destroy(:milestone))
|
||||
|
|
@ -345,6 +349,7 @@ class ProjectPolicy < BasePolicy
|
|||
enable :download_code
|
||||
enable :download_wiki_code
|
||||
enable :read_cycle_analytics
|
||||
enable :read_pages_content
|
||||
|
||||
# NOTE: may be overridden by IssuePolicy
|
||||
enable :read_issue
|
||||
|
|
|
|||
|
|
@ -1,10 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class BuildDetailsEntity < JobEntity
|
||||
include EnvironmentHelper
|
||||
include RequestAwareEntity
|
||||
include CiStatusHelper
|
||||
|
||||
expose :coverage, :erased_at, :duration
|
||||
expose :tag_list, as: :tags
|
||||
expose :has_trace?, as: :has_trace
|
||||
|
|
@ -15,10 +11,6 @@ class BuildDetailsEntity < JobEntity
|
|||
expose :deployment_status, if: -> (*) { build.has_environment? } do
|
||||
expose :deployment_status, as: :status
|
||||
|
||||
expose :icon do |build|
|
||||
ci_label_for_status(build.status)
|
||||
end
|
||||
|
||||
expose :persisted_environment, as: :environment, with: EnvironmentEntity
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -57,10 +57,10 @@ module MergeRequests
|
|||
# Returns all origin and fork merge requests from `@project` satisfying passed arguments.
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def merge_requests_for(source_branch, mr_states: [:opened])
|
||||
MergeRequest
|
||||
@project.source_of_merge_requests
|
||||
.with_state(mr_states)
|
||||
.where(source_branch: source_branch, source_project_id: @project.id)
|
||||
.preload(:source_project) # we don't need a #includes since we're just preloading for the #select
|
||||
.where(source_branch: source_branch)
|
||||
.preload(:source_project) # we don't need #includes since we're just preloading for the #select
|
||||
.select(&:source_project)
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
|
|
|
|||
|
|
@ -3,17 +3,16 @@
|
|||
module MergeRequests
|
||||
class RefreshService < MergeRequests::BaseService
|
||||
def execute(oldrev, newrev, ref)
|
||||
return true unless Gitlab::Git.branch_ref?(ref)
|
||||
@push = Gitlab::Git::Push.new(@project, oldrev, newrev, ref)
|
||||
|
||||
do_execute(oldrev, newrev, ref)
|
||||
return true unless @push.branch_push?
|
||||
|
||||
refresh_merge_requests!
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def do_execute(oldrev, newrev, ref)
|
||||
@oldrev, @newrev = oldrev, newrev
|
||||
@branch_name = Gitlab::Git.ref_name(ref)
|
||||
|
||||
def refresh_merge_requests!
|
||||
Gitlab::GitalyClient.allow_n_plus_1_calls(&method(:find_new_commits))
|
||||
# Be sure to close outstanding MRs before reloading them to avoid generating an
|
||||
# empty diff during a manual merge
|
||||
|
|
@ -25,7 +24,7 @@ module MergeRequests
|
|||
cache_merge_requests_closing_issues
|
||||
|
||||
# Leave a system note if a branch was deleted/added
|
||||
if branch_added? || branch_removed?
|
||||
if @push.branch_added? || @push.branch_removed?
|
||||
comment_mr_branch_presence_changed
|
||||
end
|
||||
|
||||
|
|
@ -54,8 +53,10 @@ module MergeRequests
|
|||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def post_merge_manually_merged
|
||||
commit_ids = @commits.map(&:id)
|
||||
merge_requests = @project.merge_requests.preload(:latest_merge_request_diff).opened.where(target_branch: @branch_name).to_a
|
||||
merge_requests = merge_requests.select(&:diff_head_commit)
|
||||
merge_requests = @project.merge_requests.opened
|
||||
.preload(:latest_merge_request_diff)
|
||||
.where(target_branch: @push.branch_name).to_a
|
||||
.select(&:diff_head_commit)
|
||||
|
||||
merge_requests = merge_requests.select do |merge_request|
|
||||
commit_ids.include?(merge_request.diff_head_sha) &&
|
||||
|
|
@ -70,24 +71,20 @@ module MergeRequests
|
|||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
|
||||
def force_push?
|
||||
Gitlab::Checks::ForcePush.force_push?(@project, @oldrev, @newrev)
|
||||
end
|
||||
|
||||
# Refresh merge request diff if we push to source or target branch of merge request
|
||||
# Note: we should update merge requests from forks too
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def reload_merge_requests
|
||||
merge_requests = @project.merge_requests.opened
|
||||
.by_source_or_target_branch(@branch_name).to_a
|
||||
.by_source_or_target_branch(@push.branch_name).to_a
|
||||
|
||||
# Fork merge requests
|
||||
merge_requests += MergeRequest.opened
|
||||
.where(source_branch: @branch_name, source_project: @project)
|
||||
.where(source_branch: @push.branch_name, source_project: @project)
|
||||
.where.not(target_project: @project).to_a
|
||||
|
||||
filter_merge_requests(merge_requests).each do |merge_request|
|
||||
if merge_request.source_branch == @branch_name || force_push?
|
||||
if merge_request.source_branch == @push.branch_name || @push.force_push?
|
||||
merge_request.reload_diff(current_user)
|
||||
else
|
||||
mr_commit_ids = merge_request.commit_shas
|
||||
|
|
@ -117,7 +114,7 @@ module MergeRequests
|
|||
end
|
||||
|
||||
def find_new_commits
|
||||
if branch_added?
|
||||
if @push.branch_added?
|
||||
@commits = []
|
||||
|
||||
merge_request = merge_requests_for_source_branch.first
|
||||
|
|
@ -126,28 +123,28 @@ module MergeRequests
|
|||
begin
|
||||
# Since any number of commits could have been made to the restored branch,
|
||||
# find the common root to see what has been added.
|
||||
common_ref = @project.repository.merge_base(merge_request.diff_head_sha, @newrev)
|
||||
common_ref = @project.repository.merge_base(merge_request.diff_head_sha, @push.newrev)
|
||||
# If the a commit no longer exists in this repo, gitlab_git throws
|
||||
# a Rugged::OdbError. This is fixed in https://gitlab.com/gitlab-org/gitlab_git/merge_requests/52
|
||||
@commits = @project.repository.commits_between(common_ref, @newrev) if common_ref
|
||||
@commits = @project.repository.commits_between(common_ref, @push.newrev) if common_ref
|
||||
rescue
|
||||
end
|
||||
elsif branch_removed?
|
||||
elsif @push.branch_removed?
|
||||
# No commits for a deleted branch.
|
||||
@commits = []
|
||||
else
|
||||
@commits = @project.repository.commits_between(@oldrev, @newrev)
|
||||
@commits = @project.repository.commits_between(@push.oldrev, @push.newrev)
|
||||
end
|
||||
end
|
||||
|
||||
# Add comment about branches being deleted or added to merge requests
|
||||
def comment_mr_branch_presence_changed
|
||||
presence = branch_added? ? :add : :delete
|
||||
presence = @push.branch_added? ? :add : :delete
|
||||
|
||||
merge_requests_for_source_branch.each do |merge_request|
|
||||
SystemNoteService.change_branch_presence(
|
||||
merge_request, merge_request.project, @current_user,
|
||||
:source, @branch_name, presence)
|
||||
:source, @push.branch_name, presence)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -164,7 +161,7 @@ module MergeRequests
|
|||
|
||||
SystemNoteService.add_commits(merge_request, merge_request.project,
|
||||
@current_user, new_commits,
|
||||
existing_commits, @oldrev)
|
||||
existing_commits, @push.oldrev)
|
||||
|
||||
notification_service.push_to_merge_request(merge_request, @current_user, new_commits: new_commits, existing_commits: existing_commits)
|
||||
end
|
||||
|
|
@ -195,7 +192,7 @@ module MergeRequests
|
|||
# Call merge request webhook with update branches
|
||||
def execute_mr_web_hooks
|
||||
merge_requests_for_source_branch.each do |merge_request|
|
||||
execute_hooks(merge_request, 'update', old_rev: @oldrev)
|
||||
execute_hooks(merge_request, 'update', old_rev: @push.oldrev)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -203,7 +200,7 @@ module MergeRequests
|
|||
# `MergeRequestsClosingIssues` model (as a performance optimization).
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def cache_merge_requests_closing_issues
|
||||
@project.merge_requests.where(source_branch: @branch_name).each do |merge_request|
|
||||
@project.merge_requests.where(source_branch: @push.branch_name).each do |merge_request|
|
||||
merge_request.cache_merge_request_closes_issues!(@current_user)
|
||||
end
|
||||
end
|
||||
|
|
@ -215,15 +212,7 @@ module MergeRequests
|
|||
|
||||
def merge_requests_for_source_branch(reload: false)
|
||||
@source_merge_requests = nil if reload
|
||||
@source_merge_requests ||= merge_requests_for(@branch_name)
|
||||
end
|
||||
|
||||
def branch_added?
|
||||
Gitlab::Git.blank_ref?(@oldrev)
|
||||
end
|
||||
|
||||
def branch_removed?
|
||||
Gitlab::Git.blank_ref?(@newrev)
|
||||
@source_merge_requests ||= merge_requests_for(@push.branch_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -29,6 +29,10 @@ module Projects
|
|||
QuickActions::InterpretService.new(project, current_user).available_commands(noteable)
|
||||
end
|
||||
|
||||
def snippets
|
||||
SnippetsFinder.new(current_user, project: project).execute.select([:id, :title])
|
||||
end
|
||||
|
||||
def labels_as_hash(target)
|
||||
super(target, project_id: project.id, include_ancestor_groups: true)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -21,7 +21,9 @@ module Projects
|
|||
def pages_config
|
||||
{
|
||||
domains: pages_domains_config,
|
||||
https_only: project.pages_https_only?
|
||||
https_only: project.pages_https_only?,
|
||||
id: project.project_id,
|
||||
access_control: !project.public_pages?
|
||||
}
|
||||
end
|
||||
|
||||
|
|
@ -31,7 +33,9 @@ module Projects
|
|||
domain: domain.domain,
|
||||
certificate: domain.certificate,
|
||||
key: domain.key,
|
||||
https_only: project.pages_https_only? && domain.https?
|
||||
https_only: project.pages_https_only? && domain.https?,
|
||||
id: project.project_id,
|
||||
access_control: !project.public_pages?
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -72,7 +72,11 @@ module Projects
|
|||
system_hook_service.execute_hooks_for(project, :update)
|
||||
end
|
||||
|
||||
update_pages_config if changing_pages_https_only?
|
||||
update_pages_config if changing_pages_related_config?
|
||||
end
|
||||
|
||||
def changing_pages_related_config?
|
||||
changing_pages_https_only? || changing_pages_access_level?
|
||||
end
|
||||
|
||||
def update_failed!
|
||||
|
|
@ -102,6 +106,10 @@ module Projects
|
|||
params.dig(:project_feature_attributes, :wiki_access_level).to_i > ProjectFeature::DISABLED
|
||||
end
|
||||
|
||||
def changing_pages_access_level?
|
||||
params.dig(:project_feature_attributes, :pages_access_level)
|
||||
end
|
||||
|
||||
def ensure_wiki_exists
|
||||
ProjectWiki.new(project, project.owner).wiki
|
||||
rescue ProjectWiki::CouldNotCreateWikiError
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add autocomplete drop down filter for project snippets
|
||||
merge_request: 21458
|
||||
author: Fabian Schneider
|
||||
type: added
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
title: Fix sorting by priority or popularity on group issues page, when also searching
|
||||
issue content
|
||||
merge_request: 21521
|
||||
author:
|
||||
type: fixed
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Allows to filter issues by Any milestone in the API
|
||||
merge_request: 22080
|
||||
author: Jacopo Beschi @jacopo-beschi
|
||||
type: added
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add access control to GitLab pages and make it possible to enable/disable it in project settings
|
||||
merge_request: 18589
|
||||
author: Tuomo Ala-Vannesluoma
|
||||
type: added
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add support for pipeline only/except policy for modified paths
|
||||
merge_request: 21981
|
||||
author:
|
||||
type: added
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Enable unauthenticated access to public SSH keys via the API
|
||||
merge_request: 20118
|
||||
author: Ronald Claveau
|
||||
type: changed
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Banzai label ref finder - minimize SQL calls by sharing context more aggresively
|
||||
merge_request: 22070
|
||||
author:
|
||||
type: performance
|
||||
|
|
@ -210,6 +210,7 @@ production: &base
|
|||
## GitLab Pages
|
||||
pages:
|
||||
enabled: false
|
||||
access_control: false
|
||||
# The location where pages are stored (default: shared/pages).
|
||||
# path: shared/pages
|
||||
|
||||
|
|
|
|||
|
|
@ -200,6 +200,7 @@ Settings.registry['path'] = Settings.absolute(Settings.registry['path
|
|||
#
|
||||
Settings['pages'] ||= Settingslogic.new({})
|
||||
Settings.pages['enabled'] = false if Settings.pages['enabled'].nil?
|
||||
Settings.pages['access_control'] = false if Settings.pages['access_control'].nil?
|
||||
Settings.pages['path'] = Settings.absolute(Settings.pages['path'] || File.join(Settings.shared['path'], "pages"))
|
||||
Settings.pages['https'] = false if Settings.pages['https'].nil?
|
||||
Settings.pages['host'] ||= "example.com"
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
|
|||
get 'labels'
|
||||
get 'milestones'
|
||||
get 'commands'
|
||||
get 'snippets'
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
class AddPagesAccessLevelToProjectFeature < ActiveRecord::Migration
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
disable_ddl_transaction!
|
||||
|
||||
DOWNTIME = false
|
||||
|
||||
def up
|
||||
add_column_with_default(:project_features, :pages_access_level, :integer, default: ProjectFeature::PUBLIC, allow_null: false)
|
||||
|
||||
change_column_default(:project_features, :pages_access_level, ProjectFeature::ENABLED)
|
||||
end
|
||||
|
||||
def down
|
||||
remove_column :project_features, :pages_access_level
|
||||
end
|
||||
end
|
||||
|
|
@ -1580,6 +1580,7 @@ ActiveRecord::Schema.define(version: 20180924201039) do
|
|||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
t.integer "repository_access_level", default: 20, null: false
|
||||
t.integer "pages_access_level", default: 20, null: false
|
||||
end
|
||||
|
||||
add_index "project_features", ["project_id"], name: "index_project_features_on_project_id", unique: true, using: :btree
|
||||
|
|
|
|||
|
|
@ -92,9 +92,8 @@ where `example.io` is the domain under which GitLab Pages will be served
|
|||
and `192.0.2.1` is the IPv4 address of your GitLab instance and `2001::1` is the
|
||||
IPv6 address. If you don't have IPv6, you can omit the AAAA record.
|
||||
|
||||
> **Note:**
|
||||
You should not use the GitLab domain to serve user pages. For more information
|
||||
see the [security section](#security).
|
||||
NOTE: **Note:**
|
||||
You should not use the GitLab domain to serve user pages. For more information see the [security section](#security).
|
||||
|
||||
[wiki-wildcard-dns]: https://en.wikipedia.org/wiki/Wildcard_DNS_record
|
||||
|
||||
|
|
@ -107,12 +106,13 @@ since that is needed in all configurations.
|
|||
|
||||
### Wildcard domains
|
||||
|
||||
> **Requirements:**
|
||||
> - [Wildcard DNS setup](#dns-configuration)
|
||||
>
|
||||
> ---
|
||||
>
|
||||
> URL scheme: `http://page.example.io`
|
||||
**Requirements:**
|
||||
|
||||
- [Wildcard DNS setup](#dns-configuration)
|
||||
|
||||
---
|
||||
|
||||
URL scheme: `http://page.example.io`
|
||||
|
||||
This is the minimum setup that you can use Pages with. It is the base for all
|
||||
other setups as described below. Nginx will proxy all requests to the daemon.
|
||||
|
|
@ -126,18 +126,18 @@ The Pages daemon doesn't listen to the outside world.
|
|||
|
||||
1. [Reconfigure GitLab][reconfigure]
|
||||
|
||||
|
||||
Watch the [video tutorial][video-admin] for this configuration.
|
||||
|
||||
### Wildcard domains with TLS support
|
||||
|
||||
> **Requirements:**
|
||||
> - [Wildcard DNS setup](#dns-configuration)
|
||||
> - Wildcard TLS certificate
|
||||
>
|
||||
> ---
|
||||
>
|
||||
> URL scheme: `https://page.example.io`
|
||||
**Requirements:**
|
||||
|
||||
- [Wildcard DNS setup](#dns-configuration)
|
||||
- Wildcard TLS certificate
|
||||
|
||||
---
|
||||
|
||||
URL scheme: `https://page.example.io`
|
||||
|
||||
Nginx will proxy all requests to the daemon. Pages daemon doesn't listen to the
|
||||
outside world.
|
||||
|
|
@ -168,13 +168,14 @@ you have IPv6 as well as IPv4 addresses, you can use them both.
|
|||
|
||||
### Custom domains
|
||||
|
||||
> **Requirements:**
|
||||
> - [Wildcard DNS setup](#dns-configuration)
|
||||
> - Secondary IP
|
||||
>
|
||||
> ---
|
||||
>
|
||||
> URL scheme: `http://page.example.io` and `http://domain.com`
|
||||
**Requirements:**
|
||||
|
||||
- [Wildcard DNS setup](#dns-configuration)
|
||||
- Secondary IP
|
||||
|
||||
---
|
||||
|
||||
URL scheme: `http://page.example.io` and `http://domain.com`
|
||||
|
||||
In that case, the Pages daemon is running, Nginx still proxies requests to
|
||||
the daemon but the daemon is also able to receive requests from the outside
|
||||
|
|
@ -197,14 +198,15 @@ world. Custom domains are supported, but no TLS.
|
|||
|
||||
### Custom domains with TLS support
|
||||
|
||||
> **Requirements:**
|
||||
> - [Wildcard DNS setup](#dns-configuration)
|
||||
> - Wildcard TLS certificate
|
||||
> - Secondary IP
|
||||
>
|
||||
> ---
|
||||
>
|
||||
> URL scheme: `https://page.example.io` and `https://domain.com`
|
||||
**Requirements:**
|
||||
|
||||
- [Wildcard DNS setup](#dns-configuration)
|
||||
- Wildcard TLS certificate
|
||||
- Secondary IP
|
||||
|
||||
---
|
||||
|
||||
URL scheme: `https://page.example.io` and `https://domain.com`
|
||||
|
||||
In that case, the Pages daemon is running, Nginx still proxies requests to
|
||||
the daemon but the daemon is also able to receive requests from the outside
|
||||
|
|
@ -320,12 +322,12 @@ latest previous version.
|
|||
|
||||
---
|
||||
|
||||
**GitLab 8.17 ([documentation][8-17-docs])**
|
||||
**GitLab 8.17 ([documentation](https://gitlab.com/gitlab-org/gitlab-ce/blob/8-17-stable/doc/administration/pages/index.md))**
|
||||
|
||||
- GitLab Pages were ported to Community Edition in GitLab 8.17.
|
||||
- Documentation was refactored to be more modular and easy to follow.
|
||||
|
||||
**GitLab 8.5 ([documentation][8-5-docs])**
|
||||
**GitLab 8.5 ([documentation](https://gitlab.com/gitlab-org/gitlab-ee/blob/8-5-stable-ee/doc/pages/administration.md))**
|
||||
|
||||
- In GitLab 8.5 we introduced the [gitlab-pages][] daemon which is now the
|
||||
recommended way to set up GitLab Pages.
|
||||
|
|
@ -334,13 +336,10 @@ latest previous version.
|
|||
- Custom CNAME and TLS certificates support.
|
||||
- Documentation was moved to one place.
|
||||
|
||||
**GitLab 8.3 ([documentation][8-3-docs])**
|
||||
**GitLab 8.3 ([documentation](https://gitlab.com/gitlab-org/gitlab-ee/blob/8-3-stable-ee/doc/pages/administration.md))**
|
||||
|
||||
- GitLab Pages feature was introduced.
|
||||
|
||||
[8-3-docs]: https://gitlab.com/gitlab-org/gitlab-ee/blob/8-3-stable-ee/doc/pages/administration.md
|
||||
[8-5-docs]: https://gitlab.com/gitlab-org/gitlab-ee/blob/8-5-stable-ee/doc/pages/administration.md
|
||||
[8-17-docs]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-17-stable-ce/doc/administration/pages/index.md
|
||||
[backup]: ../../raketasks/backup_restore.md
|
||||
[ce-14605]: https://gitlab.com/gitlab-org/gitlab-ce/issues/14605
|
||||
[ee-80]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/80
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ GET /issues?my_reaction_emoji=star
|
|||
| ------------------- | ---------------- | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `state` | string | no | Return all issues or just those that are `opened` or `closed` |
|
||||
| `labels` | string | no | Comma-separated list of label names, issues must have all labels to be returned. `No+Label` lists all issues with no labels |
|
||||
| `milestone` | string | no | The milestone title. `No+Milestone` lists all issues with no milestone |
|
||||
| `milestone` | string | no | The milestone title. `No+Milestone` lists all issues with no milestone. `Any+Milestone` lists all issues that have an assigned milestone |
|
||||
| `scope` | string | no | Return issues for the given scope: `created_by_me`, `assigned_to_me` or `all`. Defaults to `created_by_me`<br> For versions before 11.0, use the now deprecated `created-by-me` or `assigned-to-me` scopes instead.<br> _([Introduced][ce-13004] in GitLab 9.5. [Changed to snake_case][ce-18935] in GitLab 11.0)_ |
|
||||
| `author_id` | integer | no | Return issues created by the given user `id`. Combine with `scope=all` or `scope=assigned_to_me`. _([Introduced][ce-13004] in GitLab 9.5)_ |
|
||||
| `assignee_id` | integer | no | Return issues assigned to the given user `id` _([Introduced][ce-13004] in GitLab 9.5)_ |
|
||||
|
|
|
|||
|
|
@ -558,7 +558,7 @@ Parameters:
|
|||
|
||||
## List SSH keys for user
|
||||
|
||||
Get a list of a specified user's SSH keys. Available only for admin
|
||||
Get a list of a specified user's SSH keys.
|
||||
|
||||
```
|
||||
GET /users/:id/keys
|
||||
|
|
|
|||
|
|
@ -387,6 +387,8 @@ except master.
|
|||
> `refs` and `kubernetes` policies introduced in GitLab 10.0
|
||||
>
|
||||
> `variables` policy introduced in 10.7
|
||||
>
|
||||
> `changes` policy [introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/19232) in 11.4
|
||||
|
||||
CAUTION: **Warning:**
|
||||
This an _alpha_ feature, and it it subject to change at any time without
|
||||
|
|
@ -398,10 +400,15 @@ policy configuration.
|
|||
GitLab now supports both, simple and complex strategies, so it is possible to
|
||||
use an array and a hash configuration scheme.
|
||||
|
||||
Three keys are now available: `refs`, `kubernetes` and `variables`.
|
||||
Four keys are now available: `refs`, `kubernetes` and `variables` and `changes`.
|
||||
|
||||
### `refs` and `kubernetes`
|
||||
|
||||
Refs strategy equals to simplified only/except configuration, whereas
|
||||
kubernetes strategy accepts only `active` keyword.
|
||||
|
||||
### `variables`
|
||||
|
||||
`variables` keyword is used to define variables expressions. In other words
|
||||
you can use predefined variables / project / group or
|
||||
environment-scoped variables to define an expression GitLab is going to
|
||||
|
|
@ -445,6 +452,46 @@ end-to-end:
|
|||
|
||||
Learn more about variables expressions on [a separate page][variables-expressions].
|
||||
|
||||
### `changes`
|
||||
|
||||
Using `changes` keyword with `only` or `except` makes it possible to define if
|
||||
a job should be created based on files modified by a git push event.
|
||||
|
||||
For example:
|
||||
|
||||
```yaml
|
||||
docker build:
|
||||
script: docker build -t my-image:$CI_COMMIT_REF_SLUG .
|
||||
only:
|
||||
changes:
|
||||
- Dockerfile
|
||||
- docker/scripts/*
|
||||
```
|
||||
|
||||
In the scenario above, if you are pushing multiple commits to GitLab to an
|
||||
existing branch, GitLab creates and triggers `docker build` job, provided that
|
||||
one of the commits contains changes to either:
|
||||
|
||||
- The `Dockerfile` file.
|
||||
- Any of the files inside `docker/scripts/` directory.
|
||||
|
||||
CAUTION: **Warning:**
|
||||
There are some caveats when using this feature with new branches and tags. See
|
||||
the section below.
|
||||
|
||||
#### Using `changes` with new branches and tags
|
||||
|
||||
If you are pushing a **new** branch or a **new** tag to GitLab, the policy
|
||||
always evaluates to true and GitLab will create a job. This feature is not
|
||||
connected with merge requests yet, and because GitLab is creating pipelines
|
||||
before an user can create a merge request we don't know a target branch at
|
||||
this point.
|
||||
|
||||
Without a target branch, it is not possible to know what the common ancestor is,
|
||||
thus we always create a job in that case. This feature works best for stable
|
||||
branches like `master` because in that case GitLab uses the previous commit
|
||||
that is present in a branch to compare against the latest SHA that was pushed.
|
||||
|
||||
## `tags`
|
||||
|
||||
`tags` is used to select specific Runners from the list of all Runners that are
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ people.
|
|||
|
||||
The current team labels are:
|
||||
|
||||
- ~Configuration
|
||||
- ~Configure
|
||||
- ~"CI/CD"
|
||||
- ~Create
|
||||
- ~Distribution
|
||||
|
|
|
|||
|
|
@ -110,6 +110,7 @@ which visibility level you select on project settings.
|
|||
- Disabled: disabled for everyone
|
||||
- Only team members: only team members will see even if your project is public or internal
|
||||
- Everyone with access: everyone can see depending on your project visibility level
|
||||
- Everyone: enabled for everyone (only available for GitLab Pages)
|
||||
|
||||
### Protected branches
|
||||
|
||||
|
|
@ -242,6 +243,7 @@ which visibility level you select on project settings.
|
|||
- Disabled: disabled for everyone
|
||||
- Only team members: only team members will see even if your project is public or internal
|
||||
- Everyone with access: everyone can see depending on your project visibility level
|
||||
- Everyone: enabled for everyone (only available for GitLab Pages)
|
||||
|
||||
## GitLab CI/CD permissions
|
||||
|
||||
|
|
|
|||
|
|
@ -287,6 +287,12 @@ module API
|
|||
present_projects forks
|
||||
end
|
||||
|
||||
desc 'Check pages access of this project'
|
||||
get ':id/pages_access' do
|
||||
authorize! :read_pages_content, user_project unless user_project.public_pages?
|
||||
status 200
|
||||
end
|
||||
|
||||
desc 'Update an existing project' do
|
||||
success Entities::Project
|
||||
end
|
||||
|
|
|
|||
|
|
@ -256,7 +256,7 @@ module API
|
|||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
|
||||
desc 'Get the SSH keys of a specified user. Available only for admins.' do
|
||||
desc 'Get the SSH keys of a specified user.' do
|
||||
success Entities::SSHKey
|
||||
end
|
||||
params do
|
||||
|
|
@ -265,10 +265,8 @@ module API
|
|||
end
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
get ':id/keys' do
|
||||
authenticated_as_admin!
|
||||
|
||||
user = User.find_by(id: params[:id])
|
||||
not_found!('User') unless user
|
||||
not_found!('User') unless user && can?(current_user, :read_user, user)
|
||||
|
||||
present paginate(user.keys), with: Entities::SSHKey
|
||||
end
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ module Banzai
|
|||
# Returns a Project, or nil if the reference can't be found
|
||||
def parent_from_ref(ref)
|
||||
return context[:project] || context[:group] unless ref
|
||||
return context[:project] if context[:project]&.full_path == ref
|
||||
|
||||
Project.find_by_full_path(ref)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ module Banzai
|
|||
include_ancestor_groups: true,
|
||||
only_group_labels: true }
|
||||
else
|
||||
{ project_id: parent.id,
|
||||
{ project: parent,
|
||||
include_ancestor_groups: true }
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Ci
|
||||
module Build
|
||||
module Policy
|
||||
class Changes < Policy::Specification
|
||||
def initialize(globs)
|
||||
@globs = Array(globs)
|
||||
end
|
||||
|
||||
def satisfied_by?(pipeline, seed)
|
||||
return true unless pipeline.branch_updated?
|
||||
|
||||
pipeline.modified_paths.any? do |path|
|
||||
@globs.any? do |glob|
|
||||
File.fnmatch?(glob, path, File::FNM_PATHNAME | File::FNM_DOTMATCH)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -25,17 +25,19 @@ module Gitlab
|
|||
include Entry::Validatable
|
||||
include Entry::Attributable
|
||||
|
||||
attributes :refs, :kubernetes, :variables
|
||||
ALLOWED_KEYS = %i[refs kubernetes variables changes].freeze
|
||||
attributes :refs, :kubernetes, :variables, :changes
|
||||
|
||||
validations do
|
||||
validates :config, presence: true
|
||||
validates :config, allowed_keys: %i[refs kubernetes variables]
|
||||
validates :config, allowed_keys: ALLOWED_KEYS
|
||||
validate :variables_expressions_syntax
|
||||
|
||||
with_options allow_nil: true do
|
||||
validates :refs, array_of_strings_or_regexps: true
|
||||
validates :kubernetes, allowed_values: %w[active]
|
||||
validates :variables, array_of_strings: true
|
||||
validates :changes, array_of_strings: true
|
||||
end
|
||||
|
||||
def variables_expressions_syntax
|
||||
|
|
|
|||
|
|
@ -689,9 +689,6 @@ rollout 100%:
|
|||
helm version --client
|
||||
tiller -version
|
||||
|
||||
helm init --client-only
|
||||
helm plugin install https://github.com/adamreese/helm-local
|
||||
|
||||
curl -L -o /usr/bin/kubectl "https://storage.googleapis.com/kubernetes-release/release/v${KUBERNETES_VERSION}/bin/linux/amd64/kubectl"
|
||||
chmod +x /usr/bin/kubectl
|
||||
kubectl version --client
|
||||
|
|
@ -800,9 +797,9 @@ rollout 100%:
|
|||
function initialize_tiller() {
|
||||
echo "Checking Tiller..."
|
||||
|
||||
helm local start
|
||||
helm local status
|
||||
export HELM_HOST=":44134"
|
||||
tiller -listen ${HELM_HOST} -alsologtostderr > /dev/null 2>&1 &
|
||||
echo "Tiller is listening on ${HELM_HOST}"
|
||||
|
||||
if ! helm version --debug; then
|
||||
echo "Failed to init Tiller."
|
||||
|
|
|
|||
|
|
@ -18,6 +18,10 @@ module Gitlab
|
|||
indexed_by_path[path]
|
||||
end
|
||||
|
||||
def paths
|
||||
@collection.map(&:path)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def indexed_by_path
|
||||
|
|
|
|||
|
|
@ -0,0 +1,56 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Git
|
||||
class Push
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
|
||||
attr_reader :oldrev, :newrev
|
||||
|
||||
def initialize(project, oldrev, newrev, ref)
|
||||
@project = project
|
||||
@oldrev = oldrev.presence || Gitlab::Git::BLANK_SHA
|
||||
@newrev = newrev.presence || Gitlab::Git::BLANK_SHA
|
||||
@ref = ref
|
||||
end
|
||||
|
||||
def branch_name
|
||||
strong_memoize(:branch_name) do
|
||||
Gitlab::Git.branch_name(@ref)
|
||||
end
|
||||
end
|
||||
|
||||
def branch_added?
|
||||
Gitlab::Git.blank_ref?(@oldrev)
|
||||
end
|
||||
|
||||
def branch_removed?
|
||||
Gitlab::Git.blank_ref?(@newrev)
|
||||
end
|
||||
|
||||
def branch_updated?
|
||||
branch_push? && !branch_added? && !branch_removed?
|
||||
end
|
||||
|
||||
def force_push?
|
||||
Gitlab::Checks::ForcePush.force_push?(@project, @oldrev, @newrev)
|
||||
end
|
||||
|
||||
def branch_push?
|
||||
strong_memoize(:branch_push) do
|
||||
Gitlab::Git.branch_ref?(@ref)
|
||||
end
|
||||
end
|
||||
|
||||
def modified_paths
|
||||
unless branch_updated?
|
||||
raise ArgumentError, 'Unable to calculate modified paths!'
|
||||
end
|
||||
|
||||
strong_memoize(:modified_paths) do
|
||||
@project.repository.diff_stats(@oldrev, @newrev).paths
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -220,7 +220,7 @@ module Gitlab
|
|||
result
|
||||
end
|
||||
|
||||
SERVER_FEATURE_FLAGS = %w[gogit_findcommit git_v2].freeze
|
||||
SERVER_FEATURE_FLAGS = %w[gogit_findcommit].freeze
|
||||
|
||||
def self.server_feature_flags
|
||||
SERVER_FEATURE_FLAGS.map do |f|
|
||||
|
|
|
|||
3
qa/qa.rb
3
qa/qa.rb
|
|
@ -257,6 +257,9 @@ module QA
|
|||
autoload :Dropzone, 'qa/page/component/dropzone'
|
||||
autoload :GroupsFilter, 'qa/page/component/groups_filter'
|
||||
autoload :Select2, 'qa/page/component/select2'
|
||||
module Issuable
|
||||
autoload :Common, 'qa/page/component/issuable/common'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
module Page
|
||||
module Component
|
||||
module Issuable
|
||||
module Common
|
||||
def self.included(base)
|
||||
base.view 'app/assets/javascripts/issue_show/components/title.vue' do
|
||||
element :edit_button
|
||||
end
|
||||
|
||||
base.view 'app/assets/javascripts/issue_show/components/fields/title.vue' do
|
||||
element :title_input
|
||||
end
|
||||
|
||||
base.view 'app/assets/javascripts/issue_show/components/fields/description.vue' do
|
||||
element :description_textarea
|
||||
end
|
||||
|
||||
base.view 'app/assets/javascripts/issue_show/components/edit_actions.vue' do
|
||||
element :save_button
|
||||
element :delete_button
|
||||
end
|
||||
|
||||
base.view 'app/assets/javascripts/issue_show/components/edit_actions.vue' do
|
||||
element :save_button
|
||||
element :delete_button
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -5,6 +5,8 @@ module QA
|
|||
module Project
|
||||
module Issue
|
||||
class Show < Page::Base
|
||||
include Page::Component::Issuable::Common
|
||||
|
||||
view 'app/views/projects/issues/show.html.haml' do
|
||||
element :issue_details, '.issue-details'
|
||||
element :title, '.title'
|
||||
|
|
|
|||
|
|
@ -202,8 +202,8 @@ describe GroupsController do
|
|||
end
|
||||
|
||||
describe 'GET #issues' do
|
||||
let(:issue_1) { create(:issue, project: project) }
|
||||
let(:issue_2) { create(:issue, project: project) }
|
||||
let(:issue_1) { create(:issue, project: project, title: 'foo') }
|
||||
let(:issue_2) { create(:issue, project: project, title: 'bar') }
|
||||
|
||||
before do
|
||||
create_list(:award_emoji, 3, awardable: issue_2)
|
||||
|
|
@ -224,6 +224,31 @@ describe GroupsController do
|
|||
expect(assigns(:issues)).to eq [issue_2, issue_1]
|
||||
end
|
||||
end
|
||||
|
||||
context 'searching' do
|
||||
# Remove as part of https://gitlab.com/gitlab-org/gitlab-ce/issues/52271
|
||||
before do
|
||||
stub_feature_flags(use_cte_for_group_issues_search: false)
|
||||
end
|
||||
|
||||
it 'works with popularity sort' do
|
||||
get :issues, id: group.to_param, search: 'foo', sort: 'popularity'
|
||||
|
||||
expect(assigns(:issues)).to eq([issue_1])
|
||||
end
|
||||
|
||||
it 'works with priority sort' do
|
||||
get :issues, id: group.to_param, search: 'foo', sort: 'priority'
|
||||
|
||||
expect(assigns(:issues)).to eq([issue_1])
|
||||
end
|
||||
|
||||
it 'works with label priority sort' do
|
||||
get :issues, id: group.to_param, search: 'foo', sort: 'label_priority'
|
||||
|
||||
expect(assigns(:issues)).to eq([issue_1])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET #merge_requests' do
|
||||
|
|
|
|||
|
|
@ -637,6 +637,18 @@ describe Projects::IssuesController do
|
|||
project_id: project,
|
||||
id: id
|
||||
end
|
||||
|
||||
it 'avoids (most) N+1s loading labels' do
|
||||
label = create(:label, project: project).to_reference
|
||||
labels = create_list(:label, 10, project: project).map(&:to_reference)
|
||||
issue = create(:issue, project: project, description: 'Test issue')
|
||||
|
||||
control_count = ActiveRecord::QueryRecorder.new { issue.update(description: [issue.description, label].join(' ')) }.count
|
||||
|
||||
# Follow-up to get rid of this `2 * label.count` requirement: https://gitlab.com/gitlab-org/gitlab-ce/issues/52230
|
||||
expect { issue.update(description: [issue.description, labels].join(' ')) }
|
||||
.not_to exceed_query_limit(control_count + 2 * labels.count)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET #realtime_changes' do
|
||||
|
|
|
|||
|
|
@ -225,7 +225,6 @@ describe Projects::JobsController, :clean_gitlab_redis_shared_state do
|
|||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(json_response).to match_schema('job/job_details')
|
||||
expect(json_response['deployment_status']["status"]).to eq 'creating'
|
||||
expect(json_response['deployment_status']["icon"]).to eq 'passed'
|
||||
expect(json_response['deployment_status']["environment"]).not_to be_nil
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
require_relative '../support/helpers/test_env'
|
||||
|
||||
FactoryBot.define do
|
||||
PAGES_ACCESS_LEVEL_SCHEMA_VERSION = 20180423204600
|
||||
|
||||
# Project without repository
|
||||
#
|
||||
# Project does not have bare repository.
|
||||
|
|
@ -23,6 +25,7 @@ FactoryBot.define do
|
|||
issues_access_level ProjectFeature::ENABLED
|
||||
merge_requests_access_level ProjectFeature::ENABLED
|
||||
repository_access_level ProjectFeature::ENABLED
|
||||
pages_access_level ProjectFeature::ENABLED
|
||||
|
||||
# we can't assign the delegated `#ci_cd_settings` attributes directly, as the
|
||||
# `#ci_cd_settings` relation needs to be created first
|
||||
|
|
@ -34,13 +37,20 @@ FactoryBot.define do
|
|||
builds_access_level = [evaluator.builds_access_level, evaluator.repository_access_level].min
|
||||
merge_requests_access_level = [evaluator.merge_requests_access_level, evaluator.repository_access_level].min
|
||||
|
||||
project.project_feature.update(
|
||||
hash = {
|
||||
wiki_access_level: evaluator.wiki_access_level,
|
||||
builds_access_level: builds_access_level,
|
||||
snippets_access_level: evaluator.snippets_access_level,
|
||||
issues_access_level: evaluator.issues_access_level,
|
||||
merge_requests_access_level: merge_requests_access_level,
|
||||
repository_access_level: evaluator.repository_access_level)
|
||||
repository_access_level: evaluator.repository_access_level
|
||||
}
|
||||
|
||||
if ActiveRecord::Migrator.current_version >= PAGES_ACCESS_LEVEL_SCHEMA_VERSION
|
||||
hash.store("pages_access_level", evaluator.pages_access_level)
|
||||
end
|
||||
|
||||
project.project_feature.update(hash)
|
||||
|
||||
# Normally the class Projects::CreateService is used for creating
|
||||
# projects, and this class takes care of making sure the owner and current
|
||||
|
|
@ -244,6 +254,10 @@ FactoryBot.define do
|
|||
trait(:repository_enabled) { repository_access_level ProjectFeature::ENABLED }
|
||||
trait(:repository_disabled) { repository_access_level ProjectFeature::DISABLED }
|
||||
trait(:repository_private) { repository_access_level ProjectFeature::PRIVATE }
|
||||
trait(:pages_public) { pages_access_level ProjectFeature::PUBLIC }
|
||||
trait(:pages_enabled) { pages_access_level ProjectFeature::ENABLED }
|
||||
trait(:pages_disabled) { pages_access_level ProjectFeature::DISABLED }
|
||||
trait(:pages_private) { pages_access_level ProjectFeature::PRIVATE }
|
||||
|
||||
trait :auto_devops do
|
||||
association :auto_devops, factory: :project_auto_devops
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ describe 'GFM autocomplete', :js do
|
|||
let(:project) { create(:project) }
|
||||
let(:label) { create(:label, project: project, title: 'special+') }
|
||||
let(:issue) { create(:issue, project: project) }
|
||||
let!(:project_snippet) { create(:project_snippet, project: project, title: 'code snippet') }
|
||||
|
||||
before do
|
||||
project.add_maintainer(user)
|
||||
|
|
@ -301,6 +302,16 @@ describe 'GFM autocomplete', :js do
|
|||
end
|
||||
end
|
||||
|
||||
it 'shows project snippets' do
|
||||
page.within '.timeline-content-form' do
|
||||
find('#note-body').native.send_keys('$')
|
||||
end
|
||||
|
||||
page.within '.atwho-container' do
|
||||
expect(page).to have_content(project_snippet.title)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def expect_to_wrap(should_wrap, item, note, value)
|
||||
|
|
|
|||
|
|
@ -125,6 +125,14 @@ describe IssuesFinder do
|
|||
end
|
||||
end
|
||||
|
||||
context 'filtering by any milestone' do
|
||||
let(:params) { { milestone_title: Milestone::Any.title } }
|
||||
|
||||
it 'returns issues with any assigned milestone' do
|
||||
expect(issues).to contain_exactly(issue1)
|
||||
end
|
||||
end
|
||||
|
||||
context 'filtering by upcoming milestone' do
|
||||
let(:params) { { milestone_title: Milestone::Upcoming.name } }
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
"type": "object",
|
||||
"required": [
|
||||
"status",
|
||||
"icon",
|
||||
"environment"
|
||||
],
|
||||
"properties": {
|
||||
|
|
@ -20,7 +19,6 @@
|
|||
{ "type": "null" }
|
||||
]
|
||||
},
|
||||
"icon": { "type": "string" },
|
||||
"environment": { "$ref": "../environment.json" }
|
||||
},
|
||||
"additionalProperties": false
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ describe Types::PermissionTypes::Project do
|
|||
:read_commit_status, :request_access, :create_pipeline, :create_pipeline_schedule,
|
||||
:create_merge_request_from, :create_wiki, :push_code, :create_deployment, :push_to_delete_protected_branch,
|
||||
:admin_wiki, :admin_project, :update_pages, :admin_remote_mirror, :create_label,
|
||||
:update_wiki, :destroy_wiki, :create_pages, :destroy_pages
|
||||
:update_wiki, :destroy_wiki, :create_pages, :destroy_pages, :read_pages_content
|
||||
]
|
||||
|
||||
expect(described_class).to have_graphql_fields(expected_permissions)
|
||||
|
|
|
|||
|
|
@ -174,9 +174,7 @@ describe ApplicationHelper do
|
|||
|
||||
it 'returns paths for autocomplete_sources_controller' do
|
||||
sources = helper.autocomplete_data_sources(project, noteable_type)
|
||||
|
||||
expect(sources.keys).to match_array([:members, :issues, :mergeRequests, :labels, :milestones, :commands])
|
||||
|
||||
expect(sources.keys).to match_array([:members, :issues, :mergeRequests, :labels, :milestones, :commands, :snippets])
|
||||
sources.keys.each do |key|
|
||||
expect(sources[key]).not_to be_nil
|
||||
end
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ describe('GfmAutoComplete', function () {
|
|||
gfmAutoCompleteCallbacks.matcher.call(context, flag, subtext)
|
||||
);
|
||||
|
||||
const flagsUseDefaultMatcher = ['@', '#', '!', '~', '%'];
|
||||
const flagsUseDefaultMatcher = ['@', '#', '!', '~', '%', '$'];
|
||||
const otherFlags = ['/', ':'];
|
||||
const flags = flagsUseDefaultMatcher.concat(otherFlags);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,16 +1,21 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Banzai::CrossProjectReference do
|
||||
include described_class
|
||||
let(:including_class) { Class.new.include(described_class).new }
|
||||
|
||||
before do
|
||||
allow(including_class).to receive(:context).and_return({})
|
||||
allow(including_class).to receive(:parent_from_ref).and_call_original
|
||||
end
|
||||
|
||||
describe '#parent_from_ref' do
|
||||
context 'when no project was referenced' do
|
||||
it 'returns the project from context' do
|
||||
project = double
|
||||
|
||||
allow(self).to receive(:context).and_return({ project: project })
|
||||
allow(including_class).to receive(:context).and_return({ project: project })
|
||||
|
||||
expect(parent_from_ref(nil)).to eq project
|
||||
expect(including_class.parent_from_ref(nil)).to eq project
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -18,15 +23,15 @@ describe Banzai::CrossProjectReference do
|
|||
it 'returns the group from context' do
|
||||
group = double
|
||||
|
||||
allow(self).to receive(:context).and_return({ group: group })
|
||||
allow(including_class).to receive(:context).and_return({ group: group })
|
||||
|
||||
expect(parent_from_ref(nil)).to eq group
|
||||
expect(including_class.parent_from_ref(nil)).to eq group
|
||||
end
|
||||
end
|
||||
|
||||
context 'when referenced project does not exist' do
|
||||
it 'returns nil' do
|
||||
expect(parent_from_ref('invalid/reference')).to be_nil
|
||||
expect(including_class.parent_from_ref('invalid/reference')).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -37,7 +42,7 @@ describe Banzai::CrossProjectReference do
|
|||
expect(Project).to receive(:find_by_full_path)
|
||||
.with('cross/reference').and_return(project2)
|
||||
|
||||
expect(parent_from_ref('cross/reference')).to eq project2
|
||||
expect(including_class.parent_from_ref('cross/reference')).to eq project2
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ describe Banzai::Filter::CommitRangeReferenceFilter do
|
|||
exp = act = "See #{commit1.id.reverse}...#{commit2.id}"
|
||||
|
||||
allow(project.repository).to receive(:commit).with(commit1.id.reverse)
|
||||
allow(project.repository).to receive(:commit).with(commit2.id)
|
||||
expect(reference_filter(act).to_html).to eq exp
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,107 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::Ci::Build::Policy::Changes do
|
||||
set(:project) { create(:project) }
|
||||
|
||||
describe '#satisfied_by?' do
|
||||
describe 'paths matching matching' do
|
||||
let(:pipeline) do
|
||||
build(:ci_empty_pipeline, project: project,
|
||||
ref: 'master',
|
||||
source: :push,
|
||||
sha: '1234abcd',
|
||||
before_sha: '0123aabb')
|
||||
end
|
||||
|
||||
let(:ci_build) do
|
||||
build(:ci_build, pipeline: pipeline, project: project, ref: 'master')
|
||||
end
|
||||
|
||||
let(:seed) { double('build seed', to_resource: ci_build) }
|
||||
|
||||
before do
|
||||
allow(pipeline).to receive(:modified_paths) do
|
||||
%w[some/modified/ruby/file.rb some/other_file.txt some/.dir/file]
|
||||
end
|
||||
end
|
||||
|
||||
it 'is satisfied by matching literal path' do
|
||||
policy = described_class.new(%w[some/other_file.txt])
|
||||
|
||||
expect(policy).to be_satisfied_by(pipeline, seed)
|
||||
end
|
||||
|
||||
it 'is satisfied by matching simple pattern' do
|
||||
policy = described_class.new(%w[some/*.txt])
|
||||
|
||||
expect(policy).to be_satisfied_by(pipeline, seed)
|
||||
end
|
||||
|
||||
it 'is satisfied by matching recusive pattern' do
|
||||
policy = described_class.new(%w[some/**/*.rb])
|
||||
|
||||
expect(policy).to be_satisfied_by(pipeline, seed)
|
||||
end
|
||||
|
||||
it 'is satisfied by matching a pattern with a dot' do
|
||||
policy = described_class.new(%w[some/*/file])
|
||||
|
||||
expect(policy).to be_satisfied_by(pipeline, seed)
|
||||
end
|
||||
|
||||
it 'is not satisfied when pattern does not match path' do
|
||||
policy = described_class.new(%w[some/*.rb])
|
||||
|
||||
expect(policy).not_to be_satisfied_by(pipeline, seed)
|
||||
end
|
||||
|
||||
it 'is not satisfied when pattern does not match' do
|
||||
policy = described_class.new(%w[invalid/*.md])
|
||||
|
||||
expect(policy).not_to be_satisfied_by(pipeline, seed)
|
||||
end
|
||||
|
||||
context 'when pipelines does not run for a branch update' do
|
||||
before do
|
||||
pipeline.before_sha = Gitlab::Git::BLANK_SHA
|
||||
end
|
||||
|
||||
it 'is always satisfied' do
|
||||
policy = described_class.new(%w[invalid/*])
|
||||
|
||||
expect(policy).to be_satisfied_by(pipeline, seed)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'gitaly integration' do
|
||||
set(:project) { create(:project, :repository) }
|
||||
|
||||
let(:pipeline) do
|
||||
create(:ci_empty_pipeline, project: project,
|
||||
ref: 'master',
|
||||
source: :push,
|
||||
sha: '498214d',
|
||||
before_sha: '281d3a7')
|
||||
end
|
||||
|
||||
let(:build) do
|
||||
create(:ci_build, pipeline: pipeline, project: project, ref: 'master')
|
||||
end
|
||||
|
||||
let(:seed) { double('build seed', to_resource: build) }
|
||||
|
||||
it 'is satisfied by changes introduced by a push' do
|
||||
policy = described_class.new(['with space/*.md'])
|
||||
|
||||
expect(policy).to be_satisfied_by(pipeline, seed)
|
||||
end
|
||||
|
||||
it 'is not satisfied by changes that are not in the push' do
|
||||
policy = described_class.new(%w[files/js/commit.js])
|
||||
|
||||
expect(policy).not_to be_satisfied_by(pipeline, seed)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
require 'spec_helper'
|
||||
require 'fast_spec_helper'
|
||||
require_dependency 'active_model'
|
||||
|
||||
describe Gitlab::Ci::Config::Entry::Policy do
|
||||
let(:entry) { described_class.new(config) }
|
||||
|
|
@ -124,6 +125,23 @@ describe Gitlab::Ci::Config::Entry::Policy do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when specifying a valid changes policy' do
|
||||
let(:config) { { changes: %w[some/* paths/**/*.rb] } }
|
||||
|
||||
it 'is a correct configuraton' do
|
||||
expect(entry).to be_valid
|
||||
expect(entry.value).to eq(config)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when changes policy is invalid' do
|
||||
let(:config) { { changes: [1, 2] } }
|
||||
|
||||
it 'returns errors' do
|
||||
expect(entry.errors).to include /changes should be an array of strings/
|
||||
end
|
||||
end
|
||||
|
||||
context 'when specifying unknown policy' do
|
||||
let(:config) { { refs: ['master'], invalid: :something } }
|
||||
|
||||
|
|
|
|||
|
|
@ -1369,7 +1369,7 @@ module Gitlab
|
|||
end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "jobs:rspec dependencies should be an array of strings")
|
||||
end
|
||||
|
||||
it 'returns errors if pipeline variables expression is invalid' do
|
||||
it 'returns errors if pipeline variables expression policy is invalid' do
|
||||
config = YAML.dump({ rspec: { script: 'test', only: { variables: ['== null'] } } })
|
||||
|
||||
expect { Gitlab::Ci::YamlProcessor.new(config) }
|
||||
|
|
@ -1377,6 +1377,14 @@ module Gitlab
|
|||
'jobs:rspec:only variables invalid expression syntax')
|
||||
end
|
||||
|
||||
it 'returns errors if pipeline changes policy is invalid' do
|
||||
config = YAML.dump({ rspec: { script: 'test', only: { changes: [1] } } })
|
||||
|
||||
expect { Gitlab::Ci::YamlProcessor.new(config) }
|
||||
.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError,
|
||||
'jobs:rspec:only changes should be an array of strings')
|
||||
end
|
||||
|
||||
it 'returns errors if extended hash configuration is invalid' do
|
||||
config = YAML.dump({ rspec: { extends: 'something', script: 'test' } })
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ describe Gitlab::Git::DiffStatsCollection do
|
|||
let(:diff_stats) { [stats_a, stats_b] }
|
||||
let(:collection) { described_class.new(diff_stats) }
|
||||
|
||||
describe '.find_by_path' do
|
||||
describe '#find_by_path' do
|
||||
it 'returns stats by path when found' do
|
||||
expect(collection.find_by_path('foo')).to eq(stats_a)
|
||||
end
|
||||
|
|
@ -23,4 +23,10 @@ describe Gitlab::Git::DiffStatsCollection do
|
|||
expect(collection.find_by_path('no-file')).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
describe '#paths' do
|
||||
it 'returns only modified paths' do
|
||||
expect(collection.paths).to eq %w[foo bar]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,166 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::Git::Push do
|
||||
set(:project) { create(:project, :repository) }
|
||||
|
||||
let(:oldrev) { project.commit('HEAD~2').id }
|
||||
let(:newrev) { project.commit.id }
|
||||
let(:ref) { 'refs/heads/some-branch' }
|
||||
|
||||
subject { described_class.new(project, oldrev, newrev, ref) }
|
||||
|
||||
describe '#branch_name' do
|
||||
context 'when it is a branch push' do
|
||||
let(:ref) { 'refs/heads/my-branch' }
|
||||
|
||||
it 'returns branch name' do
|
||||
expect(subject.branch_name).to eq 'my-branch'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when it is a tag push' do
|
||||
let(:ref) { 'refs/tags/my-branch' }
|
||||
|
||||
it 'returns nil' do
|
||||
expect(subject.branch_name).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#branch_push?' do
|
||||
context 'when pushing a branch ref' do
|
||||
let(:ref) { 'refs/heads/my-branch' }
|
||||
|
||||
it { is_expected.to be_branch_push }
|
||||
end
|
||||
|
||||
context 'when it is a tag push' do
|
||||
let(:ref) { 'refs/tags/my-tag' }
|
||||
|
||||
it { is_expected.not_to be_branch_push }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#branch_updated?' do
|
||||
context 'when it is a branch push with correct old and new revisions' do
|
||||
it { is_expected.to be_branch_updated }
|
||||
end
|
||||
|
||||
context 'when it is not a branch push' do
|
||||
let(:ref) { 'refs/tags/my-tag' }
|
||||
|
||||
it { is_expected.not_to be_branch_updated }
|
||||
end
|
||||
|
||||
context 'when old revision is blank' do
|
||||
let(:oldrev) { Gitlab::Git::BLANK_SHA }
|
||||
|
||||
it { is_expected.not_to be_branch_updated }
|
||||
end
|
||||
|
||||
context 'when it is not a branch push' do
|
||||
let(:newrev) { Gitlab::Git::BLANK_SHA }
|
||||
|
||||
it { is_expected.not_to be_branch_updated }
|
||||
end
|
||||
|
||||
context 'when oldrev is nil' do
|
||||
let(:oldrev) { nil }
|
||||
|
||||
it { is_expected.not_to be_branch_updated }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#force_push?' do
|
||||
context 'when old revision is an ancestor of the new revision' do
|
||||
let(:oldrev) { 'HEAD~3' }
|
||||
let(:newrev) { 'HEAD~1' }
|
||||
|
||||
it { is_expected.not_to be_force_push }
|
||||
end
|
||||
|
||||
context 'when old revision is not an ancestor of the new revision' do
|
||||
let(:oldrev) { 'HEAD~3' }
|
||||
let(:newrev) { '123456' }
|
||||
|
||||
it { is_expected.to be_force_push }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#branch_added?' do
|
||||
context 'when old revision is defined' do
|
||||
it { is_expected.not_to be_branch_added }
|
||||
end
|
||||
|
||||
context 'when old revision is not defined' do
|
||||
let(:oldrev) { Gitlab::Git::BLANK_SHA }
|
||||
|
||||
it { is_expected.to be_branch_added }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#branch_removed?' do
|
||||
context 'when new revision is defined' do
|
||||
it { is_expected.not_to be_branch_removed }
|
||||
end
|
||||
|
||||
context 'when new revision is not defined' do
|
||||
let(:newrev) { Gitlab::Git::BLANK_SHA }
|
||||
|
||||
it { is_expected.to be_branch_removed }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#modified_paths' do
|
||||
context 'when a push is a branch update' do
|
||||
let(:newrev) { '498214d' }
|
||||
let(:oldrev) { '281d3a7' }
|
||||
|
||||
it 'returns modified paths' do
|
||||
expect(subject.modified_paths).to eq ['bar/branch-test.txt',
|
||||
'files/js/commit.coffee',
|
||||
'with space/README.md']
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a push is not a branch update' do
|
||||
let(:oldrev) { Gitlab::Git::BLANK_SHA }
|
||||
|
||||
it 'raises an error' do
|
||||
expect { subject.modified_paths }.to raise_error(ArgumentError)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#oldrev' do
|
||||
context 'when a valid oldrev is provided' do
|
||||
it 'returns oldrev' do
|
||||
expect(subject.oldrev).to eq oldrev
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a nil valud is provided' do
|
||||
let(:oldrev) { nil }
|
||||
|
||||
it 'returns blank SHA' do
|
||||
expect(subject.oldrev).to eq Gitlab::Git::BLANK_SHA
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#newrev' do
|
||||
context 'when valid newrev is provided' do
|
||||
it 'returns newrev' do
|
||||
expect(subject.newrev).to eq newrev
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a nil valud is provided' do
|
||||
let(:newrev) { nil }
|
||||
|
||||
it 'returns blank SHA' do
|
||||
expect(subject.newrev).to eq Gitlab::Git::BLANK_SHA
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -493,6 +493,7 @@ ProjectFeature:
|
|||
- snippets_access_level
|
||||
- builds_access_level
|
||||
- repository_access_level
|
||||
- pages_access_level
|
||||
- created_at
|
||||
- updated_at
|
||||
ProtectedBranch::MergeAccessLevel:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
require 'spec_helper'
|
||||
require Rails.root.join('db', 'migrate', '20180423204600_add_pages_access_level_to_project_feature.rb')
|
||||
|
||||
describe AddPagesAccessLevelToProjectFeature, :migration do
|
||||
let(:namespaces) { table(:namespaces) }
|
||||
let(:projects) { table(:projects) }
|
||||
let(:features) { table(:project_features) }
|
||||
let!(:namespace) { namespaces.create(name: 'gitlab', path: 'gitlab') }
|
||||
let!(:first_project) { projects.create(name: 'gitlab1', path: 'gitlab1', namespace_id: namespace.id) }
|
||||
let!(:first_project_features) { features.create(project_id: first_project.id) }
|
||||
let!(:second_project) { projects.create(name: 'gitlab2', path: 'gitlab2', namespace_id: namespace.id) }
|
||||
let!(:second_project_features) { features.create(project_id: second_project.id) }
|
||||
|
||||
it 'correctly migrate pages for old projects to be public' do
|
||||
migrate!
|
||||
|
||||
# For old projects pages should be public
|
||||
expect(first_project_features.reload.pages_access_level).to eq ProjectFeature::PUBLIC
|
||||
expect(second_project_features.reload.pages_access_level).to eq ProjectFeature::PUBLIC
|
||||
end
|
||||
|
||||
it 'after migration pages are enabled as default' do
|
||||
migrate!
|
||||
|
||||
# For new project default is enabled
|
||||
third_project = projects.create(name: 'gitlab3', path: 'gitlab3', namespace_id: namespace.id)
|
||||
third_project_features = features.create(project_id: third_project.id)
|
||||
expect(third_project_features.reload.pages_access_level).to eq ProjectFeature::ENABLED
|
||||
end
|
||||
end
|
||||
|
|
@ -837,6 +837,57 @@ describe Ci::Pipeline, :mailer do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#branch_updated?' do
|
||||
context 'when pipeline has before SHA' do
|
||||
before do
|
||||
pipeline.update_column(:before_sha, 'a1b2c3d4')
|
||||
end
|
||||
|
||||
it 'runs on a branch update push' do
|
||||
expect(pipeline.before_sha).not_to be Gitlab::Git::BLANK_SHA
|
||||
expect(pipeline.branch_updated?).to be true
|
||||
end
|
||||
end
|
||||
|
||||
context 'when pipeline does not have before SHA' do
|
||||
before do
|
||||
pipeline.update_column(:before_sha, Gitlab::Git::BLANK_SHA)
|
||||
end
|
||||
|
||||
it 'does not run on a branch updating push' do
|
||||
expect(pipeline.branch_updated?).to be false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#modified_paths' do
|
||||
context 'when old and new revisions are set' do
|
||||
let(:project) { create(:project, :repository) }
|
||||
|
||||
before do
|
||||
pipeline.update(before_sha: '1234abcd', sha: '2345bcde')
|
||||
end
|
||||
|
||||
it 'fetches stats for changes between commits' do
|
||||
expect(project.repository)
|
||||
.to receive(:diff_stats).with('1234abcd', '2345bcde')
|
||||
.and_call_original
|
||||
|
||||
pipeline.modified_paths
|
||||
end
|
||||
end
|
||||
|
||||
context 'when either old or new revision is missing' do
|
||||
before do
|
||||
pipeline.update_column(:before_sha, Gitlab::Git::BLANK_SHA)
|
||||
end
|
||||
|
||||
it 'raises an error' do
|
||||
expect { pipeline.modified_paths }.to raise_error(ArgumentError)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#has_kubernetes_active?' do
|
||||
context 'when kubernetes is active' do
|
||||
shared_examples 'same behavior between KubernetesService and Platform::Kubernetes' do
|
||||
|
|
|
|||
|
|
@ -65,7 +65,8 @@ describe InternalId do
|
|||
context 'with an insufficient schema version' do
|
||||
before do
|
||||
described_class.reset_column_information
|
||||
expect(ActiveRecord::Migrator).to receive(:current_version).and_return(InternalId::REQUIRED_SCHEMA_VERSION - 1)
|
||||
# Project factory will also call the current_version
|
||||
expect(ActiveRecord::Migrator).to receive(:current_version).twice.and_return(InternalId::REQUIRED_SCHEMA_VERSION - 1)
|
||||
end
|
||||
|
||||
let(:init) { double('block') }
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ describe ProjectFeature do
|
|||
end
|
||||
|
||||
describe '#feature_available?' do
|
||||
let(:features) { %w(issues wiki builds merge_requests snippets repository) }
|
||||
let(:features) { %w(issues wiki builds merge_requests snippets repository pages) }
|
||||
|
||||
context 'when features are disabled' do
|
||||
it "returns false" do
|
||||
|
|
@ -73,6 +73,22 @@ describe ProjectFeature do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when feature is disabled by a feature flag' do
|
||||
it 'returns false' do
|
||||
stub_feature_flags(issues: false)
|
||||
|
||||
expect(project.feature_available?(:issues, user)).to eq(false)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when feature is enabled by a feature flag' do
|
||||
it 'returns true' do
|
||||
stub_feature_flags(issues: true)
|
||||
|
||||
expect(project.feature_available?(:issues, user)).to eq(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'repository related features' do
|
||||
|
|
@ -96,6 +112,19 @@ describe ProjectFeature do
|
|||
end
|
||||
end
|
||||
|
||||
context 'public features' do
|
||||
it "does not allow public for other than pages" do
|
||||
features = %w(issues wiki builds merge_requests snippets repository)
|
||||
project_feature = project.project_feature
|
||||
|
||||
features.each do |feature|
|
||||
field = "#{feature}_access_level".to_sym
|
||||
project_feature.update_attribute(field, ProjectFeature::PUBLIC)
|
||||
expect(project_feature.valid?).to be_falsy
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#*_enabled?' do
|
||||
let(:features) { %w(wiki builds merge_requests) }
|
||||
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ describe API::Issues do
|
|||
let!(:note) { create(:note_on_issue, author: user, project: project, noteable: issue) }
|
||||
|
||||
let(:no_milestone_title) { URI.escape(Milestone::None.title) }
|
||||
let(:any_milestone_title) { URI.escape(Milestone::Any.title) }
|
||||
|
||||
before(:all) do
|
||||
project.add_reporter(user)
|
||||
|
|
@ -811,6 +812,15 @@ describe API::Issues do
|
|||
expect(json_response.first['id']).to eq(confidential_issue.id)
|
||||
end
|
||||
|
||||
it 'returns an array of issues with any milestone' do
|
||||
get api("#{base_url}/issues?milestone=#{any_milestone_title}", user)
|
||||
|
||||
response_ids = json_response.map { |issue| issue['id'] }
|
||||
|
||||
expect_paginated_array_response(size: 2)
|
||||
expect(response_ids).to contain_exactly(closed_issue.id, issue.id)
|
||||
end
|
||||
|
||||
it 'sorts by created_at descending by default' do
|
||||
get api("#{base_url}/issues", user)
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,88 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe "Internal Project Pages Access" do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
include AccessMatchers
|
||||
|
||||
set(:group) { create(:group) }
|
||||
set(:project) { create(:project, :internal, pages_access_level: ProjectFeature::ENABLED, namespace: group) }
|
||||
|
||||
set(:admin) { create(:admin) }
|
||||
set(:owner) { create(:user) }
|
||||
set(:master) { create(:user) }
|
||||
set(:developer) { create(:user) }
|
||||
set(:reporter) { create(:user) }
|
||||
set(:guest) { create(:user) }
|
||||
set(:user) { create(:user) }
|
||||
|
||||
before do
|
||||
allow(Gitlab.config.pages).to receive(:access_control).and_return(true)
|
||||
group.add_owner(owner)
|
||||
project.add_master(master)
|
||||
project.add_developer(developer)
|
||||
project.add_reporter(reporter)
|
||||
project.add_guest(guest)
|
||||
end
|
||||
|
||||
describe "Project should be internal" do
|
||||
describe '#internal?' do
|
||||
subject { project.internal? }
|
||||
it { is_expected.to be_truthy }
|
||||
end
|
||||
end
|
||||
|
||||
describe "GET /projects/:id/pages_access" do
|
||||
context 'access depends on the level' do
|
||||
where(:pages_access_level, :with_user, :expected_result) do
|
||||
ProjectFeature::DISABLED | "admin" | 403
|
||||
ProjectFeature::DISABLED | "owner" | 403
|
||||
ProjectFeature::DISABLED | "master" | 403
|
||||
ProjectFeature::DISABLED | "developer" | 403
|
||||
ProjectFeature::DISABLED | "reporter" | 403
|
||||
ProjectFeature::DISABLED | "guest" | 403
|
||||
ProjectFeature::DISABLED | "user" | 403
|
||||
ProjectFeature::DISABLED | nil | 404
|
||||
ProjectFeature::PUBLIC | "admin" | 200
|
||||
ProjectFeature::PUBLIC | "owner" | 200
|
||||
ProjectFeature::PUBLIC | "master" | 200
|
||||
ProjectFeature::PUBLIC | "developer" | 200
|
||||
ProjectFeature::PUBLIC | "reporter" | 200
|
||||
ProjectFeature::PUBLIC | "guest" | 200
|
||||
ProjectFeature::PUBLIC | "user" | 200
|
||||
ProjectFeature::PUBLIC | nil | 404
|
||||
ProjectFeature::ENABLED | "admin" | 200
|
||||
ProjectFeature::ENABLED | "owner" | 200
|
||||
ProjectFeature::ENABLED | "master" | 200
|
||||
ProjectFeature::ENABLED | "developer" | 200
|
||||
ProjectFeature::ENABLED | "reporter" | 200
|
||||
ProjectFeature::ENABLED | "guest" | 200
|
||||
ProjectFeature::ENABLED | "user" | 200
|
||||
ProjectFeature::ENABLED | nil | 404
|
||||
ProjectFeature::PRIVATE | "admin" | 200
|
||||
ProjectFeature::PRIVATE | "owner" | 200
|
||||
ProjectFeature::PRIVATE | "master" | 200
|
||||
ProjectFeature::PRIVATE | "developer" | 200
|
||||
ProjectFeature::PRIVATE | "reporter" | 200
|
||||
ProjectFeature::PRIVATE | "guest" | 200
|
||||
ProjectFeature::PRIVATE | "user" | 403
|
||||
ProjectFeature::PRIVATE | nil | 404
|
||||
end
|
||||
|
||||
with_them do
|
||||
before do
|
||||
project.project_feature.update(pages_access_level: pages_access_level)
|
||||
end
|
||||
it "correct return value" do
|
||||
if !with_user.nil?
|
||||
user = public_send(with_user)
|
||||
get api("/projects/#{project.id}/pages_access", user)
|
||||
else
|
||||
get api("/projects/#{project.id}/pages_access")
|
||||
end
|
||||
|
||||
expect(response).to have_gitlab_http_status(expected_result)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe "Private Project Pages Access" do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
include AccessMatchers
|
||||
|
||||
set(:group) { create(:group) }
|
||||
set(:project) { create(:project, :private, pages_access_level: ProjectFeature::ENABLED, namespace: group) }
|
||||
|
||||
set(:admin) { create(:admin) }
|
||||
set(:owner) { create(:user) }
|
||||
set(:master) { create(:user) }
|
||||
set(:developer) { create(:user) }
|
||||
set(:reporter) { create(:user) }
|
||||
set(:guest) { create(:user) }
|
||||
set(:user) { create(:user) }
|
||||
|
||||
before do
|
||||
allow(Gitlab.config.pages).to receive(:access_control).and_return(true)
|
||||
group.add_owner(owner)
|
||||
project.add_master(master)
|
||||
project.add_developer(developer)
|
||||
project.add_reporter(reporter)
|
||||
project.add_guest(guest)
|
||||
end
|
||||
|
||||
describe "Project should be private" do
|
||||
describe '#private?' do
|
||||
subject { project.private? }
|
||||
it { is_expected.to be_truthy }
|
||||
end
|
||||
end
|
||||
|
||||
describe "GET /projects/:id/pages_access" do
|
||||
context 'access depends on the level' do
|
||||
where(:pages_access_level, :with_user, :expected_result) do
|
||||
ProjectFeature::DISABLED | "admin" | 403
|
||||
ProjectFeature::DISABLED | "owner" | 403
|
||||
ProjectFeature::DISABLED | "master" | 403
|
||||
ProjectFeature::DISABLED | "developer" | 403
|
||||
ProjectFeature::DISABLED | "reporter" | 403
|
||||
ProjectFeature::DISABLED | "guest" | 403
|
||||
ProjectFeature::DISABLED | "user" | 404
|
||||
ProjectFeature::DISABLED | nil | 404
|
||||
ProjectFeature::PUBLIC | "admin" | 200
|
||||
ProjectFeature::PUBLIC | "owner" | 200
|
||||
ProjectFeature::PUBLIC | "master" | 200
|
||||
ProjectFeature::PUBLIC | "developer" | 200
|
||||
ProjectFeature::PUBLIC | "reporter" | 200
|
||||
ProjectFeature::PUBLIC | "guest" | 200
|
||||
ProjectFeature::PUBLIC | "user" | 404
|
||||
ProjectFeature::PUBLIC | nil | 404
|
||||
ProjectFeature::ENABLED | "admin" | 200
|
||||
ProjectFeature::ENABLED | "owner" | 200
|
||||
ProjectFeature::ENABLED | "master" | 200
|
||||
ProjectFeature::ENABLED | "developer" | 200
|
||||
ProjectFeature::ENABLED | "reporter" | 200
|
||||
ProjectFeature::ENABLED | "guest" | 200
|
||||
ProjectFeature::ENABLED | "user" | 404
|
||||
ProjectFeature::ENABLED | nil | 404
|
||||
ProjectFeature::PRIVATE | "admin" | 200
|
||||
ProjectFeature::PRIVATE | "owner" | 200
|
||||
ProjectFeature::PRIVATE | "master" | 200
|
||||
ProjectFeature::PRIVATE | "developer" | 200
|
||||
ProjectFeature::PRIVATE | "reporter" | 200
|
||||
ProjectFeature::PRIVATE | "guest" | 200
|
||||
ProjectFeature::PRIVATE | "user" | 404
|
||||
ProjectFeature::PRIVATE | nil | 404
|
||||
end
|
||||
|
||||
with_them do
|
||||
before do
|
||||
project.project_feature.update(pages_access_level: pages_access_level)
|
||||
end
|
||||
it "correct return value" do
|
||||
if !with_user.nil?
|
||||
user = public_send(with_user)
|
||||
get api("/projects/#{project.id}/pages_access", user)
|
||||
else
|
||||
get api("/projects/#{project.id}/pages_access")
|
||||
end
|
||||
|
||||
expect(response).to have_gitlab_http_status(expected_result)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe "Public Project Pages Access" do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
include AccessMatchers
|
||||
|
||||
set(:group) { create(:group) }
|
||||
set(:project) { create(:project, :public, pages_access_level: ProjectFeature::ENABLED, namespace: group) }
|
||||
|
||||
set(:admin) { create(:admin) }
|
||||
set(:owner) { create(:user) }
|
||||
set(:master) { create(:user) }
|
||||
set(:developer) { create(:user) }
|
||||
set(:reporter) { create(:user) }
|
||||
set(:guest) { create(:user) }
|
||||
set(:user) { create(:user) }
|
||||
|
||||
before do
|
||||
allow(Gitlab.config.pages).to receive(:access_control).and_return(true)
|
||||
group.add_owner(owner)
|
||||
project.add_master(master)
|
||||
project.add_developer(developer)
|
||||
project.add_reporter(reporter)
|
||||
project.add_guest(guest)
|
||||
end
|
||||
|
||||
describe "Project should be public" do
|
||||
describe '#public?' do
|
||||
subject { project.public? }
|
||||
it { is_expected.to be_truthy }
|
||||
end
|
||||
end
|
||||
|
||||
describe "GET /projects/:id/pages_access" do
|
||||
context 'access depends on the level' do
|
||||
where(:pages_access_level, :with_user, :expected_result) do
|
||||
ProjectFeature::DISABLED | "admin" | 403
|
||||
ProjectFeature::DISABLED | "owner" | 403
|
||||
ProjectFeature::DISABLED | "master" | 403
|
||||
ProjectFeature::DISABLED | "developer" | 403
|
||||
ProjectFeature::DISABLED | "reporter" | 403
|
||||
ProjectFeature::DISABLED | "guest" | 403
|
||||
ProjectFeature::DISABLED | "user" | 403
|
||||
ProjectFeature::DISABLED | nil | 403
|
||||
ProjectFeature::PUBLIC | "admin" | 200
|
||||
ProjectFeature::PUBLIC | "owner" | 200
|
||||
ProjectFeature::PUBLIC | "master" | 200
|
||||
ProjectFeature::PUBLIC | "developer" | 200
|
||||
ProjectFeature::PUBLIC | "reporter" | 200
|
||||
ProjectFeature::PUBLIC | "guest" | 200
|
||||
ProjectFeature::PUBLIC | "user" | 200
|
||||
ProjectFeature::PUBLIC | nil | 200
|
||||
ProjectFeature::ENABLED | "admin" | 200
|
||||
ProjectFeature::ENABLED | "owner" | 200
|
||||
ProjectFeature::ENABLED | "master" | 200
|
||||
ProjectFeature::ENABLED | "developer" | 200
|
||||
ProjectFeature::ENABLED | "reporter" | 200
|
||||
ProjectFeature::ENABLED | "guest" | 200
|
||||
ProjectFeature::ENABLED | "user" | 200
|
||||
ProjectFeature::ENABLED | nil | 200
|
||||
ProjectFeature::PRIVATE | "admin" | 200
|
||||
ProjectFeature::PRIVATE | "owner" | 200
|
||||
ProjectFeature::PRIVATE | "master" | 200
|
||||
ProjectFeature::PRIVATE | "developer" | 200
|
||||
ProjectFeature::PRIVATE | "reporter" | 200
|
||||
ProjectFeature::PRIVATE | "guest" | 200
|
||||
ProjectFeature::PRIVATE | "user" | 403
|
||||
ProjectFeature::PRIVATE | nil | 403
|
||||
end
|
||||
|
||||
with_them do
|
||||
before do
|
||||
project.project_feature.update(pages_access_level: pages_access_level)
|
||||
end
|
||||
it "correct return value" do
|
||||
if !with_user.nil?
|
||||
user = public_send(with_user)
|
||||
get api("/projects/#{project.id}/pages_access", user)
|
||||
else
|
||||
get api("/projects/#{project.id}/pages_access")
|
||||
end
|
||||
|
||||
expect(response).to have_gitlab_http_status(expected_result)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -785,35 +785,25 @@ describe API::Users do
|
|||
end
|
||||
|
||||
describe 'GET /user/:id/keys' do
|
||||
before do
|
||||
admin
|
||||
it 'returns 404 for non-existing user' do
|
||||
user_id = not_existing_user_id
|
||||
|
||||
get api("/users/#{user_id}/keys")
|
||||
|
||||
expect(response).to have_gitlab_http_status(404)
|
||||
expect(json_response['message']).to eq('404 User Not Found')
|
||||
end
|
||||
|
||||
context 'when unauthenticated' do
|
||||
it 'returns authentication error' do
|
||||
get api("/users/#{user.id}/keys")
|
||||
expect(response).to have_gitlab_http_status(401)
|
||||
end
|
||||
end
|
||||
it 'returns array of ssh keys' do
|
||||
user.keys << key
|
||||
user.save
|
||||
|
||||
context 'when authenticated' do
|
||||
it 'returns 404 for non-existing user' do
|
||||
get api('/users/999999/keys', admin)
|
||||
expect(response).to have_gitlab_http_status(404)
|
||||
expect(json_response['message']).to eq('404 User Not Found')
|
||||
end
|
||||
get api("/users/#{user.id}/keys")
|
||||
|
||||
it 'returns array of ssh keys' do
|
||||
user.keys << key
|
||||
user.save
|
||||
|
||||
get api("/users/#{user.id}/keys", admin)
|
||||
|
||||
expect(response).to have_gitlab_http_status(200)
|
||||
expect(response).to include_pagination_headers
|
||||
expect(json_response).to be_an Array
|
||||
expect(json_response.first['title']).to eq(key.title)
|
||||
end
|
||||
expect(response).to have_gitlab_http_status(200)
|
||||
expect(response).to include_pagination_headers
|
||||
expect(json_response).to be_an Array
|
||||
expect(json_response.first['title']).to eq(key.title)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -133,8 +133,9 @@ describe 'project routing' do
|
|||
# labels_namespace_project_autocomplete_sources_path GET /:project_id/autocomplete_sources/labels(.:format) projects/autocomplete_sources#labels
|
||||
# milestones_namespace_project_autocomplete_sources_path GET /:project_id/autocomplete_sources/milestones(.:format) projects/autocomplete_sources#milestones
|
||||
# commands_namespace_project_autocomplete_sources_path GET /:project_id/autocomplete_sources/commands(.:format) projects/autocomplete_sources#commands
|
||||
# snippets_namespace_project_autocomplete_sources_path GET /:project_id/autocomplete_sources/snippets(.:format) projects/autocomplete_sources#snippets
|
||||
describe Projects::AutocompleteSourcesController, 'routing' do
|
||||
[:members, :issues, :merge_requests, :labels, :milestones, :commands].each do |action|
|
||||
[:members, :issues, :merge_requests, :labels, :milestones, :commands, :snippets].each do |action|
|
||||
it "to ##{action}" do
|
||||
expect(get("/gitlab/gitlabhq/autocomplete_sources/#{action}")).to route_to("projects/autocomplete_sources##{action}", namespace_id: 'gitlab', project_id: 'gitlabhq')
|
||||
end
|
||||
|
|
|
|||
|
|
@ -340,6 +340,27 @@ describe Projects::UpdateService do
|
|||
call_service
|
||||
end
|
||||
end
|
||||
|
||||
context 'when updating #pages_access_level' do
|
||||
subject(:call_service) do
|
||||
update_project(project, admin, project_feature_attributes: { pages_access_level: ProjectFeature::PRIVATE })
|
||||
end
|
||||
|
||||
it 'updates the attribute' do
|
||||
expect { call_service }
|
||||
.to change { project.project_feature.pages_access_level }
|
||||
.to(ProjectFeature::PRIVATE)
|
||||
end
|
||||
|
||||
it 'calls Projects::UpdatePagesConfigurationService' do
|
||||
expect(Projects::UpdatePagesConfigurationService)
|
||||
.to receive(:new)
|
||||
.with(project)
|
||||
.and_call_original
|
||||
|
||||
call_service
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#run_auto_devops_pipeline?' do
|
||||
|
|
|
|||
|
|
@ -34,6 +34,11 @@ Rainbow.enabled = false
|
|||
# Requires supporting ruby files with custom matchers and macros, etc,
|
||||
# in spec/support/ and its subdirectories.
|
||||
# Requires helpers, and shared contexts/examples first since they're used in other support files
|
||||
|
||||
# Load these first since they may be required by other helpers
|
||||
require Rails.root.join("spec/support/helpers/git_helpers.rb")
|
||||
|
||||
# Then the rest
|
||||
Dir[Rails.root.join("spec/support/helpers/*.rb")].each { |f| require f }
|
||||
Dir[Rails.root.join("spec/support/shared_contexts/*.rb")].each { |f| require f }
|
||||
Dir[Rails.root.join("spec/support/shared_examples/*.rb")].each { |f| require f }
|
||||
|
|
|
|||
|
|
@ -616,14 +616,10 @@
|
|||
lodash "^4.17.10"
|
||||
to-fast-properties "^2.0.0"
|
||||
|
||||
"@gitlab-org/gitlab-svgs@^1.23.0":
|
||||
version "1.29.0"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-svgs/-/gitlab-svgs-1.29.0.tgz#03b65b513f9099bbda6ecf94d673a2952f8c6c70"
|
||||
integrity sha512-sCl6nP3ph36+8P3nrw9VanAR648rgOUEBlEoLPHkhKm79xB1dUkXGBtI0uaSJVgbJx40M1/Ts8HSdMv+PF3EIg==
|
||||
|
||||
"@gitlab-org/gitlab-svgs@^1.29.0":
|
||||
"@gitlab-org/gitlab-svgs@^1.23.0", "@gitlab-org/gitlab-svgs@^1.29.0":
|
||||
version "1.31.0"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-svgs/-/gitlab-svgs-1.31.0.tgz#495b074669f93af40e34f9978ce887773dea470a"
|
||||
integrity sha512-tJbf99XX/ddFkXCXxQr9a0GJD9rPVoW3qMbU14dkxwG4WBmPEoVg+e7sLvm9OWTD1uUqiVW3qWKp++SGhhcRlw==
|
||||
|
||||
"@gitlab-org/gitlab-ui@^1.8.0":
|
||||
version "1.8.0"
|
||||
|
|
|
|||
Loading…
Reference in New Issue