Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-01-05 15:09:59 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-01-05 15:09:59 +0300
commitb90cf01a88df981f452a7f6b6d74e8fd0ccbf90b (patch)
tree669bd8c7dab7f6aad2b9eef6d03cdb7bb4e687db
parentf9053931de583bbc3c6a8033f70929a91ce02e93 (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/extensions/base.vue2
-rw-r--r--app/assets/javascripts/work_items/components/work_item_links/work_item_links_form.vue5
-rw-r--r--app/assets/javascripts/work_items/constants.js12
-rw-r--r--app/controllers/concerns/integrations/params.rb1
-rw-r--r--app/finders/branches_finder.rb18
-rw-r--r--app/helpers/integrations_helper.rb8
-rw-r--r--app/mailers/emails/profile.rb5
-rw-r--r--app/models/analytics/cycle_analytics/aggregation.rb8
-rw-r--r--app/models/analytics/cycle_analytics/project_stage.rb3
-rw-r--r--app/models/ci/bridge.rb12
-rw-r--r--app/models/concerns/analytics/cycle_analytics/parentable.rb22
-rw-r--r--app/models/concerns/analytics/cycle_analytics/stage.rb16
-rw-r--r--app/models/deployment.rb2
-rw-r--r--app/models/integration.rb1
-rw-r--r--app/models/integrations/base_chat_notification.rb6
-rw-r--r--app/models/integrations/chat_message/issue_message.rb10
-rw-r--r--app/models/project.rb1
-rw-r--r--app/presenters/ci/build_runner_presenter.rb4
-rw-r--r--app/services/issues/base_service.rb10
-rw-r--r--app/services/notification_service.rb4
-rw-r--r--app/views/dashboard/todos/_todo.html.haml9
-rw-r--r--app/views/notify/access_token_expired_email.html.haml12
-rw-r--r--app/views/notify/access_token_expired_email.text.erb10
-rw-r--r--app/workers/issues/rebalancing_worker.rb2
-rw-r--r--app/workers/personal_access_tokens/expired_notification_worker.rb18
-rw-r--r--app/workers/projects/delete_branch_worker.rb1
-rw-r--r--config/feature_flags/development/ci_raw_variables_in_yaml_config.yml8
-rw-r--r--config/feature_flags/development/track_and_raise_delete_source_errors.yml8
-rw-r--r--config/webpack.config.js5
-rw-r--r--doc/ci/yaml/index.md5
-rw-r--r--doc/development/fe_guide/merge_request_widget_extensions.md2
-rw-r--r--doc/development/fe_guide/style/scss.md35
-rw-r--r--doc/user/project/integrations/webhook_events.md39
-rw-r--r--lib/api/branches.rb1
-rw-r--r--lib/api/entities/project_integration_basic.rb1
-rw-r--r--lib/api/helpers/integrations_helpers.rb12
-rw-r--r--lib/gitlab/analytics/cycle_analytics/request_params.rb4
-rw-r--r--lib/gitlab/ci/config/entry/variable.rb22
-rw-r--r--lib/gitlab/ci/variables/collection.rb16
-rw-r--r--lib/gitlab/ci/yaml_processor/result.rb7
-rw-r--r--locale/gitlab.pot164
-rw-r--r--package.json3
-rw-r--r--qa/qa/page/component/groups_filter.rb45
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/ci_variable/raw_variables_defined_in_yaml_spec.rb75
-rw-r--r--spec/factories/analytics/cycle_analytics/aggregations.rb2
-rw-r--r--spec/features/projects/issues/design_management/user_uploads_designs_spec.rb2
-rw-r--r--spec/finders/branches_finder_spec.rb30
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/integration.json3
-rw-r--r--spec/frontend/__mocks__/@cubejs-client/core.js26
-rw-r--r--spec/frontend/work_items/components/work_item_links/work_item_links_form_spec.js2
-rw-r--r--spec/lib/gitlab/ci/config/entry/variable_spec.rb8
-rw-r--r--spec/lib/gitlab/ci/yaml_processor_spec.rb26
-rw-r--r--spec/mailers/emails/profile_spec.rb8
-rw-r--r--spec/models/analytics/cycle_analytics/aggregation_spec.rb22
-rw-r--r--spec/models/analytics/cycle_analytics/project_stage_spec.rb2
-rw-r--r--spec/models/ci/bridge_spec.rb19
-rw-r--r--spec/models/ci/namespace_mirror_spec.rb2
-rw-r--r--spec/models/deployment_spec.rb10
-rw-r--r--spec/models/integration_spec.rb1
-rw-r--r--spec/models/integrations/base_chat_notification_spec.rb6
-rw-r--r--spec/models/integrations/chat_message/issue_message_spec.rb1
-rw-r--r--spec/models/project_spec.rb1
-rw-r--r--spec/presenters/ci/build_runner_presenter_spec.rb17
-rw-r--r--spec/services/ci/create_pipeline_service/variables_spec.rb37
-rw-r--r--spec/services/issues/close_service_spec.rb2
-rw-r--r--spec/services/issues/update_service_spec.rb2
-rw-r--r--spec/services/notification_service_spec.rb7
-rw-r--r--spec/support/helpers/cycle_analytics_helpers.rb2
-rw-r--r--spec/support/shared_examples/analytics/cycle_analytics/parentable_examples.rb28
-rw-r--r--spec/support/shared_examples/models/cycle_analytics_stage_shared_examples.rb2
-rw-r--r--spec/workers/personal_access_tokens/expired_notification_worker_spec.rb4
-rw-r--r--spec/workers/projects/delete_branch_worker_spec.rb18
-rw-r--r--yarn.lock11
73 files changed, 610 insertions, 345 deletions
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 = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: @target_url }
+ - pat_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.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: '</a>'.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
+
+ # 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
+
+ notification_service.access_token_expired(user, token_names)
- notification_service.access_token_expired(user)
+ Gitlab::AppLogger.info "#{self.class}: Notifying User #{user.id} about expired tokens"
- user.personal_access_tokens.without_impersonation.expired_today_and_not_notified.update_all(after_expiry_notification_delivered: true)
+ 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:
- <https://tailwindcss.com/docs/utility-first>
- <https://tailwindcss.com/docs/extracting-components>
+#### 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
@@ -63,6 +63,12 @@ module API
},
{
required: false,
+ name: :incident_channel,
+ type: String,
+ desc: 'The name of the channel to receive incident_events notifications'
+ },
+ {
+ required: false,
name: :confidential_issue_channel,
type: String,
desc: 'The name of the channel to receive confidential_issues_events notifications'
@@ -116,6 +122,12 @@ module API
},
{
required: false,
+ name: :incident_events,
+ type: Boolean,
+ desc: 'Enable notifications for incident_events'
+ },
+ {
+ required: false,
name: :confidential_issues_events,
type: Boolean,
desc: 'Enable notifications for 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)
-
- # 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)
-
- # 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
+ filter_group(name)
+ return true if page.has_link?(name, wait: 0) # element containing link to group
+
+ return false unless has_element?(:public_groups_tab, wait: 0)
- # 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"