diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-09-30 18:09:46 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-09-30 18:09:46 +0300 |
commit | dd240e5cc4e0abc4eef8b97962c247dab43e3777 (patch) | |
tree | bc0752959bc0713afb555e358bb43d65cf04f48e /app | |
parent | 6aa5c04c74d2d70ee7d19ef3a155b2def9dd46de (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
36 files changed, 266 insertions, 170 deletions
diff --git a/app/assets/javascripts/alert_management/components/alert_details.vue b/app/assets/javascripts/alert_management/components/alert_details.vue index beb636ee8fa..072ed2fa663 100644 --- a/app/assets/javascripts/alert_management/components/alert_details.vue +++ b/app/assets/javascripts/alert_management/components/alert_details.vue @@ -81,15 +81,12 @@ export default { default: '', }, alertId: { - type: String, default: '', }, projectId: { - type: String, default: '', }, projectIssuesPath: { - type: String, default: '', }, }, diff --git a/app/assets/javascripts/alert_management/components/alert_sidebar.vue b/app/assets/javascripts/alert_management/components/alert_sidebar.vue index 64e4089c85a..41d77716592 100644 --- a/app/assets/javascripts/alert_management/components/alert_sidebar.vue +++ b/app/assets/javascripts/alert_management/components/alert_sidebar.vue @@ -18,7 +18,6 @@ export default { default: '', }, projectId: { - type: String, default: '', }, }, diff --git a/app/assets/javascripts/behaviors/shortcuts/shortcuts.js b/app/assets/javascripts/behaviors/shortcuts/shortcuts.js index 8a8b61a57cd..3cb2d6719c8 100644 --- a/app/assets/javascripts/behaviors/shortcuts/shortcuts.js +++ b/app/assets/javascripts/behaviors/shortcuts/shortcuts.js @@ -117,9 +117,9 @@ export default class Shortcuts { e.preventDefault(); const performanceBarCookieName = 'perf_bar_enabled'; if (parseBoolean(Cookies.get(performanceBarCookieName))) { - Cookies.set(performanceBarCookieName, 'false', { path: '/' }); + Cookies.set(performanceBarCookieName, 'false', { expires: 365, path: '/' }); } else { - Cookies.set(performanceBarCookieName, 'true', { path: '/' }); + Cookies.set(performanceBarCookieName, 'true', { expires: 365, path: '/' }); } refreshCurrentPage(); } diff --git a/app/assets/javascripts/clusters/components/fluentd_output_settings.vue b/app/assets/javascripts/clusters/components/fluentd_output_settings.vue index e6001b11296..7b55efaaccd 100644 --- a/app/assets/javascripts/clusters/components/fluentd_output_settings.vue +++ b/app/assets/javascripts/clusters/components/fluentd_output_settings.vue @@ -1,7 +1,7 @@ <script> import { GlAlert, - GlDeprecatedButton, + GlButton, GlDeprecatedDropdown, GlDeprecatedDropdownItem, GlFormCheckbox, @@ -16,7 +16,7 @@ const { UPDATING, UNINSTALLING, INSTALLING, INSTALLED, UPDATED } = APPLICATION_S export default { components: { GlAlert, - GlDeprecatedButton, + GlButton, GlDeprecatedDropdown, GlDeprecatedDropdownItem, GlFormCheckbox, @@ -221,20 +221,21 @@ export default { <strong>{{ s__('ClusterIntegration|Send Container Network Policies Logs') }}</strong> </gl-form-checkbox> </div> - <div v-if="showButtons" class="mt-3"> - <gl-deprecated-button + <div v-if="showButtons" class="gl-mt-5 gl-display-flex"> + <gl-button ref="saveBtn" - class="mr-1" + class="gl-mr-3" variant="success" + category="primary" :loading="isSaving" :disabled="saveButtonDisabled" @click="updateApplication" > {{ saveButtonLabel }} - </gl-deprecated-button> - <gl-deprecated-button ref="cancelBtn" :disabled="saveButtonDisabled" @click="resetStatus"> + </gl-button> + <gl-button ref="cancelBtn" :disabled="saveButtonDisabled" @click="resetStatus"> {{ __('Cancel') }} - </gl-deprecated-button> + </gl-button> </div> </div> </div> diff --git a/app/assets/javascripts/design_management/pages/index.vue b/app/assets/javascripts/design_management/pages/index.vue index 7bf9454240b..fb6a91abcdc 100644 --- a/app/assets/javascripts/design_management/pages/index.vue +++ b/app/assets/javascripts/design_management/pages/index.vue @@ -113,9 +113,7 @@ export default { return !this.isSaving && !this.hasDesigns; }, isDesignCollectionCopying() { - return ( - this.designCollection && ['PENDING', 'COPYING'].includes(this.designCollection.copyState) - ); + return this.designCollection && this.designCollection.copyState === 'IN_PROGRESS'; }, designDropzoneWrapperClass() { return this.isDesignListEmpty @@ -370,11 +368,11 @@ export default { </gl-alert> <header v-else-if="isDesignCollectionCopying" - class="card gl-p-3" + class="card" data-testid="design-collection-is-copying" > <div class="card-header design-card-header border-bottom-0"> - <div class="card-title gl-my-0 gl-h-7"> + <div class="card-title gl-display-flex gl-align-items-center gl-my-0 gl-h-7"> {{ s__( 'DesignManagement|Your designs are being copied and are on their way… Please refresh to update.', diff --git a/app/assets/javascripts/diffs/components/diff_file_header.vue b/app/assets/javascripts/diffs/components/diff_file_header.vue index 6205d113e5e..e403fc1f432 100644 --- a/app/assets/javascripts/diffs/components/diff_file_header.vue +++ b/app/assets/javascripts/diffs/components/diff_file_header.vue @@ -224,7 +224,7 @@ export default { <a ref="titleWrapper" :v-once="!viewDiffsFileByFile" - class="gl-mr-2" + class="gl-mr-2 gl-text-decoration-none!" :href="titleLink" @click="handleFileNameClick" > diff --git a/app/assets/javascripts/pages/admin/keys/index.js b/app/assets/javascripts/pages/admin/keys/index.js new file mode 100644 index 00000000000..45b83ffcd67 --- /dev/null +++ b/app/assets/javascripts/pages/admin/keys/index.js @@ -0,0 +1,5 @@ +import initConfirmModal from '~/confirm_modal'; + +document.addEventListener('DOMContentLoaded', () => { + initConfirmModal(); +}); diff --git a/app/assets/javascripts/pages/admin/users/keys/index.js b/app/assets/javascripts/pages/admin/users/keys/index.js new file mode 100644 index 00000000000..45b83ffcd67 --- /dev/null +++ b/app/assets/javascripts/pages/admin/users/keys/index.js @@ -0,0 +1,5 @@ +import initConfirmModal from '~/confirm_modal'; + +document.addEventListener('DOMContentLoaded', () => { + initConfirmModal(); +}); diff --git a/app/assets/javascripts/pages/profiles/keys/index.js b/app/assets/javascripts/pages/profiles/keys/index.js index d3dcd21f456..4214d5bffb2 100644 --- a/app/assets/javascripts/pages/profiles/keys/index.js +++ b/app/assets/javascripts/pages/profiles/keys/index.js @@ -1,6 +1,9 @@ +import initConfirmModal from '~/confirm_modal'; import AddSshKeyValidation from '~/profile/add_ssh_key_validation'; document.addEventListener('DOMContentLoaded', () => { + initConfirmModal(); + const input = document.querySelector('.js-add-ssh-key-validation-input'); if (!input) return; diff --git a/app/assets/javascripts/pipeline_new/components/pipeline_new_form.vue b/app/assets/javascripts/pipeline_new/components/pipeline_new_form.vue index c74258d102a..1cec08b93bd 100644 --- a/app/assets/javascripts/pipeline_new/components/pipeline_new_form.vue +++ b/app/assets/javascripts/pipeline_new/components/pipeline_new_form.vue @@ -170,13 +170,13 @@ export default { .map(({ variable_type, key, value }) => ({ variable_type, key, - value, + secret_value: value, })); return axios .post(this.pipelinesPath, { ref: this.refValue, - variables: filteredVariables, + variables_attributes: filteredVariables, }) .then(({ data }) => { redirectTo(`${this.pipelinesPath}/${data.id}`); diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/time_ago.vue b/app/assets/javascripts/pipelines/components/pipelines_list/time_ago.vue index 7d13ee582c6..8de18aef639 100644 --- a/app/assets/javascripts/pipelines/components/pipelines_list/time_ago.vue +++ b/app/assets/javascripts/pipelines/components/pipelines_list/time_ago.vue @@ -1,12 +1,11 @@ <script> -import { GlIcon } from '@gitlab/ui'; +import { GlIcon, GlTooltipDirective } from '@gitlab/ui'; import '~/lib/utils/datetime_utility'; -import tooltip from '~/vue_shared/directives/tooltip'; import timeagoMixin from '~/vue_shared/mixins/timeago'; export default { directives: { - tooltip, + GlTooltip: GlTooltipDirective, }, components: { GlIcon }, mixins: [timeagoMixin], @@ -63,7 +62,7 @@ export default { <gl-icon name="calendar" class="gl-vertical-align-baseline!" aria-hidden="true" /> <time - v-tooltip + v-gl-tooltip :title="tooltipTitle(finishedTime)" data-placement="top" data-container="body" diff --git a/app/assets/javascripts/snippet/snippet_show.js b/app/assets/javascripts/snippet/snippet_show.js index bbddfc579c5..1899ff91f87 100644 --- a/app/assets/javascripts/snippet/snippet_show.js +++ b/app/assets/javascripts/snippet/snippet_show.js @@ -1,21 +1,33 @@ -import LineHighlighter from '~/line_highlighter'; -import BlobViewer from '~/blob/viewer'; -import ZenMode from '~/zen_mode'; import initNotes from '~/init_notes'; -import snippetEmbed from '~/snippet/snippet_embed'; -import { SnippetShowInit } from '~/snippets'; import loadAwardsHandler from '~/awards_handler'; -document.addEventListener('DOMContentLoaded', () => { - if (!gon.features.snippetsVue) { - new LineHighlighter(); // eslint-disable-line no-new - new BlobViewer(); // eslint-disable-line no-new - initNotes(); - new ZenMode(); // eslint-disable-line no-new - snippetEmbed(); - } else { - SnippetShowInit(); - initNotes(); - } - loadAwardsHandler(); -}); +if (!gon.features.snippetsVue) { + const LineHighlighterModule = import('~/line_highlighter'); + const BlobViewerModule = import('~/blob/viewer'); + const ZenModeModule = import('~/zen_mode'); + const SnippetEmbedModule = import('~/snippet/snippet_embed'); + + Promise.all([LineHighlighterModule, BlobViewerModule, ZenModeModule, SnippetEmbedModule]) + .then( + ([ + { default: LineHighlighter }, + { default: BlobViewer }, + { default: ZenMode }, + { default: SnippetEmbed }, + ]) => { + new LineHighlighter(); // eslint-disable-line no-new + new BlobViewer(); // eslint-disable-line no-new + new ZenMode(); // eslint-disable-line no-new + SnippetEmbed(); + }, + ) + .catch(() => {}); +} else { + import('~/snippets') + .then(({ SnippetShowInit }) => { + SnippetShowInit(); + }) + .catch(() => {}); +} +initNotes(); +loadAwardsHandler(); diff --git a/app/assets/javascripts/snippets/index.js b/app/assets/javascripts/snippets/index.js index 16635a081a2..d3caec42ce7 100644 --- a/app/assets/javascripts/snippets/index.js +++ b/app/assets/javascripts/snippets/index.js @@ -3,8 +3,6 @@ import VueApollo from 'vue-apollo'; import Translate from '~/vue_shared/translate'; import createDefaultClient from '~/lib/graphql'; -import SnippetsShow from './components/show.vue'; -import SnippetsEdit from './components/edit.vue'; import { SNIPPET_LEVELS_MAP, SNIPPET_VISIBILITY_PRIVATE } from '~/snippets/constants'; Vue.use(VueApollo); @@ -48,9 +46,17 @@ function appFactory(el, Component) { } export const SnippetShowInit = () => { - appFactory(document.getElementById('js-snippet-view'), SnippetsShow); + import('./components/show.vue') + .then(({ default: SnippetsShow }) => { + appFactory(document.getElementById('js-snippet-view'), SnippetsShow); + }) + .catch(() => {}); }; export const SnippetEditInit = () => { - appFactory(document.getElementById('js-snippet-edit'), SnippetsEdit); + import('./components/edit.vue') + .then(({ default: SnippetsEdit }) => { + appFactory(document.getElementById('js-snippet-edit'), SnippetsEdit); + }) + .catch(() => {}); }; diff --git a/app/assets/stylesheets/fontawesome_custom.scss b/app/assets/stylesheets/fontawesome_custom.scss index b884a6ea9fb..7ea03c4127b 100644 --- a/app/assets/stylesheets/fontawesome_custom.scss +++ b/app/assets/stylesheets/fontawesome_custom.scss @@ -253,10 +253,6 @@ content: '\f081'; } -.fa-unlink::before { - content: '\f127'; -} - .fa-file-pdf-o::before { content: '\f1c1'; } diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss index 7baaa26fbec..e8d37fcf40b 100644 --- a/app/assets/stylesheets/framework/dropdowns.scss +++ b/app/assets/stylesheets/framework/dropdowns.scss @@ -189,15 +189,6 @@ background-color: $gray-darker; color: $gl-text-color; outline: 0; - - // make sure the text color is not overridden - &.text-danger { - color: $brand-danger; - } - - .avatar { - border-color: $white; - } } @mixin dropdown-link { @@ -216,11 +207,6 @@ text-align: left; width: 100%; - // make sure the text color is not overridden - &.text-danger { - color: $brand-danger; - } - &.disable-hover { text-decoration: none; } @@ -232,10 +218,6 @@ @include dropdown-item-hover; text-decoration: none; - - .badge.badge-pill { - background-color: darken($blue-50, 5%); - } } &.dropdown-menu-user-link { diff --git a/app/assets/stylesheets/framework/secondary_navigation_elements.scss b/app/assets/stylesheets/framework/secondary_navigation_elements.scss index 292d57f132c..bbfe65e6eda 100644 --- a/app/assets/stylesheets/framework/secondary_navigation_elements.scss +++ b/app/assets/stylesheets/framework/secondary_navigation_elements.scss @@ -28,10 +28,6 @@ text-decoration: none; color: $black; border-bottom: 2px solid $gray-darkest; - - .badge.badge-pill { - color: $black; - } } } diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss index 8b5fa6c1b6c..c15d46d43b2 100644 --- a/app/assets/stylesheets/framework/typography.scss +++ b/app/assets/stylesheets/framework/typography.scss @@ -439,10 +439,6 @@ content: '\f0c6'; } - &:hover::before { - text-decoration: none; - } - &.no-attachment-icon { &::before { display: none; diff --git a/app/assets/stylesheets/framework/wells.scss b/app/assets/stylesheets/framework/wells.scss index 55996a074c6..774ba5d9964 100644 --- a/app/assets/stylesheets/framework/wells.scss +++ b/app/assets/stylesheets/framework/wells.scss @@ -29,11 +29,6 @@ .ref-name { font-size: 12px; - - &:hover { - text-decoration: underline; - color: $gl-text-color; - } } } diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss index fb13e776e13..ef2ee8d57c2 100644 --- a/app/assets/stylesheets/pages/diff.scss +++ b/app/assets/stylesheets/pages/diff.scss @@ -70,10 +70,6 @@ } } - a:hover { - text-decoration: none; - } - &:hover { background-color: $gray-normal; } diff --git a/app/controllers/projects/runners_controller.rb b/app/controllers/projects/runners_controller.rb index ca62f54813b..fb00156d320 100644 --- a/app/controllers/projects/runners_controller.rb +++ b/app/controllers/projects/runners_controller.rb @@ -50,6 +50,10 @@ class Projects::RunnersController < Projects::ApplicationController end def toggle_shared_runners + if Feature.enabled?(:disable_shared_runners_on_group, default_enabled: true) && !project.shared_runners_enabled && project.group && project.group.shared_runners_setting == 'disabled_and_unoverridable' + return redirect_to project_runners_path(@project), alert: _("Cannot enable shared runners because parent group does not allow it") + end + project.toggle!(:shared_runners_enabled) redirect_to project_settings_ci_cd_path(@project, anchor: 'js-runners-settings') diff --git a/app/graphql/mutations/metrics/dashboard/annotations/create.rb b/app/graphql/mutations/metrics/dashboard/annotations/create.rb index f99688aeac6..6f316e76e2a 100644 --- a/app/graphql/mutations/metrics/dashboard/annotations/create.rb +++ b/app/graphql/mutations/metrics/dashboard/annotations/create.rb @@ -80,7 +80,7 @@ module Mutations raise Gitlab::Graphql::Errors::ArgumentError, ANNOTATION_SOURCE_ARGUMENT_ERROR end - super(args) + super(**args) end def find_object(id:) diff --git a/app/helpers/profiles_helper.rb b/app/helpers/profiles_helper.rb index 5a42e581867..44d869fbd8f 100644 --- a/app/helpers/profiles_helper.rb +++ b/app/helpers/profiles_helper.rb @@ -29,4 +29,19 @@ module ProfilesHelper def user_profile? params[:controller] == 'users' end + + def ssh_key_delete_modal_data(key, is_admin) + { + path: path_to_key(key, is_admin), + method: 'delete', + qa_selector: 'delete_ssh_key_button', + modal_attributes: { + 'data-qa-selector': 'ssh_key_delete_modal', + title: _('Are you sure you want to delete this SSH key?'), + message: _('This action cannot be undone, and will permanently delete the %{key} SSH key') % { key: key.title }, + okVariant: 'danger', + okTitle: _('Delete') + } + } + end end diff --git a/app/models/group.rb b/app/models/group.rb index abb3f6c96c4..4ecd096b2b4 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -19,8 +19,6 @@ class Group < Namespace ACCESS_REQUEST_APPROVERS_TO_BE_NOTIFIED_LIMIT = 10 - UpdateSharedRunnersError = Class.new(StandardError) - has_many :all_group_members, -> { where(requested_at: nil) }, dependent: :destroy, as: :source, class_name: 'GroupMember' # rubocop:disable Cop/ActiveRecordDependent has_many :group_members, -> { where(requested_at: nil).where.not(members: { access_level: Gitlab::Access::MINIMAL_ACCESS }) }, dependent: :destroy, as: :source # rubocop:disable Cop/ActiveRecordDependent alias_method :members, :group_members @@ -538,53 +536,14 @@ class Group < Namespace preloader.preload(self, shared_with_group_links: [shared_with_group: :route]) end - def shared_runners_allowed? - shared_runners_enabled? || allow_descendants_override_disabled_shared_runners? - end - - def parent_allows_shared_runners? - return true unless has_parent? - - parent.shared_runners_allowed? - end - - def parent_enabled_shared_runners? - return true unless has_parent? - - parent.shared_runners_enabled? - end - - def enable_shared_runners! - raise UpdateSharedRunnersError, 'Shared Runners disabled for the parent group' unless parent_enabled_shared_runners? - - update_column(:shared_runners_enabled, true) - end - - def disable_shared_runners! - group_ids = self_and_descendants - return if group_ids.empty? - - Group.by_id(group_ids).update_all(shared_runners_enabled: false) - - all_projects.update_all(shared_runners_enabled: false) - end - - def allow_descendants_override_disabled_shared_runners! - raise UpdateSharedRunnersError, 'Shared Runners enabled' if shared_runners_enabled? - raise UpdateSharedRunnersError, 'Group level shared Runners not allowed' unless parent_allows_shared_runners? - - update_column(:allow_descendants_override_disabled_shared_runners, true) - end - - def disallow_descendants_override_disabled_shared_runners! - raise UpdateSharedRunnersError, 'Shared Runners enabled' if shared_runners_enabled? + def update_shared_runners_setting!(state) + raise ArgumentError unless SHARED_RUNNERS_SETTINGS.include?(state) - group_ids = self_and_descendants - return if group_ids.empty? - - Group.by_id(group_ids).update_all(allow_descendants_override_disabled_shared_runners: false) - - all_projects.update_all(shared_runners_enabled: false) + case state + when 'disabled_and_unoverridable' then disable_shared_runners! # also disallows override + when 'disabled_with_override' then disable_shared_runners_and_allow_override! + when 'enabled' then enable_shared_runners! # set both to true + end end def default_owner @@ -668,6 +627,45 @@ class Group < Namespace .new(Group.where(id: group_ids)) .base_and_descendants end + + def disable_shared_runners! + update!( + shared_runners_enabled: false, + allow_descendants_override_disabled_shared_runners: false) + + group_ids = descendants + unless group_ids.empty? + Group.by_id(group_ids).update_all( + shared_runners_enabled: false, + allow_descendants_override_disabled_shared_runners: false) + end + + all_projects.update_all(shared_runners_enabled: false) + end + + def disable_shared_runners_and_allow_override! + # enabled -> disabled_with_override + if shared_runners_enabled? + update!( + shared_runners_enabled: false, + allow_descendants_override_disabled_shared_runners: true) + + group_ids = descendants + unless group_ids.empty? + Group.by_id(group_ids).update_all(shared_runners_enabled: false) + end + + all_projects.update_all(shared_runners_enabled: false) + + # disabled_and_unoverridable -> disabled_with_override + else + update!(allow_descendants_override_disabled_shared_runners: true) + end + end + + def enable_shared_runners! + update!(shared_runners_enabled: true) + end end Group.prepend_if_ee('EE::Group') diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 1965c5dacc7..6a732410cb1 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -1690,6 +1690,10 @@ class MergeRequest < ApplicationRecord Feature.enabled?(:merge_request_reviewers, project) end + def allows_multiple_reviewers? + false + end + private def with_rebase_lock diff --git a/app/models/namespace.rb b/app/models/namespace.rb index 527fa9d52d0..f0550713d01 100644 --- a/app/models/namespace.rb +++ b/app/models/namespace.rb @@ -18,6 +18,8 @@ class Namespace < ApplicationRecord # Android repo (15) + some extra backup. NUMBER_OF_ANCESTORS_ALLOWED = 20 + SHARED_RUNNERS_SETTINGS = %w[disabled_and_unoverridable disabled_with_override enabled].freeze + cache_markdown_field :description, pipeline: :description has_many :projects, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent @@ -59,6 +61,8 @@ class Namespace < ApplicationRecord validates :max_artifacts_size, numericality: { only_integer: true, greater_than: 0, allow_nil: true } validate :nesting_level_allowed + validate :changing_shared_runners_enabled_is_allowed + validate :changing_allow_descendants_override_disabled_shared_runners_is_allowed validates_associated :runners @@ -378,6 +382,52 @@ class Namespace < ApplicationRecord actual_plan.name end + def changing_shared_runners_enabled_is_allowed + return unless Feature.enabled?(:disable_shared_runners_on_group, default_enabled: true) + return unless new_record? || changes.has_key?(:shared_runners_enabled) + + if shared_runners_enabled && has_parent? && parent.shared_runners_setting == 'disabled_and_unoverridable' + errors.add(:shared_runners_enabled, _('cannot be enabled because parent group has shared Runners disabled')) + end + end + + def changing_allow_descendants_override_disabled_shared_runners_is_allowed + return unless Feature.enabled?(:disable_shared_runners_on_group, default_enabled: true) + return unless new_record? || changes.has_key?(:allow_descendants_override_disabled_shared_runners) + + if shared_runners_enabled && !new_record? + errors.add(:allow_descendants_override_disabled_shared_runners, _('cannot be changed if shared runners are enabled')) + end + + if allow_descendants_override_disabled_shared_runners && has_parent? && parent.shared_runners_setting == 'disabled_and_unoverridable' + errors.add(:allow_descendants_override_disabled_shared_runners, _('cannot be enabled because parent group does not allow it')) + end + end + + def shared_runners_setting + if shared_runners_enabled + 'enabled' + else + if allow_descendants_override_disabled_shared_runners + 'disabled_with_override' + else + 'disabled_and_unoverridable' + end + end + end + + def shared_runners_setting_higher_than?(other_setting) + if other_setting == 'enabled' + false + elsif other_setting == 'disabled_with_override' + shared_runners_setting == 'enabled' + elsif other_setting == 'disabled_and_unoverridable' + shared_runners_setting == 'enabled' || shared_runners_setting == 'disabled_with_override' + else + raise ArgumentError + end + end + private def all_projects_with_pages diff --git a/app/models/project.rb b/app/models/project.rb index ca3729e277a..d726b9aece6 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -435,6 +435,7 @@ class Project < ApplicationRecord validate :visibility_level_allowed_by_group, if: :should_validate_visibility_level? validate :visibility_level_allowed_as_fork, if: :should_validate_visibility_level? validate :validate_pages_https_only, if: -> { changes.has_key?(:pages_https_only) } + validate :changing_shared_runners_enabled_is_allowed validates :repository_storage, presence: true, inclusion: { in: ->(_object) { Gitlab.config.repositories.storages.keys } } @@ -1189,6 +1190,15 @@ class Project < ApplicationRecord end end + def changing_shared_runners_enabled_is_allowed + return unless Feature.enabled?(:disable_shared_runners_on_group, default_enabled: true) + return unless new_record? || changes.has_key?(:shared_runners_enabled) + + if shared_runners_enabled && group && group.shared_runners_setting == 'disabled_and_unoverridable' + errors.add(:shared_runners_enabled, _('cannot be enabled because parent group does not allow it')) + end + end + def to_param if persisted? && errors.include?(:path) path_was diff --git a/app/services/groups/create_service.rb b/app/services/groups/create_service.rb index 51dca43fa4a..4747e1d5ac5 100644 --- a/app/services/groups/create_service.rb +++ b/app/services/groups/create_service.rb @@ -15,6 +15,8 @@ module Groups after_build_hook(@group, params) + inherit_group_shared_runners_settings + unless can_use_visibility_level? && can_create_group? return @group end @@ -86,6 +88,13 @@ module Groups params[:visibility_level] = Gitlab::CurrentSettings.current_application_settings.default_group_visibility end + + def inherit_group_shared_runners_settings + return unless @group.parent + + @group.shared_runners_enabled = @group.parent.shared_runners_enabled + @group.allow_descendants_override_disabled_shared_runners = @group.parent.allow_descendants_override_disabled_shared_runners + end end end diff --git a/app/services/groups/transfer_service.rb b/app/services/groups/transfer_service.rb index 2bd571f60af..70f5c7e2ea7 100644 --- a/app/services/groups/transfer_service.rb +++ b/app/services/groups/transfer_service.rb @@ -103,6 +103,9 @@ module Groups @group.parent = @new_parent_group @group.clear_memoization(:self_and_ancestors_ids) + + inherit_group_shared_runners_settings + @group.save! end @@ -161,6 +164,17 @@ module Groups group_contains_npm_packages: s_('TransferGroup|Group contains projects with NPM packages.') }.freeze end + + def inherit_group_shared_runners_settings + parent_setting = @group.parent&.shared_runners_setting + return unless parent_setting + + if @group.shared_runners_setting_higher_than?(parent_setting) + result = Groups::UpdateSharedRunnersService.new(@group, current_user, shared_runners_setting: parent_setting).execute + + raise TransferError, result[:message] unless result[:status] == :success + end + end end end diff --git a/app/services/groups/update_service.rb b/app/services/groups/update_service.rb index 81393681dc0..382a3dbf0f7 100644 --- a/app/services/groups/update_service.rb +++ b/app/services/groups/update_service.rb @@ -19,6 +19,8 @@ module Groups return false unless valid_path_change_with_npm_packages? + return false unless update_shared_runners + before_assignment_hook(group, params) group.assign_attributes(params) @@ -98,6 +100,17 @@ module Groups params[:share_with_group_lock] != group.share_with_group_lock end + + def update_shared_runners + return true if params[:shared_runners_setting].nil? + + result = Groups::UpdateSharedRunnersService.new(group, current_user, shared_runners_setting: params.delete(:shared_runners_setting)).execute + + return true if result[:status] == :success + + group.errors.add(:update_shared_runners, result[:message]) + false + end end end diff --git a/app/services/groups/update_shared_runners_service.rb b/app/services/groups/update_shared_runners_service.rb index 63f57104510..639c5bf6ae0 100644 --- a/app/services/groups/update_shared_runners_service.rb +++ b/app/services/groups/update_shared_runners_service.rb @@ -7,44 +7,24 @@ module Groups validate_params - enable_or_disable_shared_runners! - allow_or_disallow_descendants_override_disabled_shared_runners! + update_shared_runners success - rescue Group::UpdateSharedRunnersError => error + rescue ActiveRecord::RecordInvalid, ArgumentError => error error(error.message) end private def validate_params - if Gitlab::Utils.to_boolean(params[:shared_runners_enabled]) && !params[:allow_descendants_override_disabled_shared_runners].nil? - raise Group::UpdateSharedRunnersError, 'Cannot set shared_runners_enabled to true and allow_descendants_override_disabled_shared_runners' + unless Namespace::SHARED_RUNNERS_SETTINGS.include?(params[:shared_runners_setting]) + raise ArgumentError, "state must be one of: #{Namespace::SHARED_RUNNERS_SETTINGS.join(', ')}" end end - def enable_or_disable_shared_runners! - return if params[:shared_runners_enabled].nil? - - if Gitlab::Utils.to_boolean(params[:shared_runners_enabled]) - group.enable_shared_runners! - else - group.disable_shared_runners! - end - end - - def allow_or_disallow_descendants_override_disabled_shared_runners! - return if params[:allow_descendants_override_disabled_shared_runners].nil? - - # Needs to reset group because if both params are present could result in error - group.reset - - if Gitlab::Utils.to_boolean(params[:allow_descendants_override_disabled_shared_runners]) - group.allow_descendants_override_disabled_shared_runners! - else - group.disallow_descendants_override_disabled_shared_runners! - end + def update_shared_runners + group.update_shared_runners_setting!(params[:shared_runners_setting]) end end end diff --git a/app/services/merge_requests/base_service.rb b/app/services/merge_requests/base_service.rb index 40a39be2fa5..aa591312c6a 100644 --- a/app/services/merge_requests/base_service.rb +++ b/app/services/merge_requests/base_service.rb @@ -110,6 +110,10 @@ module MergeRequests return end + unless merge_request.allows_multiple_reviewers? + params[:reviewer_ids] = params[:reviewer_ids].first(1) + end + reviewer_ids = params[:reviewer_ids].select { |reviewer_id| user_can_read?(merge_request, reviewer_id) } if params[:reviewer_ids].map(&:to_s) == [IssuableFinder::Params::NONE] diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb index 9943dc46731..6fc8e8f8935 100644 --- a/app/services/projects/create_service.rb +++ b/app/services/projects/create_service.rb @@ -19,6 +19,10 @@ module Projects @project = Project.new(params) + # If a project is newly created it should have shared runners settings + # based on its group having it enabled. This is like the "default value" + @project.shared_runners_enabled = false if !params.key?(:shared_runners_enabled) && @project.group && @project.group.shared_runners_setting != 'enabled' + # Make sure that the user is allowed to use the specified visibility level if project_visibility.restricted? deny_visibility_level(@project, project_visibility.visibility_level) diff --git a/app/services/projects/transfer_service.rb b/app/services/projects/transfer_service.rb index dba5177718d..013861631a1 100644 --- a/app/services/projects/transfer_service.rb +++ b/app/services/projects/transfer_service.rb @@ -88,6 +88,10 @@ module Projects # Move uploads move_project_uploads(project) + # If a project is being transferred to another group it means it can already + # have shared runners enabled but we need to check whether the new group allows that. + project.shared_runners_enabled = false if project.group && project.group.shared_runners_setting == 'disabled_and_unoverridable' + project.old_path_with_namespace = @old_path update_repository_configuration(@new_path) diff --git a/app/views/profiles/keys/_key.html.haml b/app/views/profiles/keys/_key.html.haml index 02b45853aa0..3f0c1596396 100644 --- a/app/views/profiles/keys/_key.html.haml +++ b/app/views/profiles/keys/_key.html.haml @@ -23,9 +23,10 @@ %span.expires.gl-mr-3 = s_('Profiles|Expires:') = key.expires_at ? key.expires_at.to_date : _('Never') - %span.key-created-at - = s_('Profiles|Created %{time_ago}'.html_safe) % { time_ago:time_ago_with_tooltip(key.created_at)} + %span.key-created-at.gl-display-flex.gl-align-items-center + = s_('Profiles|Created%{time_ago}'.html_safe) % { time_ago: time_ago_with_tooltip(key.created_at, html_class: 'gl-ml-2')} - if key.can_delete? - = link_to path_to_key(key, is_admin), data: { confirm: _('Are you sure?')}, method: :delete, class: "btn btn-transparent gl-ml-3 align-baseline" do - %span.sr-only= _('Remove') - = sprite_icon('remove') + .gl-ml-3 + = button_to '#', class: "btn btn-default gl-button btn-default-tertiary js-confirm-modal-button", data: ssh_key_delete_modal_data(key, is_admin) do + %span.sr-only= _('Delete') + = sprite_icon('remove') diff --git a/app/views/profiles/keys/_key_details.html.haml b/app/views/profiles/keys/_key_details.html.haml index 59d953678e7..2bc7e9eccb8 100644 --- a/app/views/profiles/keys/_key_details.html.haml +++ b/app/views/profiles/keys/_key_details.html.haml @@ -38,4 +38,4 @@ .col-md-12 .float-right - if @key.can_delete? - = link_to _('Remove'), path_to_key(@key, is_admin), data: {confirm: _('Are you sure?')}, method: :delete, class: "btn btn-remove delete-key qa-delete-key-button" + = button_to _('Delete'), '#', class: "btn btn-danger gl-button delete-key js-confirm-modal-button", data: ssh_key_delete_modal_data(@key, is_admin) diff --git a/app/views/projects/diffs/_stats.html.haml b/app/views/projects/diffs/_stats.html.haml index b438fbbf446..cee479aab0a 100644 --- a/app/views/projects/diffs/_stats.html.haml +++ b/app/views/projects/diffs/_stats.html.haml @@ -1,5 +1,5 @@ -- sum_added_lines = diff_files.sum(&:added_lines) # rubocop: disable CodeReuse/ActiveRecord -- sum_removed_lines = diff_files.sum(&:removed_lines) # rubocop: disable CodeReuse/ActiveRecord +- sum_added_lines = diff_files.sum(&:added_lines) +- sum_removed_lines = diff_files.sum(&:removed_lines) .commit-stat-summary.dropdown Showing %button.diff-stats-summary-toggler.js-diff-stats-dropdown{ type: "button", data: { toggle: "dropdown", display: "static" } }< |