Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-06-28 09:09:38 +00:00
parent 8b7a679616
commit d81f7fc0b3
43 changed files with 278 additions and 697 deletions

View File

@ -13,8 +13,6 @@ inherit_from:
<% end %>
- '.rubocop_todo.yml'
<% end %>
<%# See https://gitlab.com/gitlab-org/gitlab/-/issues/321982#rubymine-note for context on why namespaced_class has special handling here %>
- ./.rubocop_todo/gitlab/namespaced_class.yml
- ./rubocop/rubocop-migrations.yml
- ./rubocop/rubocop-usage-data.yml
- ./rubocop/rubocop-code_reuse.yml

View File

@ -10,8 +10,8 @@ export const OPENED = 'opened';
export const REOPENED = 'reopened';
export const CLOSED = 'closed';
export const MERGED = 'merged';
export const ISSUE_NOTEABLE_TYPE = 'issue';
export const EPIC_NOTEABLE_TYPE = 'epic';
export const ISSUE_NOTEABLE_TYPE = 'Issue';
export const EPIC_NOTEABLE_TYPE = 'Epic';
export const MERGE_REQUEST_NOTEABLE_TYPE = 'MergeRequest';
export const UNRESOLVE_NOTE_METHOD_NAME = 'delete';
export const RESOLVE_NOTE_METHOD_NAME = 'post';

View File

