Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
f9053931de
commit
b90cf01a88
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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?
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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?
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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|
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
|
|
|
|||
|
|
@ -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 } %>
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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) =>
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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`.
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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' }
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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?
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
FactoryBot.define do
|
||||
factory :cycle_analytics_aggregation, class: 'Analytics::CycleAnalytics::Aggregation' do
|
||||
group
|
||||
namespace { association(:group) }
|
||||
|
||||
enabled { true }
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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' } }
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,9 @@
|
|||
"issues_events": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"incident_events": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"confidential_issues_events": {
|
||||
"type": "boolean"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ RSpec.describe Integrations::ChatMessage::IssueMessage do
|
|||
|
||||
let(:args) do
|
||||
{
|
||||
object_kind: 'issue',
|
||||
user: {
|
||||
name: 'Test User',
|
||||
username: 'test.user',
|
||||
|
|
|
|||
|
|
@ -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) }
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
11
yarn.lock
11
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"
|
||||
|
|
|
|||
Loading…
Reference in New Issue