diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-03-14 21:07:46 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-03-14 21:07:46 +0300 |
commit | fbf2955cfc9ffc319d57960a0b0df1ee1b5fd05f (patch) | |
tree | 6964ec0aaac3d432a4795878e87d78566f7bf719 /app | |
parent | 739467f1fa4d5d4042b47ff6637a567d1ad6a4a4 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
28 files changed, 178 insertions, 113 deletions
diff --git a/app/assets/javascripts/clusters_list/components/clusters_actions.vue b/app/assets/javascripts/clusters_list/components/clusters_actions.vue index 81aa867ab00..ccb973f1eb8 100644 --- a/app/assets/javascripts/clusters_list/components/clusters_actions.vue +++ b/app/assets/javascripts/clusters_list/components/clusters_actions.vue @@ -1,5 +1,6 @@ <script> import { + GlButton, GlDropdown, GlDropdownItem, GlModalDirective, @@ -14,6 +15,7 @@ export default { i18n: CLUSTERS_ACTIONS, INSTALL_AGENT_MODAL_ID, components: { + GlButton, GlDropdown, GlDropdownItem, GlDropdownDivider, @@ -23,7 +25,13 @@ export default { GlModalDirective, GlTooltip: GlTooltipDirective, }, - inject: ['newClusterPath', 'addClusterPath', 'canAddCluster', 'displayClusterAgents'], + inject: [ + 'newClusterPath', + 'addClusterPath', + 'canAddCluster', + 'displayClusterAgents', + 'certificateBasedClustersEnabled', + ], computed: { tooltip() { const { connectWithAgent, connectExistingCluster, dropdownDisabledHint } = this.$options.i18n; @@ -46,6 +54,7 @@ export default { <template> <div class="nav-controls gl-ml-auto"> <gl-dropdown + v-if="certificateBasedClustersEnabled" ref="dropdown" v-gl-modal-directive="shouldTriggerModal && $options.INSTALL_AGENT_MODAL_ID" v-gl-tooltip="tooltip" @@ -75,5 +84,15 @@ export default { {{ $options.i18n.connectExistingCluster }} </gl-dropdown-item> </gl-dropdown> + <gl-button + v-else + v-gl-modal-directive="$options.INSTALL_AGENT_MODAL_ID" + v-gl-tooltip="tooltip" + :disabled="!canAddCluster" + category="primary" + variant="confirm" + > + {{ $options.i18n.connectWithAgent }} + </gl-button> </div> </template> diff --git a/app/assets/javascripts/clusters_list/components/clusters_main_view.vue b/app/assets/javascripts/clusters_list/components/clusters_main_view.vue index eaaff74286a..aab6d3dc1f0 100644 --- a/app/assets/javascripts/clusters_list/components/clusters_main_view.vue +++ b/app/assets/javascripts/clusters_list/components/clusters_main_view.vue @@ -9,6 +9,7 @@ import { AGENT, EVENT_LABEL_TABS, EVENT_ACTIONS_CHANGE, + AGENT_TAB, } from '../constants'; import Agents from './agents.vue'; import InstallAgentModal from './install_agent_modal.vue'; @@ -28,9 +29,8 @@ export default { Agents, InstallAgentModal, }, - CLUSTERS_TABS, mixins: [trackingMixin], - inject: ['displayClusterAgents'], + inject: ['displayClusterAgents', 'certificateBasedClustersEnabled'], props: { defaultBranchName: { default: '.noBranch', @@ -45,21 +45,27 @@ export default { }; }, computed: { - clusterTabs() { - return this.displayClusterAgents ? CLUSTERS_TABS : [CERTIFICATE_TAB]; + availableTabs() { + const clusterTabs = this.displayClusterAgents ? CLUSTERS_TABS : [CERTIFICATE_TAB]; + return this.certificateBasedClustersEnabled ? clusterTabs : [AGENT_TAB]; }, }, watch: { - selectedTabIndex(val) { - this.onTabChange(val); + selectedTabIndex: { + handler(val) { + this.onTabChange(val); + }, + immediate: true, }, }, methods: { setSelectedTab(tabName) { - this.selectedTabIndex = this.clusterTabs.findIndex((tab) => tab.queryParamValue === tabName); + this.selectedTabIndex = this.availableTabs.findIndex( + (tab) => tab.queryParamValue === tabName, + ); }, onTabChange(tab) { - const tabName = this.clusterTabs[tab].queryParamValue; + const tabName = this.availableTabs[tab].queryParamValue; this.maxAgents = tabName === AGENT ? MAX_LIST_COUNT : MAX_CLUSTERS_LIST; this.track(EVENT_ACTIONS_CHANGE, { property: tabName }); @@ -76,7 +82,7 @@ export default { lazy > <gl-tab - v-for="(tab, idx) in clusterTabs" + v-for="(tab, idx) in availableTabs" :key="idx" :title="tab.title" :query-param-value="tab.queryParamValue" diff --git a/app/assets/javascripts/clusters_list/constants.js b/app/assets/javascripts/clusters_list/constants.js index ba7eedcf6bf..046a3f7e4fc 100644 --- a/app/assets/javascripts/clusters_list/constants.js +++ b/app/assets/javascripts/clusters_list/constants.js @@ -232,25 +232,24 @@ export const CERTIFICATE_BASED_CARD_INFO = { export const MAX_CLUSTERS_LIST = 6; +export const ALL_TAB = { + title: s__('ClusterAgents|All'), + component: 'ClustersViewAll', + queryParamValue: 'all', +}; + +export const AGENT_TAB = { + title: s__('ClusterAgents|Agent'), + component: 'agents', + queryParamValue: 'agent', +}; export const CERTIFICATE_TAB = { title: s__('ClusterAgents|Certificate'), component: 'clusters', queryParamValue: 'certificate_based', }; -export const CLUSTERS_TABS = [ - { - title: s__('ClusterAgents|All'), - component: 'ClustersViewAll', - queryParamValue: 'all', - }, - { - title: s__('ClusterAgents|Agent'), - component: 'agents', - queryParamValue: 'agent', - }, - CERTIFICATE_TAB, -]; +export const CLUSTERS_TABS = [ALL_TAB, AGENT_TAB, CERTIFICATE_TAB]; export const CLUSTERS_ACTIONS = { actionsButton: s__('ClusterAgents|Actions'), diff --git a/app/assets/javascripts/clusters_list/index.js b/app/assets/javascripts/clusters_list/index.js index da1774b3135..27eebc9d891 100644 --- a/app/assets/javascripts/clusters_list/index.js +++ b/app/assets/javascripts/clusters_list/index.js @@ -31,6 +31,7 @@ export default () => { canAdminCluster, gitlabVersion, displayClusterAgents, + certificateBasedClustersEnabled, } = el.dataset; return new Vue({ @@ -48,6 +49,7 @@ export default () => { canAdminCluster: parseBoolean(canAdminCluster), gitlabVersion, displayClusterAgents: parseBoolean(displayClusterAgents), + certificateBasedClustersEnabled: parseBoolean(certificateBasedClustersEnabled), }, store: createStore(el.dataset), render(createElement) { diff --git a/app/assets/javascripts/content_editor/extensions/paste_markdown.js b/app/assets/javascripts/content_editor/extensions/paste_markdown.js index 31774e0ec51..c349aa42a62 100644 --- a/app/assets/javascripts/content_editor/extensions/paste_markdown.js +++ b/app/assets/javascripts/content_editor/extensions/paste_markdown.js @@ -34,15 +34,15 @@ export default Extension.create({ deserializer .deserialize({ schema: editor.schema, content: markdown }) - .then((doc) => { - if (!doc) { + .then(({ document }) => { + if (!document) { return; } const { state, view } = editor; const { tr, selection } = state; - tr.replaceWith(selection.from - 1, selection.to, doc.content); + tr.replaceWith(selection.from - 1, selection.to, document.content); view.dispatch(tr); eventHub.$emit(LOADING_SUCCESS_EVENT); }) diff --git a/app/assets/javascripts/content_editor/services/content_editor.js b/app/assets/javascripts/content_editor/services/content_editor.js index e0995a5974c..c5638da2daf 100644 --- a/app/assets/javascripts/content_editor/services/content_editor.js +++ b/app/assets/javascripts/content_editor/services/content_editor.js @@ -40,13 +40,14 @@ export class ContentEditor { try { eventHub.$emit(LOADING_CONTENT_EVENT); - const newDoc = await deserializer.deserialize({ + const { document } = await deserializer.deserialize({ schema: editor.schema, content: serializedContent, }); - if (newDoc) { + + if (document) { tr.setSelection(selection) - .replaceSelectionWith(newDoc, false) + .replaceSelectionWith(document, false) .setMeta('preventUpdate', true); editor.view.dispatch(tr); } diff --git a/app/assets/javascripts/content_editor/services/markdown_deserializer.js b/app/assets/javascripts/content_editor/services/markdown_deserializer.js index ccffcd4cee8..cd4863d8eac 100644 --- a/app/assets/javascripts/content_editor/services/markdown_deserializer.js +++ b/app/assets/javascripts/content_editor/services/markdown_deserializer.js @@ -4,16 +4,22 @@ export default ({ render }) => { /** * Converts a Markdown string into a ProseMirror JSONDocument based * on a ProseMirror schema. + * + * @param {Object} options — The schema and content for deserialization * @param {ProseMirror.Schema} params.schema A ProseMirror schema that defines * the types of content supported in the document * @param {String} params.content An arbitrary markdown string - * @returns A ProseMirror JSONDocument + * + * @returns An object with the following properties: + * - document: A ProseMirror document object generated from the deserialized Markdown + * - dom: The Markdown Deserializer renders Markdown as HTML to generate the ProseMirror + * document. The dom property contains the HTML generated from the Markdown Source. */ return { deserialize: async ({ schema, content }) => { const html = await render(content); - if (!html) return null; + if (!html) return {}; const parser = new DOMParser(); const { body } = parser.parseFromString(html, 'text/html'); @@ -21,7 +27,7 @@ export default ({ render }) => { // append original source as a comment that nodes can access body.append(document.createComment(content)); - return ProseMirrorDOMParser.fromSchema(schema).parse(body); + return { document: ProseMirrorDOMParser.fromSchema(schema).parse(body), dom: body }; }, }; }; diff --git a/app/assets/javascripts/jobs/components/log/line_header.vue b/app/assets/javascripts/jobs/components/log/line_header.vue index 3bb1f58573c..c72d488f844 100644 --- a/app/assets/javascripts/jobs/components/log/line_header.vue +++ b/app/assets/javascripts/jobs/components/log/line_header.vue @@ -43,7 +43,7 @@ export default { <template> <div - class="log-line collapsible-line d-flex justify-content-between ws-normal" + class="log-line collapsible-line d-flex justify-content-between ws-normal gl-align-items-flex-start" role="button" @click="handleOnClick" > diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb index 1d0930ba73c..f9df0307a01 100644 --- a/app/controllers/admin/application_settings_controller.rb +++ b/app/controllers/admin/application_settings_controller.rb @@ -71,7 +71,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController end def reset_registration_token - @application_setting.reset_runners_registration_token! + ::Ci::Runners::ResetRegistrationTokenService.new(@application_setting, current_user).execute flash[:notice] = _('New runners registration token has been generated!') redirect_to admin_runners_path diff --git a/app/controllers/admin/clusters_controller.rb b/app/controllers/admin/clusters_controller.rb index 9a642e53d86..052c8821588 100644 --- a/app/controllers/admin/clusters_controller.rb +++ b/app/controllers/admin/clusters_controller.rb @@ -2,6 +2,7 @@ class Admin::ClustersController < Clusters::ClustersController include EnforcesAdminAuthentication + before_action :ensure_feature_enabled! layout 'admin' diff --git a/app/controllers/groups/clusters_controller.rb b/app/controllers/groups/clusters_controller.rb index 666a96d6fc0..2fe9faa252f 100644 --- a/app/controllers/groups/clusters_controller.rb +++ b/app/controllers/groups/clusters_controller.rb @@ -3,6 +3,7 @@ class Groups::ClustersController < Clusters::ClustersController include ControllerWithCrossProjectAccessCheck + before_action :ensure_feature_enabled! prepend_before_action :group requires_cross_project_access diff --git a/app/controllers/groups/settings/ci_cd_controller.rb b/app/controllers/groups/settings/ci_cd_controller.rb index a290ef9b5e7..9b9e3f7b0bc 100644 --- a/app/controllers/groups/settings/ci_cd_controller.rb +++ b/app/controllers/groups/settings/ci_cd_controller.rb @@ -36,7 +36,7 @@ module Groups end def reset_registration_token - @group.reset_runners_token! + ::Ci::Runners::ResetRegistrationTokenService.new(@group, current_user).execute flash[:notice] = _('GroupSettings|New runners registration token has been generated!') redirect_to group_settings_ci_cd_path diff --git a/app/controllers/projects/settings/ci_cd_controller.rb b/app/controllers/projects/settings/ci_cd_controller.rb index dd2fb57f7ac..3f4d26bb6ec 100644 --- a/app/controllers/projects/settings/ci_cd_controller.rb +++ b/app/controllers/projects/settings/ci_cd_controller.rb @@ -64,7 +64,7 @@ module Projects end def reset_registration_token - @project.reset_runners_token! + ::Ci::Runners::ResetRegistrationTokenService.new(@project, current_user).execute flash[:toast] = _("New runners registration token has been generated!") redirect_to namespace_project_settings_ci_cd_path diff --git a/app/graphql/mutations/ci/runners_registration_token/reset.rb b/app/graphql/mutations/ci/runners_registration_token/reset.rb index 7976e8fb70d..29ef7aa2e81 100644 --- a/app/graphql/mutations/ci/runners_registration_token/reset.rb +++ b/app/graphql/mutations/ci/runners_registration_token/reset.rb @@ -45,20 +45,19 @@ module Mutations def reset_token(type:, **args) id = args[:id] + scope = nil case type when 'instance_type' raise Gitlab::Graphql::Errors::ArgumentError, "id must not be specified for '#{type}' scope" if id.present? - authorize!(:global) - - ApplicationSetting.current.reset_runners_registration_token! - ApplicationSetting.current_without_cache.runners_registration_token + scope = ApplicationSetting.current + authorize!(scope) when 'group_type', 'project_type' - project_or_group = authorized_find!(type: type, id: id) - project_or_group.reset_runners_token! - project_or_group.runners_token + scope = authorized_find!(type: type, id: id) end + + ::Ci::Runners::ResetRegistrationTokenService.new(scope, current_user).execute if scope end end end diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb index d9a1731e820..a9c13b2fdeb 100644 --- a/app/helpers/application_settings_helper.rb +++ b/app/helpers/application_settings_helper.rb @@ -465,7 +465,10 @@ module ApplicationSettingsHelper end def instance_clusters_enabled? - can?(current_user, :read_cluster, Clusters::Instance.new) + clusterable = Clusters::Instance.new + + Feature.enabled?(:certificate_based_clusters, clusterable, default_enabled: :yaml, type: :ops) && + can?(current_user, :read_cluster, clusterable) end def omnibus_protected_paths_throttle? diff --git a/app/helpers/clusters_helper.rb b/app/helpers/clusters_helper.rb index ae890685dc6..62d93d75b11 100644 --- a/app/helpers/clusters_helper.rb +++ b/app/helpers/clusters_helper.rb @@ -31,7 +31,8 @@ module ClustersHelper add_cluster_path: clusterable.new_path(tab: 'add'), can_add_cluster: clusterable.can_add_cluster?.to_s, can_admin_cluster: clusterable.can_admin_cluster?.to_s, - display_cluster_agents: display_cluster_agents?(clusterable).to_s + display_cluster_agents: display_cluster_agents?(clusterable).to_s, + certificate_based_clusters_enabled: Feature.enabled?(:certificate_based_clusters, clusterable, default_enabled: :yaml, type: :ops).to_s } end diff --git a/app/models/concerns/bulk_member_access_load.rb b/app/models/concerns/bulk_member_access_load.rb index 927d6ccb28f..efc65e55e40 100644 --- a/app/models/concerns/bulk_member_access_load.rb +++ b/app/models/concerns/bulk_member_access_load.rb @@ -1,61 +1,19 @@ # frozen_string_literal: true -# Returns and caches in thread max member access for a resource -# module BulkMemberAccessLoad extend ActiveSupport::Concern included do - # Determine the maximum access level for a group of resources in bulk. - # - # Returns a Hash mapping resource ID -> maximum access level. - def max_member_access_for_resource_ids(resource_klass, resource_ids, &block) - raise 'Block is mandatory' unless block_given? - - memoization_index = self.id - memoization_class = self.class - - resource_ids = resource_ids.uniq - memo_id = "#{memoization_class}:#{memoization_index}" - access = load_access_hash(resource_klass, memo_id) - - # Look up only the IDs we need - resource_ids -= access.keys - - return access if resource_ids.empty? - - resource_access = yield(resource_ids) - - access.merge!(resource_access) - - missing_resource_ids = resource_ids - resource_access.keys - - missing_resource_ids.each do |resource_id| - access[resource_id] = Gitlab::Access::NO_ACCESS - end - - access - end - def merge_value_to_request_store(resource_klass, resource_id, value) - max_member_access_for_resource_ids(resource_klass, [resource_id]) do + Gitlab::SafeRequestLoader.execute(resource_key: max_member_access_for_resource_key(resource_klass), + resource_ids: [resource_id], + default_value: Gitlab::Access::NO_ACCESS) do { resource_id => value } end end - private - - def max_member_access_for_resource_key(klass, memoization_index) - "max_member_access_for_#{klass.name.underscore.pluralize}:#{memoization_index}" - end - - def load_access_hash(resource_klass, memo_id) - return {} unless Gitlab::SafeRequestStore.active? - - key = max_member_access_for_resource_key(resource_klass, memo_id) - Gitlab::SafeRequestStore[key] ||= {} - - Gitlab::SafeRequestStore[key] + def max_member_access_for_resource_key(klass) + "max_member_access_for_#{klass.name.underscore.pluralize}:#{self.class}:#{self.id}" end end end diff --git a/app/models/group.rb b/app/models/group.rb index e9fb4c36ba6..14d088dd38b 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -816,7 +816,9 @@ class Group < Namespace private def max_member_access(user_ids) - max_member_access_for_resource_ids(User, user_ids) do |user_ids| + Gitlab::SafeRequestLoader.execute(resource_key: max_member_access_for_resource_key(User), + resource_ids: user_ids, + default_value: Gitlab::Access::NO_ACCESS) do |user_ids| members_with_parents.where(user_id: user_ids).group(:user_id).maximum(:access_level) end end diff --git a/app/models/project.rb b/app/models/project.rb index 6457cce9364..e55395b32e7 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -1567,14 +1567,17 @@ class Project < ApplicationRecord # rubocop: disable CodeReuse/ServiceClass def execute_hooks(data, hooks_scope = :push_hooks) run_after_commit_or_now do - hooks.hooks_for(hooks_scope).select_active(hooks_scope, data).each do |hook| - hook.async_execute(data, hooks_scope.to_s) - end + triggered_hooks(hooks_scope, data).execute SystemHooksService.new.execute_hooks(data, hooks_scope) end end # rubocop: enable CodeReuse/ServiceClass + def triggered_hooks(hooks_scope, data) + triggered = ::Projects::TriggeredHooks.new(hooks_scope, data) + triggered.add_hooks(hooks) + end + def execute_integrations(data, hooks_scope = :push_hooks) # Call only service hooks that are active for this scope run_after_commit_or_now do diff --git a/app/models/project_authorization.rb b/app/models/project_authorization.rb index c76332b21cd..5c6fdec16ca 100644 --- a/app/models/project_authorization.rb +++ b/app/models/project_authorization.rb @@ -9,7 +9,7 @@ class ProjectAuthorization < ApplicationRecord validates :project, presence: true validates :access_level, inclusion: { in: Gitlab::Access.all_values }, presence: true - validates :user, uniqueness: { scope: [:project, :access_level] }, presence: true + validates :user, uniqueness: { scope: :project }, presence: true def self.select_from_union(relations) from_union(relations) diff --git a/app/models/project_team.rb b/app/models/project_team.rb index ee5ecc2dd3c..d5e0d112aeb 100644 --- a/app/models/project_team.rb +++ b/app/models/project_team.rb @@ -179,7 +179,9 @@ class ProjectTeam # # Returns a Hash mapping user ID -> maximum access level. def max_member_access_for_user_ids(user_ids) - project.max_member_access_for_resource_ids(User, user_ids) do |user_ids| + Gitlab::SafeRequestLoader.execute(resource_key: project.max_member_access_for_resource_key(User), + resource_ids: user_ids, + default_value: Gitlab::Access::NO_ACCESS) do |user_ids| project.project_authorizations .where(user: user_ids) .group(:user_id) diff --git a/app/models/projects/triggered_hooks.rb b/app/models/projects/triggered_hooks.rb new file mode 100644 index 00000000000..e3aa3d106b7 --- /dev/null +++ b/app/models/projects/triggered_hooks.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module Projects + class TriggeredHooks + def initialize(scope, data) + @scope = scope + @data = data + @relations = [] + end + + def add_hooks(relation) + @relations << relation + self + end + + def execute + # Assumes that the relations implement TriggerableHooks + @relations.each do |hooks| + hooks.hooks_for(@scope).select_active(@scope, @data).each do |hook| + hook.async_execute(@data, @scope.to_s) + end + end + end + end +end diff --git a/app/models/user.rb b/app/models/user.rb index fa58455ad35..4c375fe5230 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1862,7 +1862,9 @@ class User < ApplicationRecord # # Returns a Hash mapping project ID -> maximum access level. def max_member_access_for_project_ids(project_ids) - max_member_access_for_resource_ids(Project, project_ids) do |project_ids| + Gitlab::SafeRequestLoader.execute(resource_key: max_member_access_for_resource_key(Project), + resource_ids: project_ids, + default_value: Gitlab::Access::NO_ACCESS) do |project_ids| project_authorizations.where(project: project_ids) .group(:project_id) .maximum(:access_level) @@ -1877,7 +1879,9 @@ class User < ApplicationRecord # # Returns a Hash mapping project ID -> maximum access level. def max_member_access_for_group_ids(group_ids) - max_member_access_for_resource_ids(Group, group_ids) do |group_ids| + Gitlab::SafeRequestLoader.execute(resource_key: max_member_access_for_resource_key(Group), + resource_ids: group_ids, + default_value: Gitlab::Access::NO_ACCESS) do |group_ids| group_members.where(source: group_ids).group(:source_id).maximum(:access_level) end end diff --git a/app/policies/application_setting_policy.rb b/app/policies/application_setting_policy.rb index 114c71fd99d..6d0b5f36fa4 100644 --- a/app/policies/application_setting_policy.rb +++ b/app/policies/application_setting_policy.rb @@ -1,5 +1,8 @@ # frozen_string_literal: true class ApplicationSettingPolicy < BasePolicy # rubocop:disable Gitlab/NamespacedClass - rule { admin }.enable :read_application_setting + rule { admin }.policy do + enable :read_application_setting + enable :update_runners_registration_token + end end diff --git a/app/policies/global_policy.rb b/app/policies/global_policy.rb index 2a2ddf29899..fa7b117f3cd 100644 --- a/app/policies/global_policy.rb +++ b/app/policies/global_policy.rb @@ -115,7 +115,6 @@ class GlobalPolicy < BasePolicy enable :approve_user enable :reject_user enable :read_usage_trends_measurement - enable :update_runners_registration_token end # We can't use `read_statistics` because the user may have different permissions for different projects diff --git a/app/services/ci/runners/reset_registration_token_service.rb b/app/services/ci/runners/reset_registration_token_service.rb new file mode 100644 index 00000000000..bbe49c04644 --- /dev/null +++ b/app/services/ci/runners/reset_registration_token_service.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module Ci + module Runners + class ResetRegistrationTokenService + # @param [ApplicationSetting, Project, Group] scope: the scope of the reset operation + # @param [User] user: the user performing the operation + def initialize(scope, user) + @scope = scope + @user = user + end + + def execute + return unless @user.present? && @user.can?(:update_runners_registration_token, scope) + + case scope + when ::ApplicationSetting + scope.reset_runners_registration_token! + ApplicationSetting.current_without_cache.runners_registration_token + when ::Group, ::Project + scope.reset_runners_token! + scope.runners_token + end + end + + private + + attr_reader :scope, :user + end + end +end diff --git a/app/views/clusters/clusters/_gcp_signup_offer_banner.html.haml b/app/views/clusters/clusters/_gcp_signup_offer_banner.html.haml index 9d249931a34..3a4632affdc 100644 --- a/app/views/clusters/clusters/_gcp_signup_offer_banner.html.haml +++ b/app/views/clusters/clusters/_gcp_signup_offer_banner.html.haml @@ -1,11 +1,10 @@ - link = link_to(s_('ClusterIntegration|sign up'), 'https://console.cloud.google.com/freetrial?utm_campaign=2018_cpanel&utm_source=gitlab&utm_medium=referral', target: '_blank', rel: 'noopener noreferrer') -.gcp-signup-offer.gl-alert.gl-alert-info.gl-my-3{ role: 'alert', data: { feature_id: Users::CalloutsHelper::GCP_SIGNUP_OFFER, dismiss_endpoint: callouts_path } } - .gl-alert-container - %button.js-close.btn.gl-dismiss-btn.btn-default.btn-sm.gl-button.btn-default-tertiary.btn-icon{ type: 'button', 'aria-label' => _('Dismiss') } - = sprite_icon('close', size: 16, css_class: 'gl-icon') - = sprite_icon('information-o', size: 16, css_class: 'gl-icon gl-alert-icon gl-alert-icon-no-title') - .gl-alert-content - %h4.gl-alert-title= s_('ClusterIntegration|Did you know?') - %p.gl-alert-body= s_('ClusterIntegration|Every new Google Cloud Platform (GCP) account receives $300 in credit upon %{sign_up_link}. In partnership with Google, GitLab is able to offer an additional $200 for both new and existing GCP accounts to get started with GitLab\'s Google Kubernetes Engine Integration.').html_safe % { sign_up_link: link } - %a.gl-button.btn-confirm.text-decoration-none{ href: 'https://cloud.google.com/partners/partnercredit/?pcn_code=0014M00001h35gDQAQ#contact-form', target: '_blank', rel: 'noopener noreferrer' } - = s_("ClusterIntegration|Apply for credit") += render 'shared/global_alert', + title: s_('ClusterIntegration|Did you know?'), + alert_class: 'gcp-signup-offer', + alert_data: { feature_id: Users::CalloutsHelper::GCP_SIGNUP_OFFER, dismiss_endpoint: callouts_path } do + .gl-alert-body + = s_('ClusterIntegration|Every new Google Cloud Platform (GCP) account receives $300 in credit upon %{sign_up_link}. In partnership with Google, GitLab is able to offer an additional $200 for both new and existing GCP accounts to get started with GitLab\'s Google Kubernetes Engine Integration.').html_safe % { sign_up_link: link } + .gl-alert-actions + %a.gl-button.btn-confirm.text-decoration-none{ href: 'https://cloud.google.com/partners/partnercredit/?pcn_code=0014M00001h35gDQAQ#contact-form', target: '_blank', rel: 'noopener noreferrer' } + = s_("ClusterIntegration|Apply for credit") diff --git a/app/views/projects/_new_project_fields.html.haml b/app/views/projects/_new_project_fields.html.haml index b8a15ab698c..2d0a4ae8605 100644 --- a/app/views/projects/_new_project_fields.html.haml +++ b/app/views/projects/_new_project_fields.html.haml @@ -46,7 +46,8 @@ = s_('ProjectsNew|Project description %{tag_start}(optional)%{tag_end}').html_safe % { tag_start: '<span>'.html_safe, tag_end: '</span>'.html_safe } = f.text_area :description, placeholder: s_('ProjectsNew|Description format'), class: "form-control gl-form-input", rows: 3, maxlength: 250, data: { qa_selector: 'project_description', track_label: "#{track_label}", track_action: "activate_form_input", track_property: "project_description", track_value: "" } -.js-deployment-target-select +- unless Gitlab::CurrentSettings.current_application_settings.hide_third_party_offers? + .js-deployment-target-select = f.label :visibility_level, class: 'label-bold' do = s_('ProjectsNew|Visibility Level') |