@ -83,14 +83,17 @@ export const setExpandDiscussions = ({ commit }, { discussionIds, expanded }) =>
commit(types.SET_EXPAND_DISCUSSIONS, { discussionIds, expanded });
};
export const fetchDiscussions = ({ commit, dispatch }, { path, filter, persistFilter }) => {
export const fetchDiscussions = (
{ commit, dispatch, getters },
{ path, filter, persistFilter },
) => {
const config =
filter !== undefined
? { params: { notes_filter: filter, persist_filter: persistFilter } }
: null;
if (
window.gon?.features?.paginatedIssueDiscussions ||
getters.noteableType === constants.ISSUE_NOTEABLE_TYPE ||
window.gon?.features?.paginatedMrDiscussions
) {
return dispatch('fetchDiscussionsBatch', { path, config, perPage: 20 });
@ -114,7 +117,7 @@ export const fetchDiscussionsBatch = ({ commit, dispatch }, { path, config, curs
return axios.get(path, { params }).then(({ data, headers }) => {
commit(types.ADD_OR_UPDATE_DISCUSSIONS, data);
if (headers['x-next-page-cursor']) {
if (headers && headers['x-next-page-cursor']) {
const nextConfig = { ...config };
if (config?.params?.persist_filter) {

View File

@ -184,7 +184,6 @@ module IssuableActions
def paginated_discussions
return if params[:per_page].blank?
return if issuable.instance_of?(Issue) && Feature.disabled?(:paginated_issue_discussions, project)
return if issuable.instance_of?(MergeRequest) && Feature.disabled?(:paginated_mr_discussions, project)
strong_memoize(:paginated_discussions) do

View File

@ -47,7 +47,6 @@ class Projects::IssuesController < Projects::ApplicationController
before_action only: :show do
push_frontend_feature_flag(:confidential_notes, project&.group)
push_frontend_feature_flag(:issue_assignees_widget, project)
push_frontend_feature_flag(:paginated_issue_discussions, project)
push_frontend_feature_flag(:realtime_labels, project)
push_force_frontend_feature_flag(:work_items, project&.work_items_feature_flag_enabled?)
push_frontend_feature_flag(:work_items_mvc_2)

View File

@ -6,13 +6,15 @@
module NotificationBranchSelection
extend ActiveSupport::Concern
def branch_choices
[
[_('All branches'), 'all'].freeze,
[_('Default branch'), 'default'].freeze,
[_('Protected branches'), 'protected'].freeze,
[_('Default branch and protected branches'), 'default_and_protected'].freeze
].freeze
class_methods do
def branch_choices
[
[_('All branches'), 'all'].freeze,
[_('Default branch'), 'default'].freeze,
[_('Protected branches'), 'protected'].freeze,
[_('Default branch and protected branches'), 'default_and_protected'].freeze
].freeze
end
end
def notify_for_branch?(data)

View File

@ -4,9 +4,22 @@ require 'asana'
module Integrations
class Asana < Integration
prop_accessor :api_key, :restrict_to_branch
validates :api_key, presence: true, if: :activated?
field :api_key,
type: 'password',
title: 'API key',
help: -> { s_('AsanaService|User Personal Access Token. User must have access to the task. All comments are attributed to this user.') },
non_empty_password_title: -> { s_('ProjectService|Enter new API key') },
non_empty_password_help: -> { s_('ProjectService|Leave blank to use your current API key.') },
# Example Personal Access Token from Asana docs
placeholder: '0/68a9e79b868c6789e79a124c30b0',
required: true
field :restrict_to_branch,
title: -> { s_('Integrations|Restrict to branch (optional)') },
help: -> { s_('AsanaService|Comma-separated list of branches to be automatically inspected. Leave blank to include all branches.') }
def title
'Asana'
end
@ -24,28 +37,6 @@ module Integrations
'asana'
end
def fields
[
{
type: 'password',
name: 'api_key',
title: 'API key',
help: s_('AsanaService|User Personal Access Token. User must have access to the task. All comments are attributed to this user.'),
non_empty_password_title: s_('ProjectService|Enter new API key'),
non_empty_password_help: s_('ProjectService|Leave blank to use your current API key.'),
# Example Personal Access Token from Asana docs
placeholder: '0/68a9e79b868c6789e79a124c30b0',
required: true
},
{
type: 'text',
name: 'restrict_to_branch',
title: 'Restrict to branch (optional)',
help: s_('AsanaService|Comma-separated list of branches to be automatically inspected. Leave blank to include all branches.')
}
]
end
def self.supported_events
%w(push)
end

View File

@ -2,9 +2,18 @@
module Integrations
class Assembla < Integration
prop_accessor :token, :subdomain
validates :token, presence: true, if: :activated?
field :token,
type: 'password',
non_empty_password_title: -> { s_('ProjectService|Enter new token') },
non_empty_password_help: -> { s_('ProjectService|Leave blank to use your current token.') },
placeholder: '',
required: true
field :subdomain,
placeholder: ''
def title
'Assembla'
end
@ -17,24 +26,6 @@ module Integrations
'assembla'
end
def fields
[
{
type: 'password',
name: 'token',
non_empty_password_title: s_('ProjectService|Enter new token'),
non_empty_password_help: s_('ProjectService|Leave blank to use your current token.'),
placeholder: '',
required: true
},
{
type: 'text',
name: 'subdomain',
placeholder: ''
}
]
end
def self.supported_events
%w(push)
end

View File

@ -78,7 +78,7 @@ module Integrations
type: 'select',
name: 'branches_to_be_notified',
title: s_('Integrations|Branches for which notifications are to be sent'),
choices: branch_choices
choices: self.class.branch_choices
}.freeze,
{
type: 'text',
@ -118,7 +118,7 @@ module Integrations
event_type = data[:event_type] || object_kind
channel_names = get_channel_field(event_type).presence || channel.presence
channel_names = event_channel_value(event_type).presence || channel.presence
channels = channel_names&.split(',')&.map(&:strip)
opts = {}
@ -161,7 +161,7 @@ module Integrations
EVENT_CHANNEL[event]
end
def get_channel_field(event)
def event_channel_value(event)
field_name = event_channel_name(event)
self.public_send(field_name) # rubocop:disable GitlabSecurity/PublicSend
end

View File

@ -6,11 +6,14 @@ module Integrations
VALID_HOST_MATCH = %r{\A.+\.atlassian\.net\Z}.freeze
VALID_PATH_MATCH = %r{\A/wiki(/|\Z)}.freeze
prop_accessor :confluence_url
validates :confluence_url, presence: true, if: :activated?
validate :validate_confluence_url_is_cloud, if: :activated?
field :confluence_url,
title: -> { s_('Confluence Cloud Workspace URL') },
placeholder: 'https://example.atlassian.net/wiki',
required: true
def self.to_param
'confluence'
end
@ -38,18 +41,6 @@ module Integrations
end
end
def fields
[
{
type: 'text',
name: 'confluence_url',
title: s_('Confluence Cloud Workspace URL'),
placeholder: 'https://example.atlassian.net/wiki',
required: true
}
]
end
def testable?
false
end

View File

@ -39,7 +39,7 @@ module Integrations
type: 'select',
name: 'branches_to_be_notified',
title: s_('Integrations|Branches for which notifications are to be sent'),
choices: branch_choices
choices: self.class.branch_choices
}
]
end

View File

@ -6,12 +6,35 @@ module Integrations
RECIPIENTS_LIMIT = 750
boolean_accessor :send_from_committer_email
boolean_accessor :disable_diffs
prop_accessor :recipients, :branches_to_be_notified
validates :recipients, presence: true, if: :validate_recipients?
validate :number_of_recipients_within_limit, if: :validate_recipients?
field :send_from_committer_email,
type: 'checkbox',
title: -> { s_("EmailsOnPushService|Send from committer") },
help: -> do
@help ||= begin
domains = Notify.allowed_email_domains.map { |domain| "user@#{domain}" }.join(", ")
s_("EmailsOnPushService|Send notifications from the committer's email address if the domain matches the domain used by your GitLab instance (such as %{domains}).") % { domains: domains }
end
end
field :disable_diffs,
type: 'checkbox',
title: -> { s_("EmailsOnPushService|Disable code diffs") },
help: -> { s_("EmailsOnPushService|Don't include possibly sensitive code diffs in notification body.") }
field :branches_to_be_notified,
type: 'select',
title: -> { s_('Integrations|Branches for which notifications are to be sent') },
choices: branch_choices
field :recipients,
type: 'textarea',
placeholder: -> { s_('EmailsOnPushService|tanuki@example.com gitlab@example.com') },
help: -> { s_('EmailsOnPushService|Emails separated by whitespace.') }
def self.valid_recipients(recipients)
recipients.split.grep(Devise.email_regexp).uniq(&:downcase)
end
@ -67,28 +90,6 @@ module Integrations
Gitlab::Utils.to_boolean(self.disable_diffs)
end
def fields
domains = Notify.allowed_email_domains.map { |domain| "user@#{domain}" }.join(", ")
[
{ type: 'checkbox', name: 'send_from_committer_email', title: s_("EmailsOnPushService|Send from committer"),
help: s_("EmailsOnPushService|Send notifications from the committer's email address if the domain matches the domain used by your GitLab instance (such as %{domains}).") % { domains: domains } },
{ type: 'checkbox', name: 'disable_diffs', title: s_("EmailsOnPushService|Disable code diffs"),
help: s_("EmailsOnPushService|Don't include possibly sensitive code diffs in notification body.") },
{
type: 'select',
name: 'branches_to_be_notified',
title: s_('Integrations|Branches for which notifications are to be sent'),
choices: branch_choices
},
{
type: 'textarea',
name: 'recipients',
placeholder: s_('EmailsOnPushService|tanuki@example.com gitlab@example.com'),
help: s_('EmailsOnPushService|Emails separated by whitespace.')
}
]
end
private
def number_of_recipients_within_limit

View File

@ -2,9 +2,14 @@
module Integrations
class ExternalWiki < Integration
prop_accessor :external_wiki_url
validates :external_wiki_url, presence: true, public_url: true, if: :activated?
field :external_wiki_url,
title: -> { s_('ExternalWikiService|External wiki URL') },
placeholder: -> { s_('ExternalWikiService|https://example.com/xxx/wiki/...') },
help: -> { s_('ExternalWikiService|Enter the URL to the external wiki.') },
required: true
def title
s_('ExternalWikiService|External wiki')
end
@ -17,19 +22,6 @@ module Integrations
'external_wiki'
end
def fields
[
{
type: 'text',
name: 'external_wiki_url',
title: s_('ExternalWikiService|External wiki URL'),
placeholder: s_('ExternalWikiService|https://example.com/xxx/wiki/...'),
help: 'Enter the URL to the external wiki.',
required: true
}
]
end
def help
docs_link = ActionController::Base.helpers.link_to _('Learn more.'), Rails.application.routes.url_helpers.help_page_url('user/project/wiki/index', anchor: 'link-an-external-wiki'), target: '_blank', rel: 'noopener noreferrer'

View File

@ -2,9 +2,16 @@
module Integrations
class Flowdock < Integration
prop_accessor :token
validates :token, presence: true, if: :activated?
field :token,
type: 'password',
help: -> { s_('FlowdockService|Enter your Flowdock token.') },
non_empty_password_title: -> { s_('ProjectService|Enter new token') },
non_empty_password_help: -> { s_('ProjectService|Leave blank to use your current token.') },
placeholder: '1b609b52537...',
required: true
def title
'Flowdock'
end
@ -22,20 +29,6 @@ module Integrations
'flowdock'
end
def fields
[
{
type: 'password',
name: 'token',
help: s_('FlowdockService|Enter your Flowdock token.'),
non_empty_password_title: s_('ProjectService|Enter new token'),
non_empty_password_help: s_('ProjectService|Leave blank to use your current token.'),
placeholder: '1b609b52537...',
required: true
}
]
end
def self.supported_events
%w(push)
end

View File

@ -39,7 +39,7 @@ module Integrations
type: 'select',
name: 'branches_to_be_notified',
title: s_('Integrations|Branches for which notifications are to be sent'),
choices: branch_choices
choices: self.class.branch_choices
}
]
end

View File

@ -44,7 +44,7 @@ module Integrations
section: SECTION_TYPE_CONFIGURATION,
name: 'branches_to_be_notified',
title: s_('Integrations|Branches for which notifications are to be sent'),
choices: branch_choices
choices: self.class.branch_choices
}
]
end

View File

@ -76,7 +76,7 @@ module Integrations
{ type: 'select',
name: 'branches_to_be_notified',
title: s_('Integrations|Branches for which notifications are to be sent'),
choices: branch_choices }
choices: self.class.branch_choices }
]
end

View File

@ -4,9 +4,22 @@ module Integrations
class Pivotaltracker < Integration
API_ENDPOINT = 'https://www.pivotaltracker.com/services/v5/source_commits'
prop_accessor :token, :restrict_to_branch
validates :token, presence: true, if: :activated?
field :token,
type: 'password',
help: -> { s_('PivotalTrackerService|Pivotal Tracker API token. User must have access to the story. All comments are attributed to this user.') },
non_empty_password_title: -> { s_('ProjectService|Enter new token') },
non_empty_password_help: -> { s_('ProjectService|Leave blank to use your current token.') },
required: true
field :restrict_to_branch,
title: -> { s_('Integrations|Restrict to branch (optional)') },
help: -> do
s_('PivotalTrackerService|Comma-separated list of branches to ' \
'automatically inspect. Leave blank to include all branches.')
end
def title
'Pivotal Tracker'
end
@ -24,26 +37,6 @@ module Integrations
'pivotaltracker'
end
def fields
[
{
type: 'password',
name: 'token',
help: s_('PivotalTrackerService|Pivotal Tracker API token. User must have access to the story. All comments are attributed to this user.'),
non_empty_password_title: s_('ProjectService|Enter new token'),
non_empty_password_help: s_('ProjectService|Leave blank to use your current token.'),
required: true
},
{
type: 'text',
name: 'restrict_to_branch',
title: 'Restrict to branch (optional)',
help: s_('PivotalTrackerService|Comma-separated list of branches to ' \
'automatically inspect. Leave blank to include all branches.')
}
]
end
def self.supported_events
%w(push)
end

View File

@ -4,11 +4,30 @@ module Integrations
class Prometheus < BaseMonitoring
include PrometheusAdapter
# Access to prometheus is directly through the API
prop_accessor :api_url
prop_accessor :google_iap_service_account_json
prop_accessor :google_iap_audience_client_id
boolean_accessor :manual_configuration
field :manual_configuration,
type: 'checkbox',
title: -> { s_('PrometheusService|Active') },
help: -> { s_('PrometheusService|Select this checkbox to override the auto configuration settings with your own settings.') },
required: true
field :api_url,
title: 'API URL',
placeholder: -> { s_('PrometheusService|https://prometheus.example.com/') },
help: -> { s_('PrometheusService|The Prometheus API base URL.') },
required: true
field :google_iap_audience_client_id,
title: 'Google IAP Audience Client ID',
placeholder: -> { s_('PrometheusService|IAP_CLIENT_ID.apps.googleusercontent.com') },
help: -> { s_('PrometheusService|The ID of the IAP-secured resource.') },
required: false
field :google_iap_service_account_json,
type: 'textarea',
title: 'Google IAP Service Account JSON',
placeholder: -> { s_('PrometheusService|{ "type": "service_account", "project_id": ... }') },
help: -> { s_('PrometheusService|The contents of the credentials.json file of your service account.') },
required: false
# We need to allow the self-monitoring project to connect to the internal
# Prometheus instance.
@ -45,43 +64,6 @@ module Integrations
'prometheus'
end
def fields
[
{
type: 'checkbox',
name: 'manual_configuration',
title: s_('PrometheusService|Active'),
help: s_('PrometheusService|Select this checkbox to override the auto configuration settings with your own settings.'),
required: true
},
{
type: 'text',
name: 'api_url',
title: 'API URL',
placeholder: s_('PrometheusService|https://prometheus.example.com/'),
help: s_('PrometheusService|The Prometheus API base URL.'),
required: true
},
{
type: 'text',
name: 'google_iap_audience_client_id',
title: 'Google IAP Audience Client ID',
placeholder: s_('PrometheusService|IAP_CLIENT_ID.apps.googleusercontent.com'),
help: s_('PrometheusService|The ID of the IAP-secured resource.'),
autocomplete: 'off',
required: false
},
{
type: 'textarea',
name: 'google_iap_service_account_json',
title: 'Google IAP Service Account JSON',
placeholder: s_('PrometheusService|{ "type": "service_account", "project_id": ... }'),
help: s_('PrometheusService|The contents of the credentials.json file of your service account.'),
required: false
}
]
end
# Check we can connect to the Prometheus API
def test(*args)
return { success: false, result: 'Prometheus configuration error' } unless prometheus_client

View File

@ -35,7 +35,7 @@ module Integrations
type: 'select',
name: 'branches_to_be_notified',
title: s_('Integrations|Branches for which notifications are to be sent'),
choices: branch_choices
choices: self.class.branch_choices
}
]
end

