diff options
Diffstat (limited to 'app')
34 files changed, 189 insertions, 204 deletions
diff --git a/app/assets/javascripts/milestone_select.js b/app/assets/javascripts/milestone_select.js index 01ed27c49fb..43949d5cc86 100644 --- a/app/assets/javascripts/milestone_select.js +++ b/app/assets/javascripts/milestone_select.js @@ -55,7 +55,7 @@ export default class MilestoneSelect { const $sidebarCollapsedValue = $block.find('.sidebar-collapsed-icon'); const $value = $block.find('.value'); const $loading = $block.find('.block-loading').fadeOut(); - selectedMilestoneDefault = showAny ? __('Any Milestone') : null; + selectedMilestoneDefault = showAny ? '' : null; selectedMilestoneDefault = showNo && defaultNo ? __('No Milestone') : selectedMilestoneDefault; selectedMilestone = $dropdown.data('selected') || selectedMilestoneDefault; @@ -74,16 +74,14 @@ export default class MilestoneSelect { if (showAny) { extraOptions.push({ id: null, - // eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings - name: 'Any', + name: null, title: __('Any Milestone'), }); } if (showNo) { extraOptions.push({ id: -1, - // eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings - name: 'None', + name: __('No Milestone'), title: __('No Milestone'), }); } diff --git a/app/assets/javascripts/visual_review_toolbar/components/comment.js b/app/assets/javascripts/visual_review_toolbar/components/comment.js index 20effc1751d..a03dc14b319 100644 --- a/app/assets/javascripts/visual_review_toolbar/components/comment.js +++ b/app/assets/javascripts/visual_review_toolbar/components/comment.js @@ -1,5 +1,5 @@ import { nextView } from '../store'; -import { localStorage, COMMENT_BOX, LOGOUT } from '../shared'; +import { localStorage, COMMENT_BOX, LOGOUT, STORAGE_MR_ID, STORAGE_TOKEN } from '../shared'; import { clearNote } from './note'; import { buttonClearStyles } from './utils'; import { addForm } from './wrapper'; @@ -27,8 +27,8 @@ const comment = state => { // If we reach a design where we can logout from multiple views, promote this // to it's own package const logoutUser = state => { - localStorage.removeItem('token'); - localStorage.removeItem('mergeRequestId'); + localStorage.removeItem(STORAGE_TOKEN); + localStorage.removeItem(STORAGE_MR_ID); state.token = ''; state.mergeRequestId = ''; diff --git a/app/assets/javascripts/visual_review_toolbar/components/comment_mr_note.js b/app/assets/javascripts/visual_review_toolbar/components/comment_mr_note.js index f71ffbf4f20..da67763261c 100644 --- a/app/assets/javascripts/visual_review_toolbar/components/comment_mr_note.js +++ b/app/assets/javascripts/visual_review_toolbar/components/comment_mr_note.js @@ -1,5 +1,5 @@ import { nextView } from '../store'; -import { localStorage, CHANGE_MR_ID_BUTTON, COMMENT_BOX } from '../shared'; +import { localStorage, CHANGE_MR_ID_BUTTON, COMMENT_BOX, STORAGE_MR_ID } from '../shared'; import { clearNote } from './note'; import { buttonClearStyles } from './utils'; import { addForm } from './wrapper'; @@ -18,7 +18,7 @@ const selectedMrNote = state => { }; const clearMrId = state => { - localStorage.removeItem('mergeRequestId'); + localStorage.removeItem(STORAGE_MR_ID); state.mergeRequestId = ''; }; diff --git a/app/assets/javascripts/visual_review_toolbar/components/comment_storage.js b/app/assets/javascripts/visual_review_toolbar/components/comment_storage.js index 32a9e7e2f05..49c9400437e 100644 --- a/app/assets/javascripts/visual_review_toolbar/components/comment_storage.js +++ b/app/assets/javascripts/visual_review_toolbar/components/comment_storage.js @@ -1,7 +1,7 @@ import { selectCommentBox } from './utils'; -import { sessionStorage } from '../shared'; +import { sessionStorage, STORAGE_COMMENT } from '../shared'; -const getSavedComment = () => sessionStorage.getItem('comment') || ''; +const getSavedComment = () => sessionStorage.getItem(STORAGE_COMMENT) || ''; const saveComment = () => { const currentComment = selectCommentBox(); @@ -9,12 +9,12 @@ const saveComment = () => { // This may be added to any view via top-level beforeunload listener // so let's skip if it does not apply if (currentComment && currentComment.value) { - sessionStorage.setItem('comment', currentComment.value); + sessionStorage.setItem(STORAGE_COMMENT, currentComment.value); } }; const clearSavedComment = () => { - sessionStorage.removeItem('comment'); + sessionStorage.removeItem(STORAGE_COMMENT); }; export { getSavedComment, saveComment, clearSavedComment }; diff --git a/app/assets/javascripts/visual_review_toolbar/components/login.js b/app/assets/javascripts/visual_review_toolbar/components/login.js index 4a6976ef2fd..20ab01bc690 100644 --- a/app/assets/javascripts/visual_review_toolbar/components/login.js +++ b/app/assets/javascripts/visual_review_toolbar/components/login.js @@ -1,5 +1,5 @@ import { nextView } from '../store'; -import { localStorage, LOGIN, TOKEN_BOX } from '../shared'; +import { localStorage, LOGIN, TOKEN_BOX, STORAGE_TOKEN } from '../shared'; import { clearNote, postError } from './note'; import { rememberBox, submitButton } from './form_elements'; import { selectRemember, selectToken } from './utils'; @@ -22,7 +22,7 @@ const storeToken = (token, state) => { const rememberMe = selectRemember().checked; if (rememberMe) { - localStorage.setItem('token', token); + localStorage.setItem(STORAGE_TOKEN, token); } state.token = token; diff --git a/app/assets/javascripts/visual_review_toolbar/components/mr_id.js b/app/assets/javascripts/visual_review_toolbar/components/mr_id.js index f51e9631dd2..695b3af8aa0 100644 --- a/app/assets/javascripts/visual_review_toolbar/components/mr_id.js +++ b/app/assets/javascripts/visual_review_toolbar/components/mr_id.js @@ -1,5 +1,5 @@ import { nextView } from '../store'; -import { MR_ID, MR_ID_BUTTON, localStorage } from '../shared'; +import { MR_ID, MR_ID_BUTTON, STORAGE_MR_ID, localStorage } from '../shared'; import { clearNote, postError } from './note'; import { rememberBox, submitButton } from './form_elements'; import { selectForm, selectMrBox, selectRemember } from './utils'; @@ -23,7 +23,7 @@ const storeMR = (id, state) => { const rememberMe = selectRemember().checked; if (rememberMe) { - localStorage.setItem('mergeRequestId', id); + localStorage.setItem(STORAGE_MR_ID, id); } state.mergeRequestId = id; diff --git a/app/assets/javascripts/visual_review_toolbar/shared/constants.js b/app/assets/javascripts/visual_review_toolbar/shared/constants.js index a56ea378b14..0d5b666ab0a 100644 --- a/app/assets/javascripts/visual_review_toolbar/shared/constants.js +++ b/app/assets/javascripts/visual_review_toolbar/shared/constants.js @@ -15,6 +15,12 @@ const REMEMBER_ITEM = 'gitlab-remember-item'; const REVIEW_CONTAINER = 'gitlab-review-container'; const TOKEN_BOX = 'gitlab-token'; +// Storage keys +const STORAGE_PREFIX = '--gitlab'; // Using `--` to make the prefix more unique +const STORAGE_MR_ID = `${STORAGE_PREFIX}-merge-request-id`; +const STORAGE_TOKEN = `${STORAGE_PREFIX}-token`; +const STORAGE_COMMENT = `${STORAGE_PREFIX}-comment`; + // colors — these are applied programmatically // rest of styles belong in ./styles const BLACK = 'rgba(46, 46, 46, 1)'; @@ -39,6 +45,9 @@ export { REMEMBER_ITEM, REVIEW_CONTAINER, TOKEN_BOX, + STORAGE_MR_ID, + STORAGE_TOKEN, + STORAGE_COMMENT, BLACK, CLEAR, MUTED, diff --git a/app/assets/javascripts/visual_review_toolbar/shared/index.js b/app/assets/javascripts/visual_review_toolbar/shared/index.js index 751eae74dde..d8ccb170592 100644 --- a/app/assets/javascripts/visual_review_toolbar/shared/index.js +++ b/app/assets/javascripts/visual_review_toolbar/shared/index.js @@ -14,6 +14,9 @@ import { REMEMBER_ITEM, REVIEW_CONTAINER, TOKEN_BOX, + STORAGE_MR_ID, + STORAGE_TOKEN, + STORAGE_COMMENT, BLACK, CLEAR, MUTED, @@ -41,6 +44,9 @@ export { REMEMBER_ITEM, REVIEW_CONTAINER, TOKEN_BOX, + STORAGE_MR_ID, + STORAGE_TOKEN, + STORAGE_COMMENT, BLACK, CLEAR, MUTED, diff --git a/app/assets/javascripts/visual_review_toolbar/store/state.js b/app/assets/javascripts/visual_review_toolbar/store/state.js index 741a5c7d99c..b7853bb0723 100644 --- a/app/assets/javascripts/visual_review_toolbar/store/state.js +++ b/app/assets/javascripts/visual_review_toolbar/store/state.js @@ -1,5 +1,5 @@ import { comment, login, mrForm } from '../components'; -import { localStorage, COMMENT_BOX, LOGIN, MR_ID } from '../shared'; +import { localStorage, COMMENT_BOX, LOGIN, MR_ID, STORAGE_MR_ID, STORAGE_TOKEN } from '../shared'; const state = { browser: '', @@ -74,8 +74,8 @@ const initializeState = (wind, doc) => { }; const getInitialView = () => { - const token = localStorage.getItem('token'); - const mrId = localStorage.getItem('mergeRequestId'); + const token = localStorage.getItem(STORAGE_TOKEN); + const mrId = localStorage.getItem(STORAGE_MR_ID); if (token) { state.token = token; diff --git a/app/assets/javascripts/visual_review_toolbar/styles/toolbar.css b/app/assets/javascripts/visual_review_toolbar/styles/toolbar.css index e5732fd5d93..d1a8d66ef40 100644 --- a/app/assets/javascripts/visual_review_toolbar/styles/toolbar.css +++ b/app/assets/javascripts/visual_review_toolbar/styles/toolbar.css @@ -42,7 +42,7 @@ position: fixed; bottom: 1rem; right: 1rem; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell, + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Noto Sans', Ubuntu, Cantarell, 'Helvetica Neue', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; font-size: .8rem; diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss index b9b8eabf909..6c03dbb56a7 100644 --- a/app/assets/stylesheets/pages/notes.scss +++ b/app/assets/stylesheets/pages/notes.scss @@ -806,7 +806,7 @@ $note-form-margin-left: 72px; border-radius: $border-radius-base; border: 1px solid $border-gray-normal; color: $note-disabled-comment-color; - padding: 90px 0; + padding: $gl-padding-8 0; &.discussion-locked { border: 0; diff --git a/app/assets/stylesheets/pages/settings.scss b/app/assets/stylesheets/pages/settings.scss index 3b62121eb0d..79de1d78a6e 100644 --- a/app/assets/stylesheets/pages/settings.scss +++ b/app/assets/stylesheets/pages/settings.scss @@ -272,7 +272,7 @@ } .custom-monitored-metrics { - .card-title { + .card-header { display: flex; align-items: center; @@ -292,17 +292,6 @@ } } - .loading-metrics, - .empty-metrics { - padding: 30px 10px; - - p, - .btn { - margin-top: 10px; - margin-bottom: 0; - } - } - .loading-metrics .metrics-load-spinner { color: $gl-gray-700; } diff --git a/app/controllers/projects/environments_controller.rb b/app/controllers/projects/environments_controller.rb index 07eb689d031..b709ac85e39 100644 --- a/app/controllers/projects/environments_controller.rb +++ b/app/controllers/projects/environments_controller.rb @@ -11,7 +11,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController before_action :verify_api_request!, only: :terminal_websocket_authorize before_action :expire_etag_cache, only: [:index] before_action only: [:metrics, :additional_metrics, :metrics_dashboard] do - push_frontend_feature_flag(:environment_metrics_use_prometheus_endpoint) + push_frontend_feature_flag(:environment_metrics_use_prometheus_endpoint, default_enabled: true) push_frontend_feature_flag(:environment_metrics_show_multiple_dashboards) push_frontend_feature_flag(:environment_metrics_additional_panel_types) push_frontend_feature_flag(:prometheus_computed_alerts) diff --git a/app/controllers/projects/variables_controller.rb b/app/controllers/projects/variables_controller.rb index 646728e8167..1dffc57fcf0 100644 --- a/app/controllers/projects/variables_controller.rb +++ b/app/controllers/projects/variables_controller.rb @@ -38,6 +38,6 @@ class Projects::VariablesController < Projects::ApplicationController end def variable_params_attributes - %i[id variable_type key secret_value protected masked _destroy] + %i[id variable_type key secret_value protected masked environment_scope _destroy] end end diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb index 86970ae3219..1773ac2d508 100644 --- a/app/finders/issuable_finder.rb +++ b/app/finders/issuable_finder.rb @@ -484,19 +484,22 @@ class IssuableFinder # rubocop: disable CodeReuse/ActiveRecord def by_milestone(items) - return items unless milestones? - return items if filter_by_any_milestone? - - if filter_by_no_milestone? - items.left_joins_milestones.where(milestone_id: nil) - elsif filter_by_upcoming_milestone? - upcoming_ids = Milestone.upcoming_ids(projects, related_groups) - items.left_joins_milestones.where(milestone_id: upcoming_ids) - elsif filter_by_started_milestone? - items.left_joins_milestones.merge(Milestone.started) - else - items.with_milestone(params[:milestone_title]) + if milestones? + if filter_by_no_milestone? + items = items.left_joins_milestones.where(milestone_id: [-1, nil]) + elsif filter_by_any_milestone? + items = items.any_milestone + elsif filter_by_upcoming_milestone? + upcoming_ids = Milestone.upcoming_ids(projects, related_groups) + items = items.left_joins_milestones.where(milestone_id: upcoming_ids) + elsif filter_by_started_milestone? + items = items.left_joins_milestones.merge(Milestone.started) + else + items = items.with_milestone(params[:milestone_title]) + end end + + items end # rubocop: enable CodeReuse/ActiveRecord diff --git a/app/helpers/services_helper.rb b/app/helpers/services_helper.rb index 01ccf163b45..d4b50b7ecfb 100644 --- a/app/helpers/services_helper.rb +++ b/app/helpers/services_helper.rb @@ -39,7 +39,7 @@ module ServicesHelper end def disable_fields_service?(service) - service.is_a?(KubernetesService) || (!current_controller?("admin/services") && service.deprecated?) + !current_controller?("admin/services") && service.deprecated? end extend self diff --git a/app/models/ci/variable.rb b/app/models/ci/variable.rb index a77bbef0fca..760872d3e6b 100644 --- a/app/models/ci/variable.rb +++ b/app/models/ci/variable.rb @@ -6,6 +6,7 @@ module Ci include HasVariable include Presentable include Maskable + prepend HasEnvironmentScope belongs_to :project diff --git a/app/models/concerns/has_environment_scope.rb b/app/models/concerns/has_environment_scope.rb new file mode 100644 index 00000000000..9553abe4dd3 --- /dev/null +++ b/app/models/concerns/has_environment_scope.rb @@ -0,0 +1,78 @@ +# frozen_string_literal: true + +module HasEnvironmentScope + extend ActiveSupport::Concern + + prepended do + validates( + :environment_scope, + presence: true, + format: { with: ::Gitlab::Regex.environment_scope_regex, + message: ::Gitlab::Regex.environment_scope_regex_message } + ) + + ## + # Select rows which have a scope that matches the given environment name. + # Rows are ordered by relevance, by default. The most relevant row is + # placed at the end of a list. + # + # options: + # - relevant_only: (boolean) + # You can get the most relevant row only. Other rows are not be + # selected even if its scope matches the environment name. + # This is equivalent to using `#last` from SQL standpoint. + # + scope :on_environment, -> (environment_name, relevant_only: false) do + order_direction = relevant_only ? 'DESC' : 'ASC' + + where = <<~SQL + environment_scope IN (:wildcard, :environment_name) OR + :environment_name LIKE + #{::Gitlab::SQL::Glob.to_like('environment_scope')} + SQL + + order = <<~SQL + CASE environment_scope + WHEN :wildcard THEN 0 + WHEN :environment_name THEN 2 + ELSE 1 + END #{order_direction} + SQL + + values = { + wildcard: '*', + environment_name: environment_name + } + + sanitized_order_sql = sanitize_sql_array([order, values]) + + # The query is trying to find variables with scopes matching the + # current environment name. Suppose the environment name is + # 'review/app', and we have variables with environment scopes like: + # * variable A: review + # * variable B: review/app + # * variable C: review/* + # * variable D: * + # And the query should find variable B, C, and D, because it would + # try to convert the scope into a LIKE pattern for each variable: + # * A: review + # * B: review/app + # * C: review/% + # * D: % + # Note that we'll match % and _ literally therefore we'll escape them. + # In this case, B, C, and D would match. We also want to prioritize + # the exact matched name, and put * last, and everything else in the + # middle. So the order should be: D < C < B + relation = where(where, values) + .order(Arel.sql(sanitized_order_sql)) # `order` cannot escape for us! + + relation = relation.limit(1) if relevant_only + + relation + end + end + + def environment_scope=(new_environment_scope) + super(new_environment_scope.to_s.strip) + end +end diff --git a/app/models/milestone.rb b/app/models/milestone.rb index 60266992ee1..2ad2838111e 100644 --- a/app/models/milestone.rb +++ b/app/models/milestone.rb @@ -4,8 +4,8 @@ class Milestone < ApplicationRecord # Represents a "No Milestone" state used for filtering Issues and Merge # Requests that have no milestone assigned. MilestoneStruct = Struct.new(:title, :name, :id) - None = MilestoneStruct.new('No Milestone', 'No Milestone', -1) - Any = MilestoneStruct.new('Any Milestone', '', nil) + None = MilestoneStruct.new('No Milestone', 'No Milestone', 0) + Any = MilestoneStruct.new('Any Milestone', '', -1) Upcoming = MilestoneStruct.new('Upcoming', '#upcoming', -2) Started = MilestoneStruct.new('Started', '#started', -3) diff --git a/app/models/project.rb b/app/models/project.rb index 960795b73cb..a6e43efa1f3 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -162,7 +162,6 @@ class Project < ApplicationRecord has_one :bugzilla_service has_one :gitlab_issue_tracker_service, inverse_of: :project has_one :external_wiki_service - has_one :kubernetes_service, inverse_of: :project has_one :prometheus_service, inverse_of: :project has_one :mock_ci_service has_one :mock_deployment_service @@ -1828,11 +1827,16 @@ class Project < ApplicationRecord end def ci_variables_for(ref:, environment: nil) - # EE would use the environment - if protected_for?(ref) - variables + result = if protected_for?(ref) + variables + else + variables.unprotected + end + + if environment + result.on_environment(environment) else - variables.unprotected + result.where(environment_scope: '*') end end diff --git a/app/models/project_services/kubernetes_service.rb b/app/models/project_services/kubernetes_service.rb deleted file mode 100644 index 9f5c226f4c9..00000000000 --- a/app/models/project_services/kubernetes_service.rb +++ /dev/null @@ -1,133 +0,0 @@ -# frozen_string_literal: true - -class KubernetesService < Service - default_value_for :category, 'deployment' - - # Namespace defaults to the project path, but can be overridden in case that - # is an invalid or inappropriate name - prop_accessor :namespace - - # Access to kubernetes is directly through the API - prop_accessor :api_url - - # Bearer authentication - # TODO: user/password auth, client certificates - prop_accessor :token - - # Provide a custom CA bundle for self-signed deployments - prop_accessor :ca_pem - - with_options presence: true, if: :activated? do - validates :api_url, public_url: true - validates :token - end - - before_validation :enforce_namespace_to_lower_case - - attr_accessor :skip_deprecation_validation - - validate :deprecation_validation, unless: :skip_deprecation_validation - - validates :namespace, - allow_blank: true, - length: 1..63, - if: :activated?, - format: { - with: Gitlab::Regex.kubernetes_namespace_regex, - message: Gitlab::Regex.kubernetes_namespace_regex_message - } - - def self.supported_events - %w() - end - - def can_test? - false - end - - def initialize_properties - self.properties = {} if properties.nil? - end - - def title - 'Kubernetes' - end - - def description - 'Kubernetes / OpenShift integration' - end - - def self.to_param - 'kubernetes' - end - - def fields - [ - { type: 'text', - name: 'api_url', - title: 'API URL', - placeholder: 'Kubernetes API URL, like https://kube.example.com/' }, - { type: 'textarea', - name: 'ca_pem', - title: 'CA Certificate', - placeholder: 'Certificate Authority bundle (PEM format)' }, - { type: 'text', - name: 'namespace', - title: 'Project namespace (optional/unique)', - placeholder: namespace_placeholder }, - { type: 'text', - name: 'token', - title: 'Token', - placeholder: 'Service token' } - ] - end - - def deprecated? - true - end - - def editable? - false - end - - def deprecation_message - content = if project - _("Kubernetes service integration has been disabled. Fields on this page are not used by GitLab, you can configure your Kubernetes clusters using the new <a href=\"%{url}\"/>Kubernetes Clusters</a> page") % { - url: Gitlab::Routing.url_helpers.project_clusters_path(project) - } - else - _("The instance-level Kubernetes service integration is disabled. Your data has been migrated to an <a href=\"%{url}\"/>instance-level cluster</a>.") % { - url: Gitlab::Routing.url_helpers.admin_clusters_path - } - end - - content.html_safe - end - - TEMPLATE_PLACEHOLDER = 'Kubernetes namespace'.freeze - - private - - def namespace_placeholder - default_namespace || TEMPLATE_PLACEHOLDER - end - - def default_namespace - return unless project - - slug = "#{project.path}-#{project.id}".downcase - slug.gsub(/[^-a-z0-9]/, '-').gsub(/^-+/, '') - end - - def enforce_namespace_to_lower_case - self.namespace = self.namespace&.downcase - end - - def deprecation_validation - return if active_changed?(from: true, to: false) || (new_record? && !active?) - - if deprecated? - errors[:base] << deprecation_message - end - end -end diff --git a/app/models/service.rb b/app/models/service.rb index 752467622f2..f6d8fb1fb46 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -260,7 +260,6 @@ class Service < ApplicationRecord hipchat irker jira - kubernetes mattermost_slash_commands mattermost packagist diff --git a/app/serializers/variable_entity.rb b/app/serializers/variable_entity.rb index 4d48e13cfca..8b19925f153 100644 --- a/app/serializers/variable_entity.rb +++ b/app/serializers/variable_entity.rb @@ -7,4 +7,5 @@ class VariableEntity < Grape::Entity expose :protected?, as: :protected expose :masked?, as: :masked + expose :environment_scope end diff --git a/app/services/create_snippet_service.rb b/app/services/create_snippet_service.rb index 6f1fce4989e..6e5bf823cc7 100644 --- a/app/services/create_snippet_service.rb +++ b/app/services/create_snippet_service.rb @@ -23,6 +23,7 @@ class CreateSnippetService < BaseService if snippet.save UserAgentDetailService.new(snippet, @request).create + Gitlab::UsageDataCounters::SnippetCounter.count(:create) end snippet diff --git a/app/services/notes/base_service.rb b/app/services/notes/base_service.rb index c1260837c12..b4d04c47cc0 100644 --- a/app/services/notes/base_service.rb +++ b/app/services/notes/base_service.rb @@ -9,5 +9,9 @@ module Notes note.noteable.diffs.clear_cache end end + + def increment_usage_counter(note) + Gitlab::UsageDataCounters::NoteCounter.count(:create, note.noteable_type) + end end end diff --git a/app/services/notes/create_service.rb b/app/services/notes/create_service.rb index 194c4a43dbc..a09272f0d83 100644 --- a/app/services/notes/create_service.rb +++ b/app/services/notes/create_service.rb @@ -41,6 +41,7 @@ module Notes todo_service.new_note(note, current_user) clear_noteable_diffs_cache(note) Suggestions::CreateService.new(note).execute + increment_usage_counter(note) end if quick_actions_service.commands_executed_count.to_i > 0 diff --git a/app/services/update_snippet_service.rb b/app/services/update_snippet_service.rb index 15bc1046a4e..2969c360de5 100644 --- a/app/services/update_snippet_service.rb +++ b/app/services/update_snippet_service.rb @@ -25,6 +25,8 @@ class UpdateSnippetService < BaseService snippet.assign_attributes(params) spam_check(snippet, current_user) - snippet.save + snippet.save.tap do |succeeded| + Gitlab::UsageDataCounters::SnippetCounter.count(:update) if succeeded + end end end diff --git a/app/views/admin/services/_form.html.haml b/app/views/admin/services/_form.html.haml index ab08d5c4906..495ee6a04ea 100644 --- a/app/views/admin/services/_form.html.haml +++ b/app/views/admin/services/_form.html.haml @@ -6,6 +6,5 @@ = form_for :service, url: admin_application_settings_service_path, method: :put, html: { class: 'fieldset-form' } do |form| = render 'shared/service_settings', form: form, subject: @service - - unless @service.is_a?(KubernetesService) - .footer-block.row-content-block - = form.submit 'Save', class: 'btn btn-success' + .footer-block.row-content-block + = form.submit 'Save', class: 'btn btn-success' diff --git a/app/views/ci/variables/_environment_scope.html.haml b/app/views/ci/variables/_environment_scope.html.haml new file mode 100644 index 00000000000..15e61d85881 --- /dev/null +++ b/app/views/ci/variables/_environment_scope.html.haml @@ -0,0 +1,21 @@ +- form_field = local_assigns.fetch(:form_field, nil) +- variable = local_assigns.fetch(:variable, nil) + +- if @project + - environment_scope = variable&.environment_scope || '*' + - environment_scope_label = environment_scope == '*' ? s_('CiVariable|All environments') : environment_scope + + %input{ type: "hidden", name: "#{form_field}[variables_attributes][][environment_scope]", value: environment_scope } + = dropdown_tag(environment_scope_label, + options: { wrapper_class: 'ci-variable-body-item js-variable-environment-dropdown-wrapper', + toggle_class: 'js-variable-environment-toggle wide', + filter: true, + dropdown_class: "dropdown-menu-selectable", + placeholder: s_('CiVariable|Search environments'), + footer_content: true, + data: { selected: environment_scope } }) do + %ul.dropdown-footer-list + %li + %button{ class: "dropdown-create-new-item-button js-dropdown-create-new-item", title: s_('CiVariable|New environment') } + = s_('CiVariable|Create wildcard') + %code diff --git a/app/views/ci/variables/_environment_scope_header.html.haml b/app/views/ci/variables/_environment_scope_header.html.haml new file mode 100644 index 00000000000..4ba4ceec16c --- /dev/null +++ b/app/views/ci/variables/_environment_scope_header.html.haml @@ -0,0 +1,2 @@ +.bold.table-section.section-15.append-right-10 + = s_('CiVariables|Scope') diff --git a/app/views/projects/forks/_fork_button.html.haml b/app/views/projects/forks/_fork_button.html.haml index 3f0798a898d..c7ed6a5094d 100644 --- a/app/views/projects/forks/_fork_button.html.haml +++ b/app/views/projects/forks/_fork_button.html.haml @@ -5,9 +5,9 @@ .bordered-box.fork-thumbnail.text-center.prepend-left-default.append-right-default.prepend-top-default.append-bottom-default.forked = link_to project_path(forked_project) do - if /no_((\w*)_)*avatar/.match(avatar) - = group_icon(namespace, class: "avatar rect-avatar s100 identicon") + = group_icon(namespace, class: "avatar rect-avatar s100 identicon mx-auto") - else - .avatar-container.s100 + .avatar-container.s100.mx-auto = image_tag(avatar, class: "avatar s100") %h5.prepend-top-default = namespace.human_name @@ -18,9 +18,9 @@ class: ("disabled has-tooltip" unless can_create_project), title: (_('You have reached your project limit') unless can_create_project) do - if /no_((\w*)_)*avatar/.match(avatar) - = group_icon(namespace, class: "avatar rect-avatar s100 identicon") + = group_icon(namespace, class: "avatar rect-avatar s100 identicon mx-auto") - else - .avatar-container.s100 + .avatar-container.s100.mx-auto = image_tag(avatar, class: "avatar s100") %h5.prepend-top-default = namespace.human_name diff --git a/app/views/projects/services/prometheus/_metrics.html.haml b/app/views/projects/services/prometheus/_metrics.html.haml index 3aefb3fdbb9..7685dee08fc 100644 --- a/app/views/projects/services/prometheus/_metrics.html.haml +++ b/app/views/projects/services/prometheus/_metrics.html.haml @@ -8,15 +8,16 @@ .col-lg-9 .card.js-panel-monitored-metrics{ data: { active_metrics: active_common_project_prometheus_metrics_path(project, :json), metrics_help_path: help_page_path('user/project/integrations/prometheus_library/index') } } .card-header - = s_('PrometheusService|Common metrics') + %strong + = s_('PrometheusService|Common metrics') %span.badge.badge-pill.js-monitored-count 0 .card-body .loading-metrics.js-loading-metrics - %p.prepend-top-10.prepend-left-10 + %p.m-3 = icon('spinner spin', class: 'metrics-load-spinner') = s_('PrometheusService|Finding and configuring metrics...') .empty-metrics.hidden.js-empty-metrics - %p.text-tertiary.prepend-top-10.prepend-left-10 + %p.text-tertiary.m-3 = s_('PrometheusService|Waiting for your first deployment to an environment to find common metrics') %ul.list-unstyled.metrics-list.hidden.js-metrics-list diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml index 07a7b5ce9de..214e87052da 100644 --- a/app/views/shared/issuable/_form.html.haml +++ b/app/views/shared/issuable/_form.html.haml @@ -33,8 +33,6 @@ = render_if_exists 'shared/issuable/approvals', issuable: issuable, presenter: presenter, form: form -= render_if_exists "shared/issuable/form/merge_request_blocks", issuable: issuable, form: form - = render 'shared/issuable/form/branch_chooser', issuable: issuable, form: form = render 'shared/issuable/form/merge_params', issuable: issuable diff --git a/app/views/shared/issuable/form/_metadata.html.haml b/app/views/shared/issuable/form/_metadata.html.haml index 1e03440a5dc..90a6a98235d 100644 --- a/app/views/shared/issuable/form/_metadata.html.haml +++ b/app/views/shared/issuable/form/_metadata.html.haml @@ -23,6 +23,7 @@ = render "shared/issuable/label_dropdown", classes: ["js-issuable-form-dropdown"], selected: issuable.labels, data_options: { field_name: "#{issuable.class.model_name.param_key}[label_ids][]", show_any: false }, dropdown_title: "Select label" = render_if_exists "shared/issuable/form/weight", issuable: issuable, form: form + = render_if_exists "shared/issuable/form/merge_request_blocks", issuable: issuable, form: form - if has_due_date .col-lg-6 |