From b90cf01a88df981f452a7f6b6d74e8fd0ccbf90b Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Thu, 5 Jan 2023 12:09:59 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- .../components/extensions/base.vue | 2 +- .../work_item_links/work_item_links_form.vue | 5 +- .../javascripts/work_items/constants.js | 12 ++ .../concerns/integrations/params.rb | 1 + app/finders/branches_finder.rb | 18 +- app/helpers/integrations_helper.rb | 8 + app/mailers/emails/profile.rb | 5 +- .../analytics/cycle_analytics/aggregation.rb | 8 +- .../cycle_analytics/project_stage.rb | 3 +- app/models/ci/bridge.rb | 12 +- .../analytics/cycle_analytics/parentable.rb | 22 +++ .../analytics/cycle_analytics/stage.rb | 16 +- app/models/deployment.rb | 2 +- app/models/integration.rb | 1 + .../integrations/base_chat_notification.rb | 6 +- .../chat_message/issue_message.rb | 10 +- app/models/project.rb | 1 + app/presenters/ci/build_runner_presenter.rb | 4 +- app/services/issues/base_service.rb | 10 ++ app/services/notification_service.rb | 4 +- app/views/dashboard/todos/_todo.html.haml | 9 +- .../access_token_expired_email.html.haml | 12 +- .../access_token_expired_email.text.erb | 10 +- app/workers/issues/rebalancing_worker.rb | 2 +- .../expired_notification_worker.rb | 18 +- app/workers/projects/delete_branch_worker.rb | 1 - .../ci_raw_variables_in_yaml_config.yml | 8 - .../track_and_raise_delete_source_errors.yml | 8 - config/webpack.config.js | 5 + doc/ci/yaml/index.md | 5 +- .../merge_request_widget_extensions.md | 2 +- doc/development/fe_guide/style/scss.md | 35 ++++ .../project/integrations/webhook_events.md | 39 +++-- lib/api/branches.rb | 1 + lib/api/entities/project_integration_basic.rb | 1 + lib/api/helpers/integrations_helpers.rb | 12 ++ .../cycle_analytics/request_params.rb | 4 +- lib/gitlab/ci/config/entry/variable.rb | 22 +-- lib/gitlab/ci/variables/collection.rb | 16 +- lib/gitlab/ci/yaml_processor/result.rb | 7 +- locale/gitlab.pot | 164 +++++++++++++++++- package.json | 3 +- qa/qa/page/component/groups_filter.rb | 43 ++--- .../raw_variables_defined_in_yaml_spec.rb | 75 ++------ .../analytics/cycle_analytics/aggregations.rb | 2 +- .../user_uploads_designs_spec.rb | 2 +- spec/finders/branches_finder_spec.rb | 30 +++- .../schemas/public_api/v4/integration.json | 3 + .../frontend/__mocks__/@cubejs-client/core.js | 26 +++ .../work_item_links_form_spec.js | 2 + .../gitlab/ci/config/entry/variable_spec.rb | 8 - spec/lib/gitlab/ci/yaml_processor_spec.rb | 26 --- spec/mailers/emails/profile_spec.rb | 8 +- .../cycle_analytics/aggregation_spec.rb | 22 ++- .../cycle_analytics/project_stage_spec.rb | 2 +- spec/models/ci/bridge_spec.rb | 19 -- spec/models/ci/namespace_mirror_spec.rb | 2 +- spec/models/deployment_spec.rb | 10 ++ spec/models/integration_spec.rb | 1 + .../base_chat_notification_spec.rb | 6 + .../chat_message/issue_message_spec.rb | 1 + spec/models/project_spec.rb | 1 + .../ci/build_runner_presenter_spec.rb | 17 -- .../create_pipeline_service/variables_spec.rb | 37 ---- spec/services/issues/close_service_spec.rb | 2 +- spec/services/issues/update_service_spec.rb | 2 + spec/services/notification_service_spec.rb | 7 +- .../helpers/cycle_analytics_helpers.rb | 2 +- .../cycle_analytics/parentable_examples.rb | 28 +++ .../cycle_analytics_stage_shared_examples.rb | 2 +- .../expired_notification_worker_spec.rb | 4 +- .../projects/delete_branch_worker_spec.rb | 18 -- yarn.lock | 11 +- 73 files changed, 609 insertions(+), 344 deletions(-) create mode 100644 app/models/concerns/analytics/cycle_analytics/parentable.rb delete mode 100644 config/feature_flags/development/ci_raw_variables_in_yaml_config.yml delete mode 100644 config/feature_flags/development/track_and_raise_delete_source_errors.yml create mode 100644 spec/frontend/__mocks__/@cubejs-client/core.js create mode 100644 spec/support/shared_examples/analytics/cycle_analytics/parentable_examples.rb diff --git a/app/assets/javascripts/vue_merge_request_widget/components/extensions/base.vue b/app/assets/javascripts/vue_merge_request_widget/components/extensions/base.vue index e8cc9b2eb2a..7cfc9431c2a 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/extensions/base.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/extensions/base.vue @@ -69,7 +69,7 @@ export default { isCollapsible() { if (!this.isLoadingSummary && this.loadingState !== LOADING_STATES.collapsedError) { if (this.shouldCollapse) { - return this.shouldCollapse(); + return this.shouldCollapse(this.collapsedData); } return true; diff --git a/app/assets/javascripts/work_items/components/work_item_links/work_item_links_form.vue b/app/assets/javascripts/work_items/components/work_item_links/work_item_links_form.vue index 436ecee07dc..d79aaab38f2 100644 --- a/app/assets/javascripts/work_items/components/work_item_links/work_item_links_form.vue +++ b/app/assets/javascripts/work_items/components/work_item_links/work_item_links_form.vue @@ -173,6 +173,9 @@ export default { childrenTypeName() { return WORK_ITEMS_TYPE_MAP[this.childrenType]?.name; }, + childrenTypeValue() { + return WORK_ITEMS_TYPE_MAP[this.childrenType]?.value; + }, addOrCreateButtonLabel() { if (this.isCreateForm) { return sprintfWorkItem(I18N_WORK_ITEM_CREATE_BUTTON_LABEL, this.childrenTypeName); @@ -198,7 +201,7 @@ export default { return this.isCreateForm ? this.createChild : this.addChild; }, childWorkItemType() { - return this.workItemTypes.find((type) => type.name === this.childrenTypeName)?.id; + return this.workItemTypes.find((type) => type.name === this.childrenTypeValue)?.id; }, parentIterationId() { return this.parentIteration?.id; diff --git a/app/assets/javascripts/work_items/constants.js b/app/assets/javascripts/work_items/constants.js index 21af1449e50..09d5909dac0 100644 --- a/app/assets/javascripts/work_items/constants.js +++ b/app/assets/javascripts/work_items/constants.js @@ -31,7 +31,12 @@ export const WORK_ITEM_TYPE_ENUM_REQUIREMENTS = 'REQUIREMENTS'; export const WORK_ITEM_TYPE_ENUM_OBJECTIVE = 'OBJECTIVE'; export const WORK_ITEM_TYPE_ENUM_KEY_RESULT = 'KEY_RESULT'; +export const WORK_ITEM_TYPE_VALUE_INCIDENT = 'Incident'; export const WORK_ITEM_TYPE_VALUE_ISSUE = 'Issue'; +export const WORK_ITEM_TYPE_VALUE_TASK = 'Task'; +export const WORK_ITEM_TYPE_VALUE_TEST_CASE = 'Test case'; +export const WORK_ITEM_TYPE_VALUE_REQUIREMENTS = 'Requirements'; +export const WORK_ITEM_TYPE_VALUE_KEY_RESULT = 'Key result'; export const WORK_ITEM_TYPE_VALUE_OBJECTIVE = 'Objective'; export const i18n = { @@ -103,30 +108,37 @@ export const WORK_ITEMS_TYPE_MAP = { [WORK_ITEM_TYPE_ENUM_INCIDENT]: { icon: `issue-type-incident`, name: s__('WorkItem|Incident'), + value: WORK_ITEM_TYPE_VALUE_INCIDENT, }, [WORK_ITEM_TYPE_ENUM_ISSUE]: { icon: `issue-type-issue`, name: s__('WorkItem|Issue'), + value: WORK_ITEM_TYPE_VALUE_ISSUE, }, [WORK_ITEM_TYPE_ENUM_TASK]: { icon: `issue-type-task`, name: s__('WorkItem|Task'), + value: WORK_ITEM_TYPE_VALUE_TASK, }, [WORK_ITEM_TYPE_ENUM_TEST_CASE]: { icon: `issue-type-test-case`, name: s__('WorkItem|Test case'), + value: WORK_ITEM_TYPE_VALUE_TEST_CASE, }, [WORK_ITEM_TYPE_ENUM_REQUIREMENTS]: { icon: `issue-type-requirements`, name: s__('WorkItem|Requirements'), + value: WORK_ITEM_TYPE_VALUE_REQUIREMENTS, }, [WORK_ITEM_TYPE_ENUM_OBJECTIVE]: { icon: `issue-type-objective`, name: s__('WorkItem|Objective'), + value: WORK_ITEM_TYPE_VALUE_OBJECTIVE, }, [WORK_ITEM_TYPE_ENUM_KEY_RESULT]: { icon: `issue-type-keyresult`, name: s__('WorkItem|Key Result'), + value: WORK_ITEM_TYPE_VALUE_KEY_RESULT, }, }; diff --git a/app/controllers/concerns/integrations/params.rb b/app/controllers/concerns/integrations/params.rb index 1da612893ce..4d181ded071 100644 --- a/app/controllers/concerns/integrations/params.rb +++ b/app/controllers/concerns/integrations/params.rb @@ -41,6 +41,7 @@ module Integrations :external_wiki_url, :google_iap_service_account_json, :google_iap_audience_client_id, + :incident_events, :inherit_from_id, # We're using `issues_events` and `merge_requests_events` # in the view so we still need to explicitly state them diff --git a/app/finders/branches_finder.rb b/app/finders/branches_finder.rb index a62d47071d4..dc7b9f6a0ce 100644 --- a/app/finders/branches_finder.rb +++ b/app/finders/branches_finder.rb @@ -6,11 +6,12 @@ class BranchesFinder < GitRefsFinder end def execute(gitaly_pagination: false) - if gitaly_pagination && names.blank? && search.blank? + if gitaly_pagination && names.blank? && search.blank? && regex.blank? repository.branches_sorted_by(sort, pagination_params) else branches = repository.branches_sorted_by(sort) branches = by_search(branches) + branches = by_regex(branches) by_names(branches) end end @@ -29,6 +30,11 @@ class BranchesFinder < GitRefsFinder @params[:per_page].presence end + def regex + @params[:regex].to_s.presence + end + strong_memoize_attr :regex + def page_token "#{Gitlab::Git::BRANCH_REF_PREFIX}#{@params[:page_token]}" if @params[:page_token] end @@ -45,4 +51,14 @@ class BranchesFinder < GitRefsFinder branch_names.include?(branch.name) end end + + def by_regex(branches) + return branches unless regex + + branch_filter = ::Gitlab::UntrustedRegexp.new(regex) + + branches.select do |branch| + branch_filter.match?(branch.name) + end + end end diff --git a/app/helpers/integrations_helper.rb b/app/helpers/integrations_helper.rb index 0650af33e37..5471109e6d5 100644 --- a/app/helpers/integrations_helper.rb +++ b/app/helpers/integrations_helper.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true module IntegrationsHelper + # rubocop:disable Metrics/CyclomaticComplexity def integration_event_title(event) case event when "push", "push_events" @@ -27,8 +28,11 @@ module IntegrationsHelper _("Deployment") when "alert" _("Alert") + when "incident" + _("Incident") end end + # rubocop:enable Metrics/CyclomaticComplexity def integration_event_description(integration, event) case integration @@ -230,6 +234,7 @@ module IntegrationsHelper end end + # rubocop:disable Metrics/CyclomaticComplexity def default_integration_event_description(event) case event when "push", "push_events" @@ -256,8 +261,11 @@ module IntegrationsHelper s_("ProjectService|Trigger event when a deployment starts or finishes.") when "alert" s_("ProjectService|Trigger event when a new, unique alert is recorded.") + when "incident" + s_("ProjectService|Trigger event when an incident is created.") end end + # rubocop:enable Metrics/CyclomaticComplexity def trigger_events_for_integration(integration) Integrations::EventSerializer.new(integration: integration).represent(integration.configurable_events).to_json diff --git a/app/mailers/emails/profile.rb b/app/mailers/emails/profile.rb index ede6007e0e2..5b1750400d8 100644 --- a/app/mailers/emails/profile.rb +++ b/app/mailers/emails/profile.rb @@ -83,14 +83,15 @@ module Emails end end - def access_token_expired_email(user) + def access_token_expired_email(user, token_names = []) return unless user && user.active? @user = user + @token_names = token_names @target_url = profile_personal_access_tokens_url Gitlab::I18n.with_locale(@user.preferred_language) do - mail_with_locale(to: @user.notification_email_or_default, subject: subject(_("Your personal access token has expired"))) + mail_with_locale(to: @user.notification_email_or_default, subject: subject(_("Your personal access tokens have expired"))) end end diff --git a/app/models/analytics/cycle_analytics/aggregation.rb b/app/models/analytics/cycle_analytics/aggregation.rb index a888422a6b4..b432955ad88 100644 --- a/app/models/analytics/cycle_analytics/aggregation.rb +++ b/app/models/analytics/cycle_analytics/aggregation.rb @@ -2,8 +2,7 @@ class Analytics::CycleAnalytics::Aggregation < ApplicationRecord include FromUnion - - belongs_to :group, optional: false + include Analytics::CycleAnalytics::Parentable validates :incremental_runtimes_in_seconds, :incremental_processed_records, :full_runtimes_in_seconds, :full_processed_records, presence: true, length: { maximum: 10 }, allow_blank: true @@ -58,7 +57,10 @@ class Analytics::CycleAnalytics::Aggregation < ApplicationRecord estimation < 1 ? nil : estimation.from_now end - def self.safe_create_for_group(group) + def self.safe_create_for_namespace(group_or_project_namespace) + # Namespaces::ProjectNamespace has no root_ancestor + # Related: https://gitlab.com/gitlab-org/gitlab/-/issues/386124 + group = group_or_project_namespace.is_a?(Group) ? group_or_project_namespace : group_or_project_namespace.parent top_level_group = group.root_ancestor aggregation = find_by(group_id: top_level_group.id) return aggregation if aggregation.present? diff --git a/app/models/analytics/cycle_analytics/project_stage.rb b/app/models/analytics/cycle_analytics/project_stage.rb index 8d3a032812e..68fe43890bd 100644 --- a/app/models/analytics/cycle_analytics/project_stage.rb +++ b/app/models/analytics/cycle_analytics/project_stage.rb @@ -5,8 +5,7 @@ module Analytics class ProjectStage < ApplicationRecord include Analytics::CycleAnalytics::Stage - validates :project, presence: true - belongs_to :project + belongs_to :project, optional: false belongs_to :value_stream, class_name: 'Analytics::CycleAnalytics::ProjectValueStream', foreign_key: :project_value_stream_id alias_attribute :parent, :project diff --git a/app/models/ci/bridge.rb b/app/models/ci/bridge.rb index 22dcc7c18a0..4af31fd37f2 100644 --- a/app/models/ci/bridge.rb +++ b/app/models/ci/bridge.rb @@ -281,7 +281,7 @@ module Ci return [] unless forward_yaml_variables? yaml_variables.to_a.map do |hash| - if hash[:raw] && ci_raw_variables_in_yaml_config_enabled? + if hash[:raw] { key: hash[:key], value: hash[:value], raw: true } else { key: hash[:key], value: ::ExpandVariables.expand(hash[:value], expand_variables) } @@ -293,7 +293,7 @@ module Ci return [] unless forward_pipeline_variables? pipeline.variables.to_a.map do |variable| - if variable.raw? && ci_raw_variables_in_yaml_config_enabled? + if variable.raw? { key: variable.key, value: variable.value, raw: true } else { key: variable.key, value: ::ExpandVariables.expand(variable.value, expand_variables) } @@ -306,7 +306,7 @@ module Ci return [] unless pipeline.pipeline_schedule pipeline.pipeline_schedule.variables.to_a.map do |variable| - if variable.raw? && ci_raw_variables_in_yaml_config_enabled? + if variable.raw? { key: variable.key, value: variable.value, raw: true } else { key: variable.key, value: ::ExpandVariables.expand(variable.value, expand_variables) } @@ -329,12 +329,6 @@ module Ci result.nil? ? FORWARD_DEFAULTS[:pipeline_variables] : result end end - - def ci_raw_variables_in_yaml_config_enabled? - strong_memoize(:ci_raw_variables_in_yaml_config_enabled) do - ::Feature.enabled?(:ci_raw_variables_in_yaml_config, project) - end - end end end diff --git a/app/models/concerns/analytics/cycle_analytics/parentable.rb b/app/models/concerns/analytics/cycle_analytics/parentable.rb new file mode 100644 index 00000000000..785f6eea6bf --- /dev/null +++ b/app/models/concerns/analytics/cycle_analytics/parentable.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module Analytics + module CycleAnalytics + module Parentable + extend ActiveSupport::Concern + + included do + belongs_to :namespace, class_name: 'Namespace', foreign_key: :group_id, optional: false # rubocop: disable Rails/InverseOf + + validate :ensure_namespace_type + + def ensure_namespace_type + return if namespace.nil? + return if namespace.is_a?(::Namespaces::ProjectNamespace) || namespace.is_a?(::Group) + + errors.add(:namespace, s_('CycleAnalytics|the assigned object is not supported')) + end + end + end + end +end diff --git a/app/models/concerns/analytics/cycle_analytics/stage.rb b/app/models/concerns/analytics/cycle_analytics/stage.rb index d9e6756ab86..9293002109b 100644 --- a/app/models/concerns/analytics/cycle_analytics/stage.rb +++ b/app/models/concerns/analytics/cycle_analytics/stage.rb @@ -119,21 +119,23 @@ module Analytics end def validate_labels - validate_label_within_group(:start_event_label_id, start_event_label_id) if start_event_label_id_changed? - validate_label_within_group(:end_event_label_id, end_event_label_id) if end_event_label_id_changed? + validate_label_within_namespace(:start_event_label_id, start_event_label_id) if start_event_label_id_changed? + validate_label_within_namespace(:end_event_label_id, end_event_label_id) if end_event_label_id_changed? end - def validate_label_within_group(association_name, label_id) + def validate_label_within_namespace(association_name, label_id) return unless label_id - return unless group - unless label_available_for_group?(label_id) + unless label_available_for_namespace?(label_id) errors.add(association_name, s_('CycleAnalyticsStage|is not available for the selected group')) end end - def label_available_for_group?(label_id) - LabelsFinder.new(nil, { group_id: group.id, include_ancestor_groups: true, only_group_labels: true }) + def label_available_for_namespace?(label_id) + subject = is_a?(::Analytics::CycleAnalytics::GroupStage) ? namespace : project.group + return unless subject + + LabelsFinder.new(nil, { group_id: subject.id, include_ancestor_groups: true, only_group_labels: true }) .execute(skip_authorization: true) .id_in(label_id) .exists? diff --git a/app/models/deployment.rb b/app/models/deployment.rb index c80f208ca1d..1ae7d9925a5 100644 --- a/app/models/deployment.rb +++ b/app/models/deployment.rb @@ -294,7 +294,7 @@ class Deployment < ApplicationRecord end def older_than_last_successful_deployment? - last_deployment_id = environment.last_deployment&.id + last_deployment_id = environment&.last_deployment&.id return false unless last_deployment_id.present? return false if self.id == last_deployment_id diff --git a/app/models/integration.rb b/app/models/integration.rb index df1560b87a5..e3af4b580c3 100644 --- a/app/models/integration.rb +++ b/app/models/integration.rb @@ -75,6 +75,7 @@ class Integration < ApplicationRecord attribute :active, default: false attribute :alert_events, default: true + attribute :incident_events, default: false attribute :category, default: 'common' attribute :commit_events, default: true attribute :confidential_issues_events, default: true diff --git a/app/models/integrations/base_chat_notification.rb b/app/models/integrations/base_chat_notification.rb index f2a707c2214..43cfe9d7fd8 100644 --- a/app/models/integrations/base_chat_notification.rb +++ b/app/models/integrations/base_chat_notification.rb @@ -10,7 +10,7 @@ module Integrations SUPPORTED_EVENTS = %w[ push issue confidential_issue merge_request note confidential_note - tag_push pipeline wiki_page deployment + tag_push pipeline wiki_page deployment incident ].freeze SUPPORTED_EVENTS_FOR_LABEL_FILTER = %w[issue confidential_issue merge_request note confidential_note].freeze @@ -224,6 +224,7 @@ module Integrations data.merge(project_url: project_url, project_name: project_name).with_indifferent_access end + # rubocop:disable Metrics/CyclomaticComplexity def get_message(object_kind, data) case object_kind when "push", "tag_push" @@ -240,8 +241,11 @@ module Integrations Integrations::ChatMessage::WikiPageMessage.new(data) when "deployment" Integrations::ChatMessage::DeploymentMessage.new(data) if notify_for_ref?(data) + when "incident" + Integrations::ChatMessage::IssueMessage.new(data) unless update?(data) end end + # rubocop:enable Metrics/CyclomaticComplexity def build_event_channels event_channel_names.map do |channel_field| diff --git a/app/models/integrations/chat_message/issue_message.rb b/app/models/integrations/chat_message/issue_message.rb index ca8ef670e67..1c234630370 100644 --- a/app/models/integrations/chat_message/issue_message.rb +++ b/app/models/integrations/chat_message/issue_message.rb @@ -9,6 +9,7 @@ module Integrations attr_reader :action attr_reader :state attr_reader :description + attr_reader :object_kind def initialize(params) super @@ -21,6 +22,7 @@ module Integrations @action = obj_attr[:action] @state = obj_attr[:state] @description = obj_attr[:description] || '' + @object_kind = params[:object_kind] end def attachments @@ -32,7 +34,7 @@ module Integrations def activity { - title: "Issue #{state} by #{strip_markup(user_combined_name)}", + title: "#{issue_type} #{state} by #{strip_markup(user_combined_name)}", subtitle: "in #{project_link}", text: issue_link, image: user_avatar @@ -42,7 +44,7 @@ module Integrations private def message - "[#{project_link}] Issue #{issue_link} #{state} by #{strip_markup(user_combined_name)}" + "[#{project_link}] #{issue_type} #{issue_link} #{state} by #{strip_markup(user_combined_name)}" end def opened_issue? @@ -69,6 +71,10 @@ module Integrations def issue_title "#{Issue.reference_prefix}#{issue_iid} #{strip_markup(title)}" end + + def issue_type + @issue_type ||= object_kind == 'incident' ? 'Incident' : 'Issue' + end end end end diff --git a/app/models/project.rb b/app/models/project.rb index 116f81207f6..64a4d25b4ef 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -270,6 +270,7 @@ class Project < ApplicationRecord has_many :integrations has_many :alert_hooks_integrations, -> { alert_hooks }, class_name: 'Integration' + has_many :incident_hooks_integrations, -> { incident_hooks }, class_name: 'Integration' has_many :archive_trace_hooks_integrations, -> { archive_trace_hooks }, class_name: 'Integration' has_many :confidential_issue_hooks_integrations, -> { confidential_issue_hooks }, class_name: 'Integration' has_many :confidential_note_hooks_integrations, -> { confidential_note_hooks }, class_name: 'Integration' diff --git a/app/presenters/ci/build_runner_presenter.rb b/app/presenters/ci/build_runner_presenter.rb index ff9522e2db0..9a586a1733f 100644 --- a/app/presenters/ci/build_runner_presenter.rb +++ b/app/presenters/ci/build_runner_presenter.rb @@ -33,12 +33,10 @@ module Ci end def runner_variables - stop_expanding_raw_refs = ::Feature.enabled?(:ci_raw_variables_in_yaml_config, project) - variables .sort_and_expand_all(keep_undefined: true, expand_file_refs: false, - expand_raw_refs: !stop_expanding_raw_refs) + expand_raw_refs: false) .to_runner_variables end diff --git a/app/services/issues/base_service.rb b/app/services/issues/base_service.rb index 46f343ccbdd..553fb6e2ac9 100644 --- a/app/services/issues/base_service.rb +++ b/app/services/issues/base_service.rb @@ -97,6 +97,16 @@ module Issues hooks_scope = issue.confidential? ? :confidential_issue_hooks : :issue_hooks issue.project.execute_hooks(issue_data, hooks_scope) issue.project.execute_integrations(issue_data, hooks_scope) + + execute_incident_hooks(issue, issue_data) if issue.incident? + end + + # We can remove this code after proposal in + # https://gitlab.com/gitlab-org/gitlab/-/issues/367550#proposal is updated. + def execute_incident_hooks(issue, issue_data) + issue_data[:object_kind] = 'incident' + issue_data[:event_type] = 'incident' + issue.project.execute_integrations(issue_data, :incident_hooks) end def update_project_counter_caches?(issue) diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb index 550bd6d4c55..777d02c590d 100644 --- a/app/services/notification_service.rb +++ b/app/services/notification_service.rb @@ -91,10 +91,10 @@ class NotificationService end # Notify the user when at least one of their personal access tokens has expired today - def access_token_expired(user) + def access_token_expired(user, token_names = []) return unless user.can?(:receive_notifications) - mailer.access_token_expired_email(user).deliver_later + mailer.access_token_expired_email(user, token_names).deliver_later end # Notify the user when one of their personal access tokens is revoked diff --git a/app/views/dashboard/todos/_todo.html.haml b/app/views/dashboard/todos/_todo.html.haml index b0f21eed348..c54ad094883 100644 --- a/app/views/dashboard/todos/_todo.html.haml +++ b/app/views/dashboard/todos/_todo.html.haml @@ -25,13 +25,15 @@ .todo-body.gl-mb-2.gl-px-2.gl-display-flex.gl-align-items-flex-start.gl-lg-align-items-center .todo-avatar.gl-display-none.gl-sm-display-inline-block = author_avatar(todo, size: 24) - .todo-note{ :class => ("gl-display-flex gl-flex-direction-column" if todo.note.present? && two_line_mention) } + .todo-note - if todo_author_display?(todo) - .author-name.bold.gl-display-inline + .author-name.bold.gl-display-inline< - if todo.author = link_to_author(todo, self_added: todo.self_added?) - else = _('(removed)') + - if todo.note.present? && two_line_mention + \: %span.action-name{ data: { qa_selector: "todo_action_name_content" } }< - if two_line_mention @@ -52,7 +54,8 @@ \. - if todo.note.present? %span.action-description{ :class => ("gl-font-style-italic" if !two_line_mention) }< - = first_line_in_markdown(todo, :body, 100, is_todo: true, project: todo.project, group: todo.group) + - max_chars = two_line_mention ? 125 : 100 + = first_line_in_markdown(todo, :body, max_chars, is_todo: true, project: todo.project, group: todo.group) .todo-timestamp.gl-white-space-nowrap.gl-sm-ml-3.gl-mt-2.gl-mb-2.gl-sm-my-0.gl-px-2.gl-sm-px-0 %span.todo-timestamp.gl-font-sm.gl-text-gray-500 diff --git a/app/views/notify/access_token_expired_email.html.haml b/app/views/notify/access_token_expired_email.html.haml index 1e7c07c2282..9c3ef4cfdff 100644 --- a/app/views/notify/access_token_expired_email.html.haml +++ b/app/views/notify/access_token_expired_email.html.haml @@ -1,7 +1,15 @@ %p = _('Hi %{username}!') % { username: sanitize_name(@user.name) } %p - = _('One or more of your personal access tokens has expired.') + - if @token_names.empty? + = _('One or more of your personal access tokens has expired.') + - else + = _('The following personal access tokens have expired:') + + %p + %ul + - @token_names.each do |token| + %li= token %p - - pat_link_start = ''.html_safe % { url: @target_url } + - pat_link_start = ''.html_safe % { url: @target_url } = html_escape(_('You can create a new one or check them in your %{pat_link_start}personal access tokens%{pat_link_end} settings.')) % { pat_link_start: pat_link_start, pat_link_end: ''.html_safe } diff --git a/app/views/notify/access_token_expired_email.text.erb b/app/views/notify/access_token_expired_email.text.erb index 4dc67e85dc2..6f6a9d38192 100644 --- a/app/views/notify/access_token_expired_email.text.erb +++ b/app/views/notify/access_token_expired_email.text.erb @@ -1,5 +1,13 @@ <%= _('Hi %{username}!') % { username: sanitize_name(@user.name) } %> -<%= _('One or more of your personal access tokens has expired.') %> +<%- if @token_names.empty? -%> +<%= _('One or more of your personal access tokens have expired.') %> +<%- else -%> +<%= _('The following personal access tokens have expired:') %> + +<%- @token_names.each do |token| -%> + - <%= token %> +<%- end -%> +<%- end -%> <%= _('You can create a new one or check them in your personal access tokens settings %{pat_link}.') % { pat_link: @target_url } %> diff --git a/app/workers/issues/rebalancing_worker.rb b/app/workers/issues/rebalancing_worker.rb index 8de0588a2a1..14cb97ab0e8 100644 --- a/app/workers/issues/rebalancing_worker.rb +++ b/app/workers/issues/rebalancing_worker.rb @@ -19,7 +19,7 @@ module Issues return if project_id.nil? && root_namespace_id.nil? return if ::Gitlab::Issues::Rebalancing::State.rebalance_recently_finished?(project_id, root_namespace_id) - # pull the projects collection to be rebalanced either the project if namespace is not a group(i.e. user namesapce) + # pull the projects collection to be rebalanced either the project if namespace is not a group(i.e. user namespace) # or the root namespace, this also makes the worker backward compatible with previous version where a project_id was # passed as the param projects_to_rebalance = projects_collection(project_id, root_namespace_id) diff --git a/app/workers/personal_access_tokens/expired_notification_worker.rb b/app/workers/personal_access_tokens/expired_notification_worker.rb index 2d0ea3d3aa4..b119957fa2c 100644 --- a/app/workers/personal_access_tokens/expired_notification_worker.rb +++ b/app/workers/personal_access_tokens/expired_notification_worker.rb @@ -10,16 +10,28 @@ module PersonalAccessTokens feature_category :authentication_and_authorization + MAX_TOKENS = 100 + def perform(*args) notification_service = NotificationService.new User.with_personal_access_tokens_expired_today.find_each do |user| with_context(user: user) do - Gitlab::AppLogger.info "#{self.class}: Notifying User #{user.id} about an expired token" + expiring_user_tokens = user.personal_access_tokens.without_impersonation.expired_today_and_not_notified - notification_service.access_token_expired(user) + # rubocop: disable CodeReuse/ActiveRecord + # We never materialise the token instances. We need the names to mention them in the + # email. Later we trigger an update query on the entire relation, not on individual instances. + token_names = expiring_user_tokens.limit(MAX_TOKENS).pluck(:name) + # rubocop: enable CodeReuse/ActiveRecord - user.personal_access_tokens.without_impersonation.expired_today_and_not_notified.update_all(after_expiry_notification_delivered: true) + notification_service.access_token_expired(user, token_names) + + Gitlab::AppLogger.info "#{self.class}: Notifying User #{user.id} about expired tokens" + + expiring_user_tokens.each_batch do |expiring_tokens| + expiring_tokens.update_all(after_expiry_notification_delivered: true) + end end end end diff --git a/app/workers/projects/delete_branch_worker.rb b/app/workers/projects/delete_branch_worker.rb index 1949fb67e83..22506d4459b 100644 --- a/app/workers/projects/delete_branch_worker.rb +++ b/app/workers/projects/delete_branch_worker.rb @@ -20,7 +20,6 @@ module Projects delete_service_result = ::Branches::DeleteService.new(project, user) .execute(branch_name) - return unless Feature.enabled?(:track_and_raise_delete_source_errors, project) # Only want to raise on 400 to avoid permission and non existant branch error return unless delete_service_result[:http_status] == 400 diff --git a/config/feature_flags/development/ci_raw_variables_in_yaml_config.yml b/config/feature_flags/development/ci_raw_variables_in_yaml_config.yml deleted file mode 100644 index 0b6fc6022f4..00000000000 --- a/config/feature_flags/development/ci_raw_variables_in_yaml_config.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: ci_raw_variables_in_yaml_config -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/98420 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/375034 -milestone: '15.6' -type: development -group: group::pipeline authoring -default_enabled: true diff --git a/config/feature_flags/development/track_and_raise_delete_source_errors.yml b/config/feature_flags/development/track_and_raise_delete_source_errors.yml deleted file mode 100644 index 5f34ab47f19..00000000000 --- a/config/feature_flags/development/track_and_raise_delete_source_errors.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: track_and_raise_delete_source_errors -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/103842 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/382440 -milestone: '15.6' -type: development -group: group::code review -default_enabled: false diff --git a/config/webpack.config.js b/config/webpack.config.js index c76ffcb672e..fd58e22bb99 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -312,6 +312,11 @@ module.exports = { test: /\.mjs$/, use: [], }, + { + test: /(@cubejs-client\/vue).*\.(js)?$/, + include: /node_modules/, + loader: 'babel-loader', + }, WEBPACK_USE_ESBUILD_LOADER && { test: /\.(js|cjs)$/, exclude: (modulePath) => diff --git a/doc/ci/yaml/index.md b/doc/ci/yaml/index.md index 48d23609542..dbb34937adc 100644 --- a/doc/ci/yaml/index.md +++ b/doc/ci/yaml/index.md @@ -4384,10 +4384,7 @@ variables: > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/353991) in GitLab 15.6 [with a flag](../../administration/feature_flags.md) named `ci_raw_variables_in_yaml_config`. Disabled by default. > - [Enabled on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/375034) in GitLab 15.6. > - [Enabled on self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/375034) in GitLab 15.7. - -FLAG: -On self-managed GitLab, by default this feature is available. To hide the feature per project, -ask an administrator to [disable the feature flag](../../administration/feature_flags.md) named `ci_raw_variables_in_yaml_config`. +> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/375034) in GitLab 15.8. Feature flag `ci_raw_variables_in_yaml_config` removed. Use the `expand` keyword to configure a variable to be expandable or not. diff --git a/doc/development/fe_guide/merge_request_widget_extensions.md b/doc/development/fe_guide/merge_request_widget_extensions.md index 49c6664c6d6..d6f4fde3320 100644 --- a/doc/development/fe_guide/merge_request_widget_extensions.md +++ b/doc/development/fe_guide/merge_request_widget_extensions.md @@ -40,7 +40,7 @@ export default { summary(data) {}, // Required: Level 1 summary text statusIcon(data) {}, // Required: Level 1 status icon tertiaryButtons() {}, // Optional: Level 1 action buttons - shouldCollapse() {}, // Optional: Add logic to determine if the widget can expand or not + shouldCollapse(data) {}, // Optional: Add logic to determine if the widget can expand or not }, methods: { fetchCollapsedData(props) {}, // Required: Fetches data required for collapsed state diff --git a/doc/development/fe_guide/style/scss.md b/doc/development/fe_guide/style/scss.md index 7a5c955db93..b84f41311b6 100644 --- a/doc/development/fe_guide/style/scss.md +++ b/doc/development/fe_guide/style/scss.md @@ -61,6 +61,41 @@ Inspiration: - - +#### Utility mixins + +In addition to utility classes GitLab UI provides utility mixins named after the utility classes. + +For example a utility class `.gl-mt-3` will have a corresponding mixin `gl-mt-3`. Here's how it can be used in an SCSS file: + +```scss +.my-class { + @include gl-mt-3; +} +``` + +These mixins should be used to replace _magic values_ in our code. +For example a `margin-top: 8px` is a good candidate for the `@include gl-mt-3` mixin replacement. + +Avoid using utility mixins for [pre-defined CSS keywords](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Values_and_Units#pre-defined_keyword_values). +For example prefer `display: flex` over `@include gl-display-flex`. + +```scss +// Bad +.my-class { + @include gl-display-flex; +} + +// Good +.my-class { + display: flex; +} + +// Good +.my-class { + @include gl-mt-3; +} +``` + ### Naming Filenames should use `snake_case`. diff --git a/doc/user/project/integrations/webhook_events.md b/doc/user/project/integrations/webhook_events.md index bc6c59ed94f..dcb42bf595b 100644 --- a/doc/user/project/integrations/webhook_events.md +++ b/doc/user/project/integrations/webhook_events.md @@ -1362,17 +1362,6 @@ Payload example: ## Job events -- Number of retries (`retries_count`) [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/382046) in GitLab 15.6 [with a flag](../../../administration/feature_flags.md) - named `job_webhook_retries_count`. Disabled by default. -- Pipeline name (`commit.name`) [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/107963) in GitLab 15.8 [with a flag](../../../administration/feature_flags.md) - named `pipeline_name`. Enabled by default. - -FLAG: -On self-managed GitLab, by default this feature is not available. To make it available, -ask an administrator to [enable the feature flag](../../../administration/feature_flags.md) named -`job_webhook_retries_count`. -On GitLab.com, this feature is not available. - Job events are triggered when the status of a job changes. The `commit.id` in the payload is the ID of the pipeline, not the ID of the commit. @@ -1405,7 +1394,7 @@ Payload example: "build_duration": null, "build_allow_failure": false, "build_failure_reason": "script_failure", - "retries_count": 2, // 2 indicates this is the 2nd retry of this job + "retries_count": 2, // the second retry of this job "pipeline_id": 2366, "project_id": 380, "project_name": "gitlab-org/gitlab-test", @@ -1450,6 +1439,32 @@ Payload example: } ``` +### Number of retries + +> `retries_count` [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/382046) in GitLab 15.6 [with a flag](../../../administration/feature_flags.md) named `job_webhook_retries_count`. Disabled by default. + +FLAG: +On self-managed GitLab, by default this feature is not available. To make it available, +ask an administrator to [enable the feature flag](../../../administration/feature_flags.md) named +`job_webhook_retries_count`. +On GitLab.com, this feature is not available. + +`retries_count` is an integer that indicates if the job is a retry. `0` means that the job +has not been retried. `1` means that it's the first retry. + +### Pipeline name + +> `commit.name` [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/107963) in GitLab 15.8 [with a flag](../../../administration/feature_flags.md) named `pipeline_name`. Enabled by default. + +FLAG: +On self-managed GitLab, by default this feature is available. To hide the feature, +ask an administrator to [disable the feature flag](../../../administration/feature_flags.md) named +`pipeline_name`. +On GitLab.com, this feature is available. + +You can set custom names for pipelines with [`workflow:name`](../../../ci/yaml/index.md#workflowname). +If the pipeline has a name, that name is the value of `commit.name`. + ## Deployment events Deployment events are triggered when a deployment: diff --git a/lib/api/branches.rb b/lib/api/branches.rb index 845e42c2ed8..5ae1a80a7fd 100644 --- a/lib/api/branches.rb +++ b/lib/api/branches.rb @@ -24,6 +24,7 @@ module API helpers do params :filter_params do optional :search, type: String, desc: 'Return list of branches matching the search criteria' + optional :regex, type: String, desc: 'Return list of branches matching the regex' optional :sort, type: String, desc: 'Return list of branches sorted by the given field', values: %w[name_asc updated_asc updated_desc] end end diff --git a/lib/api/entities/project_integration_basic.rb b/lib/api/entities/project_integration_basic.rb index aa0ad158b83..b7c56d7cca1 100644 --- a/lib/api/entities/project_integration_basic.rb +++ b/lib/api/entities/project_integration_basic.rb @@ -14,6 +14,7 @@ module API expose :commit_events, documentation: { type: 'boolean' } expose :push_events, documentation: { type: 'boolean' } expose :issues_events, documentation: { type: 'boolean' } + expose :incident_events, documentation: { type: 'boolean' } expose :confidential_issues_events, documentation: { type: 'boolean' } expose :merge_requests_events, documentation: { type: 'boolean' } expose :tag_push_events, documentation: { type: 'boolean' } diff --git a/lib/api/helpers/integrations_helpers.rb b/lib/api/helpers/integrations_helpers.rb index e2549c4bffb..31328facd69 100644 --- a/lib/api/helpers/integrations_helpers.rb +++ b/lib/api/helpers/integrations_helpers.rb @@ -61,6 +61,12 @@ module API type: String, desc: 'The name of the channel to receive issues_events notifications' }, + { + required: false, + name: :incident_channel, + type: String, + desc: 'The name of the channel to receive incident_events notifications' + }, { required: false, name: :confidential_issue_channel, @@ -114,6 +120,12 @@ module API type: Boolean, desc: 'Enable notifications for issues_events' }, + { + required: false, + name: :incident_events, + type: Boolean, + desc: 'Enable notifications for incident_events' + }, { required: false, name: :confidential_issues_events, diff --git a/lib/gitlab/analytics/cycle_analytics/request_params.rb b/lib/gitlab/analytics/cycle_analytics/request_params.rb index ac9c465bf7d..42a7d8eecb0 100644 --- a/lib/gitlab/analytics/cycle_analytics/request_params.rb +++ b/lib/gitlab/analytics/cycle_analytics/request_params.rb @@ -118,14 +118,14 @@ module Gitlab end def aggregation - @aggregation ||= ::Analytics::CycleAnalytics::Aggregation.safe_create_for_group(group) + @aggregation ||= ::Analytics::CycleAnalytics::Aggregation.safe_create_for_namespace(group) end def group_data_attributes { id: group.id, + namespace_id: group.id, name: group.name, - parent_id: group.parent_id, full_path: group.full_path, avatar_url: group.avatar_url } diff --git a/lib/gitlab/ci/config/entry/variable.rb b/lib/gitlab/ci/config/entry/variable.rb index decb568ffc9..a5c6aaa1e3a 100644 --- a/lib/gitlab/ci/config/entry/variable.rb +++ b/lib/gitlab/ci/config/entry/variable.rb @@ -54,9 +54,7 @@ module Gitlab validates :key, alphanumeric: true validates :config_value, alphanumeric: true, allow_nil: true validates :config_description, alphanumeric: true, allow_nil: true - validates :config_expand, boolean: true, allow_nil: true, if: -> { - ci_raw_variables_in_yaml_config_enabled? - } + validates :config_expand, boolean: true, allow_nil: true validates :config_options, array_of_strings: true, allow_nil: true validate do @@ -82,16 +80,10 @@ module Gitlab end def value_with_data - if ci_raw_variables_in_yaml_config_enabled? - { - value: config_value.to_s, - raw: (!config_expand if has_config_expand?) - }.compact - else - { - value: config_value.to_s - }.compact - end + { + value: config_value.to_s, + raw: (!config_expand if has_config_expand?) + }.compact end def value_with_prefill_data @@ -100,10 +92,6 @@ module Gitlab options: config_options ).compact end - - def ci_raw_variables_in_yaml_config_enabled? - YamlProcessor::FeatureFlags.enabled?(:ci_raw_variables_in_yaml_config) - end end class UnknownStrategy < ::Gitlab::Config::Entry::Node diff --git a/lib/gitlab/ci/variables/collection.rb b/lib/gitlab/ci/variables/collection.rb index 8ff0a2538a1..63519877971 100644 --- a/lib/gitlab/ci/variables/collection.rb +++ b/lib/gitlab/ci/variables/collection.rb @@ -72,7 +72,6 @@ module Gitlab Collection.new(@variables.reject(&block)) end - # `expand_raw_refs` will be deleted with the FF `ci_raw_variables_in_yaml_config`. def expand_value(value, keep_undefined: false, expand_file_refs: true, expand_raw_refs: true) value.gsub(Item::VARIABLES_REGEXP) do match = Regexp.last_match # it is either a valid variable definition or a ($$ / %%) @@ -90,8 +89,18 @@ module Gitlab if variable.file? expand_file_refs ? variable.value : full_match elsif variable.raw? - # With `full_match`, we defer the expansion of raw variables to the runner. If we expand them here, - # the runner will not know the expanded value is a raw variable and it tries to expand it again. + # Normally, it's okay to expand a raw variable if it's referenced in another variable because + # its rawness is not broken. However, the runner also tries to expand variables. + # Here, with `full_match`, we defer the expansion of raw variables to the runner. + # If we expand them here, the runner will not know that the expanded value is a raw variable + # and it tries to expand it again. + # Example: `A` is a normal variable with value `normal`. + # `B` is a raw variable with value `raw-$A`. + # `C` is a normal variable with value `$B`. + # If we expanded `C` here, the runner would receive `C` as `raw-$A`. And since `A` is a normal + # variable, the runner would expand it. So, the result would be `raw-normal`. + # With `full_match`, the runner receives `C` as `$B`. And since `B` is a raw variable, the + # runner expanded it as `raw-$A`, which is what we want. # Discussion: https://gitlab.com/gitlab-org/gitlab/-/issues/353991#note_1103274951 expand_raw_refs ? variable.value : full_match else @@ -106,7 +115,6 @@ module Gitlab end end - # `expand_raw_refs` will be deleted with the FF `ci_raw_variables_in_yaml_config`. def sort_and_expand_all(keep_undefined: false, expand_file_refs: true, expand_raw_refs: true) sorted = Sort.new(self) return self.class.new(self, sorted.errors) unless sorted.valid? diff --git a/lib/gitlab/ci/yaml_processor/result.rb b/lib/gitlab/ci/yaml_processor/result.rb index f2c1ad0575d..d867439b10b 100644 --- a/lib/gitlab/ci/yaml_processor/result.rb +++ b/lib/gitlab/ci/yaml_processor/result.rb @@ -64,12 +64,7 @@ module Gitlab private def assign_valid_attributes - @root_variables = if YamlProcessor::FeatureFlags.enabled?(:ci_raw_variables_in_yaml_config) - transform_to_array(@ci_config.variables_with_data) - else - transform_to_array(@ci_config.variables) - end - + @root_variables = transform_to_array(@ci_config.variables_with_data) @root_variables_with_prefill_data = @ci_config.variables_with_prefill_data @stages = @ci_config.stages diff --git a/locale/gitlab.pot b/locale/gitlab.pot index f018f85a031..e6601430590 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -11367,6 +11367,9 @@ msgstr "" msgid "Country" msgstr "" +msgid "Counts" +msgstr "" + msgid "Counts reflect children you may not have access to." msgstr "" @@ -12263,6 +12266,9 @@ msgstr "" msgid "CycleAnalytics|project dropdown filter" msgstr "" +msgid "CycleAnalytics|the assigned object is not supported" +msgstr "" + msgid "DAG visualization requires at least 3 dependent jobs." msgstr "" @@ -28984,6 +28990,9 @@ msgstr "" msgid "One or more of your personal access tokens has expired." msgstr "" +msgid "One or more of your personal access tokens have expired." +msgstr "" + msgid "One or more of your personal access tokens will expire in %{days_to_expire} days or less:" msgstr "" @@ -31725,30 +31734,177 @@ msgstr "" msgid "Product analytics" msgstr "" +msgid "ProductAnalytics|Add another dimension" +msgstr "" + msgid "ProductAnalytics|Add to Dashboard" msgstr "" +msgid "ProductAnalytics|All clicks compared" +msgstr "" + +msgid "ProductAnalytics|All events compared" +msgstr "" + +msgid "ProductAnalytics|All features" +msgstr "" + +msgid "ProductAnalytics|All pages" +msgstr "" + msgid "ProductAnalytics|An error occured while loading the %{widgetTitle} widget." msgstr "" msgid "ProductAnalytics|An error occurred while fetching data. Refresh the page to try again." msgstr "" +msgid "ProductAnalytics|Any Click on elements" +msgstr "" + msgid "ProductAnalytics|Audience" msgstr "" +msgid "ProductAnalytics|Browser" +msgstr "" + +msgid "ProductAnalytics|Browser Family" +msgstr "" + +msgid "ProductAnalytics|Choose a chart type on the right" +msgstr "" + +msgid "ProductAnalytics|Choose a measurement to start" +msgstr "" + +msgid "ProductAnalytics|Click Events" +msgstr "" + +msgid "ProductAnalytics|Code" +msgstr "" + +msgid "ProductAnalytics|Compares all events against each other" +msgstr "" + +msgid "ProductAnalytics|Compares click events against each other" +msgstr "" + +msgid "ProductAnalytics|Compares feature usage of all features against each other" +msgstr "" + +msgid "ProductAnalytics|Compares pageviews of all pages against each other" +msgstr "" + msgid "ProductAnalytics|Dashboards are created by editing the projects dashboard files." msgstr "" +msgid "ProductAnalytics|Data" +msgstr "" + +msgid "ProductAnalytics|Data Table" +msgstr "" + +msgid "ProductAnalytics|Dimensions" +msgstr "" + +msgid "ProductAnalytics|Event Type" +msgstr "" + +msgid "ProductAnalytics|Events" +msgstr "" + +msgid "ProductAnalytics|Events grouped by %{granularity}" +msgstr "" + +msgid "ProductAnalytics|Events over time" +msgstr "" + +msgid "ProductAnalytics|Feature Usage" +msgstr "" + +msgid "ProductAnalytics|Feature usage" +msgstr "" + +msgid "ProductAnalytics|Host" +msgstr "" + +msgid "ProductAnalytics|Language" +msgstr "" + +msgid "ProductAnalytics|Line Chart" +msgstr "" + +msgid "ProductAnalytics|Measure All tracked Events" +msgstr "" + +msgid "ProductAnalytics|Measure all or specific Page Views" +msgstr "" + +msgid "ProductAnalytics|Measuring" +msgstr "" + msgid "ProductAnalytics|New Analytics Widget Title" msgstr "" +msgid "ProductAnalytics|OS" +msgstr "" + +msgid "ProductAnalytics|OS Version" +msgstr "" + +msgid "ProductAnalytics|On what do you want to get insights?" +msgstr "" + +msgid "ProductAnalytics|Page Language" +msgstr "" + +msgid "ProductAnalytics|Page Path" +msgstr "" + +msgid "ProductAnalytics|Page Title" +msgstr "" + +msgid "ProductAnalytics|Page Views" +msgstr "" + +msgid "ProductAnalytics|Pages" +msgstr "" + msgid "ProductAnalytics|Product analytics dashboards" msgstr "" +msgid "ProductAnalytics|Referer" +msgstr "" + +msgid "ProductAnalytics|Resulting Data" +msgstr "" + msgid "ProductAnalytics|There is no data for this type of chart currently. Please see the Setup tab if you have not configured the product analytics tool already." msgstr "" +msgid "ProductAnalytics|Track specific features" +msgstr "" + +msgid "ProductAnalytics|URL" +msgstr "" + +msgid "ProductAnalytics|User activity" +msgstr "" + +msgid "ProductAnalytics|Users" +msgstr "" + +msgid "ProductAnalytics|Viewport" +msgstr "" + +msgid "ProductAnalytics|Visualization Type" +msgstr "" + +msgid "ProductAnalytics|What do you want to measure?" +msgstr "" + +msgid "ProductAnalytics|Widget" +msgstr "" + msgid "Productivity" msgstr "" @@ -32634,6 +32790,9 @@ msgstr "" msgid "ProjectService|Trigger event when a wiki page is created or updated." msgstr "" +msgid "ProjectService|Trigger event when an incident is created." +msgstr "" + msgid "ProjectService|Trigger event when an issue is created, updated, or closed." msgstr "" @@ -41662,6 +41821,9 @@ msgid_plural "The following personal access tokens: %{token_names} were revoked, msgstr[0] "" msgstr[1] "" +msgid "The following personal access tokens have expired:" +msgstr "" + msgid "The fork relationship has been removed." msgstr "" @@ -48444,7 +48606,7 @@ msgstr "" msgid "Your password reset token has expired." msgstr "" -msgid "Your personal access token has expired" +msgid "Your personal access tokens have expired" msgstr "" msgid "Your personal access tokens will expire in %{days_to_expire} days or less" diff --git a/package.json b/package.json index 88dba557c95..097816ffa9e 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,8 @@ "@babel/core": "^7.18.5", "@babel/preset-env": "^7.18.2", "@codesandbox/sandpack-client": "^1.2.2", - "@cubejs-client/core": "^0.31.0", + "@cubejs-client/core": "^0.31.15", + "@cubejs-client/vue": "^0.31.19", "@gitlab/at.js": "1.5.7", "@gitlab/favicon-overlay": "2.0.0", "@gitlab/fonts": "^1.0.1", diff --git a/qa/qa/page/component/groups_filter.rb b/qa/qa/page/component/groups_filter.rb index ec59d010718..ea91ced8679 100644 --- a/qa/qa/page/component/groups_filter.rb +++ b/qa/qa/page/component/groups_filter.rb @@ -24,36 +24,29 @@ module QA private + # Check if a group exists in private or public tab + # @param name [String] group name + # @return [Boolean] whether a group with given name exists def has_filtered_group?(name) - # Filter and submit to reload the page and only retrieve the filtered results - find_element(:groups_filter_field).set(name).send_keys(:return) + filter_group(name) + return true if page.has_link?(name, wait: 0) # element containing link to group - # Since we submitted after filtering, the presence of - # groups_list_tree_container means we have the complete filtered list - # of groups - has_element?(:groups_list_tree_container, wait: QA::Support::Repeater::DEFAULT_MAX_WAIT_TIME) - # If there are no groups we'll know immediately because we filtered the list - if page.has_text?('No groups or projects matched your search', -wait: 0) || page.has_text?('No groups matched your search', wait: 0) - return false unless has_element?(:public_groups_tab) + return false unless has_element?(:public_groups_tab, wait: 0) - # Try for public groups - click_element(:public_groups_tab) - # Filter and submit to reload the page and only retrieve the filtered results - find_element(:groups_filter_field).set(name).send_keys(:return) - - # Since we submitted after filtering, the presence of - # groups_list_tree_container means we have the complete filtered list - # of groups - has_element?(:groups_list_tree_container, wait: QA::Support::Repeater::DEFAULT_MAX_WAIT_TIME) - - return false if page.has_text?('No groups or projects matched your search', -wait: 0) || page.has_text?('No groups matched your search', wait: 0) - end - - # The name will be present as filter input so we check for a link, not text + # Check public groups + click_element(:public_groups_tab) + filter_group(name) page.has_link?(name, wait: 0) end + + # Filter by group name + # @param name [String] group name + # @return [Boolean] whether the filter returned any group + def filter_group(name) + fill_element(:groups_filter_field, name).send_keys(:return) + finished_loading? + has_element?(:groups_list_tree_container, wait: 1) + end end end end diff --git a/qa/qa/specs/features/browser_ui/4_verify/ci_variable/raw_variables_defined_in_yaml_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/ci_variable/raw_variables_defined_in_yaml_spec.rb index a4849d47183..9544b3d2155 100644 --- a/qa/qa/specs/features/browser_ui/4_verify/ci_variable/raw_variables_defined_in_yaml_spec.rb +++ b/qa/qa/specs/features/browser_ui/4_verify/ci_variable/raw_variables_defined_in_yaml_spec.rb @@ -2,10 +2,7 @@ module QA RSpec.describe 'Verify', :runner do - describe 'Pipeline with raw variables in YAML', product_group: :pipeline_authoring, feature_flag: { - name: 'ci_raw_variables_in_yaml_config', - scope: :project - } do + describe 'Pipeline with raw variables in YAML', product_group: :pipeline_authoring do let(:executor) { "qa-runner-#{Time.now.to_i}" } let(:pipeline_job_name) { 'rspec' } @@ -23,7 +20,7 @@ module QA end end - let(:commit_ci_file) do + let!(:commit_ci_file) do Resource::Repository::Commit.fabricate_via_api! do |commit| commit.project = project commit.commit_message = 'Add .gitlab-ci.yml' @@ -76,10 +73,7 @@ module QA let(:pipeline_id) { project.pipelines.first[:id] } let(:job_id) { project.job_by_name(pipeline_job_name)[:id] } - def before_do - # TODO: Switch to use `let!` and remove this line when removing FF - commit_ci_file - + before do Flow::Login.sign_in project.visit! Flow::Pipeline.visit_latest_pipeline(status: 'passed') @@ -92,55 +86,20 @@ module QA runner&.remove_via_api! end - context 'when FF is on', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/381487' do - before do - Runtime::Feature.enable(:ci_raw_variables_in_yaml_config, project: project) - sleep 60 - - before_do - end - - it 'expands variables according to expand: true/false', :aggregate_failures do - Page::Project::Job::Show.perform do |show| - expect(show.output).to have_content("VAR1 is JOBID-#{job_id}") - expect(show.output).to have_content("VAR2 is PIPELINEID-#{pipeline_id} and JOBID-#{job_id}") - expect(show.output).to have_content("VAR3 is PIPELINEID-$CI_PIPELINE_ID and $VAR1") - expect(show.output).to have_content("VAR4 is JOBID-$CI_JOB_ID") - expect(show.output).to have_content("VAR5 is PIPELINEID-#{pipeline_id} and JOBID-$CI_JOB_ID") - expect(show.output).to have_content("VAR6 is PIPELINEID-$CI_PIPELINE_ID and $VAR4") - expect(show.output).to have_content("VAR7 is overridden value 7 #{pipeline_id}") - expect(show.output).to have_content("VAR8 is value 8 $CI_PIPELINE_ID") - end - end - end - - # TODO: Remove this context when FF :ci_raw_variables_in_yaml_config is removed - # Also archive testcase and close related issue - context 'when FF is off', - testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/381486', - quarantine: { - issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/381806', - only: { pipeline: %w[staging staging-canary staging-ref] }, - type: :waiting_on - } do - before do - Runtime::Feature.disable(:ci_raw_variables_in_yaml_config, project: project) - sleep 60 - - before_do - end - - it 'expands all variables', :aggregate_failures do - Page::Project::Job::Show.perform do |show| - expect(show.output).to have_content("VAR1 is JOBID-#{job_id}") - expect(show.output).to have_content("VAR2 is PIPELINEID-#{pipeline_id} and JOBID-#{job_id}") - expect(show.output).to have_content("VAR3 is PIPELINEID-#{pipeline_id} and JOBID-#{job_id}") - expect(show.output).to have_content("VAR4 is JOBID-#{job_id}") - expect(show.output).to have_content("VAR5 is PIPELINEID-#{pipeline_id} and JOBID-#{job_id}") - expect(show.output).to have_content("VAR6 is PIPELINEID-#{pipeline_id} and JOBID-#{job_id}") - expect(show.output).to have_content("VAR7 is overridden value 7 #{pipeline_id}") - expect(show.output).to have_content("VAR8 is value 8 #{pipeline_id}") - end + it( + 'expands variables according to expand: true/false', + :aggregate_failures, + testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/381487' + ) do + Page::Project::Job::Show.perform do |show| + expect(show.output).to have_content("VAR1 is JOBID-#{job_id}") + expect(show.output).to have_content("VAR2 is PIPELINEID-#{pipeline_id} and JOBID-#{job_id}") + expect(show.output).to have_content("VAR3 is PIPELINEID-$CI_PIPELINE_ID and $VAR1") + expect(show.output).to have_content("VAR4 is JOBID-$CI_JOB_ID") + expect(show.output).to have_content("VAR5 is PIPELINEID-#{pipeline_id} and JOBID-$CI_JOB_ID") + expect(show.output).to have_content("VAR6 is PIPELINEID-$CI_PIPELINE_ID and $VAR4") + expect(show.output).to have_content("VAR7 is overridden value 7 #{pipeline_id}") + expect(show.output).to have_content("VAR8 is value 8 $CI_PIPELINE_ID") end end end diff --git a/spec/factories/analytics/cycle_analytics/aggregations.rb b/spec/factories/analytics/cycle_analytics/aggregations.rb index 78e82f166d0..99f0e34ede7 100644 --- a/spec/factories/analytics/cycle_analytics/aggregations.rb +++ b/spec/factories/analytics/cycle_analytics/aggregations.rb @@ -2,7 +2,7 @@ FactoryBot.define do factory :cycle_analytics_aggregation, class: 'Analytics::CycleAnalytics::Aggregation' do - group + namespace { association(:group) } enabled { true } diff --git a/spec/features/projects/issues/design_management/user_uploads_designs_spec.rb b/spec/features/projects/issues/design_management/user_uploads_designs_spec.rb index 858d6751afa..55aa6db23c7 100644 --- a/spec/features/projects/issues/design_management/user_uploads_designs_spec.rb +++ b/spec/features/projects/issues/design_management/user_uploads_designs_spec.rb @@ -18,7 +18,7 @@ RSpec.describe 'User uploads new design', :js, feature_category: :design_managem context "when the feature is available" do let(:feature_enabled) { true } - it 'uploads designs' do + it 'uploads designs', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/358845' do upload_design(logo_fixture, count: 1) expect(page).to have_selector('.js-design-list-item', count: 1) diff --git a/spec/finders/branches_finder_spec.rb b/spec/finders/branches_finder_spec.rb index 18f8d1adecc..c8bc96d345d 100644 --- a/spec/finders/branches_finder_spec.rb +++ b/spec/finders/branches_finder_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe BranchesFinder do +RSpec.describe BranchesFinder, feature_category: :source_code_management do let(:user) { create(:user) } let(:project) { create(:project, :repository) } let(:repository) { project.repository } @@ -127,6 +127,34 @@ RSpec.describe BranchesFinder do end end + context 'by invalid regex' do + let(:params) { { regex: '[' } } + + it { expect { subject }.to raise_error(RegexpError) } + end + + context 'by `|` regex' do + let(:params) { { regex: 'audio|add-ipython-files' } } + + it 'filters branches' do + branches = subject + expect(branches.first.name).to eq('add-ipython-files') + expect(branches.second.name).to eq('audio') + expect(branches.count).to eq(2) + end + end + + context 'by exclude name' do + let(:params) { { regex: '^[^a]' } } + + it 'filters branches' do + result = subject + result.each do |branch| + expect(branch.name).not_to start_with('a') + end + end + end + context 'by name with multiple wildcards' do let(:params) { { search: 'f*a*e' } } diff --git a/spec/fixtures/api/schemas/public_api/v4/integration.json b/spec/fixtures/api/schemas/public_api/v4/integration.json index d1538db7de4..18e61636fa2 100644 --- a/spec/fixtures/api/schemas/public_api/v4/integration.json +++ b/spec/fixtures/api/schemas/public_api/v4/integration.json @@ -30,6 +30,9 @@ "issues_events": { "type": "boolean" }, + "incident_events": { + "type": "boolean" + }, "confidential_issues_events": { "type": "boolean" }, diff --git a/spec/frontend/__mocks__/@cubejs-client/core.js b/spec/frontend/__mocks__/@cubejs-client/core.js new file mode 100644 index 00000000000..549899aa8d8 --- /dev/null +++ b/spec/frontend/__mocks__/@cubejs-client/core.js @@ -0,0 +1,26 @@ +let mockLoad = jest.fn(); +let mockMetadata = jest.fn(); + +export const CubejsApi = jest.fn().mockImplementation(() => ({ + load: mockLoad, + meta: mockMetadata, +})); + +export const HttpTransport = jest.fn(); + +export const GRANULARITIES = [ + { + name: 'seconds', + title: 'Seconds', + }, +]; + +// eslint-disable-next-line no-underscore-dangle +export const __setMockLoad = (x) => { + mockLoad = x; +}; + +// eslint-disable-next-line no-underscore-dangle +export const __setMockMetadata = (x) => { + mockMetadata = x; +}; diff --git a/spec/frontend/work_items/components/work_item_links/work_item_links_form_spec.js b/spec/frontend/work_items/components/work_item_links/work_item_links_form_spec.js index f143704b801..5e1c46826cc 100644 --- a/spec/frontend/work_items/components/work_item_links/work_item_links_form_spec.js +++ b/spec/frontend/work_items/components/work_item_links/work_item_links_form_spec.js @@ -102,6 +102,7 @@ describe('WorkItemLinksForm', () => { preventDefault: jest.fn(), }); await waitForPromises(); + expect(wrapper.vm.childWorkItemType).toEqual('gid://gitlab/WorkItems::Type/3'); expect(createMutationResolver).toHaveBeenCalledWith({ input: { title: 'Create task test', @@ -124,6 +125,7 @@ describe('WorkItemLinksForm', () => { preventDefault: jest.fn(), }); await waitForPromises(); + expect(wrapper.vm.childWorkItemType).toEqual('gid://gitlab/WorkItems::Type/3'); expect(createMutationResolver).toHaveBeenCalledWith({ input: { title: 'Create confidential task', diff --git a/spec/lib/gitlab/ci/config/entry/variable_spec.rb b/spec/lib/gitlab/ci/config/entry/variable_spec.rb index 97b06c8b1a5..1067db6d124 100644 --- a/spec/lib/gitlab/ci/config/entry/variable_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/variable_spec.rb @@ -257,14 +257,6 @@ RSpec.describe Gitlab::Ci::Config::Entry::Variable do subject(:value_with_data) { entry.value_with_data } it { is_expected.to eq(value: 'value', raw: true) } - - context 'when the FF ci_raw_variables_in_yaml_config is disabled' do - before do - stub_feature_flags(ci_raw_variables_in_yaml_config: false) - end - - it { is_expected.to eq(value: 'value') } - end end context 'when config expand is true' do diff --git a/spec/lib/gitlab/ci/yaml_processor_spec.rb b/spec/lib/gitlab/ci/yaml_processor_spec.rb index 41c51340eb6..80acf54bb78 100644 --- a/spec/lib/gitlab/ci/yaml_processor_spec.rb +++ b/spec/lib/gitlab/ci/yaml_processor_spec.rb @@ -1302,32 +1302,6 @@ module Gitlab 'VAR3' => { value: 'value3', raw: true } ) end - - context 'when the FF ci_raw_variables_in_yaml_config is disabled' do - before do - stub_feature_flags(ci_raw_variables_in_yaml_config: false) - end - - it 'returns variables without description and raw' do - expect(job_variables).to contain_exactly( - { key: 'VAR4', value: 'value4' }, - { key: 'VAR5', value: 'value5' }, - { key: 'VAR6', value: 'value6' } - ) - - expect(execute.root_variables).to contain_exactly( - { key: 'VAR1', value: 'value1' }, - { key: 'VAR2', value: 'value2' }, - { key: 'VAR3', value: 'value3' } - ) - - expect(execute.root_variables_with_prefill_data).to eq( - 'VAR1' => { value: 'value1' }, - 'VAR2' => { value: 'value2', description: 'description2' }, - 'VAR3' => { value: 'value3' } - ) - end - end end end diff --git a/spec/mailers/emails/profile_spec.rb b/spec/mailers/emails/profile_spec.rb index cdc298d685e..9621612217b 100644 --- a/spec/mailers/emails/profile_spec.rb +++ b/spec/mailers/emails/profile_spec.rb @@ -198,9 +198,10 @@ RSpec.describe Emails::Profile do describe 'user personal access token has expired' do let_it_be(:user) { create(:user) } + let_it_be(:pat) { create(:personal_access_token, user: user) } context 'when valid' do - subject { Notify.access_token_expired_email(user) } + subject { Notify.access_token_expired_email(user, [pat.name]) } it_behaves_like 'an email sent from GitLab' it_behaves_like 'it should not have Gmail Actions links' @@ -211,11 +212,12 @@ RSpec.describe Emails::Profile do end it 'has the correct subject' do - is_expected.to have_subject /Your personal access token has expired/ + is_expected.to have_subject /Your personal access tokens have expired/ end it 'mentions the access token has expired' do - is_expected.to have_body_text /One or more of your personal access tokens has expired/ + is_expected.to have_body_text /The following personal access tokens have expired:/ + is_expected.to have_body_text /#{pat.name}/ end it 'includes a link to personal access tokens page' do diff --git a/spec/models/analytics/cycle_analytics/aggregation_spec.rb b/spec/models/analytics/cycle_analytics/aggregation_spec.rb index 2fb40852791..a51c21dc87e 100644 --- a/spec/models/analytics/cycle_analytics/aggregation_spec.rb +++ b/spec/models/analytics/cycle_analytics/aggregation_spec.rb @@ -1,13 +1,13 @@ # frozen_string_literal: true require 'spec_helper' -RSpec.describe Analytics::CycleAnalytics::Aggregation, type: :model do +RSpec.describe Analytics::CycleAnalytics::Aggregation, type: :model, feature_category: :value_stream_management do describe 'associations' do - it { is_expected.to belong_to(:group).required } + it { is_expected.to belong_to(:namespace).required } end describe 'validations' do - it { is_expected.not_to validate_presence_of(:group) } + it { is_expected.not_to validate_presence_of(:namespace) } it { is_expected.not_to validate_presence_of(:enabled) } %i[incremental_runtimes_in_seconds incremental_processed_records full_runtimes_in_seconds full_processed_records].each do |column| @@ -18,6 +18,10 @@ RSpec.describe Analytics::CycleAnalytics::Aggregation, type: :model do expect(record.errors).to have_key(column) end end + + it_behaves_like 'value stream analytics namespace models' do + let(:factory_name) { :cycle_analytics_aggregation } + end end describe 'attribute updater methods' do @@ -126,19 +130,19 @@ RSpec.describe Analytics::CycleAnalytics::Aggregation, type: :model do end end - describe '#safe_create_for_group' do + describe '#safe_create_for_namespace' do let_it_be(:group) { create(:group) } let_it_be(:subgroup) { create(:group, parent: group) } it 'creates the aggregation record' do - record = described_class.safe_create_for_group(group) + record = described_class.safe_create_for_namespace(group) expect(record).to be_persisted end context 'when non top-level group is given' do it 'creates the aggregation record for the top-level group' do - record = described_class.safe_create_for_group(subgroup) + record = described_class.safe_create_for_namespace(subgroup) expect(record).to be_persisted end @@ -146,11 +150,11 @@ RSpec.describe Analytics::CycleAnalytics::Aggregation, type: :model do context 'when the record is already present' do it 'does nothing' do - described_class.safe_create_for_group(group) + described_class.safe_create_for_namespace(group) expect do - described_class.safe_create_for_group(group) - described_class.safe_create_for_group(subgroup) + described_class.safe_create_for_namespace(group) + described_class.safe_create_for_namespace(subgroup) end.not_to change { described_class.count } end end diff --git a/spec/models/analytics/cycle_analytics/project_stage_spec.rb b/spec/models/analytics/cycle_analytics/project_stage_spec.rb index 697b7aee022..3c7fde17355 100644 --- a/spec/models/analytics/cycle_analytics/project_stage_spec.rb +++ b/spec/models/analytics/cycle_analytics/project_stage_spec.rb @@ -4,7 +4,7 @@ require 'spec_helper' RSpec.describe Analytics::CycleAnalytics::ProjectStage do describe 'associations' do - it { is_expected.to belong_to(:project) } + it { is_expected.to belong_to(:project).required } end it 'default stages must be valid' do diff --git a/spec/models/ci/bridge_spec.rb b/spec/models/ci/bridge_spec.rb index e8102c2c1ea..70e977e37ba 100644 --- a/spec/models/ci/bridge_spec.rb +++ b/spec/models/ci/bridge_spec.rb @@ -375,25 +375,6 @@ RSpec.describe Ci::Bridge, feature_category: :continuous_integration do { key: 'VAR7', value: 'value7 $VAR1', raw: true } ) end - - context 'when the FF ci_raw_variables_in_yaml_config is disabled' do - before do - stub_feature_flags(ci_raw_variables_in_yaml_config: false) - end - - it 'ignores the raw attribute' do - expect(downstream_variables).to contain_exactly( - { key: 'BRIDGE', value: 'cross' }, - { key: 'VAR1', value: 'value1' }, - { key: 'VAR2', value: 'value2 value1' }, - { key: 'VAR3', value: 'value3 value1' }, - { key: 'VAR4', value: 'value4 value1' }, - { key: 'VAR5', value: 'value5 value1' }, - { key: 'VAR6', value: 'value6 value1' }, - { key: 'VAR7', value: 'value7 value1' } - ) - end - end end end diff --git a/spec/models/ci/namespace_mirror_spec.rb b/spec/models/ci/namespace_mirror_spec.rb index 29447cbc89d..63e6e9e6b26 100644 --- a/spec/models/ci/namespace_mirror_spec.rb +++ b/spec/models/ci/namespace_mirror_spec.rb @@ -96,7 +96,7 @@ RSpec.describe Ci::NamespaceMirror do describe '.by_namespace_id' do subject(:result) { described_class.by_namespace_id(group2.id) } - it 'returns namesapce mirrors of namespace id' do + it 'returns namespace mirrors of namespace id' do expect(result).to contain_exactly(group2.ci_namespace_mirror) end end diff --git a/spec/models/deployment_spec.rb b/spec/models/deployment_spec.rb index 6ef6dcf0e7b..f0fdc62e6c7 100644 --- a/spec/models/deployment_spec.rb +++ b/spec/models/deployment_spec.rb @@ -402,6 +402,16 @@ RSpec.describe Deployment do it { is_expected.to be_falsey } end + + context 'when environment is undefined' do + let(:deployment) { build(:deployment, :success, project: project, environment: environment) } + + before do + deployment.environment = nil + end + + it { is_expected.to be_falsey } + end end describe '#success?' do diff --git a/spec/models/integration_spec.rb b/spec/models/integration_spec.rb index 24a3ff00807..9b3250e3c08 100644 --- a/spec/models/integration_spec.rb +++ b/spec/models/integration_spec.rb @@ -29,6 +29,7 @@ RSpec.describe Integration do it { is_expected.to be_tag_push_events } it { is_expected.to be_wiki_page_events } it { is_expected.not_to be_active } + it { is_expected.not_to be_incident_events } it { expect(subject.category).to eq(:common) } end diff --git a/spec/models/integrations/base_chat_notification_spec.rb b/spec/models/integrations/base_chat_notification_spec.rb index 67fc09fd8b5..77e844df422 100644 --- a/spec/models/integrations/base_chat_notification_spec.rb +++ b/spec/models/integrations/base_chat_notification_spec.rb @@ -134,6 +134,12 @@ RSpec.describe Integrations::BaseChatNotification do it_behaves_like 'notifies the chat integration' end + + context 'Incident events' do + let(:data) { issue.to_hook_data(user).merge!({ object_kind: 'incident' }) } + + it_behaves_like 'notifies the chat integration' + end end context 'when labels_to_be_notified_behavior is not defined' do diff --git a/spec/models/integrations/chat_message/issue_message_spec.rb b/spec/models/integrations/chat_message/issue_message_spec.rb index ff9f30efdca..cd40e4c361e 100644 --- a/spec/models/integrations/chat_message/issue_message_spec.rb +++ b/spec/models/integrations/chat_message/issue_message_spec.rb @@ -7,6 +7,7 @@ RSpec.describe Integrations::ChatMessage::IssueMessage do let(:args) do { + object_kind: 'issue', user: { name: 'Test User', username: 'test.user', diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index e3e8257af72..4b51c9e2013 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -166,6 +166,7 @@ RSpec.describe Project, factory_default: :keep do it { is_expected.to have_many(:wiki_page_hooks_integrations).class_name('Integration') } it { is_expected.to have_many(:deployment_hooks_integrations).class_name('Integration') } it { is_expected.to have_many(:alert_hooks_integrations).class_name('Integration') } + it { is_expected.to have_many(:incident_hooks_integrations).class_name('Integration') } # GitLab Pages it { is_expected.to have_many(:pages_domains) } diff --git a/spec/presenters/ci/build_runner_presenter_spec.rb b/spec/presenters/ci/build_runner_presenter_spec.rb index 733b6e87087..dedfe6925c5 100644 --- a/spec/presenters/ci/build_runner_presenter_spec.rb +++ b/spec/presenters/ci/build_runner_presenter_spec.rb @@ -375,23 +375,6 @@ RSpec.describe Ci::BuildRunnerPresenter do public: false, masked: false } ) end - - context 'when the FF ci_raw_variables_in_yaml_config is disabled' do - before do - stub_feature_flags(ci_raw_variables_in_yaml_config: false) - end - - it 'returns expanded variables' do - expect(runner_variables).to include( - { key: 'regular_var', value: 'value 1', - public: false, masked: false }, - { key: 'raw_var', value: 'value 2', - public: false, masked: false, raw: true }, - { key: 'var_with_variables', value: 'value 3 and value 1 and value 2 and $undefined_var', - public: false, masked: false } - ) - end - end end end diff --git a/spec/services/ci/create_pipeline_service/variables_spec.rb b/spec/services/ci/create_pipeline_service/variables_spec.rb index e9e0cf2c6e0..fd138bde656 100644 --- a/spec/services/ci/create_pipeline_service/variables_spec.rb +++ b/spec/services/ci/create_pipeline_service/variables_spec.rb @@ -60,27 +60,6 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes { key: 'VAR8', value: "value 8 $CI_PIPELINE_ID", public: true, masked: false, raw: true } ) end - - context 'when the FF ci_raw_variables_in_yaml_config is disabled' do - before do - stub_feature_flags(ci_raw_variables_in_yaml_config: false) - end - - it 'creates the pipeline with a job that has all variables expanded' do - expect(pipeline).to be_created_successfully - - expect(Ci::BuildRunnerPresenter.new(rspec).runner_variables).to include( - { key: 'VAR1', value: "JOBID-#{rspec.id}", public: true, masked: false }, - { key: 'VAR2', value: "PIPELINEID-#{pipeline.id} and JOBID-#{rspec.id}", public: true, masked: false }, - { key: 'VAR3', value: "PIPELINEID-#{pipeline.id} and JOBID-#{rspec.id}", public: true, masked: false }, - { key: 'VAR4', value: "JOBID-#{rspec.id}", public: true, masked: false }, - { key: 'VAR5', value: "PIPELINEID-#{pipeline.id} and JOBID-#{rspec.id}", public: true, masked: false }, - { key: 'VAR6', value: "PIPELINEID-#{pipeline.id} and JOBID-#{rspec.id}", public: true, masked: false }, - { key: 'VAR7', value: "overridden value 7 #{pipeline.id}", public: true, masked: false }, - { key: 'VAR8', value: "value 8 #{pipeline.id}", public: true, masked: false } - ) - end - end end context 'when trigger variables have expand: true/false' do @@ -109,22 +88,6 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes { key: 'VAR3', value: "PIPELINEID-$CI_PIPELINE_ID and $VAR1", raw: true } ) end - - context 'when the FF ci_raw_variables_in_yaml_config is disabled' do - before do - stub_feature_flags(ci_raw_variables_in_yaml_config: false) - end - - it 'creates the pipeline with a job that has all variables expanded' do - expect(pipeline).to be_created_successfully - - expect(child.downstream_variables).to include( - { key: 'VAR1', value: "PROJECTID-#{project.id}" }, - { key: 'VAR2', value: "PIPELINEID-#{pipeline.id} and PROJECTID-$CI_PROJECT_ID" }, - { key: 'VAR3', value: "PIPELINEID-#{pipeline.id} and PROJECTID-$CI_PROJECT_ID" } - ) - end - end end end diff --git a/spec/services/issues/close_service_spec.rb b/spec/services/issues/close_service_spec.rb index e6ad755f911..ef24d1e940e 100644 --- a/spec/services/issues/close_service_spec.rb +++ b/spec/services/issues/close_service_spec.rb @@ -140,7 +140,7 @@ RSpec.describe Issues::CloseService do end context 'when the escalation status did not change to resolved' do - let(:escalation_status) { instance_double('IncidentManagement::IssuableEscalationStatus', resolve: false) } + let(:escalation_status) { instance_double('IncidentManagement::IssuableEscalationStatus', resolve: false, status_name: 'acknowledged') } before do allow(issue).to receive(:incident_management_issuable_escalation_status).and_return(escalation_status) diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb index 70fc6ffc38f..930766c520b 100644 --- a/spec/services/issues/update_service_spec.rb +++ b/spec/services/issues/update_service_spec.rb @@ -1168,6 +1168,7 @@ RSpec.describe Issues::UpdateService, :mailer do it 'triggers webhooks' do expect(project).to receive(:execute_hooks).with(an_instance_of(Hash), :issue_hooks) expect(project).to receive(:execute_integrations).with(an_instance_of(Hash), :issue_hooks) + expect(project).to receive(:execute_integrations).with(an_instance_of(Hash), :incident_hooks) update_issue(opts) end @@ -1281,6 +1282,7 @@ RSpec.describe Issues::UpdateService, :mailer do it 'triggers webhooks' do expect(project).to receive(:execute_hooks).with(an_instance_of(Hash), :issue_hooks) expect(project).to receive(:execute_integrations).with(an_instance_of(Hash), :issue_hooks) + expect(project).to receive(:execute_integrations).with(an_instance_of(Hash), :incident_hooks) update_issue(opts) end diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb index 1ca14cd430b..a18a7d0c64a 100644 --- a/spec/services/notification_service_spec.rb +++ b/spec/services/notification_service_spec.rb @@ -337,11 +337,12 @@ RSpec.describe NotificationService, :mailer do describe '#access_token_expired' do let_it_be(:user) { create(:user) } + let_it_be(:pat) { create(:personal_access_token, user: user) } - subject { notification.access_token_expired(user) } + subject { notification.access_token_expired(user, pat.name) } it 'sends email to the token owner' do - expect { subject }.to have_enqueued_email(user, mail: "access_token_expired_email") + expect { subject }.to have_enqueued_email(user, pat.name, mail: "access_token_expired_email") end context 'when user is not allowed to receive notifications' do @@ -350,7 +351,7 @@ RSpec.describe NotificationService, :mailer do end it 'does not send email to the token owner' do - expect { subject }.not_to have_enqueued_email(user, mail: "access_token_expired_email") + expect { subject }.not_to have_enqueued_email(user, pat.name, mail: "access_token_expired_email") end end end diff --git a/spec/support/helpers/cycle_analytics_helpers.rb b/spec/support/helpers/cycle_analytics_helpers.rb index 6d41d7b7414..632f3ea28ee 100644 --- a/spec/support/helpers/cycle_analytics_helpers.rb +++ b/spec/support/helpers/cycle_analytics_helpers.rb @@ -92,7 +92,7 @@ module CycleAnalyticsHelpers end def create_value_stream_group_aggregation(group) - aggregation = Analytics::CycleAnalytics::Aggregation.safe_create_for_group(group) + aggregation = Analytics::CycleAnalytics::Aggregation.safe_create_for_namespace(group) Analytics::CycleAnalytics::AggregatorService.new(aggregation: aggregation).execute end diff --git a/spec/support/shared_examples/analytics/cycle_analytics/parentable_examples.rb b/spec/support/shared_examples/analytics/cycle_analytics/parentable_examples.rb new file mode 100644 index 00000000000..5fd0e685c67 --- /dev/null +++ b/spec/support/shared_examples/analytics/cycle_analytics/parentable_examples.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +RSpec.shared_examples 'value stream analytics namespace models' do + let(:factory_name) { nil } + + context 'when ProjectNamespace is given' do + it 'is valid' do + project_namespace = create(:project_namespace) + model = build(factory_name, namespace: project_namespace) + + expect(model).to be_valid + expect(model.save).to be(true) + expect(model.namespace).to eq(project_namespace) + end + end + + context 'when Namespace is given' do + it 'fails' do + namespace = create(:namespace) + model = build(factory_name, namespace: namespace) + + expect(model).to be_invalid + + error_message = s_('CycleAnalytics|the assigned object is not supported') + expect(model.errors.messages_for(:namespace)).to eq([error_message]) + end + end +end diff --git a/spec/support/shared_examples/models/cycle_analytics_stage_shared_examples.rb b/spec/support/shared_examples/models/cycle_analytics_stage_shared_examples.rb index 457ee49938f..5eeefacdeb9 100644 --- a/spec/support/shared_examples/models/cycle_analytics_stage_shared_examples.rb +++ b/spec/support/shared_examples/models/cycle_analytics_stage_shared_examples.rb @@ -25,7 +25,7 @@ RSpec.shared_examples 'value stream analytics stage' do stage = described_class.new(valid_params.except(:parent)) expect(stage).to be_invalid - expect(stage.errors[parent_name]).to include("can't be blank") + expect(stage.errors[parent_name]).to include('must exist') end it 'validates presence of start_event_identifier' do diff --git a/spec/workers/personal_access_tokens/expired_notification_worker_spec.rb b/spec/workers/personal_access_tokens/expired_notification_worker_spec.rb index 3ff67f47523..7c3c48b3f80 100644 --- a/spec/workers/personal_access_tokens/expired_notification_worker_spec.rb +++ b/spec/workers/personal_access_tokens/expired_notification_worker_spec.rb @@ -11,7 +11,7 @@ RSpec.describe PersonalAccessTokens::ExpiredNotificationWorker, type: :worker do it 'uses notification service to send email to the user' do expect_next_instance_of(NotificationService) do |notification_service| - expect(notification_service).to receive(:access_token_expired).with(expired_today.user) + expect(notification_service).to receive(:access_token_expired).with(expired_today.user, [expired_today.name]) end worker.perform @@ -25,7 +25,7 @@ RSpec.describe PersonalAccessTokens::ExpiredNotificationWorker, type: :worker do shared_examples 'expiry notification is not required to be sent for the token' do it do expect_next_instance_of(NotificationService) do |notification_service| - expect(notification_service).not_to receive(:access_token_expired).with(token.user) + expect(notification_service).not_to receive(:access_token_expired).with(token.user, [token.name]) end worker.perform diff --git a/spec/workers/projects/delete_branch_worker_spec.rb b/spec/workers/projects/delete_branch_worker_spec.rb index c1289f56929..ec3feecccdc 100644 --- a/spec/workers/projects/delete_branch_worker_spec.rb +++ b/spec/workers/projects/delete_branch_worker_spec.rb @@ -83,24 +83,6 @@ RSpec.describe Projects::DeleteBranchWorker, feature_category: :source_code_mana expect { worker.perform(project.id, user.id, branch) }.not_to raise_error end end - - context 'when track_and_raise_delete_source_errors is disabled' do - let(:status_code) { 400 } - - before do - stub_feature_flags(track_and_raise_delete_source_errors: false) - end - - it 'does not track the exception' do - expect_next_instance_of(::Branches::DeleteService) do |instance| - expect(instance).to receive(:execute).with(branch).and_return(service_result) - end - - expect(service_result).not_to receive(:track_and_raise_exception) - - expect { worker.perform(project.id, user.id, branch) }.not_to raise_error - end - end end end diff --git a/yarn.lock b/yarn.lock index 1de68eb8401..fcb6e577baa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1047,7 +1047,7 @@ resolved "https://registry.yarnpkg.com/@csstools/selector-specificity/-/selector-specificity-2.0.1.tgz#b6b8d81780b9a9f6459f4bfe9226ac6aefaefe87" integrity sha512-aG20vknL4/YjQF9BSV7ts4EWm/yrjagAN7OWBNmlbEOUiu0llj4OGrFoOKK3g2vey4/p2omKCoHrWtPxSwV3HA== -"@cubejs-client/core@^0.31.0": +"@cubejs-client/core@^0.31.15": version "0.31.15" resolved "https://registry.yarnpkg.com/@cubejs-client/core/-/core-0.31.15.tgz#db0ee90f5ba7f33a3fae6c81e5e13ab1cf2cd71b" integrity sha512-VQqvvJn++nqO8aOr/dFtyUURNFYAlP3XlDiupiGLXmSsuUn0BuozJQAmJ5XxPPhvz5k9qBko7KkZuC6ikZTdcA== @@ -1059,6 +1059,15 @@ url-search-params-polyfill "^7.0.0" uuid "^8.3.2" +"@cubejs-client/vue@^0.31.19": + version "0.31.19" + resolved "https://registry.yarnpkg.com/@cubejs-client/vue/-/vue-0.31.19.tgz#cd8f588e14046091b085c3405f28a62f3e4d949d" + integrity sha512-SY2+flIZJARp0cpS7e9ewYwaYCqqh8ifyc11S55ENo2KJsyzdLELSIiTDqy6hppXqtALSOE4feW4Eb/blh6e0w== + dependencies: + "@cubejs-client/core" "^0.31.15" + core-js "^3.6.5" + ramda "^0.27.2" + "@discoveryjs/json-ext@^0.5.0": version "0.5.6" resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.6.tgz#d5e0706cf8c6acd8c6032f8d54070af261bbbb2f"