View File

@ -35,7 +35,7 @@ module Integrations
type: 'select',
name: 'branches_to_be_notified',
title: s_('Integrations|Branches for which notifications are to be sent'),
choices: branch_choices
choices: self.class.branch_choices
}
]
end

View File

@ -23,7 +23,7 @@ module Integrations
integration.event_channel_name(event)
end
expose :value do |event|
integration.get_channel_field(event)
integration.event_channel_value(event)
end
end

View File

@ -1,4 +1,4 @@
- add_page_startup_api_call Feature.enabled?(:paginated_issue_discussions, @project) ? discussions_path(@issue, per_page: 20) : discussions_path(@issue)
- add_page_startup_api_call discussions_path(@issue, per_page: 20)
- @gfm_form = true

View File

@ -1,8 +0,0 @@
---
name: paginated_issue_discussions
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/69933
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/345351
milestone: '14.5'
type: development
group: group::project management
default_enabled: false

View File

@ -3,7 +3,7 @@ table_name: ci_job_token_project_scope_links
classes:
- Ci::JobToken::ProjectScopeLink
feature_categories:
- pipeline_authoring
description: TODO
- continuous_integration
description: The connection between a source project, which defines the job token scope, and a target project, which is the one allowed to be accessed by the job token.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/62733
milestone: '14.0'

View File

