Commit 8a68e849 authored by Lin Jen-Shin's avatar Lin Jen-Shin

Merge remote-tracking branch 'ee/master' into ce-to-ee-2017-08-03

* ee/master:
  Add notes on when repository pulling and pushing where introduced in GitLab
  fixed specs
  EE port of 34060-simplified-general-project-settings
  Backport to CE for:
  Make new dropdown dividers full width
  Update EE doc about where to place the files
  Update JavaScript style guide according to:
  Handle project deletions in Geo::LogCursor
  Fix json schema definition
  Move the new files
  35659 Rename Pipelines tab to CI / CD in new navigation
  Move this spec to the right location and enable rubocop
  Move javascript files
  Move the new view files
  Move spec files
  Move views
  Also move models/concerns and lib
  Move EE-specific files to a standalone directory
parents 22476cad e012d8ca
/* global ListIssue */ /* global ListIssue */
import Vue from 'vue'; import Vue from 'vue';
import queryData from '../../utils/query_data'; import queryData from '~/boards/utils/query_data';
import loadingIcon from '../../../vue_shared/components/loading_icon.vue'; import loadingIcon from '~/vue_shared/components/loading_icon.vue';
import './header'; import './header';
import './list'; import './list';
import './footer'; import './footer';
......
...@@ -381,6 +381,9 @@ import initGroupAnalytics from './init_group_analytics'; ...@@ -381,6 +381,9 @@ import initGroupAnalytics from './init_group_analytics';
new UsersSelect(); new UsersSelect();
new GroupsSelect(); new GroupsSelect();
setupProjectEdit(); setupProjectEdit();
// Initialize expandable settings panels
initSettingsPanels();
new UsersSelect();
break; break;
case 'projects:imports:show': case 'projects:imports:show':
new ProjectImport(); new ProjectImport();
......
<script> <script>
import GfmAutoComplete from '~/gfm_auto_complete'; import GfmAutoComplete from '~/gfm_auto_complete';
import loadingIcon from '~/vue_shared/components/loading_icon.vue';
import eventHub from '../event_hub'; import eventHub from '../event_hub';
import issueToken from './issue_token.vue'; import issueToken from './issue_token.vue';
import loadingIcon from '../../../vue_shared/components/loading_icon.vue';
export default { export default {
name: 'AddIssuableForm', name: 'AddIssuableForm',
......
<script> <script>
import loadingIcon from '~/vue_shared/components/loading_icon.vue';
import tooltip from '~/vue_shared/directives/tooltip';
import eventHub from '../event_hub'; import eventHub from '../event_hub';
import loadingIcon from '../../../vue_shared/components/loading_icon.vue';
import tooltip from '../../../vue_shared/directives/tooltip';
import issueToken from './issue_token.vue'; import issueToken from './issue_token.vue';
import addIssuableForm from './add_issuable_form.vue'; import addIssuableForm from './add_issuable_form.vue';
......
<script> <script>
import loadingIcon from '~/vue_shared/components/loading_icon.vue';
import linkedPipelinesColumn from './linked_pipelines_column.vue'; import linkedPipelinesColumn from './linked_pipelines_column.vue';
import stageColumnComponent from './stage_column_component.vue'; import stageColumnComponent from './stage_column_component.vue';
import loadingIcon from '../../../vue_shared/components/loading_icon.vue';
export default { export default {
props: { props: {
......
/** /**
* This file is the centerpiece of an attempt to reduce potential conflicts * This file is the centerpiece of an attempt to reduce potential conflicts
* between the CE and EE versions of the MR widget. EE additions to the MR widget should * between the CE and EE versions of the MR widget. EE additions to the MR widget should
* be contained in the ./vue_merge_request_widget/ee directory, and should **extend** * be contained in the ee/vue_merge_request_widget directory, and should **extend**
* rather than mutate CE MR Widget code. * rather than mutate CE MR Widget code.
* *
* This file should be the only source of conflicts between EE and CE. EE-only components should * This file should be the only source of conflicts between EE and CE. EE-only components should
...@@ -26,7 +26,7 @@ export { default as ConflictsState } from './components/states/mr_widget_conflic ...@@ -26,7 +26,7 @@ export { default as ConflictsState } from './components/states/mr_widget_conflic
export { default as NothingToMergeState } from './components/states/mr_widget_nothing_to_merge'; export { default as NothingToMergeState } from './components/states/mr_widget_nothing_to_merge';
export { default as MissingBranchState } from './components/states/mr_widget_missing_branch'; export { default as MissingBranchState } from './components/states/mr_widget_missing_branch';
export { default as NotAllowedState } from './components/states/mr_widget_not_allowed'; export { default as NotAllowedState } from './components/states/mr_widget_not_allowed';
export { default as ReadyToMergeState } from './ee/components/states/mr_widget_ready_to_merge'; export { default as ReadyToMergeState } from 'ee/vue_merge_request_widget/components/states/mr_widget_ready_to_merge';
export { default as SHAMismatchState } from './components/states/mr_widget_sha_mismatch'; export { default as SHAMismatchState } from './components/states/mr_widget_sha_mismatch';
export { default as UnresolvedDiscussionsState } from './components/states/mr_widget_unresolved_discussions'; export { default as UnresolvedDiscussionsState } from './components/states/mr_widget_unresolved_discussions';
export { default as PipelineBlockedState } from './components/states/mr_widget_pipeline_blocked'; export { default as PipelineBlockedState } from './components/states/mr_widget_pipeline_blocked';
...@@ -34,11 +34,11 @@ export { default as PipelineFailedState } from './components/states/mr_widget_pi ...@@ -34,11 +34,11 @@ export { default as PipelineFailedState } from './components/states/mr_widget_pi
export { default as MergeWhenPipelineSucceedsState } from './components/states/mr_widget_merge_when_pipeline_succeeds'; export { default as MergeWhenPipelineSucceedsState } from './components/states/mr_widget_merge_when_pipeline_succeeds';
export { default as AutoMergeFailed } from './components/states/mr_widget_auto_merge_failed'; export { default as AutoMergeFailed } from './components/states/mr_widget_auto_merge_failed';
export { default as CheckingState } from './components/states/mr_widget_checking'; export { default as CheckingState } from './components/states/mr_widget_checking';
export { default as MRWidgetStore } from './ee/stores/mr_widget_store'; export { default as MRWidgetStore } from 'ee/vue_merge_request_widget/stores/mr_widget_store';
export { default as MRWidgetService } from './ee/services/mr_widget_service'; export { default as MRWidgetService } from 'ee/vue_merge_request_widget/services/mr_widget_service';
export { default as eventHub } from './event_hub'; export { default as eventHub } from './event_hub';
export { default as getStateKey } from './ee/stores/get_state_key'; export { default as getStateKey } from 'ee/vue_merge_request_widget/stores/get_state_key';
export { default as mrWidgetOptions } from './ee/mr_widget_options'; export { default as mrWidgetOptions } from 'ee/vue_merge_request_widget/mr_widget_options';
export { default as stateMaps } from './ee/stores/state_maps'; export { default as stateMaps } from 'ee/vue_merge_request_widget/stores/state_maps';
export { default as SquashBeforeMerge } from './ee/components/states/mr_widget_squash_before_merge'; export { default as SquashBeforeMerge } from 'ee/vue_merge_request_widget/components/states/mr_widget_squash_before_merge';
export { default as notify } from '../lib/utils/notify'; export { default as notify } from '../lib/utils/notify';
...@@ -728,6 +728,10 @@ ...@@ -728,6 +728,10 @@
@mixin new-style-dropdown { @mixin new-style-dropdown {
.dropdown-menu, .dropdown-menu,
.dropdown-menu-nav { .dropdown-menu-nav {
.divider {
margin: 6px 0;
}
li { li {
padding: 0 1px; padding: 0 1px;
......
...@@ -312,6 +312,10 @@ header.navbar-gitlab-new { ...@@ -312,6 +312,10 @@ header.navbar-gitlab-new {
// TODO: fallback to global style // TODO: fallback to global style
.dropdown-menu { .dropdown-menu {
.divider {
margin: 6px 0;
}
li { li {
padding: 0 1px; padding: 0 1px;
......
...@@ -36,7 +36,6 @@ ...@@ -36,7 +36,6 @@
} }
select { select {
background: transparent;
transition: background 2s ease-out; transition: background 2s ease-out;
&.highlight-changes { &.highlight-changes {
......
...@@ -54,8 +54,7 @@ ...@@ -54,8 +54,7 @@
.settings-content { .settings-content {
max-height: 1px; max-height: 1px;
overflow-y: scroll; overflow-y: scroll;
margin-right: -20px; padding-right: 110px;
padding-right: 130px;
animation: collapseMaxHeight 300ms ease-out; animation: collapseMaxHeight 300ms ease-out;
&.expanded { &.expanded {
...@@ -87,6 +86,23 @@ ...@@ -87,6 +86,23 @@
overflow: hidden; overflow: hidden;
margin-top: 20px; margin-top: 20px;
} }
.sub-section {
margin-bottom: 32px;
padding: 16px;
border: 1px solid $border-color;
background-color: $gray-light;
}
.bs-callout,
.checkbox:first-child,
.help-block {
margin-top: 0;
}
.label-light {
margin-bottom: 0;
}
} }
.settings-list-icon { .settings-list-icon {
......
# Shorter routing method for some project items # Shorter routing method for some project items
module GitlabRoutingHelper module GitlabRoutingHelper
include EE::GitlabRoutingHelper
extend ActiveSupport::Concern extend ActiveSupport::Concern
included do included do
......
module IssuesHelper module IssuesHelper
include EE::IssuesHelper
def issue_css_classes(issue) def issue_css_classes(issue)
classes = "issue" classes = "issue"
classes << " closed" if issue.closed? classes << " closed" if issue.closed?
......
module NamespacesHelper module NamespacesHelper
include EE::NamespaceHelper
def namespace_id_from(params) def namespace_id_from(params)
params.dig(:project, :namespace_id) || params[:namespace_id] params.dig(:project, :namespace_id) || params[:namespace_id]
end end
......
...@@ -91,8 +91,8 @@ ...@@ -91,8 +91,8 @@
Projects Projects
= nav_link(controller: :ci_cd) do = nav_link(controller: :ci_cd) do
= link_to group_settings_ci_cd_path(@group), title: 'Pipelines' do = link_to group_settings_ci_cd_path(@group), title: 'CI / CD' do
%span %span
Pipelines CI / CD
= render "groups/ee/settings_nav" = render "groups/ee/settings_nav"
...@@ -122,11 +122,11 @@ ...@@ -122,11 +122,11 @@
- if project_nav_tab? :pipelines - if project_nav_tab? :pipelines
= nav_link(controller: [:pipelines, :builds, :jobs, :pipeline_schedules, :environments, :artifacts]) do = nav_link(controller: [:pipelines, :builds, :jobs, :pipeline_schedules, :environments, :artifacts]) do
= link_to project_pipelines_path(@project), title: 'Pipelines', class: 'shortcuts-pipelines' do = link_to project_pipelines_path(@project), title: 'CI / CD', class: 'shortcuts-pipelines' do
.nav-icon-container .nav-icon-container
= custom_icon('pipeline') = custom_icon('pipeline')
%span.nav-item-name %span.nav-item-name
Pipelines CI / CD
%ul.sidebar-sub-level-items %ul.sidebar-sub-level-items
- if project_nav_tab? :pipelines - if project_nav_tab? :pipelines
...@@ -205,9 +205,9 @@ ...@@ -205,9 +205,9 @@
Repository Repository
- if @project.feature_available?(:builds, current_user) - if @project.feature_available?(:builds, current_user)
= nav_link(controller: :ci_cd) do = nav_link(controller: :ci_cd) do
= link_to project_settings_ci_cd_path(@project), title: 'Pipelines' do = link_to project_settings_ci_cd_path(@project), title: 'CI / CD' do
%span %span
Pipelines CI / CD
- if Gitlab.config.pages.enabled - if Gitlab.config.pages.enabled
= nav_link(controller: :pages) do = nav_link(controller: :pages) do
= link_to project_pages_path(@project), title: 'Pages' do = link_to project_pages_path(@project), title: 'Pages' do
......
- form = local_assigns.fetch(:form) - form = local_assigns.fetch(:form)
%fieldset.features.merge-requests-feature.append-bottom-default = render 'projects/ee/merge_request_settings', form: form, project: @project
%hr
%h5.prepend-top-0
Merge Requests
= render 'projects/ee/merge_request_settings', form: form, project: @project = render 'projects/merge_request_merge_settings', form: form
= render 'projects/merge_request_merge_settings', form: form
- page_title "General"
- @content_class = "limit-container-width" unless fluid_layout - @content_class = "limit-container-width" unless fluid_layout
- expanded = Rails.env.test?
- content_for :page_specific_javascripts do - content_for :page_specific_javascripts do
= webpack_bundle_tag('common_vue') = webpack_bundle_tag('common_vue')
...@@ -7,11 +9,15 @@ ...@@ -7,11 +9,15 @@
= render "projects/settings/head" = render "projects/settings/head"
.project-edit-container .project-edit-container
.row.prepend-top-default %section.settings.general-settings
.col-lg-4.profile-settings-sidebar .settings-header
%h4.prepend-top-0 %h4
Project settings General project settings
.col-lg-8 %button.btn.js-settings-toggle
= expanded ? 'Collapse' : 'Expand'
%p
Update your project name, description, avatar, and other general settings.
.settings-content.no-animate{ class: ('expanded' if expanded) }
.project-edit-errors .project-edit-errors
= form_for [@project.namespace.becomes(Namespace), @project], remote: true, html: { multipart: true, class: "edit-project" }, authenticity_token: true do |f| = form_for [@project.namespace.becomes(Namespace), @project], remote: true, html: { multipart: true, class: "edit-project" }, authenticity_token: true do |f|
%fieldset %fieldset
...@@ -43,101 +49,6 @@ ...@@ -43,101 +49,6 @@
= f.label :tag_list, "Tags", class: 'label-light' = f.label :tag_list, "Tags", class: 'label-light'
= f.text_field :tag_list, value: @project.tag_list.sort.join(', '), maxlength: 2000, class: "form-control" = f.text_field :tag_list, value: @project.tag_list.sort.join(', '), maxlength: 2000, class: "form-control"
%p.help-block Separate tags with commas. %p.help-block Separate tags with commas.
%hr
%fieldset
%h5.prepend-top-0
Sharing &amp; Permissions
.form_group.prepend-top-20.sharing-and-permissions
.row.js-visibility-select
.col-md-8
.label-light
= label_tag :project_visibility, 'Project Visibility', class: 'label-light', for: :project_visibility_level
= link_to icon('question-circle'), help_page_path("public_access/public_access")
%span.help-block
.col-md-4.visibility-select-container
= render('projects/visibility_select', model_method: :visibility_level, form: f, selected_level: @project.visibility_level)
= f.fields_for :project_feature do |feature_fields|
%fieldset.features
.row
.col-md-8.project-feature
= feature_fields.label :repository_access_level, "Repository", class: 'label-light'
%span.help-block View and edit files in this project
.col-md-4.js-repo-access-level
= project_feature_access_select(:repository_access_level)
.row
.col-md-8.project-feature.nested
= feature_fields.label :merge_requests_access_level, "Merge requests", class: 'label-light'
%span.help-block Submit changes to be merged upstream
.col-md-4
= project_feature_access_select(:merge_requests_access_level)
.row
.col-md-8.project-feature.nested
= feature_fields.label :builds_access_level, "Pipelines", class: 'label-light'
%span.help-block Build, test, and deploy your changes
.col-md-4
= project_feature_access_select(:builds_access_level)
.row
.col-md-8.project-feature
= feature_fields.label :snippets_access_level, "Snippets", class: 'label-light'
%span.help-block Share code pastes with others out of Git repository
.col-md-4
= project_feature_access_select(:snippets_access_level)
.row
.col-md-8.project-feature
= feature_fields.label :issues_access_level, "Issues", class: 'label-light'
%span.help-block Lightweight issue tracking system for this project
.col-md-4
= project_feature_access_select(:issues_access_level)
.row
.col-md-8.project-feature
= feature_fields.label :wiki_access_level, "Wiki", class: 'label-light'
%span.help-block Pages for project documentation
.col-md-4
= project_feature_access_select(:wiki_access_level)
.form-group
= render 'shared/allow_request_access', form: f
- if Gitlab.config.lfs.enabled && current_user.admin?
.row.js-lfs-enabled
.col-md-8
= f.label :lfs_enabled, 'LFS', class: 'label-light'
%span.help-block
Git Large File Storage
= link_to icon('question-circle'), help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs')
.col-md-4
.select-wrapper
= f.select :lfs_enabled, [%w(Enabled true), %w(Disabled false)], {}, selected: @project.lfs_enabled?, class: 'pull-right form-control project-repo-select select-control', data: { field: 'lfs_enabled' }
= icon('chevron-down')
- if Gitlab.config.registry.enabled
.form-group.js-container-registry{ style: ("display: none;" if @project.project_feature.send(:repository_access_level) == 0) }
.checkbox
= f.label :container_registry_enabled do
= f.check_box :container_registry_enabled
%strong Container Registry
%br
%span.descr Enable Container Registry for this project
= link_to icon('question-circle'), help_page_path('user/project/container_registry'), target: '_blank'
= render 'projects/ee/issues_settings', form: f
= render 'merge_request_settings', form: f
- if EE::Gitlab::ServiceDesk.enabled?(project: @project)
%hr
%fieldset.js-service-desk-setting-wrapper.features.append-bottom-default
%h5.prepend-top-0
Service Desk
= link_to icon('question-circle'), help_page_path('user/project/service_desk')
.js-service-desk-setting-root{ data: { endpoint: project_service_desk_path(@project),
enabled: "#{@project.service_desk_enabled}",
incoming_email: (@project.service_desk_address if @project.service_desk_enabled) } }
%hr
%fieldset.features.append-bottom-default
%h5.prepend-top-0 %h5.prepend-top-0
Project avatar Project avatar
.form-group .form-group
...@@ -157,41 +68,130 @@ ...@@ -157,41 +68,130 @@
= link_to 'Remove avatar', project_avatar_path(@project), data: { confirm: "Project avatar will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-sm remove-avatar" = link_to 'Remove avatar', project_avatar_path(@project), data: { confirm: "Project avatar will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-sm remove-avatar"
= f.submit 'Save changes', class: "btn btn-save" = f.submit 'Save changes', class: "btn btn-save"
.row.prepend-top-default %section.settings.sharing-permissions
%hr .settings-header
.row.prepend-top-default %h4
.col-lg-4 Sharing and permissions
%h4.prepend-top-0 %button.btn.js-settings-toggle
Housekeeping = expanded ? 'Collapse' : 'Expand'
%p.append-bottom-0 %p
%p Enable or disable certain project features and choose access levels.
Runs a number of housekeeping tasks within the current repository, .settings-content.no-animate{ class: ('expanded' if expanded) }
such as compressing file revisions and removing unreachable objects. = form_for [@project.namespace.becomes(Namespace), @project], remote: true, html: { multipart: true, class: "sharing-permissions-form" }, authenticity_token: true do |f|
.col-lg-8 .form_group.sharing-and-permissions
= link_to 'Housekeeping', housekeeping_project_path(@project), .row.js-visibility-select
method: :post, class: "btn btn-default" .col-md-8
%hr .label-light
.row.prepend-top-default = label_tag :project_visibility, 'Project Visibility', class: 'label-light', for: :project_visibility_level
.col-lg-4 = link_to icon('question-circle'), help_page_path("public_access/public_access")
%h4.prepend-top-0 %span.help-block
Export project .col-md-4.visibility-select-container
%p.append-bottom-0 = render('projects/visibility_select', model_method: :visibility_level, form: f, selected_level: @project.visibility_level)
%p = f.fields_for :project_feature do |feature_fields|
Export this project with all its related data in order to move your project to a new GitLab instance. Once the export is finished, you can import the file from the "New Project" page. %fieldset.features
%p .row
Once the exported file is ready, you will receive a notification email with a download link. .col-md-8.project-feature
= feature_fields.label :repository_access_level, "Repository", class: 'label-light'
%span.help-block View and edit files in this project
.col-md-4.js-repo-access-level
= project_feature_access_select(:repository_access_level)
.col-lg-8 .row
.col-md-8.project-feature.nested
= feature_fields.label :merge_requests_access_level, "Merge requests", class: 'label-light'
%span.help-block Submit changes to be merged upstream
.col-md-4
= project_feature_access_select(:merge_requests_access_level)
- if @project.export_project_path .row
= link_to 'Download export', download_export_project_path(@project), .col-md-8.project-feature.nested
rel: 'nofollow', download: '', method: :get, class: "btn btn-default" = feature_fields.label :builds_access_level, "Pipelines", class: 'label-light'
= link_to 'Generate new export', generate_new_export_project_path(@project), %span.help-block Build, test, and deploy your changes
method: :post, class: "btn btn-default" .col-md-4
- else = project_feature_access_select(:builds_access_level)
= link_to 'Export project', export_project_path(@project),
method: :post, class: "btn btn-default" .row
.col-md-8.project-feature
= feature_fields.label :snippets_access_level, "Snippets", class: 'label-light'
%span.help-block Share code pastes with others out of Git repository
.col-md-4
= project_feature_access_select(:snippets_access_level)
.row
.col-md-8.project-feature
= feature_fields.label :issues_access_level, "Issues", class: 'label-light'
%span.help-block Lightweight issue tracking system for this project
.col-md-4
= project_feature_access_select(:issues_access_level)
.row
.col-md-8.project-feature
= feature_fields.label :wiki_access_level, "Wiki", class: 'label-light'
%span.help-block Pages for project documentation
.col-md-4
= project_feature_access_select(:wiki_access_level)
.form-group
= render 'shared/allow_request_access', form: f
- if Gitlab.config.lfs.enabled && current_user.admin?
.row.js-lfs-enabled.form-group.sharing-and-permissions
.col-md-8
= f.label :lfs_enabled, 'Git Large File Storage', class: 'label-light'
= link_to icon('question-circle'), help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs')
%span.help-block Manages large files such as audio, video and graphics files.
.col-md-4
.select-wrapper
= f.select :lfs_enabled, [%w(Enabled true), %w(Disabled false)], {}, selected: @project.lfs_enabled?, class: 'pull-right form-control project-repo-select select-control', data: { field: 'lfs_enabled' }
= icon('chevron-down')
- if Gitlab.config.registry.enabled
.form-group.js-container-registry{ style: ("display: none;" if @project.project_feature.send(:repository_access_level) == 0) }
.checkbox
= f.label :container_registry_enabled do
= f.check_box :container_registry_enabled
%strong Container Registry
%br
%span.descr Enable Container Registry for this project
= link_to icon('question-circle'), help_page_path('user/project/container_registry'), target: '_blank'
= f.submit 'Save changes', class: "btn btn-save"
= render 'projects/ee/issues_settings'
%section.settings.merge-requests-feature{ style: ("display: none;" if @project.project_feature.send(:merge_requests_access_level) == 0) }
.settings-header
%h4
Merge request settings
%button.btn.js-settings-toggle
= expanded ? 'Collapse' : 'Expand'
%p
Customize your merge request restrictions.
.settings-content.no-animate{ class: ('expanded' if expanded) }
= form_for [@project.namespace.becomes(Namespace), @project], remote: true, html: { multipart: true, class: "merge-request-settings-form" }, authenticity_token: true do |f|
= render 'merge_request_settings', form: f
= f.submit 'Save changes', class: "btn btn-save"
- if EE::Gitlab::ServiceDesk.enabled?(project: @project)
%section.settings.js-service-desk-setting-wrapper
.settings-header
%h4
Service Desk
%button.btn.js-settings-toggle
= expanded ? 'Collapse' : 'Expand'
%p
Customize your service desk settings.
= link_to "Learn more about service desk.", help_page_path('user/project/service_desk')
.settings-content.no-animate{ class: ('expanded' if expanded) }
.js-service-desk-setting-root{ data: { endpoint: project_service_desk_path(@project),
enabled: "#{@project.service_desk_enabled}",
incoming_email: (@project.service_desk_address if @project.service_desk_enabled) } }
%section.settings
.settings-header
%h4
Export project
%button.btn.js-settings-toggle
= expanded ? 'Collapse' : 'Expand'
%p
Export this project with all its related data in order to move your project to a new GitLab instance. Once the export is finished, you can import the file from the "New Project" page.
.settings-content.no-animate{ class: ('expanded' if expanded) }
.bs-callout.bs-callout-info .bs-callout.bs-callout-info
%p.append-bottom-0 %p.append-bottom-0
%p %p
...@@ -209,110 +209,117 @@ ...@@ -209,110 +209,117 @@
%li Container registry images %li Container registry images
%li CI variables %li CI variables
%li Any encrypted tokens %li Any encrypted tokens
- if can? current_user, :archive_project, @project %p
%hr Once the exported file is ready, you will receive a notification email with a download link.
.row.prepend-top-default - if @project.export_project_path
.col-lg-4 = link_to 'Download export', download_export_project_path(@project),
%h4.warning-title.prepend-top-0 rel: 'nofollow', download: '', method: :get, class: "btn btn-default"
- if @project.archived? = link_to 'Generate new export', generate_new_export_project_path(@project),
Unarchive project method: :post, class: "btn btn-default"
- else - else
Archive project = link_to 'Export project', export_project_path(@project),
%p.append-bottom-0 method: :post, class: "btn btn-default"
%section.settings.advanced-settings
.settings-header
%h4
Advanced settings
%button.btn.js-settings-toggle
= expanded ? 'Collapse' : 'Expand'
%p
Perform advanced options such as housekeeping, exporting, archiveing, renameing, transfering, or removeing your project.
.settings-content.no-animate{ class: ('expanded' if expanded) }
.sub-section
%h4 Housekeeping
%p
Runs a number of housekeeping tasks within the current repository, such as compressing file revisions and removing unreachable objects.
= link_to 'Run housekeeping', housekeeping_project_path(@project),
method: :post, class: "btn btn-default"
- if can? current_user, :archive_project, @project
.sub-section
%h4.warning-title
- if @project.archived?
Unarchive project
- else
Archive project
- if @project.archived? - if @project.archived?
Unarchiving the project will mark its repository as active. The project can be committed to. %p
Unarchiving the project will mark its repository as active. The project can be committed to.
%strong Once active this project shows up in the search and on the dashboard.
= link_to 'Unarchive project', unarchive_project_path(@project),
data: { confirm: "Are you sure that you want to unarchive this project?\nWhen this project is unarchived it is active and can be committed to again." },
method: :post, class: "btn btn-success"
- else - else
Archiving the project will mark its repository as read-only. It is hidden from the dashboard and doesn't show up in searches. %p
.col-lg-8 Archiving the project will mark its repository as read-only. It is hidden from the dashboard and doesn't show up in searches.
- if @project.archived? %strong Archived projects cannot be committed to!
%p = link_to 'Archive project', archive_project_path(@project),
%strong Once active this project shows up in the search and on the dashboard. data: { confirm: "Are you sure that you want to archive this project?\nAn archived project cannot be committed to." },
= link_to 'Unarchive project', unarchive_project_path(@project), method: :post, class: "btn btn-warning"
data: { confirm: "Are you sure that you want to unarchive this project?\nWhen this project is unarchived it is active and can be committed to again." }, .sub-section.rename-respository
method: :post, class: "btn btn-success" %h4.warning-title
- else Rename repository
%p %p
%strong Archived projects cannot be committed to! Export this project with all its related data in order to move your project to a new GitLab instance. Once the export is finished, you can import the file from the "New Project" page.
= link_to 'Archive project', archive_project_path(@project), = render 'projects/errors'
data: { confirm: "Are you sure that you want to archive this project?\nAn archived project cannot be committed to." }, = form_for([@project.namespace.becomes(Namespace), @project]) do |f|
method: :post, class: "btn btn-warning" .form-group.project_name_holder
%hr = f.label :name, class: 'label-light' do
.row.prepend-top-default Project name
.col-lg-4 .form-group
%h4.prepend-top-0.warning-title = f.text_field :name, class: "form-control"
Rename repository
.col-lg-8
= render 'projects/errors'
= form_for([@project.namespace.becomes(Namespace), @project]) do |f|
.form-group.project_name_holder
= f.label :name, class: 'label-light' do
Project name
.form-group
= f.text_field :name, class: "form-control"
.form-group
= f.label :path, class: 'label-light' do
%span Path
.form-group
.input-group
.input-group-addon
#{URI.join(root_url, @project.namespace.full_path)}/
= f.text_field :path, class: 'form-control'
%ul
%li Be careful. Renaming a project's repository can have unintended side effects.
%li You will need to update your local repositories to point to the new location.
- if @project.deployment_services.any?
%li Your deployment services will be broken, you will need to manually fix the services after renaming.
= f.submit 'Rename project', class: "btn btn-warning"
- if can?(current_user, :change_namespace, @project)
%hr
.row.prepend-top-default
.col-lg-4
%h4.prepend-top-0.danger-title
Transfer project to new group
%p.append-bottom-0
Please select the group you want to transfer this project to in the dropdown to the right.
.col-lg-8
= form_for([@project.namespace.becomes(Namespace), @project], url: transfer_project_path(@project), method: :put, remote: true, html: { class: 'js-project-transfer-form' } ) do |f|
.form-group .form-group
= label_tag :new_namespace_id, nil, class: 'label-light' do = f.label :path, class: 'label-light' do
%span Select a new namespace %span Path
.form-group .form-group
= select_tag :new_namespace_id, namespaces_options(nil), include_blank: true, class: 'select2' .input-group
.input-group-addon
#{URI.join(root_url, @project.namespace.full_path)}/
= f.text_field :path, class: 'form-control'
%ul %ul
%li Be careful. Changing the project's namespace can have unintended side effects. %li Be careful. Renaming a project's repository can have unintended side effects.
%li You can only transfer the project to namespaces you manage.
%li You will need to update your local repositories to point to the new location. %li You will need to update your local repositories to point to the new location.
%li Project visibility level will be changed to match namespace rules when transfering to a group. - if @project.deployment_services.any?
= f.submit 'Transfer project', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => transfer_project_message(@project) } %li Your deployment services will be broken, you will need to manually fix the services after renaming.
- if @project.forked? && can?(current_user, :remove_fork_project, @project) = f.submit 'Rename project', class: "btn btn-warning"
%hr - if can?(current_user, :change_namespace, @project)
.row.prepend-top-default.append-bottom-default .sub-section
.col-lg-4 %h4.danger-title
%h4.prepend-top-0.danger-title Transfer project
Remove fork relationship = form_for([@project.namespace.becomes(Namespace), @project], url: transfer_project_path(@project), method: :put, remote: true, html: { class: 'js-project-transfer-form' } ) do |f|
%p.append-bottom-0 .form-group
= label_tag :new_namespace_id, nil, class: 'label-light' do
%span Select a new namespace
.form-group
= select_tag :new_namespace_id, namespaces_options(nil), include_blank: true, class: 'select2'
%ul
%li Be careful. Changing the project's namespace can have unintended side effects.
%li You can only transfer the project to namespaces you manage.
%li You will need to update your local repositories to point to the new location.
%li Project visibility level will be changed to match namespace rules when transfering to a group.
= f.submit 'Transfer project', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => transfer_project_message(@project) }
- if @project.forked? && can?(current_user, :remove_fork_project, @project)
.sub-section
%h4.danger-title
Remove fork relationship
%p %p
This will remove the fork relationship to source project This will remove the fork relationship to source project
= succeed "." do = succeed "." do
= link_to @project.forked_from_project.name_with_namespace, project_path(@project.forked_from_project) = link_to @project.forked_from_project.name_with_namespace, project_path(@project.forked_from_project)
.col-lg-8 = form_for([@project.namespace.becomes(Namespace), @project], url: remove_fork_project_path(@project), method: :delete, remote: true, html: { class: 'transfer-project' }) do |f|
= form_for([@project.namespace.becomes(Namespace), @project], url: remove_fork_project_path(@project), method: :delete, remote: true, html: { class: 'transfer-project' }) do |f| %p
%p %strong Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source.
%strong Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source. = button_to 'Remove fork relationship', '#', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => remove_fork_project_message(@project) }
= button_to 'Remove fork relationship', '#', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => remove_fork_project_message(@project) } - if can?(current_user, :remove_project, @project)
- if can?(current_user, :remove_project, @project) .sub-section
%hr %h4.danger-title
.row.prepend-top-default.append-bottom-default Remove project
.col-lg-4
%h4.prepend-top-0.danger-title
Remove project
%p.append-bottom-0
Removing the project will delete its repository and all related resources including issues, merge requests etc.
.col-lg-8
= form_tag(project_path(@project), method: :delete) do
%p %p
%strong Removed projects cannot be restored! Removing the project will delete its repository and all related resources including issues, merge requests etc.
= button_to 'Remove project', '#', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => remove_project_message(@project) } = form_tag(project_path(@project), method: :delete) do
%p
%strong Removed projects cannot be restored!
= button_to 'Remove project', '#', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => remove_project_message(@project) }
.save-project-loader.hide .save-project-loader.hide
.center .center
......
- if @project.feature_available?(:issuable_default_templates)
%fieldset.features.append-bottom-0.issues-feature
%hr
%h5.prepend-top-0
Issues
.form-group
= form.label :issues_template, class: 'label-light' do
Default description template for issues
= link_to icon('question-circle'), help_page_path('user/project/description_templates', anchor: 'setting-a-default-template-for-issues-and-merge-requests'), target: '_blank'
= form.text_area :issues_template, class: "form-control", rows: 3
.hint
Description parsed with #{link_to "GitLab Flavored Markdown", help_page_path('user/markdown'), target: '_blank'}.
---
title: Rename Pipelines tab to CI / CD in new navigation
merge_request:
author:
...@@ -25,19 +25,32 @@ module Gitlab ...@@ -25,19 +25,32 @@ module Gitlab
# https://github.com/rails/rails/blob/v4.2.6/railties/lib/rails/engine.rb#L687 # https://github.com/rails/rails/blob/v4.2.6/railties/lib/rails/engine.rb#L687
# This is a nice reference article on autoloading/eager loading: # This is a nice reference article on autoloading/eager loading:
# http://blog.arkency.com/2014/11/dont-forget-about-eager-load-when-extending-autoload # http://blog.arkency.com/2014/11/dont-forget-about-eager-load-when-extending-autoload
config.eager_load_paths.push(*%W(#{config.root}/lib config.eager_load_paths.push(*%W[#{config.root}/lib
#{config.root}/app/models/hooks #{config.root}/app/models/hooks
#{config.root}/app/models/members #{config.root}/app/models/members
#{config.root}/app/models/project_services #{config.root}/app/models/project_services
#{config.root}/app/workers/concerns #{config.root}/app/workers/concerns
#{config.root}/app/services/concerns #{config.root}/app/services/concerns
#{config.root}/app/uploaders/concerns #{config.root}/app/finders/concerns])
#{config.root}/app/finders/concerns))
config.generators.templates.push("#{config.root}/generator_templates") config.generators.templates.push("#{config.root}/generator_templates")
# EE specific paths. # EE specific paths.
config.eager_load_paths.push("#{config.root}/app/workers/concerns") config.eager_load_paths.push(*%W[
#{config.root}/ee/lib
#{config.root}/ee/app/controllers
#{config.root}/ee/app/helpers
#{config.root}/ee/app/mailers
#{config.root}/ee/app/models
#{config.root}/ee/app/models/concerns
#{config.root}/ee/app/policies
#{config.root}/ee/app/services
#{config.root}/ee/app/workers
])
config.paths['app/views'].push(*%W[
#{config.root}/ee/app/views
])
# Only load the plugins named here, in the order given (default is alphabetical). # Only load the plugins named here, in the order given (default is alphabetical).
# :all can be used as a placeholder for all plugins not explicitly named. # :all can be used as a placeholder for all plugins not explicitly named.
......
require_dependency Rails.root.join('lib/gitlab') # Load Gitlab as soon as possible require_dependency Rails.root.join('lib/gitlab') # Load Gitlab as soon as possible
require_dependency Rails.root.join('lib/ee') # Load EE as soon as possible
class Settings < Settingslogic class Settings < Settingslogic
source ENV.fetch('GITLAB_CONFIG') { "#{Rails.root}/config/gitlab.yml" } source ENV.fetch('GITLAB_CONFIG') { "#{Rails.root}/config/gitlab.yml" }
......
...@@ -66,9 +66,9 @@ var config = { ...@@ -66,9 +66,9 @@ var config = {
project_new: './projects/project_new.js', project_new: './projects/project_new.js',
prometheus_metrics: './prometheus_metrics', prometheus_metrics: './prometheus_metrics',
protected_branches: './protected_branches', protected_branches: './protected_branches',
ee_protected_branches: './protected_branches/ee', ee_protected_branches: 'ee/protected_branches',
protected_tags: './protected_tags', protected_tags: './protected_tags',
ee_protected_tags: './protected_tags/ee', ee_protected_tags: 'ee/protected_tags',
service_desk: './projects/settings_service_desk/service_desk_bundle.js', service_desk: './projects/settings_service_desk/service_desk_bundle.js',
sidebar: './sidebar/sidebar_bundle.js', sidebar: './sidebar/sidebar_bundle.js',
schedule_form: './pipeline_schedules/pipeline_schedule_form_bundle.js', schedule_form: './pipeline_schedules/pipeline_schedule_form_bundle.js',
...@@ -214,6 +214,7 @@ var config = { ...@@ -214,6 +214,7 @@ var config = {
resolve: { resolve: {
extensions: ['.js'], extensions: ['.js'],
alias: { alias: {
'ee': path.join(ROOT_PATH, 'ee/app/assets/javascripts'),
'~': path.join(ROOT_PATH, 'app/assets/javascripts'), '~': path.join(ROOT_PATH, 'app/assets/javascripts'),
'emojis': path.join(ROOT_PATH, 'fixtures/emojis'), 'emojis': path.join(ROOT_PATH, 'fixtures/emojis'),
'empty_states': path.join(ROOT_PATH, 'app/views/shared/empty_states'), 'empty_states': path.join(ROOT_PATH, 'app/views/shared/empty_states'),
......
...@@ -17,19 +17,19 @@ as much as possible. ...@@ -17,19 +17,19 @@ as much as possible.
Place EE-specific controllers, finders, helpers, mailers, models, policies, Place EE-specific controllers, finders, helpers, mailers, models, policies,
serializers/entities, services, validators and workers in the top-level serializers/entities, services, validators and workers in the top-level
`EE` module namespace, and in a specific `/ee/` sub-folder: `EE` module namespace, and in the `ee/` specific sub-directory:
- `app/controllers/ee/foos_controller.rb` - `ee/app/controllers/ee/foos_controller.rb`
- `app/finders/ee/foos_finder.rb` - `ee/app/finders/ee/foos_finder.rb`
- `app/helpers/ee/foos_helper.rb` - `ee/app/helpers/ee/foos_helper.rb`
- `app/mailers/ee/foos_mailer.rb` - `ee/app/mailers/ee/foos_mailer.rb`
- `app/models/ee/foo.rb` - `ee/app/models/ee/foo.rb`
- `app/policies/ee/foo_policy.rb` - `ee/app/policies/ee/foo_policy.rb`
- `app/serializers/ee/foo_entity.rb` - `ee/app/serializers/ee/foo_entity.rb`
- `app/serializers/ee/foo_serializer.rb` - `ee/app/serializers/ee/foo_serializer.rb`
- `app/services/ee/foo/create_service.rb` - `ee/app/services/ee/foo/create_service.rb`
- `app/validators/ee/foo_attr_validator.rb` - `ee/app/validators/ee/foo_attr_validator.rb`
- `app/workers/ee/foo_worker.rb` - `ee/app/workers/ee/foo_worker.rb`
If you modify an existing part of a CE controller, model, service, worker etc. If you modify an existing part of a CE controller, model, service, worker etc.
one simple solution is to use the `prepend` strategy ([presented below](#overriding-ce-methods)). one simple solution is to use the `prepend` strategy ([presented below](#overriding-ce-methods)).
...@@ -58,7 +58,7 @@ class ApplicationController < ActionController::Base ...@@ -58,7 +58,7 @@ class ApplicationController < ActionController::Base
end end
module EE module EE
class ApplicationController class ApplicationController
def after_sign_out_path_for(resource) def after_sign_out_path_for(resource)
raise NotImplementedError unless defined?(super) raise NotImplementedError unless defined?(super)
...@@ -117,7 +117,7 @@ end ...@@ -117,7 +117,7 @@ end
EE-specific models should `extend EE::Model`. EE-specific models should `extend EE::Model`.
For example, if EE has a specific `Tanuki` model, you would For example, if EE has a specific `Tanuki` model, you would
place it in `app/models/ee/tanuki.rb`. place it in `ee/app/models/ee/tanuki.rb`.
#### Code in `app/views/` #### Code in `app/views/`
...@@ -136,7 +136,7 @@ Place EE-specific logic in the top-level `EE` module namespace. Namespace the ...@@ -136,7 +136,7 @@ Place EE-specific logic in the top-level `EE` module namespace. Namespace the
class beneath the `EE` module just as you would normally. class beneath the `EE` module just as you would normally.
For example, if CE has LDAP classes in `lib/gitlab/ldap/` then you would place For example, if CE has LDAP classes in `lib/gitlab/ldap/` then you would place
EE-specific LDAP classes in `lib/ee/gitlab/ldap`. EE-specific LDAP classes in `ee/lib/ee/gitlab/ldap`.
### Classes vs. Module Mixins ### Classes vs. Module Mixins
......
...@@ -11,7 +11,7 @@ See [our current .eslintrc][eslintrc] for specific rules and patterns. ...@@ -11,7 +11,7 @@ See [our current .eslintrc][eslintrc] for specific rules and patterns.
#### ESlint #### ESlint
1. **Never** disable eslint rules unless you have a good reason. 1. **Never** disable eslint rules unless you have a good reason.
You may see a lot of legacy files with `/* eslint-disable some-rule, some-other-rule */` You may see a lot of legacy files with `/* eslint-disable some-rule, some-other-rule */`
at the top, but legacy files are a special case. Any time you develop a new feature or at the top, but legacy files are a special case. Any time you develop a new feature or
refactor an existing one, you should abide by the eslint rules. refactor an existing one, you should abide by the eslint rules.
...@@ -100,26 +100,44 @@ followed by any global declarations, then a blank newline prior to any imports o ...@@ -100,26 +100,44 @@ followed by any global declarations, then a blank newline prior to any imports o
export default Foo; export default Foo;
``` ```
1. Relative paths: Unless you are writing a test, always reference other scripts using 1. Relative paths: when importing a module in the same directory, a child
relative paths instead of `~` directory, or an immediate parent directory prefer relative paths. When
* In **app/assets/javascripts**: importing a module which is two or more levels up, prefer either `~/` or `ee/`
.
```javascript In **app/assets/javascripts/my-feature/subdir**:
// bad
import Foo from '~/foo'
// good ``` javascript
import Foo from '../foo'; // bad
``` import Foo from '~/my-feature/foo';
* In **spec/javascripts**: import Bar from '~/my-feature/subdir/bar';
import Bin from '~/my-feature/subdir/lib/bin';
```javascript // good
// bad import Foo from '../foo';
import Foo from '../../app/assets/javascripts/foo' import Bar from './bar';
import Bin from './lib/bin';
```
// good In **spec/javascripts**:
import Foo from '~/foo';
``` ``` javascript
// bad
import Foo from '../../app/assets/javascripts/my-feature/foo';
// good
import Foo from '~/my-feature/foo';
```
When referencing an **EE component**:
``` javascript
// bad
import Foo from '../../../../../ee/app/assets/javascripts/my-feature/ee-foo';
// good
import Foo from 'ee/my-feature/foo';
```
1. Avoid using IIFE. Although we have a lot of examples of files which wrap their 1. Avoid using IIFE. Although we have a lot of examples of files which wrap their
contents in IIFEs (immediately-invoked function expressions), contents in IIFEs (immediately-invoked function expressions),
...@@ -465,7 +483,7 @@ A forEach will cause side effects, it will be mutating the array being iterated. ...@@ -465,7 +483,7 @@ A forEach will cause side effects, it will be mutating the array being iterated.
#### Vue and Boostrap #### Vue and Boostrap
1. Tooltips: Do not rely on `has-tooltip` class name for Vue components 1. Tooltips: Do not rely on `has-tooltip` class name for Vue components
```javascript ```javascript
// bad // bad
<span <span
class="has-tooltip" class="has-tooltip"
......
# Repository mirroring # Repository mirroring
>[Introduced][ee-51] in GitLab Enterprise Edition 8.2.
Repository Mirroring is a way to mirror repositories from external sources. Repository Mirroring is a way to mirror repositories from external sources.
It can be used to mirror all branches, tags, and commits that you have It can be used to mirror all branches, tags, and commits that you have
...@@ -50,6 +49,8 @@ A few things/limitations to consider: ...@@ -50,6 +49,8 @@ A few things/limitations to consider:
## Pulling from a remote repository ## Pulling from a remote repository
>[Introduced][ee-51] in GitLab Enterprise Edition 8.2.
You can set up a repository to automatically have its branches, tags, and commits You can set up a repository to automatically have its branches, tags, and commits
updated from an upstream repository. This is useful when a repository you're updated from an upstream repository. This is useful when a repository you're
interested in is located on a different server, and you want to be able to interested in is located on a different server, and you want to be able to
...@@ -97,6 +98,8 @@ backoff period will be penalized each time it fails up to a maximum amount of ti ...@@ -97,6 +98,8 @@ backoff period will be penalized each time it fails up to a maximum amount of ti
## Pushing to a remote repository ## Pushing to a remote repository
>[Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/249) in GitLab Enterprise Edition 8.7.
For an existing project, you can set up mirror pushing by visiting your project's For an existing project, you can set up mirror pushing by visiting your project's
**Settings ➔ Repository** and searching for the "Push to a remote repository" **Settings ➔ Repository** and searching for the "Push to a remote repository"
section. Check the "Remote mirror repository" box and fill in the Git URL of the section. Check the "Remote mirror repository" box and fill in the Git URL of the
......
/* global Flash */ /* global Flash */
import MRWidgetAuthor from '../../../components/mr_widget_author'; import MRWidgetAuthor from '~/vue_merge_request_widget/components/mr_widget_author';
import eventHub from '../../../event_hub'; import eventHub from '~/vue_merge_request_widget/event_hub';
export default { export default {
name: 'approvals-body', name: 'approvals-body',
......
/* global Flash */ /* global Flash */
import LinkToMemberAvatar from '~/vue_shared/components/link_to_member_avatar'; import LinkToMemberAvatar from '~/vue_shared/components/link_to_member_avatar';
import eventHub from '../../../event_hub'; import eventHub from '~/vue_merge_request_widget/event_hub';
export default { export default {
name: 'approvals-footer', name: 'approvals-footer',
......
<script> <script>
import successIcon from 'icons/_icon_status_success.svg'; import successIcon from 'icons/_icon_status_success.svg';
import errorIcon from 'icons/_icon_status_failed.svg'; import errorIcon from 'icons/_icon_status_failed.svg';
import loadingIcon from '~/vue_shared/components/loading_icon.vue';
import '~/lib/utils/text_utility';
import issuesBlock from './mr_widget_code_quality_issues.vue'; import issuesBlock from './mr_widget_code_quality_issues.vue';
import loadingIcon from '../../../vue_shared/components/loading_icon.vue';
import '../../../lib/utils/text_utility';
export default { export default {
name: 'MRWidgetCodeQuality', name: 'MRWidgetCodeQuality',
......
import eventHub from '../../../event_hub'; import eventHub from '~/vue_merge_request_widget/event_hub';
import ReadyToMergeState from '../../../components/states/mr_widget_ready_to_merge'; import ReadyToMergeState from '~/vue_merge_request_widget/components/states/mr_widget_ready_to_merge';
import SquashBeforeMerge from './mr_widget_squash_before_merge'; import SquashBeforeMerge from './mr_widget_squash_before_merge';
export default { export default {
......
/* global Flash */ /* global Flash */
import simplePoll from '~/lib/utils/simple_poll'; import simplePoll from '~/lib/utils/simple_poll';
import eventHub from '../../../event_hub'; import eventHub from '~/vue_merge_request_widget/event_hub';
export default { export default {
props: { props: {
......
import eventHub from '../../../event_hub'; import eventHub from '~/vue_merge_request_widget/event_hub';
import CESquashBeforeMerge from '../../components/states/mr_widget_squash_before_merge'; import CESquashBeforeMerge from '~/vue_merge_request_widget/components/states/mr_widget_squash_before_merge';
export default { export default {
extends: CESquashBeforeMerge, extends: CESquashBeforeMerge,
......
import CEWidgetOptions from '../mr_widget_options'; import CEWidgetOptions from '~/vue_merge_request_widget/mr_widget_options';
import WidgetApprovals from './components/approvals/mr_widget_approvals'; import WidgetApprovals from './components/approvals/mr_widget_approvals';
import GeoSecondaryNode from './components/states/mr_widget_secondary_geo_node'; import GeoSecondaryNode from './components/states/mr_widget_secondary_geo_node';
import RebaseState from './components/states/mr_widget_rebase'; import RebaseState from './components/states/mr_widget_rebase';
......
import Vue from 'vue'; import Vue from 'vue';
import CEWidgetService from '../../services/mr_widget_service'; import CEWidgetService from '~/vue_merge_request_widget/services/mr_widget_service';
export default class MRWidgetService extends CEWidgetService { export default class MRWidgetService extends CEWidgetService {
constructor(mr) { constructor(mr) {
......
import CEGetStateKey from '../../stores/get_state_key'; import CEGetStateKey from '~/vue_merge_request_widget/stores/get_state_key';
export default function (data) { export default function (data) {
if (this.isGeoSecondaryNode) { if (this.isGeoSecondaryNode) {
......
import CEMergeRequestStore from '../../stores/mr_widget_store'; import CEMergeRequestStore from '~/vue_merge_request_widget/stores/mr_widget_store';
export default class MergeRequestStore extends CEMergeRequestStore { export default class MergeRequestStore extends CEMergeRequestStore {
constructor(data) { constructor(data) {
......
import stateMaps from '../../stores/state_maps'; import stateMaps from '~/vue_merge_request_widget/stores/state_maps';
stateMaps.stateToComponentMap.geoSecondaryNode = 'mr-widget-geo-secondary-node'; stateMaps.stateToComponentMap.geoSecondaryNode = 'mr-widget-geo-secondary-node';
stateMaps.stateToComponentMap.rebase = 'mr-widget-rebase'; stateMaps.stateToComponentMap.rebase = 'mr-widget-rebase';
......
...@@ -2,6 +2,8 @@ class Admin::GeoNodesController < Admin::ApplicationController ...@@ -2,6 +2,8 @@ class Admin::GeoNodesController < Admin::ApplicationController
before_action :check_license, except: [:index, :destroy] before_action :check_license, except: [:index, :destroy]
before_action :load_node, only: [:edit, :update, :destroy, :repair, :toggle, :status] before_action :load_node, only: [:edit, :update, :destroy, :repair, :toggle, :status]
helper EE::GeoHelper
def index def index
@nodes = GeoNode.all.order(:id) @nodes = GeoNode.all.order(:id)
@node = GeoNode.new @node = GeoNode.new
......
- if @project.feature_available?(:issuable_default_templates)
- expanded = Rails.env.test?
%section.settings.issues-feature{ style: ("display:none;" if @project.project_feature.send(:issues_access_level) == 0) }
.settings-header
%h4
Issue settings
%button.btn.js-settings-toggle
= expanded ? 'Collapse' : 'Expand'
%p
Customize your issue restrictions.
.settings-content.no-animate{ class: ('expanded' if expanded) }
= form_for [@project.namespace.becomes(Namespace), @project], remote: true, html: { multipart: true, class: "issue-settings-form" }, authenticity_token: true do |f|
.form-group
= f.label :issues_template, class: 'label-light' do
Default description template for issues
= link_to icon('question-circle'), help_page_path('user/project/description_templates', anchor: 'setting-a-default-template-for-issues-and-merge-requests'), target: '_blank'
= f.text_area :issues_template, class: "form-control", rows: 3
.hint
Description parsed with #{link_to "GitLab Flavored Markdown", help_page_path('user/markdown'), target: '_blank'}.
= f.submit 'Save changes', class: "btn btn-save"
...@@ -11,7 +11,9 @@ class Spinach::Features::Project < Spinach::FeatureSteps ...@@ -11,7 +11,9 @@ class Spinach::Features::Project < Spinach::FeatureSteps
end end
step 'I save project' do step 'I save project' do
click_button 'Save changes' page.within '.general-settings' do
click_button 'Save changes'
end
end end
step 'I should see project with new settings' do step 'I should see project with new settings' do
...@@ -32,7 +34,9 @@ class Spinach::Features::Project < Spinach::FeatureSteps ...@@ -32,7 +34,9 @@ class Spinach::Features::Project < Spinach::FeatureSteps
:project_avatar, :project_avatar,
File.join(Rails.root, 'spec', 'fixtures', 'banana_sample.gif') File.join(Rails.root, 'spec', 'fixtures', 'banana_sample.gif')
) )
click_button 'Save changes' page.within '.general-settings' do
click_button 'Save changes'
end
@project.reload @project.reload
end end
...@@ -51,7 +55,9 @@ class Spinach::Features::Project < Spinach::FeatureSteps ...@@ -51,7 +55,9 @@ class Spinach::Features::Project < Spinach::FeatureSteps
:project_avatar, :project_avatar,
File.join(Rails.root, 'spec', 'fixtures', 'banana_sample.gif') File.join(Rails.root, 'spec', 'fixtures', 'banana_sample.gif')
) )
click_button 'Save changes' page.within '.general-settings' do
click_button 'Save changes'
end
@project.reload @project.reload
end end
...@@ -92,7 +98,9 @@ class Spinach::Features::Project < Spinach::FeatureSteps ...@@ -92,7 +98,9 @@ class Spinach::Features::Project < Spinach::FeatureSteps
step 'change project default branch' do step 'change project default branch' do
select 'fix', from: 'project_default_branch' select 'fix', from: 'project_default_branch'
click_button 'Save changes' page.within '.general-settings' do
click_button 'Save changes'
end
end end
step 'I should see project default branch changed' do step 'I should see project default branch changed' do
......
...@@ -103,7 +103,7 @@ module SharedProject ...@@ -103,7 +103,7 @@ module SharedProject
step 'I should see project settings' do step 'I should see project settings' do
expect(current_path).to eq edit_project_path(@project) expect(current_path).to eq edit_project_path(@project)
expect(page).to have_content("Project name") expect(page).to have_content("Project name")
expect(page).to have_content("Sharing & Permissions") expect(page).to have_content("Sharing and permissions")
end end
def current_project def current_project
......
# Define the EE module
module EE
end
...@@ -64,6 +64,8 @@ module Gitlab ...@@ -64,6 +64,8 @@ module Gitlab
# Update repository # Update repository
if event.repository_updated_event if event.repository_updated_event
handle_repository_update(event.repository_updated_event) handle_repository_update(event.repository_updated_event)
elsif event.repository_deleted_event
handle_repository_delete(event.repository_deleted_event)
end end
end end
end end
...@@ -108,6 +110,25 @@ module Gitlab ...@@ -108,6 +110,25 @@ module Gitlab
registry.save! registry.save!
end end
def handle_repository_delete(deleted_event)
# Once we remove system hooks we can refactor
# GeoRepositoryDestroyWorker to avoid doing this
full_path = File.join(deleted_event.repository_storage_path,
deleted_event.deleted_path)
job_id = ::GeoRepositoryDestroyWorker.perform_async(
deleted_event.project_id,
deleted_event.deleted_project_name,
full_path)
Gitlab::Geo::Logger.info(
class: self.class.name,
message: "Deleted project",
project_id: deleted_event.project_id,
full_path: full_path,
job_id: job_id)
# No need to create a project entry if it doesn't exist
::Geo::ProjectRegistry.where(project_id: deleted_event.project_id).delete_all
end
def exit? def exit?
@exit @exit
end end
......
{ {
"allOf": [ "allOf": [
{ "$ref": "../issues.json" }, { "$ref": "../../../../../../fixtures/api/schemas/public_api/v4/issues.json" },
{ {
"properties": { "properties": {
"weight": { "type": ["integer", "null"] } "weight": { "type": ["integer", "null"] }
......
require 'spec_helper' require 'spec_helper'
require_relative '../../email_shared_blocks' require Rails.root.join('spec/lib/gitlab/email/email_shared_blocks')
describe Gitlab::Email::Handler::EE::ServiceDeskHandler do describe Gitlab::Email::Handler::EE::ServiceDeskHandler do
include_context :email_shared_context include_context :email_shared_context
......
require 'spec_helper' require 'spec_helper'
describe Gitlab::VisibilityLevel do # rubocop:disable RSpec/FilePath describe Gitlab::VisibilityLevel do
describe '.levels_for_user' do describe '.levels_for_user' do
it 'returns all levels for an auditor' do it 'returns all levels for an auditor' do
user = build(:user, :auditor) user = build(:user, :auditor)
......
...@@ -34,7 +34,7 @@ describe API::Issues, :mailer do # rubocop:disable RSpec/FilePath ...@@ -34,7 +34,7 @@ describe API::Issues, :mailer do # rubocop:disable RSpec/FilePath
get api('/issues', user) get api('/issues', user)
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
expect(response).to match_response_schema('public_api/v4/ee/issues') expect(response).to match_response_schema('public_api/v4/issues', dir: 'ee')
end end
end end
end end
......
FactoryGirl.define do FactoryGirl.define do
factory :geo_event_log, class: Geo::EventLog do factory :geo_event_log, class: Geo::EventLog do
repository_updated_event factory: :geo_repository_update_event trait :updated_event do
repository_updated_event factory: :geo_repository_update_event
end
trait :deleted_event do
repository_deleted_event factory: :geo_repository_delete_event
end
end end
factory :geo_repository_update_event, class: Geo::RepositoryUpdatedEvent do factory :geo_repository_update_event, class: Geo::RepositoryUpdatedEvent do
...@@ -9,4 +15,13 @@ FactoryGirl.define do ...@@ -9,4 +15,13 @@ FactoryGirl.define do
tags_affected 0 tags_affected 0
project project
end end
factory :geo_repository_delete_event, class: Geo::RepositoryDeletedEvent do
project
repository_storage_name { project.repository_storage }
repository_storage_path { project.repository_storage_path }
deleted_path { project.path_with_namespace }
deleted_project_name { project.name }
end
end end
...@@ -20,7 +20,10 @@ feature 'Project edit', js: true do ...@@ -20,7 +20,10 @@ feature 'Project edit', js: true do
it 'allows user to change request access settings' do it 'allows user to change request access settings' do
find('#project_request_access_enabled').set(true) find('#project_request_access_enabled').set(true)
click_button 'Save changes' page.within('.sharing-permissions') do
click_button 'Save changes'
end
wait_for_requests wait_for_requests
expect(find('#project_request_access_enabled')).to be_checked expect(find('#project_request_access_enabled')).to be_checked
......
...@@ -20,21 +20,25 @@ describe 'Edit Project Settings' do ...@@ -20,21 +20,25 @@ describe 'Edit Project Settings' do
visit edit_project_path(project) visit edit_project_path(project)
select 'Disabled', from: "project_project_feature_attributes_#{tool_name}_access_level" select 'Disabled', from: "project_project_feature_attributes_#{tool_name}_access_level"
click_button 'Save changes' page.within('.sharing-permissions') do
click_button 'Save changes'
end
wait_for_requests wait_for_requests
expect(page).not_to have_selector(".shortcuts-#{shortcut_name}") expect(page).not_to have_selector(".shortcuts-#{shortcut_name}")
select 'Everyone with access', from: "project_project_feature_attributes_#{tool_name}_access_level" select 'Everyone with access', from: "project_project_feature_attributes_#{tool_name}_access_level"
click_button 'Save changes' page.within('.sharing-permissions') do
click_button 'Save changes'
end
wait_for_requests wait_for_requests
expect(page).to have_selector(".shortcuts-#{shortcut_name}") expect(page).to have_selector(".shortcuts-#{shortcut_name}")
select 'Only team members', from: "project_project_feature_attributes_#{tool_name}_access_level" select 'Only team members', from: "project_project_feature_attributes_#{tool_name}_access_level"
click_button 'Save changes' page.within('.sharing-permissions') do
click_button 'Save changes'
end
wait_for_requests wait_for_requests
expect(page).to have_selector(".shortcuts-#{shortcut_name}") expect(page).to have_selector(".shortcuts-#{shortcut_name}")
sleep 0.1
end end
end end
end end
...@@ -174,7 +178,11 @@ describe 'Edit Project Settings' do ...@@ -174,7 +178,11 @@ describe 'Edit Project Settings' do
it "disables repository related features" do it "disables repository related features" do
select "Disabled", from: "project_project_feature_attributes_repository_access_level" select "Disabled", from: "project_project_feature_attributes_repository_access_level"
expect(find(".edit-project")).to have_selector("select.disabled", count: 2) page.within('.sharing-permissions') do
click_button "Save changes"
end
expect(find(".sharing-permissions")).to have_selector("select.disabled", count: 2)
end end
it "shows empty features project homepage" do it "shows empty features project homepage" do
...@@ -182,7 +190,9 @@ describe 'Edit Project Settings' do ...@@ -182,7 +190,9 @@ describe 'Edit Project Settings' do
select "Disabled", from: "project_project_feature_attributes_issues_access_level" select "Disabled", from: "project_project_feature_attributes_issues_access_level"
select "Disabled", from: "project_project_feature_attributes_wiki_access_level" select "Disabled", from: "project_project_feature_attributes_wiki_access_level"
click_button "Save changes" page.within('.sharing-permissions') do
click_button "Save changes"
end
wait_for_requests wait_for_requests
visit project_path(project) visit project_path(project)
...@@ -195,7 +205,9 @@ describe 'Edit Project Settings' do ...@@ -195,7 +205,9 @@ describe 'Edit Project Settings' do
select "Disabled", from: "project_project_feature_attributes_issues_access_level" select "Disabled", from: "project_project_feature_attributes_issues_access_level"
select "Disabled", from: "project_project_feature_attributes_wiki_access_level" select "Disabled", from: "project_project_feature_attributes_wiki_access_level"
click_button "Save changes" page.within('.sharing-permissions') do
click_button "Save changes"
end
wait_for_requests wait_for_requests
visit activity_project_path(project) visit activity_project_path(project)
...@@ -236,7 +248,9 @@ describe 'Edit Project Settings' do ...@@ -236,7 +248,9 @@ describe 'Edit Project Settings' do
end end
def save_changes_and_check_activity_tab def save_changes_and_check_activity_tab
click_button "Save changes" page.within('.sharing-permissions') do
click_button "Save changes"
end
wait_for_requests wait_for_requests
visit activity_project_path(project) visit activity_project_path(project)
......
...@@ -14,7 +14,9 @@ describe 'Edit Project Settings' do ...@@ -14,7 +14,9 @@ describe 'Edit Project Settings' do
it 'shows errors for invalid project name' do it 'shows errors for invalid project name' do
visit edit_project_path(project) visit edit_project_path(project)
fill_in 'project_name_edit', with: 'foo&bar' fill_in 'project_name_edit', with: 'foo&bar'
click_button 'Save changes' page.within('.general-settings') do
click_button 'Save changes'
end
expect(page).to have_field 'project_name_edit', with: 'foo&bar' expect(page).to have_field 'project_name_edit', with: 'foo&bar'
expect(page).to have_content "Name can contain only letters, digits, emojis, '_', '.', dash, space. It must start with letter, digit, emoji or '_'." expect(page).to have_content "Name can contain only letters, digits, emojis, '_', '.', dash, space. It must start with letter, digit, emoji or '_'."
expect(page).to have_button 'Save changes' expect(page).to have_button 'Save changes'
...@@ -23,7 +25,9 @@ describe 'Edit Project Settings' do ...@@ -23,7 +25,9 @@ describe 'Edit Project Settings' do
it 'shows a successful notice when the project is updated' do it 'shows a successful notice when the project is updated' do
visit edit_project_path(project) visit edit_project_path(project)
fill_in 'project_name_edit', with: 'hello world' fill_in 'project_name_edit', with: 'hello world'
click_button 'Save changes' page.within('.general-settings') do
click_button 'Save changes'
end
expect(page).to have_content "Project 'hello world' was successfully updated." expect(page).to have_content "Project 'hello world' was successfully updated."
end end
end end
......
...@@ -20,6 +20,9 @@ feature 'Project settings > Merge Requests', :js do ...@@ -20,6 +20,9 @@ feature 'Project settings > Merge Requests', :js do
expect(page).to have_content('Only allow merge requests to be merged if all discussions are resolved') expect(page).to have_content('Only allow merge requests to be merged if all discussions are resolved')
select 'Disabled', from: "project_project_feature_attributes_merge_requests_access_level" select 'Disabled', from: "project_project_feature_attributes_merge_requests_access_level"
within('.sharing-permissions-form') do
click_on('Save changes')
end
expect(page).not_to have_content('Only allow merge requests to be merged if the pipeline succeeds') expect(page).not_to have_content('Only allow merge requests to be merged if the pipeline succeeds')
expect(page).not_to have_content('Only allow merge requests to be merged if all discussions are resolved') expect(page).not_to have_content('Only allow merge requests to be merged if all discussions are resolved')
...@@ -37,6 +40,9 @@ feature 'Project settings > Merge Requests', :js do ...@@ -37,6 +40,9 @@ feature 'Project settings > Merge Requests', :js do
expect(page).to have_content('Only allow merge requests to be merged if all discussions are resolved') expect(page).to have_content('Only allow merge requests to be merged if all discussions are resolved')
select 'Everyone with access', from: "project_project_feature_attributes_builds_access_level" select 'Everyone with access', from: "project_project_feature_attributes_builds_access_level"
within('.sharing-permissions-form') do
click_on('Save changes')
end
expect(page).to have_content('Only allow merge requests to be merged if the pipeline succeeds') expect(page).to have_content('Only allow merge requests to be merged if the pipeline succeeds')
expect(page).to have_content('Only allow merge requests to be merged if all discussions are resolved') expect(page).to have_content('Only allow merge requests to be merged if all discussions are resolved')
...@@ -55,6 +61,9 @@ feature 'Project settings > Merge Requests', :js do ...@@ -55,6 +61,9 @@ feature 'Project settings > Merge Requests', :js do
expect(page).not_to have_content('Only allow merge requests to be merged if all discussions are resolved') expect(page).not_to have_content('Only allow merge requests to be merged if all discussions are resolved')
select 'Everyone with access', from: "project_project_feature_attributes_merge_requests_access_level" select 'Everyone with access', from: "project_project_feature_attributes_merge_requests_access_level"
within('.sharing-permissions-form') do
click_on('Save changes')
end
expect(page).to have_content('Only allow merge requests to be merged if the pipeline succeeds') expect(page).to have_content('Only allow merge requests to be merged if the pipeline succeeds')
expect(page).to have_content('Only allow merge requests to be merged if all discussions are resolved') expect(page).to have_content('Only allow merge requests to be merged if all discussions are resolved')
...@@ -73,7 +82,9 @@ feature 'Project settings > Merge Requests', :js do ...@@ -73,7 +82,9 @@ feature 'Project settings > Merge Requests', :js do
scenario 'when unchecked sets :printing_merge_request_link_enabled to false' do scenario 'when unchecked sets :printing_merge_request_link_enabled to false' do
uncheck('project_printing_merge_request_link_enabled') uncheck('project_printing_merge_request_link_enabled')
click_on('Save') within('.merge-request-settings-form') do
click_on('Save changes')
end
# Wait for save to complete and page to reload # Wait for save to complete and page to reload
checkbox = find_field('project_printing_merge_request_link_enabled') checkbox = find_field('project_printing_merge_request_link_enabled')
......
import Vue from 'vue'; import Vue from 'vue';
import ApprovalsBody from '~/vue_merge_request_widget/ee/components/approvals/approvals_body'; import ApprovalsBody from 'ee/vue_merge_request_widget/components/approvals/approvals_body';
(() => { (() => {
gl.ApprovalsStore = { gl.ApprovalsStore = {
......
import Vue from 'vue'; import Vue from 'vue';
import pendingAvatarSvg from 'icons/_icon_dotted_circle.svg'; import pendingAvatarSvg from 'icons/_icon_dotted_circle.svg';
import ApprovalsFooter from '~/vue_merge_request_widget/ee/components/approvals/approvals_footer'; import ApprovalsFooter from 'ee/vue_merge_request_widget/components/approvals/approvals_footer';
(() => { (() => {
gl.ApprovalsStore = { gl.ApprovalsStore = {
......
import Vue from 'vue'; import Vue from 'vue';
import mrWidgetCodeQualityIssues from '~/vue_merge_request_widget/ee/components/mr_widget_code_quality_issues.vue'; import mrWidgetCodeQualityIssues from 'ee/vue_merge_request_widget/components/mr_widget_code_quality_issues.vue';
describe('Merge Request Code Quality Issues', () => { describe('Merge Request Code Quality Issues', () => {
let vm; let vm;
......
import Vue from 'vue'; import Vue from 'vue';
import mrWidgetCodeQuality from '~/vue_merge_request_widget/ee/components/mr_widget_code_quality.vue'; import mrWidgetCodeQuality from 'ee/vue_merge_request_widget/components/mr_widget_code_quality.vue';
import Store from '~/vue_merge_request_widget/ee/stores/mr_widget_store'; import Store from 'ee/vue_merge_request_widget/stores/mr_widget_store';
import Service from '~/vue_merge_request_widget/ee/services/mr_widget_service'; import Service from 'ee/vue_merge_request_widget/services/mr_widget_service';
import mockData, { baseIssues, headIssues } from '../mock_data'; import mockData, { baseIssues, headIssues } from '../mock_data';
describe('Merge Request Code Quality', () => { describe('Merge Request Code Quality', () => {
......
import MergeRequestStore from '~/vue_merge_request_widget/ee/stores/mr_widget_store'; import MergeRequestStore from 'ee/vue_merge_request_widget/stores/mr_widget_store';
import mockData, { headIssues, baseIssues } from '../mock_data'; import mockData, { headIssues, baseIssues } from '../mock_data';
describe('MergeRequestStore', () => { describe('MergeRequestStore', () => {
......
...@@ -22,7 +22,7 @@ describe Gitlab::Geo::LogCursor::Daemon do ...@@ -22,7 +22,7 @@ describe Gitlab::Geo::LogCursor::Daemon do
end end
context 'when processing a repository updated event' do context 'when processing a repository updated event' do
let(:event_log) { create(:geo_event_log) } let(:event_log) { create(:geo_event_log, :updated_event) }
let!(:event_log_state) { create(:geo_event_log_state, event_id: event_log.id - 1) } let!(:event_log_state) { create(:geo_event_log_state, event_id: event_log.id - 1) }
let(:repository_updated_event) { event_log.repository_updated_event } let(:repository_updated_event) { event_log.repository_updated_event }
...@@ -52,5 +52,32 @@ describe Gitlab::Geo::LogCursor::Daemon do ...@@ -52,5 +52,32 @@ describe Gitlab::Geo::LogCursor::Daemon do
expect(registry.reload.resync_wiki).to be true expect(registry.reload.resync_wiki).to be true
end end
end end
context 'when processing a repository deleted event' do
let(:event_log) { create(:geo_event_log, :deleted_event) }
let(:project) { event_log.repository_deleted_event.project }
let!(:event_log_state) { create(:geo_event_log_state, event_id: event_log.id - 1) }
let(:repository_deleted_event) { event_log.repository_deleted_event }
before do
allow(subject).to receive(:exit?).and_return(false, true)
end
it 'does not create a new project registry' do
expect { subject.run! }.not_to change(Geo::ProjectRegistry, :count)
end
it 'schedules a GeoRepositoryDestroyWorker' do
project_id = repository_deleted_event.project_id
project_name = repository_deleted_event.deleted_project_name
full_path = File.join(repository_deleted_event.repository_storage_path,
repository_deleted_event.deleted_path)
expect(::GeoRepositoryDestroyWorker).to receive(:perform_async)
.with(project_id, project_name, full_path)
subject.run!
end
end
end end
end end
...@@ -29,6 +29,9 @@ end ...@@ -29,6 +29,9 @@ end
# require rainbow gem String monkeypatch, so we can test SystemChecks # require rainbow gem String monkeypatch, so we can test SystemChecks
require 'rainbow/ext/string' require 'rainbow/ext/string'
# EE specific support
Dir[Rails.root.join("spec/ee/support/**/*.rb")].each { |f| require f }
# Requires supporting ruby files with custom matchers and macros, etc, # Requires supporting ruby files with custom matchers and macros, etc,
# in spec/support/ and its subdirectories. # in spec/support/ and its subdirectories.
Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f } Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }
......
...@@ -229,7 +229,7 @@ shared_examples_for 'group and project milestones' do |route_definition| ...@@ -229,7 +229,7 @@ shared_examples_for 'group and project milestones' do |route_definition|
get api(issues_route, user) get api(issues_route, user)
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
expect(response).to match_response_schema('public_api/v4/ee/issues') expect(response).to match_response_schema('public_api/v4/issues', dir: 'ee')
end end
it 'returns a 401 error if user not authenticated' do it 'returns a 401 error if user not authenticated' do
......
def schema_path(schema) module SchemaPath
schema_directory = "#{Dir.pwd}/spec/fixtures/api/schemas" def self.expand(schema, dir = '')
"#{schema_directory}/#{schema}.json" Rails.root.join('spec', dir, "fixtures/api/schemas/#{schema}.json").to_s
end
end end
RSpec::Matchers.define :match_response_schema do |schema, **options| RSpec::Matchers.define :match_response_schema do |schema, dir: '', **options|
match do |response| match do |response|
@errors = JSON::Validator.fully_validate(schema_path(schema), response.body, options) @errors = JSON::Validator.fully_validate(
SchemaPath.expand(schema, dir), response.body, options)
@errors.empty? @errors.empty?
end end
failure_message do |response| failure_message do |response|
"didn't match the schema defined by #{schema_path(schema)}" \ "didn't match the schema defined by #{SchemaPath.expand(schema, dir)}" \
" The validation errors were:\n#{@errors.join("\n")}" " The validation errors were:\n#{@errors.join("\n")}"
end end
end end
RSpec::Matchers.define :match_schema do |schema, **options| RSpec::Matchers.define :match_schema do |schema, dir: '', **options|
match do |data| match do |data|
JSON::Validator.validate!(schema_path(schema), data, options) JSON::Validator.validate!(SchemaPath.expand(schema, dir), data, options)
end end
end end
require_relative 'ee/ldap_helpers'
module LdapHelpers module LdapHelpers
include EE::LdapHelpers include EE::LdapHelpers
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment