Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
91e8c3a6ef
commit
57a3a42c88
|
@ -172,6 +172,8 @@ review-qa-smoke:
|
|||
- .review-qa-base
|
||||
- .review:rules:review-qa-smoke
|
||||
retry: 1 # This is confusing but this means "2 runs at max".
|
||||
variables:
|
||||
QA_RUN_TYPE: review-qa-smoke
|
||||
script:
|
||||
- bin/test Test::Instance::Smoke "${CI_ENVIRONMENT_URL}"
|
||||
|
||||
|
@ -180,6 +182,8 @@ review-qa-all:
|
|||
- .review-qa-base
|
||||
- .review:rules:review-qa-all
|
||||
parallel: 5
|
||||
variables:
|
||||
QA_RUN_TYPE: review-qa-all
|
||||
script:
|
||||
- export KNAPSACK_REPORT_PATH=knapsack/master_report.json
|
||||
- export KNAPSACK_TEST_FILE_PATTERN=qa/specs/features/**/*_spec.rb
|
||||
|
|
|
@ -8,7 +8,9 @@ import CiLint from './components/ci_lint.vue';
|
|||
Vue.use(VueApollo);
|
||||
|
||||
const apolloProvider = new VueApollo({
|
||||
defaultClient: createDefaultClient(resolvers),
|
||||
defaultClient: createDefaultClient(resolvers, {
|
||||
assumeImmutableResults: true,
|
||||
}),
|
||||
});
|
||||
|
||||
export default (containerId = '#js-ci-lint') => {
|
||||
|
|
|
@ -90,17 +90,18 @@ export default {
|
|||
showMore() {
|
||||
this.$emit('showMore');
|
||||
},
|
||||
generateRowNumber(id) {
|
||||
generateRowNumber(path, id, index) {
|
||||
const key = `${path}-${id}-${index}`;
|
||||
if (!this.glFeatures.lazyLoadCommits) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!this.rowNumbers[id] && this.rowNumbers[id] !== 0) {
|
||||
if (!this.rowNumbers[key] && this.rowNumbers[key] !== 0) {
|
||||
this.$options.totalRowsLoaded += 1;
|
||||
this.rowNumbers[id] = this.$options.totalRowsLoaded;
|
||||
this.rowNumbers[key] = this.$options.totalRowsLoaded;
|
||||
}
|
||||
|
||||
return this.rowNumbers[id];
|
||||
return this.rowNumbers[key];
|
||||
},
|
||||
getCommit(fileName, type) {
|
||||
if (!this.glFeatures.lazyLoadCommits) {
|
||||
|
@ -150,7 +151,7 @@ export default {
|
|||
:lfs-oid="entry.lfsOid"
|
||||
:loading-path="loadingPath"
|
||||
:total-entries="totalEntries"
|
||||
:row-number="generateRowNumber(entry.id)"
|
||||
:row-number="generateRowNumber(entry.flatPath, entry.id, index)"
|
||||
:commit-info="getCommit(entry.name, entry.type)"
|
||||
v-on="$listeners"
|
||||
/>
|
||||
|
|
|
@ -35,13 +35,17 @@ export default {
|
|||
}
|
||||
|
||||
if (!this.rulesLeft.length) {
|
||||
return n__('Requires approval.', 'Requires %d more approvals.', this.approvalsLeft);
|
||||
return n__(
|
||||
'Requires %d approval from eligible users.',
|
||||
'Requires %d approvals from eligible users.',
|
||||
this.approvalsLeft,
|
||||
);
|
||||
}
|
||||
|
||||
return sprintf(
|
||||
n__(
|
||||
'Requires approval from %{names}.',
|
||||
'Requires %{count} more approvals from %{names}.',
|
||||
'Requires %{count} approval from %{names}.',
|
||||
'Requires %{count} approvals from %{names}.',
|
||||
this.approvalsLeft,
|
||||
),
|
||||
{
|
||||
|
|
|
@ -71,7 +71,7 @@ export default {
|
|||
};
|
||||
</script>
|
||||
<template>
|
||||
<div class="d-flex mr-source-target gl-mb-3">
|
||||
<div class="gl-display-flex mr-source-target">
|
||||
<mr-widget-icon name="git-merge" />
|
||||
<div class="git-merge-container d-flex">
|
||||
<div class="normal">
|
||||
|
|
|
@ -25,19 +25,15 @@ class Groups::GroupMembersController < Groups::ApplicationController
|
|||
def index
|
||||
@sort = params[:sort].presence || sort_value_name
|
||||
|
||||
@members = GroupMembersFinder
|
||||
.new(@group, current_user, params: filter_params)
|
||||
.execute(include_relations: requested_relations)
|
||||
|
||||
if can?(current_user, :admin_group_member, @group)
|
||||
@skip_groups = @group.related_group_ids
|
||||
|
||||
@invited_members = @members.invite
|
||||
@invited_members = invited_members
|
||||
@invited_members = @invited_members.search_invite_email(params[:search_invited]) if params[:search_invited].present?
|
||||
@invited_members = present_invited_members(@invited_members)
|
||||
end
|
||||
|
||||
@members = present_group_members(@members.non_invite)
|
||||
@members = present_group_members(non_invited_members)
|
||||
|
||||
@requesters = present_members(
|
||||
AccessRequestsFinder.new(@group).execute(current_user)
|
||||
|
@ -51,6 +47,20 @@ class Groups::GroupMembersController < Groups::ApplicationController
|
|||
|
||||
private
|
||||
|
||||
def group_members
|
||||
@group_members ||= GroupMembersFinder
|
||||
.new(@group, current_user, params: filter_params)
|
||||
.execute(include_relations: requested_relations)
|
||||
end
|
||||
|
||||
def invited_members
|
||||
group_members.invite
|
||||
end
|
||||
|
||||
def non_invited_members
|
||||
group_members.non_invite
|
||||
end
|
||||
|
||||
def present_invited_members(invited_members)
|
||||
present_members(invited_members
|
||||
.page(params[:invited_members_page])
|
||||
|
|
|
@ -19,16 +19,12 @@ class Projects::ProjectMembersController < Projects::ApplicationController
|
|||
@group_links = @project.project_group_links
|
||||
@group_links = @group_links.search(params[:search_groups]) if params[:search_groups].present?
|
||||
|
||||
project_members = MembersFinder
|
||||
.new(@project, current_user, params: filter_params)
|
||||
.execute(include_relations: requested_relations)
|
||||
|
||||
if can?(current_user, :admin_project_member, @project)
|
||||
@invited_members = present_members(project_members.invite)
|
||||
@invited_members = present_members(invited_members)
|
||||
@requesters = present_members(AccessRequestsFinder.new(@project).execute(current_user))
|
||||
end
|
||||
|
||||
@project_members = present_members(project_members.non_invite.page(params[:page]))
|
||||
@project_members = present_members(non_invited_members.page(params[:page]))
|
||||
|
||||
@project_member = @project.project_members.new
|
||||
end
|
||||
|
@ -55,6 +51,20 @@ class Projects::ProjectMembersController < Projects::ApplicationController
|
|||
|
||||
private
|
||||
|
||||
def members
|
||||
@members ||= MembersFinder
|
||||
.new(@project, current_user, params: filter_params)
|
||||
.execute(include_relations: requested_relations)
|
||||
end
|
||||
|
||||
def invited_members
|
||||
members.invite
|
||||
end
|
||||
|
||||
def non_invited_members
|
||||
members.non_invite
|
||||
end
|
||||
|
||||
def filter_params
|
||||
params.permit(:search).merge(sort: @sort)
|
||||
end
|
||||
|
|
|
@ -45,6 +45,11 @@ class RegistrationsController < Devise::RegistrationsController
|
|||
end
|
||||
|
||||
def destroy
|
||||
if current_user.required_terms_not_accepted?
|
||||
redirect_to profile_account_path, status: :see_other, alert: s_('Profiles|You must accept the Terms of Service in order to perform this action.')
|
||||
return
|
||||
end
|
||||
|
||||
if destroy_confirmation_valid?
|
||||
current_user.delete_async(deleted_by: current_user)
|
||||
session.try(:destroy)
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Resolvers
|
||||
class BoardListResolver < BaseResolver.single
|
||||
include Gitlab::Graphql::Authorize::AuthorizeResource
|
||||
include BoardItemFilterable
|
||||
|
||||
type Types::BoardListType, null: true
|
||||
description 'Find an issue board list.'
|
||||
|
||||
authorize :read_issue_board_list
|
||||
|
||||
argument :id, Types::GlobalIDType[List],
|
||||
required: true,
|
||||
description: 'Global ID of the list.'
|
||||
|
||||
argument :issue_filters, Types::Boards::BoardIssueInputType,
|
||||
required: false,
|
||||
description: 'Filters applied when getting issue metadata in the board list.'
|
||||
|
||||
def resolve(id: nil, issue_filters: {})
|
||||
context.scoped_set!(:issue_filters, item_filters(issue_filters))
|
||||
|
||||
Gitlab::Graphql::Lazy.with_value(find_list(id: id)) do |list|
|
||||
list if authorized_resource?(list)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def find_list(id:)
|
||||
GitlabSchema.object_from_id(id, expected_type: ::List)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -47,6 +47,19 @@ module Types
|
|||
.metadata
|
||||
end
|
||||
end
|
||||
|
||||
# board lists have a data dependency on label - so we batch load them here
|
||||
def title
|
||||
if object.association(:label).loaded? && object.label_id.present?
|
||||
object.title
|
||||
else
|
||||
loader = Gitlab::Graphql::Loaders::BatchModelLoader.new(Label, object.label_id)
|
||||
Gitlab::Graphql::Lazy.with_value(loader.find) do |label|
|
||||
object.label = label
|
||||
object.title
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
# rubocop: enable Graphql/AuthorizeTypes
|
||||
end
|
||||
|
|
|
@ -136,6 +136,10 @@ module Types
|
|||
complexity: 5,
|
||||
resolver: ::Resolvers::TimelogResolver
|
||||
|
||||
field :board_list, ::Types::BoardListType,
|
||||
null: true,
|
||||
resolver: Resolvers::BoardListResolver
|
||||
|
||||
def design_management
|
||||
DesignManagementObject.new(nil)
|
||||
end
|
||||
|
|
|
@ -28,8 +28,8 @@ class Environment < ApplicationRecord
|
|||
|
||||
has_one :last_deployment, -> { success.distinct_on_environment }, class_name: 'Deployment', inverse_of: :environment
|
||||
has_one :last_visible_deployment, -> { visible.distinct_on_environment }, inverse_of: :environment, class_name: 'Deployment'
|
||||
has_one :last_visible_deployable, through: :last_visible_deployment, source: 'deployable', source_type: 'CommitStatus', disable_joins: -> { ::Feature.enabled?(:environment_last_visible_pipeline_disable_joins, default_enabled: :yaml) }
|
||||
has_one :last_visible_pipeline, through: :last_visible_deployable, source: 'pipeline', disable_joins: -> { ::Feature.enabled?(:environment_last_visible_pipeline_disable_joins, default_enabled: :yaml) }
|
||||
has_one :last_visible_deployable, through: :last_visible_deployment, source: 'deployable', source_type: 'CommitStatus', disable_joins: true
|
||||
has_one :last_visible_pipeline, through: :last_visible_deployable, source: 'pipeline', disable_joins: true
|
||||
|
||||
has_one :upcoming_deployment, -> { running.distinct_on_environment }, class_name: 'Deployment', inverse_of: :environment
|
||||
has_one :latest_opened_most_severe_alert, -> { order_severity_with_open_prometheus_alert }, class_name: 'AlertManagement::Alert', inverse_of: :environment
|
||||
|
@ -198,14 +198,14 @@ class Environment < ApplicationRecord
|
|||
|
||||
# Overriding association
|
||||
def last_visible_deployable
|
||||
return super if association_cached?(:last_visible_deployable) || ::Feature.disabled?(:environment_last_visible_pipeline_disable_joins, default_enabled: :yaml)
|
||||
return super if association_cached?(:last_visible_deployable)
|
||||
|
||||
last_visible_deployment&.deployable
|
||||
end
|
||||
|
||||
# Overriding association
|
||||
def last_visible_pipeline
|
||||
return super if association_cached?(:last_visible_pipeline) || ::Feature.disabled?(:environment_last_visible_pipeline_disable_joins, default_enabled: :yaml)
|
||||
return super if association_cached?(:last_visible_pipeline)
|
||||
|
||||
last_visible_deployable&.pipeline
|
||||
end
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ListPolicy < BasePolicy # rubocop:disable Gitlab/NamespacedClass
|
||||
delegate { @subject.board.resource_parent }
|
||||
end
|
|
@ -3,10 +3,15 @@
|
|||
module Ci
|
||||
class ArchiveTraceService
|
||||
def execute(job, worker_name:)
|
||||
unless job.trace.archival_attempts_available?
|
||||
Sidekiq.logger.warn(class: worker_name, message: 'The job is out of archival attempts.', job_id: job.id)
|
||||
|
||||
job.trace.attempt_archive_cleanup!
|
||||
return
|
||||
end
|
||||
|
||||
unless job.trace.can_attempt_archival_now?
|
||||
Sidekiq.logger.warn(class: worker_name,
|
||||
message: job.trace.archival_attempts_message,
|
||||
job_id: job.id)
|
||||
Sidekiq.logger.warn(class: worker_name, message: 'The job can not be archived right now.', job_id: job.id)
|
||||
return
|
||||
end
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
%button.btn.gl-button.btn-default.js-settings-toggle{ type: 'button' }
|
||||
= expanded ? _('Collapse') : _('Expand')
|
||||
%p
|
||||
= _('Configure the %{link} integration.').html_safe % { link: link_to(_('Mailgun events'), 'https://documentation.mailgun.com/en/latest/user_manual.html#webhooks', target: '_blank') }
|
||||
= _('Configure the %{link} integration.').html_safe % { link: link_to(_('Mailgun events'), 'https://documentation.mailgun.com/en/latest/user_manual.html#webhooks', target: '_blank', rel: 'noopener noreferrer') }
|
||||
.settings-content
|
||||
= form_for @application_setting, url: general_admin_application_settings_path(anchor: 'js-mailgun-settings'), html: { class: 'fieldset-form', id: 'mailgun-settings' } do |f|
|
||||
= form_errors(@application_setting) if expanded
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
= expanded ? _('Collapse') : _('Expand')
|
||||
%p
|
||||
- link_start = '<a href="%{url}">'.html_safe % { url: help_page_path('development/snowplow/index') }
|
||||
= html_escape(_('Configure %{link} to track events. %{link_start}Learn more.%{link_end}')) % { link: link_to('Snowplow', 'https://snowplowanalytics.com/', target: '_blank').html_safe, link_start: link_start, link_end: '</a>'.html_safe }
|
||||
= html_escape(_('Configure %{link} to track events. %{link_start}Learn more.%{link_end}')) % { link: link_to('Snowplow', 'https://snowplowanalytics.com/', target: '_blank', rel: 'noopener noreferrer').html_safe, link_start: link_start, link_end: '</a>'.html_safe }
|
||||
.settings-content
|
||||
= form_for @application_setting, url: general_admin_application_settings_path(anchor: 'js-snowplow-settings'), html: { class: 'fieldset-form', id: 'snowplow-settings' } do |f|
|
||||
= form_errors(@application_setting) if expanded
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
name: environment_last_visible_pipeline_disable_joins
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68870
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/340283
|
||||
milestone: '14.3'
|
||||
type: development
|
||||
group: group::release
|
||||
default_enabled: true
|
|
@ -0,0 +1,15 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class CleanupDeleteOrphanedDeploymentsBackgroundMigration < Gitlab::Database::Migration[1.0]
|
||||
MIGRATION = 'DeleteOrphanedDeployments'
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
finalize_background_migration(MIGRATION)
|
||||
end
|
||||
|
||||
def down
|
||||
# no-op
|
||||
end
|
||||
end
|
|
@ -0,0 +1 @@
|
|||
5701681a1006584149c88da520f780b186ca32ba1facb8b952252c6d426b6c0d
|
|
@ -34,7 +34,7 @@ GET /projects/:id/dependencies?package_manager=yarn,bundler
|
|||
| Attribute | Type | Required | Description |
|
||||
| ------------- | -------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding). |
|
||||
| `package_manager` | string array | no | Returns dependencies belonging to specified package manager. Valid values: `bundler`, `composer`, `conan`, `maven`, `npm`, `pip` or `yarn`. |
|
||||
| `package_manager` | string array | no | Returns dependencies belonging to specified package manager. Valid values: `bundler`, `composer`, `conan`, `go`, `maven`, `npm`, `nuget`, `pip`, `yarn`, or `sbt`. |
|
||||
|
||||
```shell
|
||||
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/4/dependencies"
|
||||
|
|
|
@ -36,6 +36,19 @@ in [Removed Items](../removed_items.md).
|
|||
|
||||
The `Query` type contains the API's top-level entry points for all executable queries.
|
||||
|
||||
### `Query.boardList`
|
||||
|
||||
Find an issue board list.
|
||||
|
||||
Returns [`BoardList`](#boardlist).
|
||||
|
||||
#### Arguments
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="queryboardlistid"></a>`id` | [`ListID!`](#listid) | Global ID of the list. |
|
||||
| <a id="queryboardlistissuefilters"></a>`issueFilters` | [`BoardIssueInput`](#boardissueinput) | Filters applied when getting issue metadata in the board list. |
|
||||
|
||||
### `Query.ciApplicationSettings`
|
||||
|
||||
CI related settings that apply to the entire instance.
|
||||
|
|
|
@ -275,7 +275,8 @@ You can verify if the MR was deployed to GitLab.com by executing
|
|||
`/chatops run auto_deploy status <merge_sha>`. To verify existence of
|
||||
the index, you can:
|
||||
|
||||
- Use a meta-command in #database-lab, such as: `\di <index_name>`
|
||||
- Use a meta-command in #database-lab, such as: `\d <index_name>`
|
||||
- Ensure that the index is not [`invalid`](https://www.postgresql.org/docs/12/sql-createindex.html#:~:text=The%20psql%20%5Cd%20command%20will%20report%20such%20an%20index%20as%20INVALID)
|
||||
- Ask someone in #database to check if the index exists
|
||||
- With proper access, you can also verify directly on production or in a
|
||||
production clone
|
||||
|
|
|
@ -5,7 +5,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
type: howto
|
||||
---
|
||||
|
||||
# Activating GitLab EE
|
||||
# Activating GitLab EE **(PREMIUM SELF)**
|
||||
|
||||
To enable features of GitLab Enterprise Edition (EE), you need to activate your instance. Ensure you are running an enterprise edition. To verify, sign in to GitLab and browse to `/help`. The GitLab edition and version are listed at the top of the **Help** page.
|
||||
|
||||
|
@ -15,7 +15,7 @@ As of GitLab Enterprise Edition 9.4.0, a newly-installed instance without an
|
|||
uploaded license only has the Free features active. A trial license activates all Ultimate features, but after [the trial expires](#what-happens-when-your-license-expires), some functionality
|
||||
is locked.
|
||||
|
||||
## Activate GitLab EE with an Activation Code **(PREMIUM SELF)**
|
||||
## Activate GitLab EE with an Activation Code
|
||||
|
||||
As of GitLab Enterprise Edition 14.1, you need an activation code to activate your instance. You can obtain an activation code by [purchasing a license](https://about.gitlab.com/pricing/) or by signing up for a [free trial](https://about.gitlab.com/free-trial/). This activation code is a 24-character alphanumeric string you receive in a confirmation email. You can also sign in to the [Customers Portal](https://customers.gitlab.com/customers/sign_in) to copy the activation code to your clipboard.
|
||||
|
||||
|
@ -28,7 +28,7 @@ To begin the activation process with your activation code:
|
|||
1. Read and accept the terms of service.
|
||||
1. Select **Activate**.
|
||||
|
||||
## Activate GitLab EE with a License File **(PREMIUM SELF)**
|
||||
## Activate GitLab EE with a License File
|
||||
|
||||
If you receive a license file from GitLab (for example a new trial), you can upload it by signing into your GitLab instance as an administrator or adding it during installation. The license is a base64-encoded ASCII text file with a `.gitlab-license` extension.
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ module Gitlab
|
|||
|
||||
delegate :old_trace, to: :job
|
||||
delegate :can_attempt_archival_now?, :increment_archival_attempts!,
|
||||
:archival_attempts_message, to: :trace_metadata
|
||||
:archival_attempts_message, :archival_attempts_available?, to: :trace_metadata
|
||||
|
||||
def initialize(job)
|
||||
@job = job
|
||||
|
@ -122,6 +122,10 @@ module Gitlab
|
|||
end
|
||||
end
|
||||
|
||||
def attempt_archive_cleanup!
|
||||
destroy_any_orphan_trace_data!
|
||||
end
|
||||
|
||||
def update_interval
|
||||
if being_watched?
|
||||
UPDATE_FREQUENCY_WHEN_BEING_WATCHED
|
||||
|
@ -191,7 +195,10 @@ module Gitlab
|
|||
def unsafe_archive!
|
||||
raise ArchiveError, 'Job is not finished yet' unless job.complete?
|
||||
|
||||
unsafe_trace_conditionally_cleanup_before_retry!
|
||||
already_archived?.tap do |archived|
|
||||
destroy_any_orphan_trace_data!
|
||||
raise AlreadyArchivedError, 'Could not archive again' if archived
|
||||
end
|
||||
|
||||
if job.trace_chunks.any?
|
||||
Gitlab::Ci::Trace::ChunkedIO.new(job) do |stream|
|
||||
|
@ -214,16 +221,15 @@ module Gitlab
|
|||
def already_archived?
|
||||
# TODO check checksum to ensure archive completed successfully
|
||||
# See https://gitlab.com/gitlab-org/gitlab/-/issues/259619
|
||||
trace_artifact.archived_trace_exists?
|
||||
trace_artifact&.archived_trace_exists?
|
||||
end
|
||||
|
||||
def unsafe_trace_conditionally_cleanup_before_retry!
|
||||
def destroy_any_orphan_trace_data!
|
||||
return unless trace_artifact
|
||||
|
||||
if already_archived?
|
||||
# An archive already exists, so make sure to remove the trace chunks
|
||||
erase_trace_chunks!
|
||||
raise AlreadyArchivedError, 'Could not archive again'
|
||||
else
|
||||
# An archive already exists, but its associated file does not, so remove it
|
||||
trace_artifact.destroy!
|
||||
|
|
|
@ -11,6 +11,12 @@ module Gitlab
|
|||
# balancing is enabled, but no replicas have been configured (= the
|
||||
# default case).
|
||||
class PrimaryHost
|
||||
WAL_ERROR_MESSAGE = <<~MSG.strip
|
||||
Obtaining WAL information when not using any replicas results in
|
||||
redundant queries, and may break installations that don't support
|
||||
streaming replication (e.g. AWS' Aurora database).
|
||||
MSG
|
||||
|
||||
def initialize(load_balancer)
|
||||
@load_balancer = load_balancer
|
||||
end
|
||||
|
@ -51,30 +57,16 @@ module Gitlab
|
|||
end
|
||||
|
||||
def primary_write_location
|
||||
@load_balancer.primary_write_location
|
||||
raise NotImplementedError, WAL_ERROR_MESSAGE
|
||||
end
|
||||
|
||||
def database_replica_location
|
||||
row = query_and_release(<<-SQL.squish)
|
||||
SELECT pg_last_wal_replay_lsn()::text AS location
|
||||
SQL
|
||||
|
||||
row['location'] if row.any?
|
||||
rescue *Host::CONNECTION_ERRORS
|
||||
nil
|
||||
raise NotImplementedError, WAL_ERROR_MESSAGE
|
||||
end
|
||||
|
||||
def caught_up?(_location)
|
||||
true
|
||||
end
|
||||
|
||||
def query_and_release(sql)
|
||||
connection.select_all(sql).first || {}
|
||||
rescue StandardError
|
||||
{}
|
||||
ensure
|
||||
release_connection
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -42,6 +42,9 @@ module Gitlab
|
|||
end
|
||||
|
||||
def wal_location_for(load_balancer)
|
||||
# When only using the primary there's no need for any WAL queries.
|
||||
return if load_balancer.primary_only?
|
||||
|
||||
if ::Gitlab::Database::LoadBalancing::Session.current.use_primary?
|
||||
load_balancer.primary_write_location
|
||||
else
|
||||
|
|
|
@ -104,6 +104,10 @@ module Gitlab
|
|||
end
|
||||
|
||||
def with_primary_write_location
|
||||
# When only using the primary, there's no point in getting write
|
||||
# locations, as the primary is always in sync with itself.
|
||||
return if @load_balancer.primary_only?
|
||||
|
||||
location = @load_balancer.primary_write_location
|
||||
|
||||
return if location.blank?
|
||||
|
|
|
@ -26312,6 +26312,9 @@ msgstr ""
|
|||
msgid "Profiles|You don't have access to delete this user."
|
||||
msgstr ""
|
||||
|
||||
msgid "Profiles|You must accept the Terms of Service in order to perform this action."
|
||||
msgstr ""
|
||||
|
||||
msgid "Profiles|You must transfer ownership or delete groups you are an owner of before you can delete your account"
|
||||
msgstr ""
|
||||
|
||||
|
@ -28973,13 +28976,13 @@ msgstr ""
|
|||
msgid "Requirements can be based on users, stakeholders, system, software, or anything else you find important to capture."
|
||||
msgstr ""
|
||||
|
||||
msgid "Requires approval from %{names}."
|
||||
msgid_plural "Requires %{count} more approvals from %{names}."
|
||||
msgid "Requires %d approval from eligible users."
|
||||
msgid_plural "Requires %d approvals from eligible users."
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
msgid "Requires approval."
|
||||
msgid_plural "Requires %d more approvals."
|
||||
msgid "Requires %{count} approval from %{names}."
|
||||
msgid_plural "Requires %{count} approvals from %{names}."
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
|
@ -40209,6 +40212,9 @@ msgstr ""
|
|||
msgid "element is not a hierarchy"
|
||||
msgstr ""
|
||||
|
||||
msgid "eligible users"
|
||||
msgstr ""
|
||||
|
||||
msgid "email '%{email}' is not a verified email."
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -55,9 +55,9 @@
|
|||
"@babel/preset-env": "^7.10.1",
|
||||
"@gitlab/at.js": "1.5.7",
|
||||
"@gitlab/favicon-overlay": "2.0.0",
|
||||
"@gitlab/svgs": "1.215.0",
|
||||
"@gitlab/svgs": "1.218.0",
|
||||
"@gitlab/tributejs": "1.0.0",
|
||||
"@gitlab/ui": "32.18.0",
|
||||
"@gitlab/ui": "32.19.1",
|
||||
"@gitlab/visual-review-tools": "1.6.1",
|
||||
"@rails/actioncable": "6.1.4-1",
|
||||
"@rails/ujs": "6.1.4-1",
|
||||
|
|
|
@ -20,11 +20,6 @@ module Gitlab
|
|||
def additional_limits
|
||||
additional_minutes_usage[%r{([^/ ]+)$}]
|
||||
end
|
||||
|
||||
# TODO: Refactor/Remove this method once https://gitlab.com/gitlab-org/quality/chemlab/-/merge_requests/28 is merged
|
||||
def additional_minutes_exist?
|
||||
has_element?(:strong, :additional_minutes, text: 'Additional minutes')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -602,6 +602,22 @@ RSpec.describe RegistrationsController do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when user did not accept app terms' do
|
||||
let(:user) { create(:user, accepted_term: nil) }
|
||||
|
||||
before do
|
||||
stub_application_setting(password_authentication_enabled_for_web: false)
|
||||
stub_application_setting(password_authentication_enabled_for_git: false)
|
||||
stub_application_setting(enforce_terms: true)
|
||||
end
|
||||
|
||||
it 'fails with message' do
|
||||
post :destroy, params: { username: user.username }
|
||||
|
||||
expect_failure(s_('Profiles|You must accept the Terms of Service in order to perform this action.'))
|
||||
end
|
||||
end
|
||||
|
||||
it 'sets the username and caller_id in the context' do
|
||||
expect(controller).to receive(:destroy).and_wrap_original do |m, *args|
|
||||
m.call(*args)
|
||||
|
|
|
@ -61,9 +61,7 @@ describe('MRWidget approvals summary', () => {
|
|||
it('render message', () => {
|
||||
const names = toNounSeriesText(testRulesLeft());
|
||||
|
||||
expect(wrapper.text()).toContain(
|
||||
`Requires ${TEST_APPROVALS_LEFT} more approvals from ${names}.`,
|
||||
);
|
||||
expect(wrapper.text()).toContain(`Requires ${TEST_APPROVALS_LEFT} approvals from ${names}.`);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -75,7 +73,9 @@ describe('MRWidget approvals summary', () => {
|
|||
});
|
||||
|
||||
it('renders message', () => {
|
||||
expect(wrapper.text()).toContain(`Requires ${TEST_APPROVALS_LEFT} more approvals.`);
|
||||
expect(wrapper.text()).toContain(
|
||||
`Requires ${TEST_APPROVALS_LEFT} approvals from eligible users`,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Resolvers::BoardListResolver do
|
||||
include GraphqlHelpers
|
||||
include Gitlab::Graphql::Laziness
|
||||
|
||||
let_it_be(:guest) { create(:user) }
|
||||
let_it_be(:unauth_user) { create(:user) }
|
||||
let_it_be(:group) { create(:group, :private) }
|
||||
let_it_be(:group_label) { create(:group_label, group: group, name: 'Development') }
|
||||
let_it_be(:board) { create(:board, resource_parent: group) }
|
||||
let_it_be(:label_list) { create(:list, board: board, label: group_label) }
|
||||
|
||||
describe '#resolve' do
|
||||
subject { resolve_board_list(args: { id: global_id_of(label_list) }, current_user: current_user) }
|
||||
|
||||
context 'with unauthorized user' do
|
||||
let(:current_user) { unauth_user }
|
||||
|
||||
it { is_expected.to be_nil }
|
||||
end
|
||||
|
||||
context 'when authorized' do
|
||||
let(:current_user) { guest }
|
||||
|
||||
before do
|
||||
group.add_guest(guest)
|
||||
end
|
||||
|
||||
it { is_expected.to eq label_list }
|
||||
end
|
||||
end
|
||||
|
||||
def resolve_board_list(args: {}, current_user: user)
|
||||
force(resolve(described_class, obj: nil, args: args, ctx: { current_user: current_user }))
|
||||
end
|
||||
end
|
|
@ -27,6 +27,7 @@ RSpec.describe GitlabSchema.types['Query'] do
|
|||
runner
|
||||
runners
|
||||
timelogs
|
||||
board_list
|
||||
]
|
||||
|
||||
expect(described_class).to have_graphql_fields(*expected_fields).at_least
|
||||
|
@ -136,4 +137,14 @@ RSpec.describe GitlabSchema.types['Query'] do
|
|||
is_expected.to have_graphql_resolver(Resolvers::TimelogResolver)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'boardList field' do
|
||||
subject { described_class.fields['boardList'] }
|
||||
|
||||
it 'finds a board list by its gid' do
|
||||
is_expected.to have_graphql_arguments(:id, :issue_filters)
|
||||
is_expected.to have_graphql_type(Types::BoardListType)
|
||||
is_expected.to have_graphql_resolver(Resolvers::BoardListResolver)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -63,9 +63,8 @@ RSpec.describe Gitlab::Database::LoadBalancing::PrimaryHost do
|
|||
end
|
||||
|
||||
describe '#primary_write_location' do
|
||||
it 'returns the write location of the primary' do
|
||||
expect(host.primary_write_location).to be_an_instance_of(String)
|
||||
expect(host.primary_write_location).not_to be_empty
|
||||
it 'raises NotImplementedError' do
|
||||
expect { host.primary_write_location }.to raise_error(NotImplementedError)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -76,51 +75,8 @@ RSpec.describe Gitlab::Database::LoadBalancing::PrimaryHost do
|
|||
end
|
||||
|
||||
describe '#database_replica_location' do
|
||||
let(:connection) { double(:connection) }
|
||||
|
||||
it 'returns the write ahead location of the replica', :aggregate_failures do
|
||||
expect(host)
|
||||
.to receive(:query_and_release)
|
||||
.and_return({ 'location' => '0/D525E3A8' })
|
||||
|
||||
expect(host.database_replica_location).to be_an_instance_of(String)
|
||||
end
|
||||
|
||||
it 'returns nil when the database query returned no rows' do
|
||||
expect(host).to receive(:query_and_release).and_return({})
|
||||
|
||||
expect(host.database_replica_location).to be_nil
|
||||
end
|
||||
|
||||
it 'returns nil when the database connection fails' do
|
||||
allow(host).to receive(:connection).and_raise(PG::Error)
|
||||
|
||||
expect(host.database_replica_location).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
describe '#query_and_release' do
|
||||
it 'executes a SQL query' do
|
||||
results = host.query_and_release('SELECT 10 AS number')
|
||||
|
||||
expect(results).to be_an_instance_of(Hash)
|
||||
expect(results['number'].to_i).to eq(10)
|
||||
end
|
||||
|
||||
it 'releases the connection after running the query' do
|
||||
expect(host)
|
||||
.to receive(:release_connection)
|
||||
.once
|
||||
|
||||
host.query_and_release('SELECT 10 AS number')
|
||||
end
|
||||
|
||||
it 'returns an empty Hash in the event of an error' do
|
||||
expect(host.connection)
|
||||
.to receive(:select_all)
|
||||
.and_raise(RuntimeError, 'kittens')
|
||||
|
||||
expect(host.query_and_release('SELECT 10 AS number')).to eq({})
|
||||
it 'raises NotImplementedError' do
|
||||
expect { host.database_replica_location }.to raise_error(NotImplementedError)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -21,7 +21,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::SidekiqClientMiddleware do
|
|||
middleware.call(worker_class, job, nil, nil) {}
|
||||
end
|
||||
|
||||
describe '#call' do
|
||||
describe '#call', :database_replica do
|
||||
shared_context 'data consistency worker class' do |data_consistency, feature_flag|
|
||||
let(:expected_consistency) { data_consistency }
|
||||
let(:worker_class) do
|
||||
|
|
|
@ -188,6 +188,10 @@ RSpec.describe Gitlab::Database::LoadBalancing::Sticking, :redis do
|
|||
end
|
||||
|
||||
it 'sticks an entity to the primary', :aggregate_failures do
|
||||
allow(ActiveRecord::Base.connection.load_balancer)
|
||||
.to receive(:primary_only?)
|
||||
.and_return(false)
|
||||
|
||||
ids.each do |id|
|
||||
expect(sticking)
|
||||
.to receive(:set_write_location_for)
|
||||
|
@ -199,6 +203,12 @@ RSpec.describe Gitlab::Database::LoadBalancing::Sticking, :redis do
|
|||
|
||||
subject
|
||||
end
|
||||
|
||||
it 'does not update the write location when no replicas are used' do
|
||||
expect(sticking).not_to receive(:set_write_location_for)
|
||||
|
||||
subject
|
||||
end
|
||||
end
|
||||
|
||||
describe '#stick' do
|
||||
|
@ -221,12 +231,22 @@ RSpec.describe Gitlab::Database::LoadBalancing::Sticking, :redis do
|
|||
.to receive(:primary_write_location)
|
||||
.and_return('foo')
|
||||
|
||||
allow(ActiveRecord::Base.connection.load_balancer)
|
||||
.to receive(:primary_only?)
|
||||
.and_return(false)
|
||||
|
||||
expect(sticking)
|
||||
.to receive(:set_write_location_for)
|
||||
.with(:user, 42, 'foo')
|
||||
|
||||
sticking.mark_primary_write_location(:user, 42)
|
||||
end
|
||||
|
||||
it 'does nothing when no replicas are used' do
|
||||
expect(sticking).not_to receive(:set_write_location_for)
|
||||
|
||||
sticking.mark_primary_write_location(:user, 42)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#unstick' do
|
||||
|
|
|
@ -2791,17 +2791,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
|
|||
extra_update_queries = 4 # transition ... => :canceled, queue pop
|
||||
extra_generic_commit_status_validation_queries = 2 # name_uniqueness_across_types
|
||||
|
||||
# The number of extra load balancing queries depends on whether or not
|
||||
# we use a load balancer for CI. That in turn depends on the contents of
|
||||
# database.yml, so here we support both cases.
|
||||
extra_load_balancer_queries =
|
||||
if Gitlab::Database.has_config?(:ci)
|
||||
6
|
||||
else
|
||||
3
|
||||
end
|
||||
|
||||
expect(control2.count).to eq(control1.count + extra_update_queries + extra_generic_commit_status_validation_queries + extra_load_balancer_queries)
|
||||
expect(control2.count).to eq(control1.count + extra_update_queries + extra_generic_commit_status_validation_queries)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -801,38 +801,6 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching do
|
|||
expect(query_count).to eq(0)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the feature for disable_join is disabled' do
|
||||
let(:pipeline) { create(:ci_pipeline, project: project) }
|
||||
let(:ci_build) { create(:ci_build, project: project, pipeline: pipeline) }
|
||||
|
||||
before do
|
||||
stub_feature_flags(environment_last_visible_pipeline_disable_joins: false)
|
||||
create(:deployment, :failed, project: project, environment: environment, deployable: ci_build)
|
||||
end
|
||||
|
||||
context 'for preload' do
|
||||
it 'executes the original association instead of override' do
|
||||
environment.reload
|
||||
ActiveRecord::Associations::Preloader.new.preload(environment, [last_visible_deployable: []])
|
||||
|
||||
expect_any_instance_of(Deployment).not_to receive(:deployable)
|
||||
|
||||
query_count = ActiveRecord::QueryRecorder.new do
|
||||
expect(subject.id).to eq(ci_build.id)
|
||||
end.count
|
||||
|
||||
expect(query_count).to eq(0)
|
||||
end
|
||||
end
|
||||
|
||||
context 'for direct call' do
|
||||
it 'executes the original association instead of override' do
|
||||
expect_any_instance_of(Deployment).not_to receive(:deployable)
|
||||
expect(subject.id).to eq(ci_build.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#last_visible_pipeline' do
|
||||
|
@ -963,40 +931,6 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching do
|
|||
expect(query_count).to eq(0)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the feature for disable_join is disabled' do
|
||||
let(:pipeline) { create(:ci_pipeline, project: project) }
|
||||
let(:ci_build) { create(:ci_build, project: project, pipeline: pipeline) }
|
||||
|
||||
before do
|
||||
stub_feature_flags(environment_last_visible_pipeline_disable_joins: false)
|
||||
create(:deployment, :failed, project: project, environment: environment, deployable: ci_build)
|
||||
end
|
||||
|
||||
subject { environment.last_visible_pipeline }
|
||||
|
||||
context 'for preload' do
|
||||
it 'executes the original association instead of override' do
|
||||
environment.reload
|
||||
ActiveRecord::Associations::Preloader.new.preload(environment, [last_visible_pipeline: []])
|
||||
|
||||
expect_any_instance_of(Ci::Build).not_to receive(:pipeline)
|
||||
|
||||
query_count = ActiveRecord::QueryRecorder.new do
|
||||
expect(subject.id).to eq(pipeline.id)
|
||||
end.count
|
||||
|
||||
expect(query_count).to eq(0)
|
||||
end
|
||||
end
|
||||
|
||||
context 'for direct call' do
|
||||
it 'executes the original association instead of override' do
|
||||
expect_any_instance_of(Ci::Build).not_to receive(:pipeline)
|
||||
expect(subject.id).to eq(pipeline.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#upcoming_deployment' do
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe 'Querying a Board list' do
|
||||
include GraphqlHelpers
|
||||
|
||||
let_it_be(:current_user) { create(:user) }
|
||||
let_it_be(:project) { create(:project) }
|
||||
let_it_be(:board) { create(:board, resource_parent: project) }
|
||||
let_it_be(:label) { create(:label, project: project, name: 'foo') }
|
||||
let_it_be(:list) { create(:list, board: board, label: label) }
|
||||
let_it_be(:issue1) { create(:issue, project: project, labels: [label]) }
|
||||
let_it_be(:issue2) { create(:issue, project: project, labels: [label], assignees: [current_user]) }
|
||||
|
||||
let(:filters) { {} }
|
||||
let(:query) do
|
||||
graphql_query_for(
|
||||
:board_list,
|
||||
{ id: list.to_global_id.to_s, issueFilters: filters },
|
||||
%w[title issuesCount]
|
||||
)
|
||||
end
|
||||
|
||||
subject { graphql_data['boardList'] }
|
||||
|
||||
before do
|
||||
post_graphql(query, current_user: current_user)
|
||||
end
|
||||
|
||||
context 'when the user has access to the list' do
|
||||
before_all do
|
||||
project.add_guest(current_user)
|
||||
end
|
||||
|
||||
it_behaves_like 'a working graphql query'
|
||||
|
||||
it { is_expected.to include({ 'issuesCount' => 2, 'title' => list.title }) }
|
||||
|
||||
context 'with matching issue filters' do
|
||||
let(:filters) { { assigneeUsername: current_user.username } }
|
||||
|
||||
it 'filters issues metadata' do
|
||||
is_expected.to include({ 'issuesCount' => 1, 'title' => list.title })
|
||||
end
|
||||
end
|
||||
|
||||
context 'with unmatching issue filters' do
|
||||
let(:filters) { { assigneeUsername: 'foo' } }
|
||||
|
||||
it 'filters issues metadata' do
|
||||
is_expected.to include({ 'issuesCount' => 0, 'title' => list.title })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the user does not have access to the list' do
|
||||
it { is_expected.to be_nil }
|
||||
end
|
||||
|
||||
context 'when ID argument is missing' do
|
||||
let(:query) do
|
||||
graphql_query_for('boardList', {}, 'title')
|
||||
end
|
||||
|
||||
it 'raises an exception' do
|
||||
expect(graphql_errors).to include(a_hash_including('message' => "Field 'boardList' is missing required arguments: id"))
|
||||
end
|
||||
end
|
||||
|
||||
context 'when list ID is not found' do
|
||||
let(:query) do
|
||||
graphql_query_for('boardList', { id: "gid://gitlab/List/#{non_existing_record_id}" }, 'title')
|
||||
end
|
||||
|
||||
it { is_expected.to be_nil }
|
||||
end
|
||||
|
||||
it 'does not have an N+1 performance issue' do
|
||||
a, b = create_list(:list, 2, board: board)
|
||||
ctx = { current_user: current_user }
|
||||
project.add_guest(current_user)
|
||||
|
||||
baseline = graphql_query_for(:board_list, { id: global_id_of(a) }, 'title')
|
||||
query = <<~GQL
|
||||
query {
|
||||
a: #{query_graphql_field(:board_list, { id: global_id_of(a) }, 'title')}
|
||||
b: #{query_graphql_field(:board_list, { id: global_id_of(b) }, 'title')}
|
||||
}
|
||||
GQL
|
||||
|
||||
control = ActiveRecord::QueryRecorder.new do
|
||||
run_with_clean_state(baseline, context: ctx)
|
||||
end
|
||||
|
||||
expect { run_with_clean_state(query, context: ctx) }.not_to exceed_query_limit(control)
|
||||
end
|
||||
end
|
|
@ -88,6 +88,32 @@ RSpec.describe Ci::ArchiveTraceService, '#execute' do
|
|||
|
||||
subject
|
||||
end
|
||||
|
||||
context 'job has archive and chunks' do
|
||||
let(:job) { create(:ci_build, :success, :trace_artifact) }
|
||||
|
||||
before do
|
||||
create(:ci_build_trace_chunk, build: job, chunk_index: 0)
|
||||
end
|
||||
|
||||
context 'archive is not completed' do
|
||||
before do
|
||||
job.job_artifacts_trace.file.remove!
|
||||
end
|
||||
|
||||
it 'cleanups any stale archive data' do
|
||||
expect(job.job_artifacts_trace).to be_present
|
||||
|
||||
subject
|
||||
|
||||
expect(job.reload.job_artifacts_trace).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
it 'removes trace chunks' do
|
||||
expect { subject }.to change { job.trace_chunks.count }.to(0)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the archival process is backed off' do
|
||||
|
|
26
yarn.lock
26
yarn.lock
|
@ -904,23 +904,23 @@
|
|||
stylelint-declaration-strict-value "1.7.7"
|
||||
stylelint-scss "3.18.0"
|
||||
|
||||
"@gitlab/svgs@1.215.0":
|
||||
version "1.215.0"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.215.0.tgz#f2760bbb0a38b26346e1b755e63fb63eba005edd"
|
||||
integrity sha512-/bc0+EOYPQlPCMbfyOkMLxDKBn+ewEBlmTRmFwf7mXvfIRszdJPY8XCx/fJIEQwDr8+k4E28ktFnLZGnaFhCnw==
|
||||
"@gitlab/svgs@1.218.0":
|
||||
version "1.218.0"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.218.0.tgz#0715e2ef50b5cb83813e1a5e29d5919a96685734"
|
||||
integrity sha512-eckixyumeWogykEUZfP4pGjoRdhdWQIFwSTM0ks5tQqza+BikcL2xvxzicJs69T1IiCKwYtEpR1c3T/hSx39Mg==
|
||||
|
||||
"@gitlab/tributejs@1.0.0":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/tributejs/-/tributejs-1.0.0.tgz#672befa222aeffc83e7d799b0500a7a4418e59b8"
|
||||
integrity sha512-nmKw1+hB6MHvlmPz63yPwVs1qQkycHwsKgxpEbzmky16Y6mL4EJMk3w1b8QlOAF/AIAzjCERPhe/R4MJiohbZw==
|
||||
|
||||
"@gitlab/ui@32.18.0":
|
||||
version "32.18.0"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-32.18.0.tgz#cd340f050fe0183218f6233328aca2369bd6e449"
|
||||
integrity sha512-bDMmsNB9VMBX2JbezyJWfk02t0aFfAT9Ez4ALTDUJLb5/Q9GKByfE5sLycms6L1aZxzP6r1jypnu5DD0eT92eg==
|
||||
"@gitlab/ui@32.19.1":
|
||||
version "32.19.1"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-32.19.1.tgz#ab54408272cb5ee695dc0a328892e047da3d41ac"
|
||||
integrity sha512-ooc0TwCvREuWJfvn8EbOkEz1Mh4UKEu7x0MKhD+TBjG+JJwLKDClmD1cPPE05BXtWAvW5W9JUBkaeMCVQG2l3g==
|
||||
dependencies:
|
||||
"@babel/standalone" "^7.0.0"
|
||||
bootstrap-vue "2.19.0"
|
||||
bootstrap-vue "2.20.1"
|
||||
copy-to-clipboard "^3.0.8"
|
||||
dompurify "^2.3.3"
|
||||
echarts "^4.9.0"
|
||||
|
@ -2758,10 +2758,10 @@ boolbase@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
|
||||
integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24=
|
||||
|
||||
bootstrap-vue@2.19.0:
|
||||
version "2.19.0"
|
||||
resolved "https://registry.yarnpkg.com/bootstrap-vue/-/bootstrap-vue-2.19.0.tgz#5019df48251e552a5c34da57fc97dabebd53b02f"
|
||||
integrity sha512-IjAXUSrRU5Qu9x3uwUcoj6LtysKbCVeWoJOsODyI/WokStUr95M+tTIajXUjIrB/Nsk0fS+RNvZnm2sWeNFrhg==
|
||||
bootstrap-vue@2.20.1:
|
||||
version "2.20.1"
|
||||
resolved "https://registry.yarnpkg.com/bootstrap-vue/-/bootstrap-vue-2.20.1.tgz#1b6cd4368632c1a6dd4a5ed161242baa131c3cd5"
|
||||
integrity sha512-s+w83q0T2mo/RbFwTM8gExbLJMEOYpdTUqmyFaHv2Ir+TFprMvTWpeAzeNuawJ130W1gePZ3LW3cNp1t/tZbOw==
|
||||
dependencies:
|
||||
"@nuxt/opencollective" "^0.3.2"
|
||||
bootstrap ">=4.5.3 <5.0.0"
|
||||
|
|
Loading…
Reference in New Issue