@ -475,16 +475,16 @@ end
Developers can add [feature flags](../development/feature_flags/index.md) to GraphQL
fields in the following ways:
- Add the `feature_flag` property to a field. This allows the field to be _hidden_
- Add the [`feature_flag` property](#feature_flag-property) to a field. This allows the field to be _hidden_
from the GraphQL schema when the flag is disabled.
- Toggle the return value when resolving the field.
- [Toggle the return value](#toggle-the-value-of-a-field) when resolving the field.
You can refer to these guidelines to decide which approach to use:
- If your field is experimental, and its name or type is subject to
change, use the `feature_flag` property.
change, use the [`feature_flag` property](#feature_flag-property).
- If your field is stable and its definition doesn't change, even after the flag is
removed, toggle the return value of the field instead. Note that
removed, [toggle the return value](#toggle-the-value-of-a-field) of the field instead. Note that
[all fields should be nullable](#nullable-fields) anyway.
### `feature_flag` property
@ -524,6 +524,12 @@ return value of the field. This can be done in the resolver, in the
type, or even in a model method, depending on your preference and
situation.
Consider also [marking the field as Alpha](#marking-schema-items-as-alpha)
while the value of the field can be toggled. You can
[change or remove Alpha fields at any time](#breaking-change-exemptions) without needing to deprecate them.
This also signals to consumers of the public GraphQL API that the field is not
meant to be used yet.
When applying a feature flag to toggle the value of a field, the
`description` of the field must:
@ -537,6 +543,7 @@ Example:
```ruby
field :foo, GraphQL::Types::String,
null: true,
deprecated: { reason: :alpha, milestone: '10.0' },
description: 'Some test field. Returns `null`' \
'if `my_feature_flag` feature flag is disabled.'
@ -2007,13 +2014,13 @@ end
.to contain_exactly(a_graphql_entity_for(issue, :iid, :title, created_at: some_time))
```
- Use `GraphqlHelpers#empty_schema` to create an empty schema, rather than creating
- Use `GraphqlHelpers#empty_schema` to create an empty schema, rather than creating
one by hand. For example:
```ruby
# good
let(:schema) { empty_schema }
# bad
let(:query_type) { GraphQL::ObjectType.new }
let(:schema) { GraphQL::Schema.define(query: query_type, mutation: nil)}
@ -2024,7 +2031,7 @@ end
```ruby
# good
let(:query) { query_double(schema: GitlabSchema) }
# bad
let(:query) { double('Query', schema: GitlabSchema) }
```
@ -2092,9 +2099,9 @@ end
```ruby
type Types::IssueType.connection_type, null: true
```
However this might cause a cyclic definition, which can result in errors like:
```ruby
NameError: uninitialized constant Resolvers::GroupIssuesResolver
```
@ -2109,7 +2116,7 @@ end
class IssueConnectionType < CountableConnectionType
end
end
Types::IssueConnectionType.prepend_mod_with('Types::IssueConnectionType')
```
@ -2120,22 +2127,22 @@ end
```ruby
type "Types::IssueConnection", null: true
```
Only use this style if you are having spec failures. This is not intended to be a new
pattern that we use. This issue may disappear after we've upgraded to `2.x`.
- There can be instances where a spec fails because the class is not loaded correctly.
It relates to the
- There can be instances where a spec fails because the class is not loaded correctly.
It relates to the
[circular dependencies problem](https://github.com/rmosolgo/graphql-ruby/issues/1929) and
[Adding field with resolver on a Type causes "Can't determine the return type " error on a different Type](https://github.com/rmosolgo/graphql-ruby/issues/3974).
Unfortunately, the errors generated don't really indicate what the problem is. For example,
remove the quotes from the `Rspec.descrbe` in
remove the quotes from the `Rspec.descrbe` in
[ee/spec/graphql/resolvers/compliance_management/merge_requests/compliance_violation_resolver_spec.rb](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/spec/graphql/resolvers/compliance_management/merge_requests/compliance_violation_resolver_spec.rb).
Then run `rspec ee/spec/graphql/resolvers/compliance_management/merge_requests/compliance_violation_resolver_spec.rb`.
This generates errors with the expectations. For example:
```ruby
1) Resolvers::ComplianceManagement::MergeRequests::ComplianceViolationResolver#resolve user is authorized filtering the results when given an array of project IDs finds the filtered compliance violations
Failure/Error: expect(subject).to contain_exactly(compliance_violation)
@ -2145,10 +2152,10 @@ end
the extra elements were: [#<MergeRequests::ComplianceViolation id: 5, violating_user_id: 27, merge_request_id: 5, reason: "approved_by_merge_request_author", severity_level: "high">]
# ./ee/spec/graphql/resolvers/compliance_management/merge_requests/compliance_violation_resolver_spec.rb:55:in `block (6 levels) in <top (required)>'
```
However, this is not a case of the wrong result being generated, it's because of the loading order
of the `ComplianceViolationResolver` class.
The only way we've found to fix this is by quoting the class name in the spec. For example, changing
```ruby
@ -2198,7 +2205,7 @@ end
[removed eventually](https://gitlab.com/gitlab-org/gitlab/-/issues/363121),
and writing unit tests for resolvers/mutations is
[already deprecated](#writing-unit-tests-deprecated)
## Notes about Query flow and GraphQL infrastructure
The GitLab GraphQL infrastructure can be found in `lib/gitlab/graphql`.

View File

@ -104,7 +104,10 @@ for the section. For example:
> `widget_message` [introduced](<link-to-issue>) in GitLab 14.3.
```
## Attribute deprecation
## Deprecations
To document the deprecation of an API endpoint, follow the steps to
[deprecate a page or topic](versions.md#deprecate-a-page-or-topic).
To deprecate an attribute:
@ -122,8 +125,8 @@ To deprecate an attribute:
| `widget_name` | string | **{dotted-circle}** No | [Deprecated](<link-to-issue>) in GitLab 14.7 and is planned for removal in 15.4. Use `widget_id` instead. The name of the widget. |
```
1. Optional. To widely announce the change, or if it's a breaking change,
[update the deprecations and removals documentation](../deprecation_guidelines/#update-the-deprecations-and-removals-documentation).
To widely announce a deprecation, or if it's a breaking change,
[update the deprecations and removals documentation](../deprecation_guidelines/#update-the-deprecations-and-removals-documentation).
## Method description

View File

@ -15520,6 +15520,9 @@ msgstr ""
msgid "ExternalIssueIntegration|This issue is synchronized with %{trackerName}"
msgstr ""
msgid "ExternalWikiService|Enter the URL to the external wiki."
msgstr ""
msgid "ExternalWikiService|External wiki"
msgstr ""
@ -20767,6 +20770,9 @@ msgstr ""
msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Restrict to branch (optional)"
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
msgstr ""

View File

@ -151,7 +151,7 @@
"pikaday": "^1.8.0",
"popper.js": "^1.16.1",
"portal-vue": "^2.1.7",
"postcss": "8.4.5",
"postcss": "8.4.14",
"prismjs": "^1.21.0",
"prosemirror-markdown": "1.8.0",
"prosemirror-model": "^1.16.1",
@ -201,7 +201,7 @@
},
"devDependencies": {
"@gitlab/eslint-plugin": "12.1.0",
"@gitlab/stylelint-config": "4.0.0",
"@gitlab/stylelint-config": "4.1.0",
"@graphql-eslint/eslint-plugin": "3.10.4",
"@testing-library/dom": "^7.16.2",
"@types/jest": "^26.0.24",
@ -248,7 +248,7 @@
"purgecss": "^4.0.3",
"purgecss-from-html": "^4.0.3",
"sass": "^1.49.9",
"stylelint": "^14.3.0",
"stylelint": "^14.9.1",
"timezone-mock": "^1.0.8",
"vue-jest": "4.0.1",
"webpack-dev-server": "4.9.2",

View File

@ -34,7 +34,7 @@ module RuboCop
#
# class Gitlab::MyDomain::MyClass
# end
class NamespacedClass < RuboCop::Cop::Cop
class NamespacedClass < RuboCop::Cop::Base
MSG = 'Classes must be declared inside a module indicating a product domain namespace. For more info: https://gitlab.com/gitlab-org/gitlab/-/issues/321982'
# These namespaces are considered top-level semantically.
@ -51,7 +51,7 @@ module RuboCop
# Remove class name because it's not a domain namespace.
add_potential_domain_namespace(node) { _1.pop }
add_offense(node) if domain_namespaces.none?
add_offense(node.loc.name) if domain_namespaces.none?
end
private

View File

@ -14,7 +14,7 @@ jq -crM '.vulnerabilities |
) |
sort |
if length > 0 then
{ body: ("The findings below have been detected based on the [AppSec custom Semgrep rules](https://gitlab.com/gitlab-com/gl-security/appsec/sast-custom-rules/) and need attention:\n\n" + join("\n") + "\n\n/cc @gitlab-com/gl-security/appsec") }
{ body: ("The findings below have been detected based on the AppSec custom SAST rules. For more information about this bot and what to do with this comment head over to the [README](https://gitlab.com/gitlab-com/gl-security/appsec/sast-custom-rules/-/tree/main/appsec-pings). The following lines of code possibly need attention:\n\n" + join("\n") + "\n\n/cc @gitlab-com/gl-security/appsec") }
else
empty
end' gl-sast-report.json >findings.txt

View File

@ -217,8 +217,7 @@ describe('note_app', () => {
});
afterEach(() => {
waitForDiscussionsRequest();
resetHTMLFixture();
return waitForDiscussionsRequest().then(() => resetHTMLFixture());
});
it('renders skeleton notes', () => {
@ -471,7 +470,7 @@ describe('note_app', () => {
wrapper = shallowMount(NotesApp, { propsData, store: createStore() });
await waitForPromises();
expect(axiosMock.history.get[0].params).toBeUndefined();
expect(axiosMock.history.get[0].params).toEqual({ per_page: 20 });
});
});
@ -496,14 +495,14 @@ describe('note_app', () => {
wrapper = mountWithNotesFilter(undefined);
await waitForPromises();
expect(axiosMock.history.get[0].params).toBeUndefined();
expect(axiosMock.history.get[0].params).toEqual({ per_page: 20 });
});
it('does not include extra query params when filter is already set to default', async () => {
wrapper = mountWithNotesFilter(constants.DISCUSSION_FILTERS_DEFAULT_VALUE);
await waitForPromises();
expect(axiosMock.history.get[0].params).toBeUndefined();
expect(axiosMock.history.get[0].params).toEqual({ per_page: 20 });
});
it('includes extra query params when filter is not set to default', async () => {
@ -512,6 +511,7 @@ describe('note_app', () => {
expect(axiosMock.history.get[0].params).toEqual({
notes_filter: constants.DISCUSSION_FILTERS_DEFAULT_VALUE,
per_page: 20,
persist_filter: false,
});
});

View File

@ -1351,7 +1351,7 @@ describe('Actions Notes Store', () => {
return testAction(
actions.fetchDiscussions,
{},
null,
{ noteableType: notesConstants.MERGE_REQUEST_NOTEABLE_TYPE },
[
{ type: mutationTypes.ADD_OR_UPDATE_DISCUSSIONS, payload: { discussion } },
{ type: mutationTypes.SET_FETCHING_DISCUSSIONS, payload: false },
@ -1360,13 +1360,11 @@ describe('Actions Notes Store', () => {
);
});
it('dispatches `fetchDiscussionsBatch` action if `paginatedIssueDiscussions` feature flag is enabled', () => {
window.gon = { features: { paginatedIssueDiscussions: true } };
it('dispatches `fetchDiscussionsBatch` action if noteable is an Issue', () => {
return testAction(
actions.fetchDiscussions,
{ path: 'test-path', filter: 'test-filter', persistFilter: 'test-persist-filter' },
null,
{ noteableType: notesConstants.ISSUE_NOTEABLE_TYPE },
[],
[
{
@ -1389,7 +1387,7 @@ describe('Actions Notes Store', () => {
return testAction(
actions.fetchDiscussions,
{ path: 'test-path', filter: 'test-filter', persistFilter: 'test-persist-filter' },
null,
{ noteableType: notesConstants.MERGE_REQUEST_NOTEABLE_TYPE },
[],
[
{

View File

@ -1,97 +0,0 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::Elasticsearch::Logs::Lines do
let(:client) { Elasticsearch::Transport::Client }
let(:es_message_1) { { timestamp: "2019-12-13T14:35:34.034Z", pod: "production-6866bc8974-m4sk4", message: "10.8.2.1 - - [25/Oct/2019:08:03:22 UTC] \"GET / HTTP/1.1\" 200 13" } }
let(:es_message_2) { { timestamp: "2019-12-13T14:35:35.034Z", pod: "production-6866bc8974-m4sk4", message: "10.8.2.1 - - [27/Oct/2019:23:49:54 UTC] \"GET / HTTP/1.1\" 200 13" } }
let(:es_message_3) { { timestamp: "2019-12-13T14:35:36.034Z", pod: "production-6866bc8974-m4sk4", message: "10.8.2.1 - - [04/Nov/2019:23:09:24 UTC] \"GET / HTTP/1.1\" 200 13" } }
let(:es_message_4) { { timestamp: "2019-12-13T14:35:37.034Z", pod: "production-6866bc8974-m4sk4", message: "- -\u003e /" } }
let(:es_response) { Gitlab::Json.parse(fixture_file('lib/elasticsearch/logs_response.json')) }
subject { described_class.new(client) }
let(:namespace) { "autodevops-deploy-9-production" }
let(:pod_name) { "production-6866bc8974-m4sk4" }
let(:container_name) { "auto-deploy-app" }
let(:search) { "foo +bar "}
let(:start_time) { "2019-12-13T14:35:34.034Z" }
let(:end_time) { "2019-12-13T14:35:34.034Z" }
let(:cursor) { "9999934,1572449784442" }
let(:body) { Gitlab::Json.parse(fixture_file('lib/elasticsearch/query.json')) }
let(:body_with_container) { Gitlab::Json.parse(fixture_file('lib/elasticsearch/query_with_container.json')) }
let(:body_with_search) { Gitlab::Json.parse(fixture_file('lib/elasticsearch/query_with_search.json')) }
let(:body_with_times) { Gitlab::Json.parse(fixture_file('lib/elasticsearch/query_with_times.json')) }
let(:body_with_start_time) { Gitlab::Json.parse(fixture_file('lib/elasticsearch/query_with_start_time.json')) }
let(:body_with_end_time) { Gitlab::Json.parse(fixture_file('lib/elasticsearch/query_with_end_time.json')) }
let(:body_with_cursor) { Gitlab::Json.parse(fixture_file('lib/elasticsearch/query_with_cursor.json')) }
let(:body_with_filebeat_6) { Gitlab::Json.parse(fixture_file('lib/elasticsearch/query_with_filebeat_6.json')) }
RSpec::Matchers.define :a_hash_equal_to_json do |expected|
match do |actual|
actual.as_json == expected
end
end
describe '#pod_logs' do
it 'returns the logs as an array' do
expect(client).to receive(:search).with(body: a_hash_equal_to_json(body)).and_return(es_response)
result = subject.pod_logs(namespace, pod_name: pod_name)
expect(result).to eq(logs: [es_message_4, es_message_3, es_message_2, es_message_1], cursor: cursor)
end
it 'can further filter the logs by container name' do
expect(client).to receive(:search).with(body: a_hash_equal_to_json(body_with_container)).and_return(es_response)
result = subject.pod_logs(namespace, pod_name: pod_name, container_name: container_name)
expect(result).to eq(logs: [es_message_4, es_message_3, es_message_2, es_message_1], cursor: cursor)
end
it 'can further filter the logs by search' do
expect(client).to receive(:search).with(body: a_hash_equal_to_json(body_with_search)).and_return(es_response)
result = subject.pod_logs(namespace, pod_name: pod_name, search: search)
expect(result).to eq(logs: [es_message_4, es_message_3, es_message_2, es_message_1], cursor: cursor)
end
it 'can further filter the logs by start_time and end_time' do
expect(client).to receive(:search).with(body: a_hash_equal_to_json(body_with_times)).and_return(es_response)
result = subject.pod_logs(namespace, pod_name: pod_name, start_time: start_time, end_time: end_time)
expect(result).to eq(logs: [es_message_4, es_message_3, es_message_2, es_message_1], cursor: cursor)
end
it 'can further filter the logs by only start_time' do
expect(client).to receive(:search).with(body: a_hash_equal_to_json(body_with_start_time)).and_return(es_response)
result = subject.pod_logs(namespace, pod_name: pod_name, start_time: start_time)
expect(result).to eq(logs: [es_message_4, es_message_3, es_message_2, es_message_1], cursor: cursor)
end
it 'can further filter the logs by only end_time' do
expect(client).to receive(:search).with(body: a_hash_equal_to_json(body_with_end_time)).and_return(es_response)
result = subject.pod_logs(namespace, pod_name: pod_name, end_time: end_time)
expect(result).to eq(logs: [es_message_4, es_message_3, es_message_2, es_message_1], cursor: cursor)
end
it 'can search after a cursor' do
expect(client).to receive(:search).with(body: a_hash_equal_to_json(body_with_cursor)).and_return(es_response)
result = subject.pod_logs(namespace, pod_name: pod_name, cursor: cursor)
expect(result).to eq(logs: [es_message_4, es_message_3, es_message_2, es_message_1], cursor: cursor)
end
it 'can search on filebeat 6' do
expect(client).to receive(:search).with(body: a_hash_equal_to_json(body_with_filebeat_6)).and_return(es_response)
result = subject.pod_logs(namespace, pod_name: pod_name, chart_above_v2: false)
expect(result).to eq(logs: [es_message_4, es_message_3, es_message_2, es_message_1], cursor: cursor)
end
end
end

View File

@ -1,35 +0,0 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::Elasticsearch::Logs::Pods do
let(:client) { Elasticsearch::Transport::Client }
let(:es_query) { Gitlab::Json.parse(fixture_file('lib/elasticsearch/pods_query.json'), symbolize_names: true) }
let(:es_response) { Gitlab::Json.parse(fixture_file('lib/elasticsearch/pods_response.json')) }
let(:namespace) { "autodevops-deploy-9-production" }
subject { described_class.new(client) }
describe '#pods' do
it 'returns the pods' do
expect(client).to receive(:search).with(body: es_query).and_return(es_response)
result = subject.pods(namespace)
expect(result).to eq([
{
name: "runner-gitlab-runner-7bbfb5dcb5-p6smb",
container_names: %w[runner-gitlab-runner]
},
{
name: "elastic-stack-elasticsearch-master-1",
container_names: %w[elasticsearch chown sysctl]
},
{
name: "ingress-nginx-ingress-controller-76449bcc8d-8qgl6",
container_names: %w[nginx-ingress-controller]
}
])
end
end
end

View File

@ -292,15 +292,15 @@ RSpec.describe Integrations::BaseChatNotification do
end
end
describe '#get_channel_field' do
describe '#event_channel_value' do
it 'returns the channel field value for the given event' do
subject.push_channel = '#pushes'
expect(subject.get_channel_field(:push)).to eq('#pushes')
expect(subject.event_channel_value(:push)).to eq('#pushes')
end
it 'raises an error for unsupported events' do
expect { subject.get_channel_field(:foo) }.to raise_error(NoMethodError)
expect { subject.event_channel_value(:foo) }.to raise_error(NoMethodError)
end
end
end

View File

@ -475,47 +475,4 @@ RSpec.describe Integrations::Prometheus, :use_clean_rails_memory_store_caching,
end
end
end
describe '#fields' do
let(:expected_fields) do
[
{
type: 'checkbox',
name: 'manual_configuration',
title: s_('PrometheusService|Active'),
help: s_('PrometheusService|Select this checkbox to override the auto configuration settings with your own settings.'),
required: true
},
{
type: 'text',
name: 'api_url',
title: 'API URL',
placeholder: s_('PrometheusService|https://prometheus.example.com/'),
help: s_('PrometheusService|The Prometheus API base URL.'),
required: true
},
{
type: 'text',
name: 'google_iap_audience_client_id',
title: 'Google IAP Audience Client ID',
placeholder: s_('PrometheusService|IAP_CLIENT_ID.apps.googleusercontent.com'),
help: s_('PrometheusService|The ID of the IAP-secured resource.'),
autocomplete: 'off',
required: false
},
{
type: 'textarea',
name: 'google_iap_service_account_json',
title: 'Google IAP Service Account JSON',
placeholder: s_('PrometheusService|{ "type": "service_account", "project_id": ... }'),
help: s_('PrometheusService|The contents of the credentials.json file of your service account.'),
required: false
}
]
end
it 'returns fields' do
expect(integration.fields).to eq(expected_fields)
end
end
end

View File

@ -26,90 +26,7 @@ RSpec.describe API::Environments do
expect(json_response.first['tier']).to eq(environment.tier)
expect(json_response.first['external_url']).to eq(environment.external_url)
expect(json_response.first['project']).to match_schema('public_api/v4/project')
expect(json_response.first['enable_advanced_logs_querying']).to eq(false)
expect(json_response.first).not_to have_key('last_deployment')
expect(json_response.first).not_to have_key('gitlab_managed_apps_logs_path')
end
context 'when the user can read pod logs' do
context 'with successful deployment on cluster' do
let_it_be(:deployment) { create(:deployment, :on_cluster, :success, environment: environment, project: project) }
it 'returns environment with enable_advanced_logs_querying and logs_api_path' do
get api("/projects/#{project.id}/environments", user)
expect(response).to have_gitlab_http_status(:ok)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.size).to eq(1)
expect(json_response.first['gitlab_managed_apps_logs_path']).to eq(
"/#{project.full_path}/-/logs/k8s.json?cluster_id=#{deployment.cluster_id}"
)
end
end
context 'when elastic stack is available' do
before do
allow_next_found_instance_of(Environment) do |env|
allow(env).to receive(:elastic_stack_available?).and_return(true)
end
end
it 'returns environment with enable_advanced_logs_querying and logs_api_path' do
get api("/projects/#{project.id}/environments", user)
expect(response).to have_gitlab_http_status(:ok)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.size).to eq(1)
expect(json_response.first['enable_advanced_logs_querying']).to eq(true)
expect(json_response.first['logs_api_path']).to eq(
"/#{project.full_path}/-/logs/elasticsearch.json?environment_name=#{environment.name}"
)
end
end
context 'when elastic stack is not available' do
before do
allow_next_found_instance_of(Environment) do |env|
allow(env).to receive(:elastic_stack_available?).and_return(false)
end
end
it 'returns environment with enable_advanced_logs_querying logs_api_path' do
get api("/projects/#{project.id}/environments", user)
expect(response).to have_gitlab_http_status(:ok)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.size).to eq(1)
expect(json_response.first['enable_advanced_logs_querying']).to eq(false)
expect(json_response.first['logs_api_path']).to eq(
"/#{project.full_path}/-/logs/k8s.json?environment_name=#{environment.name}"
)
end
end
end
context 'when the user cannot read pod logs' do
before do
allow_next_found_instance_of(User) do |user|
allow(user).to receive(:can?).and_call_original
allow(user).to receive(:can?).with(:read_pod_logs, project).and_return(false)
end
end
it 'does not contain enable_advanced_logs_querying' do
get api("/projects/#{project.id}/environments", user)
expect(response).to have_gitlab_http_status(:ok)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.size).to eq(1)
expect(json_response.first).not_to have_key('enable_advanced_logs_querying')
expect(json_response.first).not_to have_key('logs_api_path')
expect(json_response.first).not_to have_key('gitlab_managed_apps_logs_path')
end
end
context 'when filtering' do

View File

@ -50,22 +50,6 @@ RSpec.describe Projects::IssuesController do
a_hash_including('id' => discussion_2.id.to_s)
])
end
context 'when paginated_issue_discussions is disabled' do
before do
stub_feature_flags(paginated_issue_discussions: false)
end
it 'returns all discussions and ignores per_page param' do
get_discussions(per_page: 2)
discussions = Gitlab::Json.parse(response.body)
notes = discussions.flat_map { |d| d['notes'] }
expect(discussions.count).to eq(4)
expect(notes.count).to eq(5)
end
end
end
end

View File

@ -20,7 +20,7 @@ RSpec.describe RuboCop::Cop::Gitlab::NamespacedClass do
it 'flags a class definition without additional namespace' do
expect_offense(namespaced(<<~SOURCE))
class MyClass
^^^^^^^^^^^^^ #{described_class::MSG}
^^^^^^^ #{described_class::MSG}
end
SOURCE
end
@ -28,7 +28,7 @@ RSpec.describe RuboCop::Cop::Gitlab::NamespacedClass do
it 'flags a compact class definition without additional namespace' do
expect_offense(<<~SOURCE, namespace: namespace)
class %{namespace}::MyClass
^{namespace}^^^^^^^^^^^^^^^ #{described_class::MSG}
^{namespace}^^^^^^^^^ #{described_class::MSG}
end
SOURCE
end
@ -36,7 +36,7 @@ RSpec.describe RuboCop::Cop::Gitlab::NamespacedClass do
it 'flags a class definition with inheritance without additional namespace' do
expect_offense(namespaced(<<~SOURCE))
class MyClass < ApplicationRecord
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #{described_class::MSG}
^^^^^^^ #{described_class::MSG}
def some_method
true
end

View File

@ -56,37 +56,6 @@ RSpec.describe ClusterEntity do
end
end
context 'gitlab_managed_apps_logs_path' do
let(:cluster) { create(:cluster, :project) }
let(:user) { create(:user) }
subject { described_class.new(cluster, request: request).as_json }
before do
allow_next_instance_of(Clusters::ClusterPresenter) do |presenter|
allow(presenter).to receive(:show_path).and_return(nil)
end
end
it 'return projects log explorer path' do
log_explorer_path = project_logs_path(cluster.project, cluster_id: cluster.id)
expect_next_instance_of(Clusters::ClusterPresenter, cluster, current_user: user) do |presenter|
expect(presenter).to receive(:gitlab_managed_apps_logs_path).and_return(log_explorer_path)
end
expect(subject[:gitlab_managed_apps_logs_path]).to eq(log_explorer_path)
end
context 'when feature is disabled' do
before do
stub_feature_flags(monitor_logging: false)
end
specify { is_expected.not_to include(:gitlab_managed_apps_logs_path) }
end
end
context 'enable_advanced_logs_querying' do
let(:cluster) { create(:cluster, :project) }
let(:user) { create(:user) }

View File

@ -133,54 +133,6 @@ RSpec.describe EnvironmentEntity do
end
end
context 'pod_logs' do
context 'with reporter access' do
before do
project.add_reporter(user)
end
it 'does not expose logs keys' do
expect(subject).not_to include(:logs_path)
expect(subject).not_to include(:logs_api_path)
expect(subject).not_to include(:enable_advanced_logs_querying)
end
end
context 'with developer access' do
before do
project.add_developer(user)
end
it 'exposes logs keys' do
expect(subject).to include(:logs_path)
expect(subject).to include(:logs_api_path)
expect(subject).to include(:enable_advanced_logs_querying)
end
it 'uses k8s api when ES is not available' do
expect(subject[:logs_api_path]).to eq(k8s_project_logs_path(project, environment_name: environment.name, format: :json))
end
it 'uses ES api when ES is available' do
allow(environment).to receive(:elastic_stack_available?).and_return(true)
expect(subject[:logs_api_path]).to eq(elasticsearch_project_logs_path(project, environment_name: environment.name, format: :json))
end
context 'with feature flag disabled' do
before do
stub_feature_flags(monitor_logging: false)
end
it 'does not expose logs keys' do
expect(subject).not_to include(:logs_path)
expect(subject).not_to include(:logs_api_path)
expect(subject).not_to include(:enable_advanced_logs_querying)
end
end
end
end
context 'with deployment service ready' do
before do
allow(environment).to receive(:has_terminals?).and_return(true)

169
yarn.lock
View File

@ -976,6 +976,11 @@
exec-sh "^0.3.2"
minimist "^1.2.0"
"@csstools/selector-specificity@^2.0.1":
version "2.0.1"
resolved "https://registry.yarnpkg.com/@csstools/selector-specificity/-/selector-specificity-2.0.1.tgz#b6b8d81780b9a9f6459f4bfe9226ac6aefaefe87"
integrity sha512-aG20vknL4/YjQF9BSV7ts4EWm/yrjagAN7OWBNmlbEOUiu0llj4OGrFoOKK3g2vey4/p2omKCoHrWtPxSwV3HA==
"@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"
@ -1034,14 +1039,14 @@
resolved "https://registry.yarnpkg.com/@gitlab/favicon-overlay/-/favicon-overlay-2.0.0.tgz#2f32d0b6a4d5b8ac44e2927083d9ab478a78c984"
integrity sha512-GNcORxXJ98LVGzOT9dDYKfbheqH6lNgPDD72lyXRnQIH7CjgGyos8i17aSBPq1f4s3zF3PyedFiAR4YEZbva2Q==
"@gitlab/stylelint-config@4.0.0":
version "4.0.0"
resolved "https://registry.yarnpkg.com/@gitlab/stylelint-config/-/stylelint-config-4.0.0.tgz#d24646592908d92dce35aaa8e7bea66fd53e4160"
integrity sha512-5avbb8XQLdnL/vdrxf9jvdPl/HPaXnI7Y/fArOHe1uMpgcBoBWUKmon15+lHJ4MgURsGuYSiM2FIgArDSylR4g==
"@gitlab/stylelint-config@4.1.0":
version "4.1.0"
resolved "https://registry.yarnpkg.com/@gitlab/stylelint-config/-/stylelint-config-4.1.0.tgz#bd431406c8f8725afba353652f08e42c3301a982"
integrity sha512-WiTLRKoXutmAKR0z4Z/kuXiTAtblyA2XWX6vb2/NHhLd7oboKrxR8CiznSfw7SNxmYtqW/ripaQOdanR1fYMwA==
dependencies:
postcss-scss "4.0.3"
postcss-scss "4.0.4"
stylelint-declaration-strict-value "1.8.0"
stylelint-scss "4.1.0"
stylelint-scss "4.2.0"
"@gitlab/svgs@2.21.0":
version "2.21.0"
@ -3170,7 +3175,7 @@ braces@^2.3.1:
split-string "^3.0.2"
to-regex "^3.0.1"
braces@^3.0.1, braces@~3.0.2:
braces@^3.0.2, braces@~3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
@ -4114,6 +4119,11 @@ css-color-names@0.0.4:
resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0"
integrity sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=
css-functions-list@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/css-functions-list/-/css-functions-list-3.1.0.tgz#cf5b09f835ad91a00e5959bcfc627cd498e1321b"
integrity sha512-/9lCvYZaUbBGvYUgYGFJ4dcYiyqdhSjG7IPVluoV8A1ILjkF7ilmhp1OGUz8n+nmBcu0RNrQAzgD8B6FJbrt2w==
css-loader@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-2.1.1.tgz#d8254f72e412bb2238bb44dd674ffbef497333ea"
@ -4788,7 +4798,7 @@ debug@^3.1.0, debug@^3.2.6, debug@^3.2.7:
dependencies:
ms "^2.1.1"
debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4:
debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4:
version "4.3.4"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
@ -6738,10 +6748,10 @@ html-escaper@^2.0.0:
resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.0.tgz#71e87f931de3fe09e56661ab9a29aadec707b491"
integrity sha512-a4u9BeERWGu/S8JiWEAQcdrg9v4QArtP9keViQjGMdff20fBdd8waotXaNmODqBe6uZ3Nafi7K/ho4gCQHV3Ig==
html-tags@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.1.0.tgz#7b5e6f7e665e9fb41f30007ed9e0d41e97fb2140"
integrity sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg==
html-tags@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.2.0.tgz#dbb3518d20b726524e4dd43de397eb0a95726961"
integrity sha512-vy7ClnArOZwCnqZgvv+ddgHgJiAFXe3Ge9ML5/mBctVJoUoYPCdxVucOywjDARn6CVoh3dRSFdPHy2sX80L0Wg==
html-void-elements@^2.0.0:
version "2.0.1"
@ -8117,10 +8127,10 @@ kleur@^4.0.3:
resolved "https://registry.yarnpkg.com/kleur/-/kleur-4.1.4.tgz#8c202987d7e577766d039a8cd461934c01cda04d"
integrity sha512-8QADVssbrFjivHWQU7KkMgptGTl6WAcSdlbBPY4uNF+mWr6DGcKrvY2w4FQJoXch7+fKMjj0dRrL75vk3k23OA==
known-css-properties@^0.24.0:
version "0.24.0"
resolved "https://registry.yarnpkg.com/known-css-properties/-/known-css-properties-0.24.0.tgz#19aefd85003ae5698a5560d2b55135bf5432155c"
integrity sha512-RTSoaUAfLvpR357vWzAz/50Q/BmHfmE6ETSWfutT0AJiw10e6CmcdYRQJlLRd95B53D0Y2aD1jSxD3V3ySF+PA==
known-css-properties@^0.25.0:
version "0.25.0"
resolved "https://registry.yarnpkg.com/known-css-properties/-/known-css-properties-0.25.0.tgz#6ebc4d4b412f602e5cfbeb4086bd544e34c0a776"
integrity sha512-b0/9J1O9Jcyik1GC6KC42hJ41jKwdO/Mq8Mdo5sYN+IuRTXs2YFHZC3kZSx6ueusqa95x3wLYe/ytKjbAfGixA==
latest-version@^5.0.0:
version "5.1.0"
@ -9090,13 +9100,13 @@ micromatch@^3.1.10, micromatch@^3.1.4:
snapdragon "^0.8.1"
to-regex "^3.0.2"
micromatch@^4.0.2, micromatch@^4.0.4:
version "4.0.4"
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9"
integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==
micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5:
version "4.0.5"
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6"
integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==
dependencies:
braces "^3.0.1"
picomatch "^2.2.3"
braces "^3.0.2"
picomatch "^2.3.1"
miller-rabin@^4.0.0:
version "4.0.1"
@ -9366,10 +9376,10 @@ multicast-dns@^7.2.4:
dns-packet "^5.2.2"
thunky "^1.0.2"
nanoid@^3.1.30:
version "3.2.0"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.2.0.tgz#62667522da6673971cca916a6d3eff3f415ff80c"
integrity sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==
nanoid@^3.3.4:
version "3.3.4"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab"
integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==
nanomatch@^1.2.9:
version "1.2.13"
@ -9549,11 +9559,6 @@ normalize-path@^3.0.0, normalize-path@~3.0.0:
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
normalize-selector@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/normalize-selector/-/normalize-selector-0.2.0.tgz#d0b145eb691189c63a78d201dc4fdb1293ef0c03"
integrity sha1-0LFF62kRicY6eNIB3E/bEpPvDAM=
normalize-url@^4.1.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129"
@ -10056,10 +10061,10 @@ picocolors@^1.0.0:
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3:
version "2.3.0"
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972"
integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==
picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
pify@^2.0.0:
version "2.3.0"
@ -10176,15 +10181,15 @@ postcss-safe-parser@^6.0.0:
resolved "https://registry.yarnpkg.com/postcss-safe-parser/-/postcss-safe-parser-6.0.0.tgz#bb4c29894171a94bc5c996b9a30317ef402adaa1"
integrity sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==
postcss-scss@4.0.3:
version "4.0.3"
resolved "https://registry.yarnpkg.com/postcss-scss/-/postcss-scss-4.0.3.tgz#36c23c19a804274e722e83a54d20b838ab4767ac"
integrity sha512-j4KxzWovfdHsyxwl1BxkUal/O4uirvHgdzMKS1aWJBAV0qh2qj5qAZqpeBfVUYGWv+4iK9Az7SPyZ4fyNju1uA==
postcss-scss@4.0.4:
version "4.0.4"
resolved "https://registry.yarnpkg.com/postcss-scss/-/postcss-scss-4.0.4.tgz#aa8f60e19ee18259bc193db9e4b96edfce3f3b1f"
integrity sha512-aBBbVyzA8b3hUL0MGrpydxxXKXFZc5Eqva0Q3V9qsBOLEMsjb6w49WfpsoWzpEgcqJGW4t7Rio8WXVU9Gd8vWg==
postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.6, postcss-selector-parser@^6.0.9:
version "6.0.9"
resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.9.tgz#ee71c3b9ff63d9cd130838876c13a2ec1a992b2f"
integrity sha512-UO3SgnZOVTwu4kyLR22UQ1xZh086RyNZppb7lLAKBFK8a32ttG5i87Y/P3+2bRSjZNyJ1B7hfFNo273tKe9YxQ==
postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.10, postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.6, postcss-selector-parser@^6.0.9:
version "6.0.10"
resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz#79b61e2c0d1bfc2602d549e11d0876256f8df88d"
integrity sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==
dependencies:
cssesc "^3.0.0"
util-deprecate "^1.0.2"
@ -10199,14 +10204,14 @@ postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0:
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514"
integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
postcss@8.4.5, postcss@^8.2.1, postcss@^8.4.5:
version "8.4.5"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.5.tgz#bae665764dfd4c6fcc24dc0fdf7e7aa00cc77f95"
integrity sha512-jBDboWM8qpaqwkMwItqTQTiFikhs/67OYVvblFFTM7MrZjt6yMKd6r2kgXizEbTTljacm4NldIlZnhbjr84QYg==
postcss@8.4.14, postcss@^8.2.1, postcss@^8.4.14:
version "8.4.14"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.14.tgz#ee9274d5622b4858c1007a74d76e42e56fd21caf"
integrity sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==
dependencies:
nanoid "^3.1.30"
nanoid "^3.3.4"
picocolors "^1.0.0"
source-map-js "^1.0.1"
source-map-js "^1.0.2"
postcss@^7.0.14, postcss@^7.0.5, postcss@^7.0.6:
version "7.0.39"
@ -11406,10 +11411,10 @@ sigmund@^1.0.1:
resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590"
integrity sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=
signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3:
version "3.0.6"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.6.tgz#24e630c4b0f03fea446a2bd299e62b4a6ca8d0af"
integrity sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==
signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7:
version "3.0.7"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9"
integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
sirv@^1.0.7:
version "1.0.11"
@ -11502,7 +11507,7 @@ source-list-map@^2.0.0:
resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.0.tgz#aaa47403f7b245a92fbc97ea08f250d6087ed085"
integrity sha512-I2UmuJSRr/T8jisiROLU3A3ltr+swpniSmNPI4Ml3ZCX6tVnDsuZzK7F2hl5jTqbZBWCEKlj5HRQiPExXLgE8A==
"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.1:
"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
@ -11600,11 +11605,6 @@ spdy@^4.0.2:
select-hose "^2.0.0"
spdy-transport "^3.0.0"
specificity@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/specificity/-/specificity-0.4.1.tgz#aab5e645012db08ba182e151165738d00887b019"
integrity sha512-1klA3Gi5PD1Wv9Q0wUoOQN1IWAuPu0D1U03ThXTr0cJ20+/iq2tHSDnK7Kk/0LXJ1ztUB2/1Os0wKmfyNgUQfg==
split-string@^3.0.1, split-string@^3.0.2:
version "3.1.0"
resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2"
@ -11846,10 +11846,10 @@ stylelint-declaration-strict-value@1.8.0:
css-values "^0.1.0"
shortcss "^0.1.3"
stylelint-scss@4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/stylelint-scss/-/stylelint-scss-4.1.0.tgz#39b808696f8152081163d970449257ff80b5c041"
integrity sha512-BNYTo7MMamhFOlcaAWp2dMpjg6hPyM/FFqfDIYzmYVLMmQJqc8lWRIiTqP4UX5bresj9Vo0dKC6odSh43VP2NA==
stylelint-scss@4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/stylelint-scss/-/stylelint-scss-4.2.0.tgz#e25fd390ee38a7e89fcfaec2a8f9dce2ec6ddee8"
integrity sha512-HHHMVKJJ5RM9pPIbgJ/XA67h9H0407G68Rm69H4fzFbFkyDMcTV1Byep3qdze5+fJ3c0U7mJrbj6S0Fg072uZA==
dependencies:
lodash "^4.17.21"
postcss-media-query-parser "^0.2.3"
@ -11857,15 +11857,17 @@ stylelint-scss@4.1.0:
postcss-selector-parser "^6.0.6"
postcss-value-parser "^4.1.0"
stylelint@^14.3.0:
version "14.3.0"
resolved "https://registry.yarnpkg.com/stylelint/-/stylelint-14.3.0.tgz#26b62730da7b3dc320021fc469d80048d7b77ebe"
integrity sha512-PZXSwtJe4f4qBPWBwAbHL0M0Qjrv8iHN+cLpUNsffaVMS3YzpDDRI73+2lsqLAYfQEzxRwpll6BDKImREbpHWA==
stylelint@^14.9.1:
version "14.9.1"
resolved "https://registry.yarnpkg.com/stylelint/-/stylelint-14.9.1.tgz#6494ed38f148b1e75b402d678a3b6a8aae86dfda"
integrity sha512-RdAkJdPiLqHawCSnu21nE27MjNXaVd4WcOHA4vK5GtIGjScfhNnaOuWR2wWdfKFAvcWQPOYe311iveiVKSmwsA==
dependencies:
"@csstools/selector-specificity" "^2.0.1"
balanced-match "^2.0.0"
colord "^2.9.2"
cosmiconfig "^7.0.1"
debug "^4.3.3"
css-functions-list "^3.1.0"
debug "^4.3.4"
execall "^2.0.0"
fast-glob "^3.2.11"
fastest-levenshtein "^1.0.12"
@ -11874,26 +11876,24 @@ stylelint@^14.3.0:
global-modules "^2.0.0"
globby "^11.1.0"
globjoin "^0.1.4"
html-tags "^3.1.0"
html-tags "^3.2.0"
ignore "^5.2.0"
import-lazy "^4.0.0"
imurmurhash "^0.1.4"
is-plain-object "^5.0.0"
known-css-properties "^0.24.0"
known-css-properties "^0.25.0"
mathml-tag-names "^2.1.3"
meow "^9.0.0"
micromatch "^4.0.4"
micromatch "^4.0.5"
normalize-path "^3.0.0"
normalize-selector "^0.2.0"
picocolors "^1.0.0"
postcss "^8.4.5"
postcss "^8.4.14"
postcss-media-query-parser "^0.2.3"
postcss-resolve-nested-selector "^0.1.1"
postcss-safe-parser "^6.0.0"
postcss-selector-parser "^6.0.9"
postcss-selector-parser "^6.0.10"
postcss-value-parser "^4.2.0"
resolve-from "^5.0.0"
specificity "^0.4.1"
string-width "^4.2.3"
strip-ansi "^6.0.1"
style-search "^0.1.0"
@ -11901,7 +11901,7 @@ stylelint@^14.3.0:
svg-tags "^1.0.0"
table "^6.8.0"
v8-compile-cache "^2.3.0"
write-file-atomic "^4.0.0"
write-file-atomic "^4.0.1"
stylis@^4.0.10:
version "4.0.10"
@ -12382,11 +12382,6 @@ typedarray-to-buffer@^3.1.5:
dependencies:
is-typedarray "^1.0.0"
typedarray-to-buffer@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-4.0.0.tgz#cdd2933c61dd3f5f02eda5d012d441f95bfeb50a"
integrity sha512-6dOYeZfS3O9RtRD1caom0sMxgK59b27+IwoNy8RDPsmslSGOyU+mpTamlaIW7aNKi90ZQZ9DFaZL3YRoiSCULQ==
typedarray@^0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
@ -13311,15 +13306,13 @@ write-file-atomic@^3.0.0:
signal-exit "^3.0.2"
typedarray-to-buffer "^3.1.5"
write-file-atomic@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.0.tgz#0eff5dc687d3e22535ca3fca8558124645a4b053"
integrity sha512-JhcWoKffJNF7ivO9yflBhc7tn3wKnokMUfWpBriM9yCXj4ePQnRPcWglBkkg1AHC8nsW/EfxwwhqsLtOy59djA==
write-file-atomic@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.1.tgz#9faa33a964c1c85ff6f849b80b42a88c2c537c8f"
integrity sha512-nSKUxgAbyioruk6hU87QzVbY279oYT6uiwgDoujth2ju4mJ+TZau7SQBhtbTmUyuNYTuXnSyRn66FV0+eCgcrQ==
dependencies:
imurmurhash "^0.1.4"
is-typedarray "^1.0.0"
signal-exit "^3.0.2"
typedarray-to-buffer "^4.0.0"
signal-exit "^3.0.7"
"ws@^5.2.0 || ^6.0.0 || ^7.0.0", ws@^7.2.3, ws@^7.3.1:
version "7.5.5"