Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-10-06 00:11:56 +00:00
parent ee2aa09a24
commit 85c68f14bf
44 changed files with 637 additions and 193 deletions

View file

@ -296,7 +296,6 @@ Rails/TimeZone:
- 'spec/lib/gitlab/graphql_logger_spec.rb'
- 'spec/lib/gitlab/graphs/commits_spec.rb'
- 'spec/lib/gitlab/import_export/project/relation_factory_spec.rb'
- 'spec/lib/gitlab/instrumentation_helper_spec.rb'
- 'spec/lib/gitlab/json_logger_spec.rb'
- 'spec/lib/gitlab/lfs_token_spec.rb'
- 'spec/lib/gitlab/log_timestamp_formatter_spec.rb'
@ -384,10 +383,8 @@ RSpec/TimecopFreeze:
- 'spec/lib/gitlab/checks/timed_logger_spec.rb'
- 'spec/lib/gitlab/cycle_analytics/stage_summary_spec.rb'
- 'spec/lib/gitlab/cycle_analytics/usage_data_spec.rb'
- 'spec/lib/gitlab/instrumentation_helper_spec.rb'
- 'spec/lib/gitlab/omniauth_logging/json_formatter_spec.rb'
- 'spec/lib/gitlab/puma_logging/json_formatter_spec.rb'
- 'spec/lib/gitlab/sidekiq_logging/structured_logger_spec.rb'
- 'spec/lib/json_web_token/hmac_token_spec.rb'
- 'spec/tooling/rspec_flaky/flaky_example_spec.rb'
- 'spec/tooling/rspec_flaky/listener_spec.rb'

View file

@ -100,6 +100,7 @@ export default {
variant="default"
icon="file-tree"
class="gl-mr-3 js-toggle-tree-list btn-icon"
data-qa-selector="file_tree_button"
:title="toggleFileBrowserTitle"
:aria-label="toggleFileBrowserTitle"
:selected="showTreeList"

View file

@ -62,7 +62,7 @@ export default {
</script>
<template>
<div class="tree-list-holder d-flex flex-column">
<div class="tree-list-holder d-flex flex-column" data-qa-selector="file_tree_container">
<div class="gl-mb-3 position-relative tree-list-search d-flex">
<div class="flex-fill d-flex">
<gl-icon name="search" class="position-absolute tree-list-icon" />

View file

@ -33,6 +33,7 @@ class Release < ApplicationRecord
includes(:author, :evidences, :milestones, :links, :sorted_links,
project: [:project_feature, :route, { namespace: :route }])
}
scope :with_milestones, -> { joins(:milestone_releases) }
scope :recent, -> { sorted.limit(MAX_NUMBER_TO_DISPLAY) }
scope :without_evidence, -> { left_joins(:evidences).where(::Releases::Evidence.arel_table[:id].eq(nil)) }
scope :released_within_2hrs, -> { where(released_at: Time.zone.now - 1.hour..Time.zone.now + 1.hour) }

View file

@ -3,8 +3,8 @@
module Issues
class CloseService < Issues::BaseService
# Closes the supplied issue if the current user is able to do so.
def execute(issue, commit: nil, notifications: true, system_note: true)
return issue unless can?(current_user, :update_issue, issue) || issue.is_a?(ExternalIssue)
def execute(issue, commit: nil, notifications: true, system_note: true, skip_authorization: false)
return issue unless can_close?(issue, skip_authorization: skip_authorization)
close_issue(issue,
closed_via: commit,
@ -24,7 +24,7 @@ module Issues
return issue
end
if project.issues_enabled? && issue.close(current_user)
if perform_close(issue)
event_service.close_issue(issue, current_user)
create_note(issue, closed_via) if system_note
@ -51,6 +51,15 @@ module Issues
private
# Overridden on EE
def perform_close(issue)
issue.close(current_user)
end
def can_close?(issue, skip_authorization: false)
skip_authorization || can?(current_user, :update_issue, issue) || issue.is_a?(ExternalIssue)
end
def perform_incident_management_actions(issue)
resolve_alert(issue)
end

View file

@ -2,10 +2,10 @@
module Issues
class ReopenService < Issues::BaseService
def execute(issue)
return issue unless can?(current_user, :reopen_issue, issue)
def execute(issue, skip_authorization: false)
return issue unless can_reopen?(issue, skip_authorization: skip_authorization)
if issue.reopen
if perform_reopen(issue)
event_service.reopen_issue(issue, current_user)
create_note(issue, 'reopened')
notification_service.async.reopen_issue(issue, current_user)
@ -22,6 +22,15 @@ module Issues
private
# Overriden on EE
def perform_reopen(issue)
issue.reopen
end
def can_reopen?(issue, skip_authorization: false)
skip_authorization || can?(current_user, :reopen_issue, issue)
end
def perform_incident_management_actions(issue)
end

View file

@ -0,0 +1,23 @@
---
key_path: usage_activity_by_stage_monthly.release.releases_with_milestones
description: Unique users creating releases with milestones associated
performance_indicator_type: [smau]
product_section: ops
product_stage: release
product_group: 'group::release'
product_category: Release Orchestration
value_type: number
status: active
milestone: "14.4"
introduced_by_url: 'https://gitlab.com/gitlab-org/gitlab/-/merge_requests/71287'
time_frame: 28d
data_source: database
instrumentation_class: 'CountUsersAssociatingMilestonesToReleasesMetric'
data_category: Optional
distribution:
- ce
- ee
tier:
- free
- premium
- ultimate

View file

@ -0,0 +1,23 @@
---
key_path: usage_activity_by_stage.release.releases_with_milestones
description: Unique users creating releases with milestones associated
performance_indicator_type: []
product_section: ops
product_stage: release
product_group: 'group::release'
product_category: Release Orchestration
value_type: number
status: active
milestone: "14.4"
introduced_by_url: 'https://gitlab.com/gitlab-org/gitlab/-/merge_requests/71287'
time_frame: 28d
data_source: database
instrumentation_class: 'CountUsersAssociatingMilestonesToReleasesMetric'
data_category: Optional
distribution:
- ce
- ee
tier:
- free
- premium
- ultimate

View file

@ -0,0 +1,15 @@
# frozen_string_literal: true
class AddUniqueNamespacesIndexOnNameParentIdAndType < Gitlab::Database::Migration[1.0]
INDEX_NAME = 'index_namespaces_name_parent_id_type'
disable_ddl_transaction!
def up
add_concurrent_index :namespaces, [:name, :parent_id, :type], unique: true, name: INDEX_NAME
end
def down
remove_concurrent_index_by_name :namespaces, INDEX_NAME
end
end

View file

@ -0,0 +1,15 @@
# frozen_string_literal: true
class DropUniqueNamespacesIndexOnNameAndParentId < Gitlab::Database::Migration[1.0]
INDEX_NAME = 'index_namespaces_on_name_and_parent_id'
disable_ddl_transaction!
def up
remove_concurrent_index_by_name :namespaces, INDEX_NAME
end
def down
add_concurrent_index :namespaces, [:name, :parent_id], unique: true, name: INDEX_NAME
end
end

View file

@ -0,0 +1,14 @@
# frozen_string_literal: true
class AddReleasesAuthorIdIdCreatedAtIndex < Gitlab::Database::Migration[1.0]
disable_ddl_transaction!
INDEX_NAME = 'index_releases_on_author_id_id_created_at'
def up
add_concurrent_index :releases, [:author_id, :id, :created_at], name: INDEX_NAME
end
def down
remove_concurrent_index_by_name :releases, INDEX_NAME
end
end

View file

@ -0,0 +1 @@
c50ccd9986188356776c2d19c5544a6da6e31b5bb1b16ed259455604cb6fd862

View file

@ -0,0 +1 @@
083b18b8e687ae8ff9d93ee77c6d4fc2916a2f1b77acf44132f216236b0ff06c

View file

@ -0,0 +1 @@
432dc1f1e0280a79e4a6af56c2f2cb40c99edbc09e254b82b7f48c7c9217372b

View file

@ -25683,6 +25683,8 @@ CREATE INDEX index_namespaces_id_parent_id_is_not_null ON namespaces USING btree
CREATE INDEX index_namespaces_id_parent_id_is_null ON namespaces USING btree (id) WHERE (parent_id IS NULL);
CREATE UNIQUE INDEX index_namespaces_name_parent_id_type ON namespaces USING btree (name, parent_id, type);
CREATE INDEX index_namespaces_on_created_at ON namespaces USING btree (created_at);
CREATE INDEX index_namespaces_on_custom_project_templates_group_id_and_type ON namespaces USING btree (custom_project_templates_group_id, type) WHERE (custom_project_templates_group_id IS NOT NULL);
@ -25693,8 +25695,6 @@ CREATE INDEX index_namespaces_on_ldap_sync_last_successful_update_at ON namespac
CREATE INDEX index_namespaces_on_ldap_sync_last_update_at ON namespaces USING btree (ldap_sync_last_update_at);
CREATE UNIQUE INDEX index_namespaces_on_name_and_parent_id ON namespaces USING btree (name, parent_id);
CREATE INDEX index_namespaces_on_name_trigram ON namespaces USING gin (name gin_trgm_ops);
CREATE INDEX index_namespaces_on_owner_id ON namespaces USING btree (owner_id);
@ -26263,6 +26263,8 @@ CREATE UNIQUE INDEX index_release_links_on_release_id_and_url ON release_links U
CREATE INDEX index_releases_on_author_id ON releases USING btree (author_id);
CREATE INDEX index_releases_on_author_id_id_created_at ON releases USING btree (author_id, id, created_at);
CREATE INDEX index_releases_on_project_id_and_tag ON releases USING btree (project_id, tag);
CREATE INDEX index_releases_on_released_at ON releases USING btree (released_at);

View file

@ -4,21 +4,21 @@ module Gitlab
module Database
module Migrations
class Instrumentation
RESULT_DIR = Rails.root.join('tmp', 'migration-testing').freeze
STATS_FILENAME = 'migration-stats.json'
attr_reader :observations
def initialize(observer_classes = ::Gitlab::Database::Migrations::Observers.all_observers)
def initialize(result_dir:, observer_classes: ::Gitlab::Database::Migrations::Observers.all_observers)
@observer_classes = observer_classes
@observations = []
@result_dir = result_dir
end
def observe(version:, name:, &block)
observation = Observation.new(version, name)
observation.success = true
observers = observer_classes.map { |c| c.new(observation) }
observers = observer_classes.map { |c| c.new(observation, @result_dir) }
exception = nil

View file

@ -5,11 +5,12 @@ module Gitlab
module Migrations
module Observers
class MigrationObserver
attr_reader :connection, :observation
attr_reader :connection, :observation, :output_dir
def initialize(observation)
def initialize(observation, output_dir)
@connection = ActiveRecord::Base.connection
@observation = observation
@output_dir = output_dir
end
def before

View file

@ -6,7 +6,7 @@ module Gitlab
module Observers
class QueryDetails < MigrationObserver
def before
file_path = File.join(Instrumentation::RESULT_DIR, "#{observation.version}_#{observation.name}-query-details.json")
file_path = File.join(output_dir, "#{observation.version}_#{observation.name}-query-details.json")
@file = File.open(file_path, 'wb')
@writer = Oj::StreamWriter.new(@file, {})
@writer.push_array

View file

@ -7,7 +7,7 @@ module Gitlab
class QueryLog < MigrationObserver
def before
@logger_was = ActiveRecord::Base.logger
file_path = File.join(Instrumentation::RESULT_DIR, "#{observation.version}_#{observation.name}.log")
file_path = File.join(output_dir, "#{observation.version}_#{observation.name}.log")
@logger = Logger.new(file_path)
ActiveRecord::Base.logger = @logger
end

View file

@ -0,0 +1,98 @@
# frozen_string_literal: true
module Gitlab
module Database
module Migrations
class Runner
BASE_RESULT_DIR = Rails.root.join('tmp', 'migration-testing').freeze
class << self
def up(legacy_pipeline: false)
result_dir = if legacy_pipeline
BASE_RESULT_DIR
else
BASE_RESULT_DIR.join('up')
end
Runner.new(direction: :up, migrations: migrations_for_up, result_dir: result_dir)
end
def down
Runner.new(direction: :down, migrations: migrations_for_down, result_dir: BASE_RESULT_DIR.join('down'))
end
def migration_context
@migration_context ||= ApplicationRecord.connection.migration_context
end
private
def migrations_for_up
existing_versions = migration_context.get_all_versions.to_set
migration_context.migrations.reject do |migration|
existing_versions.include?(migration.version)
end
end
def migration_file_names_this_branch
`git diff --name-only origin/HEAD...HEAD db/post_migrate db/migrate`.split("\n")
end
def migrations_for_down
versions_this_branch = migration_file_names_this_branch.map do |m_name|
m_name.match(%r{^db/(post_)?migrate/(\d+)}) { |m| m.captures[1]&.to_i }
end.to_set
existing_versions = migration_context.get_all_versions.to_set
migration_context.migrations.select do |migration|
existing_versions.include?(migration.version) && versions_this_branch.include?(migration.version)
end
end
end
attr_reader :direction, :result_dir, :migrations
delegate :migration_context, to: :class
def initialize(direction:, migrations:, result_dir:)
raise "Direction must be up or down" unless %i[up down].include?(direction)
@direction = direction
@migrations = migrations
@result_dir = result_dir
end
def run
FileUtils.mkdir_p(result_dir)
verbose_was = ActiveRecord::Migration.verbose
ActiveRecord::Migration.verbose = true
sorted_migrations = migrations.sort_by(&:version)
sorted_migrations.reverse! if direction == :down
instrumentation = Instrumentation.new(result_dir: result_dir)
sorted_migrations.each do |migration|
instrumentation.observe(version: migration.version, name: migration.name) do
ActiveRecord::Migrator.new(direction, migration_context.migrations, migration_context.schema_migration, migration.version).run
end
end
ensure
if instrumentation
File.open(File.join(result_dir, Gitlab::Database::Migrations::Instrumentation::STATS_FILENAME), 'wb+') do |io|
io << instrumentation.observations.to_json
end
end
# We clear the cache here to mirror the cache clearing that happens at the end of `db:migrate` tasks
# This clearing makes subsequent rake tasks in the same execution pick up database schema changes caused by
# the migrations that were just executed
ApplicationRecord.clear_cache!
ActiveRecord::Migration.verbose = verbose_was
end
end
end
end
end

View file

@ -131,18 +131,43 @@ module Gitlab
enqueued_at_time = convert_to_time(enqueued_at)
return unless enqueued_at_time
# Its possible that if theres clock-skew between two nodes
# this value may be less than zero. In that event, we record the value
round_elapsed_time(enqueued_at_time)
end
# Returns the time it took for a scheduled job to be enqueued in seconds, as a float,
# if the `scheduled_at` and `enqueued_at` fields are available.
#
# * If the job doesn't contain sufficient information, returns nil
# * If the job has a start time in the future, returns 0
# * If the job contains an invalid start time value, returns nil
# @param [Hash] job a Sidekiq job, represented as a hash
def self.enqueue_latency_for_scheduled_job(job)
scheduled_at = job['scheduled_at']
enqueued_at = job['enqueued_at']
return unless scheduled_at && enqueued_at
scheduled_at_time = convert_to_time(scheduled_at)
enqueued_at_time = convert_to_time(enqueued_at)
return unless scheduled_at_time && enqueued_at_time
round_elapsed_time(scheduled_at_time, enqueued_at_time)
end
def self.round_elapsed_time(start, end_time = Time.now)
# It's possible that if there is clock-skew between two nodes this
# value may be less than zero. In that event, we record the value
# as zero.
[elapsed_by_absolute_time(enqueued_at_time), 0].max.round(DURATION_PRECISION)
[elapsed_by_absolute_time(start, end_time), 0].max.round(DURATION_PRECISION)
end
# Calculates the time in seconds, as a float, from
# the provided start time until now
#
# @param [Time] start
def self.elapsed_by_absolute_time(start)
(Time.now - start).to_f.round(DURATION_PRECISION)
def self.elapsed_by_absolute_time(start, end_time)
(end_time - start).to_f.round(DURATION_PRECISION)
end
private_class_method :elapsed_by_absolute_time

View file

@ -55,6 +55,9 @@ module Gitlab
scheduling_latency_s = ::Gitlab::InstrumentationHelper.queue_duration_for_job(payload)
payload['scheduling_latency_s'] = scheduling_latency_s if scheduling_latency_s
enqueue_latency_s = ::Gitlab::InstrumentationHelper.enqueue_latency_for_scheduled_job(payload)
payload['enqueue_latency_s'] = enqueue_latency_s if enqueue_latency_s
payload
end

View file

@ -16,7 +16,12 @@ module Gitlab
worker_class = worker_class.to_s.safe_constantize
labels = create_labels(worker_class, queue, job)
labels[:scheduling] = job.key?('at') ? 'delayed' : 'immediate'
if job.key?('at')
labels[:scheduling] = 'delayed'
job[:scheduled_at] = job['at']
else
labels[:scheduling] = 'immediate'
end
@metrics.fetch(ENQUEUED).increment(labels, 1)

View file

@ -0,0 +1,18 @@
# frozen_string_literal: true
module Gitlab
module Usage
module Metrics
module Instrumentations
class CountUsersAssociatingMilestonesToReleasesMetric < DatabaseMetric
operation :distinct_count, column: :author_id
relation { Release.with_milestones }
start { Release.minimum(:author_id) }
finish { Release.maximum(:author_id) }
end
end
end
end
end

View file

@ -647,7 +647,7 @@ module Gitlab
# Omitted because of encrypted properties: `projects_jira_cloud_active`, `projects_jira_server_active`
# rubocop: disable CodeReuse/ActiveRecord
def usage_activity_by_stage_plan(time_period)
time_frame = time_period.present? ? '28d' : 'none'
time_frame = metric_time_period(time_period)
{
issues: add_metric('CountUsersCreatingIssuesMetric', time_frame: time_frame),
notes: distinct_count(::Note.where(time_period), :author_id),
@ -665,11 +665,13 @@ module Gitlab
# Omitted because no user, creator or author associated: `environments`, `feature_flags`, `in_review_folder`, `pages_domains`
# rubocop: disable CodeReuse/ActiveRecord
def usage_activity_by_stage_release(time_period)
time_frame = metric_time_period(time_period)
{
deployments: distinct_count(::Deployment.where(time_period), :user_id),
failed_deployments: distinct_count(::Deployment.failed.where(time_period), :user_id),
releases: distinct_count(::Release.where(time_period), :author_id),
successful_deployments: distinct_count(::Deployment.success.where(time_period), :user_id)
successful_deployments: distinct_count(::Deployment.success.where(time_period), :user_id),
releases_with_milestones: add_metric('CountUsersAssociatingMilestonesToReleasesMetric', time_frame: time_frame)
}
end
# rubocop: enable CodeReuse/ActiveRecord
@ -755,6 +757,10 @@ module Gitlab
private
def metric_time_period(time_period)
time_period.present? ? '28d' : 'none'
end
def gitaly_apdex
with_prometheus_client(verify: false, fallback: FALLBACK) do |client|
result = client.query('avg_over_time(gitlab_usage_ping:gitaly_apdex:ratio_avg_over_time_5m[1w])').first

View file

@ -211,37 +211,22 @@ namespace :gitlab do
exit 0
end
namespace :migration_testing do
desc 'Run migrations with instrumentation'
task up: :environment do
Gitlab::Database::Migrations::Runner.up.run
end
desc 'Run down migrations in current branch with instrumentation'
task down: :environment do
Gitlab::Database::Migrations::Runner.down.run
end
end
# TODO: Remove this rake task after migrating the database testing runner to :up / :down versions of it
desc 'Run migrations with instrumentation'
task migration_testing: :environment do
result_dir = Gitlab::Database::Migrations::Instrumentation::RESULT_DIR
FileUtils.mkdir_p(result_dir)
verbose_was = ActiveRecord::Migration.verbose
ActiveRecord::Migration.verbose = true
ctx = ActiveRecord::Base.connection.migration_context
existing_versions = ctx.get_all_versions.to_set
pending_migrations = ctx.migrations.reject do |migration|
existing_versions.include?(migration.version)
end
instrumentation = Gitlab::Database::Migrations::Instrumentation.new
pending_migrations.each do |migration|
instrumentation.observe(version: migration.version, name: migration.name) do
ActiveRecord::Migrator.new(:up, ctx.migrations, ctx.schema_migration, migration.version).run
end
end
ensure
if instrumentation
File.open(File.join(result_dir, Gitlab::Database::Migrations::Instrumentation::STATS_FILENAME), 'wb+') do |io|
io << instrumentation.observations.to_json
end
end
ActiveRecord::Base.clear_cache!
ActiveRecord::Migration.verbose = verbose_was
Gitlab::Database::Migrations::Runner.up(legacy_pipeline: true).run
end
desc 'Run all pending batched migrations'

View file

@ -63,19 +63,19 @@
"@rails/ujs": "6.1.4-1",
"@sentry/browser": "5.30.0",
"@sourcegraph/code-host-integration": "0.0.60",
"@tiptap/core": "^2.0.0-beta.116",
"@tiptap/core": "^2.0.0-beta.118",
"@tiptap/extension-blockquote": "^2.0.0-beta.15",
"@tiptap/extension-bold": "^2.0.0-beta.15",
"@tiptap/extension-bullet-list": "^2.0.0-beta.15",
"@tiptap/extension-code": "^2.0.0-beta.16",
"@tiptap/extension-code-block-lowlight": "2.0.0-beta.40",
"@tiptap/extension-code-block-lowlight": "2.0.0-beta.41",
"@tiptap/extension-document": "^2.0.0-beta.13",
"@tiptap/extension-dropcursor": "^2.0.0-beta.19",
"@tiptap/extension-gapcursor": "^2.0.0-beta.23",
"@tiptap/extension-hard-break": "^2.0.0-beta.20",
"@tiptap/extension-gapcursor": "^2.0.0-beta.24",
"@tiptap/extension-hard-break": "^2.0.0-beta.21",
"@tiptap/extension-heading": "^2.0.0-beta.15",
"@tiptap/extension-history": "^2.0.0-beta.16",
"@tiptap/extension-horizontal-rule": "^2.0.0-beta.20",
"@tiptap/extension-horizontal-rule": "^2.0.0-beta.21",
"@tiptap/extension-image": "^2.0.0-beta.15",
"@tiptap/extension-italic": "^2.0.0-beta.15",
"@tiptap/extension-link": "^2.0.0-beta.20",
@ -92,7 +92,7 @@
"@tiptap/extension-task-item": "^2.0.0-beta.18",
"@tiptap/extension-task-list": "^2.0.0-beta.17",
"@tiptap/extension-text": "^2.0.0-beta.13",
"@tiptap/vue-2": "^2.0.0-beta.56",
"@tiptap/vue-2": "^2.0.0-beta.57",
"@toast-ui/editor": "^2.5.2",
"@toast-ui/vue-editor": "^2.5.2",
"apollo-cache-inmemory": "^1.6.6",

View file

@ -25,6 +25,11 @@ module QA
view 'app/assets/javascripts/diffs/components/compare_versions.vue' do
element :target_version_dropdown
element :file_tree_button
end
view 'app/assets/javascripts/diffs/components/tree_list.vue' do
element :file_tree_container
end
view 'app/assets/javascripts/diffs/components/diff_file_header.vue' do
@ -186,11 +191,17 @@ module QA
end
def has_file?(file_name)
has_element?(:file_name_content, text: file_name)
open_file_tree
has_element?(:file_name_content, file_name: file_name)
end
def has_no_file?(file_name)
has_no_element?(:file_name_content, text: file_name)
open_file_tree
has_no_element?(:file_name_content, file_name: file_name)
end
def open_file_tree
click_element(:file_tree_button) unless has_element?(:file_tree_container)
end
def has_merge_button?

View file

@ -1,11 +1,7 @@
# frozen_string_literal: true
module QA
RSpec.describe 'Create', quarantine: {
only: { job: 'large-setup' },
issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/338324',
type: :stale
} do
RSpec.describe 'Create' do
describe 'Merged merge request' do
let(:project) do
Resource::Project.fabricate_via_api! do |project|

View file

@ -2,8 +2,13 @@
require 'spec_helper'
RSpec.describe Gitlab::Database::Migrations::Instrumentation do
let(:result_dir) { Dir.mktmpdir }
after do
FileUtils.rm_rf(result_dir)
end
describe '#observe' do
subject { described_class.new }
subject { described_class.new(result_dir: result_dir) }
let(:migration_name) { 'test' }
let(:migration_version) { '12345' }
@ -13,7 +18,7 @@ RSpec.describe Gitlab::Database::Migrations::Instrumentation do
end
context 'behavior with observers' do
subject { described_class.new([Gitlab::Database::Migrations::Observers::MigrationObserver]).observe(version: migration_version, name: migration_name) {} }
subject { described_class.new(observer_classes: [Gitlab::Database::Migrations::Observers::MigrationObserver], result_dir: result_dir).observe(version: migration_version, name: migration_name) {} }
let(:observer) { instance_double('Gitlab::Database::Migrations::Observers::MigrationObserver', before: nil, after: nil, record: nil) }
@ -24,7 +29,7 @@ RSpec.describe Gitlab::Database::Migrations::Instrumentation do
it 'instantiates observer with observation' do
expect(Gitlab::Database::Migrations::Observers::MigrationObserver)
.to receive(:new)
.with(instance_of(Gitlab::Database::Migrations::Observation)) { |observation| expect(observation.version).to eq(migration_version) }
.with(instance_of(Gitlab::Database::Migrations::Observation), anything) { |observation| expect(observation.version).to eq(migration_version) }
.and_return(observer)
subject
@ -58,7 +63,7 @@ RSpec.describe Gitlab::Database::Migrations::Instrumentation do
end
context 'on successful execution' do
subject { described_class.new.observe(version: migration_version, name: migration_name) {} }
subject { described_class.new(result_dir: result_dir).observe(version: migration_version, name: migration_name) {} }
it 'records walltime' do
expect(subject.walltime).not_to be_nil
@ -78,7 +83,7 @@ RSpec.describe Gitlab::Database::Migrations::Instrumentation do
end
context 'upon failure' do
subject { described_class.new.observe(version: migration_version, name: migration_name) { raise 'something went wrong' } }
subject { described_class.new(result_dir: result_dir).observe(version: migration_version, name: migration_name) { raise 'something went wrong' } }
it 'raises the exception' do
expect { subject }.to raise_error(/something went wrong/)
@ -93,7 +98,7 @@ RSpec.describe Gitlab::Database::Migrations::Instrumentation do
# ignore
end
let(:instance) { described_class.new }
let(:instance) { described_class.new(result_dir: result_dir) }
it 'records walltime' do
expect(subject.walltime).not_to be_nil
@ -114,7 +119,7 @@ RSpec.describe Gitlab::Database::Migrations::Instrumentation do
end
context 'sequence of migrations with failures' do
subject { described_class.new }
subject { described_class.new(result_dir: result_dir) }
let(:migration1) { double('migration1', call: nil) }
let(:migration2) { double('migration2', call: nil) }

View file

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe Gitlab::Database::Migrations::Observers::QueryDetails do
subject { described_class.new(observation) }
subject { described_class.new(observation, directory_path) }
let(:observation) { Gitlab::Database::Migrations::Observation.new(migration_version, migration_name) }
let(:connection) { ActiveRecord::Base.connection }
@ -14,10 +14,6 @@ RSpec.describe Gitlab::Database::Migrations::Observers::QueryDetails do
let(:migration_version) { 20210422152437 }
let(:migration_name) { 'test' }
before do
stub_const('Gitlab::Database::Migrations::Instrumentation::RESULT_DIR', directory_path)
end
after do
FileUtils.remove_entry(directory_path)
end

View file

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe Gitlab::Database::Migrations::Observers::QueryLog do
subject { described_class.new(observation) }
subject { described_class.new(observation, directory_path) }
let(:observation) { Gitlab::Database::Migrations::Observation.new(migration_version, migration_name) }
let(:connection) { ActiveRecord::Base.connection }
@ -11,10 +11,6 @@ RSpec.describe Gitlab::Database::Migrations::Observers::QueryLog do
let(:migration_version) { 20210422152437 }
let(:migration_name) { 'test' }
before do
stub_const('Gitlab::Database::Migrations::Instrumentation::RESULT_DIR', directory_path)
end
after do
FileUtils.remove_entry(directory_path)
end

View file

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe Gitlab::Database::Migrations::Observers::QueryStatistics do
subject { described_class.new(observation) }
subject { described_class.new(observation, double("unused path")) }
let(:observation) { Gitlab::Database::Migrations::Observation.new }
let(:connection) { ActiveRecord::Base.connection }

View file

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe Gitlab::Database::Migrations::Observers::TotalDatabaseSizeChange do
subject { described_class.new(observation) }
subject { described_class.new(observation, double('unused path')) }
let(:observation) { Gitlab::Database::Migrations::Observation.new }
let(:connection) { ActiveRecord::Base.connection }

View file

@ -0,0 +1,117 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::Database::Migrations::Runner do
let(:result_dir) { Pathname.new(Dir.mktmpdir) }
let(:migration_runs) { [] } # This list gets populated as the runner tries to run migrations
# Tests depend on all of these lists being sorted in the order migrations would be applied
let(:applied_migrations_other_branches) { [double(ActiveRecord::Migration, version: 1, name: 'migration_complete_other_branch')] }
let(:applied_migrations_this_branch) do
[
double(ActiveRecord::Migration, version: 2, name: 'older_migration_complete_this_branch'),
double(ActiveRecord::Migration, version: 3, name: 'newer_migration_complete_this_branch')
].sort_by(&:version)
end
let(:pending_migrations) do
[
double(ActiveRecord::Migration, version: 4, name: 'older_migration_pending'),
double(ActiveRecord::Migration, version: 5, name: 'newer_migration_pending')
].sort_by(&:version)
end
before do
stub_const('Gitlab::Database::Migrations::Runner::BASE_RESULT_DIR', result_dir)
allow(ActiveRecord::Migrator).to receive(:new) do |dir, _all_migrations, _schema_migration_class, version_to_migrate|
migrator = double(ActiveRecord::Migrator)
expect(migrator).to receive(:run) do
migration_runs << OpenStruct.new(dir: dir, version_to_migrate: version_to_migrate)
end
migrator
end
all_versions = (applied_migrations_other_branches + applied_migrations_this_branch).map(&:version)
migrations = applied_migrations_other_branches + applied_migrations_this_branch + pending_migrations
ctx = double(ActiveRecord::MigrationContext, get_all_versions: all_versions, migrations: migrations, schema_migration: ActiveRecord::SchemaMigration)
allow(described_class).to receive(:migration_context).and_return(ctx)
names_this_branch = (applied_migrations_this_branch + pending_migrations).map { |m| "db/migrate/#{m.version}_#{m.name}.rb"}
allow(described_class).to receive(:migration_file_names_this_branch).and_return(names_this_branch)
end
after do
FileUtils.rm_rf(result_dir)
end
it 'creates the results dir when one does not exist' do
FileUtils.rm_rf(result_dir)
expect do
described_class.new(direction: :up, migrations: [], result_dir: result_dir).run
end.to change { Dir.exist?(result_dir) }.from(false).to(true)
end
describe '.up' do
context 'result directory' do
context 'legacy mode' do
it 'uses the root result directory' do
expect(described_class.up(legacy_pipeline: true).result_dir).to eq(result_dir)
end
end
context 'not legacy mode' do
it 'uses the /up subdirectory' do
expect(described_class.up.result_dir).to eq(result_dir.join('up'))
end
end
end
context 'migrations to run' do
subject(:up) { described_class.up }
it 'is the list of pending migrations' do
expect(up.migrations).to eq(pending_migrations)
end
end
context 'running migrations' do
subject(:up) { described_class.up }
it 'runs the unapplied migrations in version order', :aggregate_failures do
up.run
expect(migration_runs.map(&:dir)).to eq([:up, :up])
expect(migration_runs.map(&:version_to_migrate)).to eq(pending_migrations.map(&:version))
end
end
end
describe '.down' do
subject(:down) { described_class.down }
context 'result directory' do
it 'is the /down subdirectory' do
expect(down.result_dir).to eq(result_dir.join('down'))
end
end
context 'migrations to run' do
it 'is the list of migrations that are up and on this branch' do
expect(down.migrations).to eq(applied_migrations_this_branch)
end
end
context 'running migrations' do
it 'runs the applied migrations for the current branch in reverse order', :aggregate_failures do
down.run
expect(migration_runs.map(&:dir)).to eq([:down, :down])
expect(migration_runs.map(&:version_to_migrate)).to eq(applied_migrations_this_branch.reverse.map(&:version))
end
end
end
end

View file

@ -149,8 +149,8 @@ RSpec.describe Gitlab::InstrumentationHelper do
end
end
describe '.queue_duration_for_job' do
where(:enqueued_at, :created_at, :time_now, :expected_duration) do
describe 'duration calculations' do
where(:end_time, :start_time, :time_now, :expected_duration) do
"2019-06-01T00:00:00.000+0000" | nil | "2019-06-01T02:00:00.000+0000" | 2.hours.to_f
"2019-06-01T02:00:00.000+0000" | nil | "2019-06-01T02:00:00.001+0000" | 0.001
"2019-06-01T02:00:00.000+0000" | "2019-05-01T02:00:00.000+0000" | "2019-06-01T02:00:01.000+0000" | 1
@ -164,15 +164,29 @@ RSpec.describe Gitlab::InstrumentationHelper do
0 | nil | "2019-10-23T12:13:16.000+0200" | nil
-1 | nil | "2019-10-23T12:13:16.000+0200" | nil
"2019-06-01T02:00:00.000+0000" | nil | "2019-06-01T00:00:00.000+0000" | 0
Time.at(1571999233) | nil | "2019-10-25T12:29:16.000+0200" | 123
Time.at(1571999233).utc | nil | "2019-10-25T12:29:16.000+0200" | 123
end
with_them do
let(:job) { { 'enqueued_at' => enqueued_at, 'created_at' => created_at } }
describe '.queue_duration_for_job' do
with_them do
let(:job) { { 'enqueued_at' => end_time, 'created_at' => start_time } }
it "returns the correct duration" do
Timecop.freeze(Time.iso8601(time_now)) do
expect(described_class.queue_duration_for_job(job)).to eq(expected_duration)
it "returns the correct duration" do
travel_to(Time.iso8601(time_now)) do
expect(described_class.queue_duration_for_job(job)).to eq(expected_duration)
end
end
end
end
describe '.enqueue_latency_for_scheduled_job' do
with_them do
let(:job) { { 'enqueued_at' => end_time, 'scheduled_at' => start_time } }
it "returns the correct duration" do
travel_to(Time.iso8601(time_now)) do
expect(described_class.enqueue_latency_for_scheduled_job(job)).to eq(expected_duration)
end
end
end
end

View file

@ -18,7 +18,7 @@ RSpec.describe Gitlab::SidekiqLogging::StructuredLogger do
end
it 'logs start and end of job' do
Timecop.freeze(timestamp) do
travel_to(timestamp) do
expect(logger).to receive(:info).with(start_payload).ordered
expect(logger).to receive(:info).with(end_payload).ordered
expect(subject).to receive(:log_job_start).and_call_original
@ -34,7 +34,7 @@ RSpec.describe Gitlab::SidekiqLogging::StructuredLogger do
"wrapped" => "TestWorker"
)
Timecop.freeze(timestamp) do
travel_to(timestamp) do
expect(logger).to receive(:info).with(start_payload).ordered
expect(logger).to receive(:info).with(end_payload).ordered
expect(subject).to receive(:log_job_start).and_call_original
@ -45,7 +45,7 @@ RSpec.describe Gitlab::SidekiqLogging::StructuredLogger do
end
it 'logs an exception in job' do
Timecop.freeze(timestamp) do
travel_to(timestamp) do
expect(logger).to receive(:info).with(start_payload)
expect(logger).to receive(:warn).with(include(exception_payload))
expect(subject).to receive(:log_job_start).and_call_original
@ -60,7 +60,7 @@ RSpec.describe Gitlab::SidekiqLogging::StructuredLogger do
end
it 'logs the root cause of an Sidekiq::JobRetry::Skip exception in the job' do
Timecop.freeze(timestamp) do
travel_to(timestamp) do
expect(logger).to receive(:info).with(start_payload)
expect(logger).to receive(:warn).with(include(exception_payload))
expect(subject).to receive(:log_job_start).and_call_original
@ -77,7 +77,7 @@ RSpec.describe Gitlab::SidekiqLogging::StructuredLogger do
end
it 'logs the root cause of an Sidekiq::JobRetry::Handled exception in the job' do
Timecop.freeze(timestamp) do
travel_to(timestamp) do
expect(logger).to receive(:info).with(start_payload)
expect(logger).to receive(:warn).with(include(exception_payload))
expect(subject).to receive(:log_job_start).and_call_original
@ -94,7 +94,7 @@ RSpec.describe Gitlab::SidekiqLogging::StructuredLogger do
end
it 'keeps Sidekiq::JobRetry::Handled exception if the cause does not exist' do
Timecop.freeze(timestamp) do
travel_to(timestamp) do
expect(logger).to receive(:info).with(start_payload)
expect(logger).to receive(:warn).with(
include(
@ -116,7 +116,7 @@ RSpec.describe Gitlab::SidekiqLogging::StructuredLogger do
end
it 'does not modify the job' do
Timecop.freeze(timestamp) do
travel_to(timestamp) do
job_copy = job.deep_dup
allow(logger).to receive(:info)
@ -130,7 +130,7 @@ RSpec.describe Gitlab::SidekiqLogging::StructuredLogger do
end
it 'does not modify the wrapped job' do
Timecop.freeze(timestamp) do
travel_to(timestamp) do
wrapped_job = job.merge(
"class" => "ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper",
"wrapped" => "TestWorker"
@ -154,7 +154,7 @@ RSpec.describe Gitlab::SidekiqLogging::StructuredLogger do
end
it 'logs start and end of job without args' do
Timecop.freeze(timestamp) do
travel_to(timestamp) do
expect(logger).to receive(:info).with(start_payload.except('args')).ordered
expect(logger).to receive(:info).with(end_payload.except('args')).ordered
expect(subject).to receive(:log_job_start).and_call_original
@ -165,7 +165,7 @@ RSpec.describe Gitlab::SidekiqLogging::StructuredLogger do
end
it 'logs without created_at and enqueued_at fields' do
Timecop.freeze(timestamp) do
travel_to(timestamp) do
excluded_fields = %w(created_at enqueued_at args scheduling_latency_s)
expect(logger).to receive(:info).with(start_payload.except(*excluded_fields)).ordered
@ -183,7 +183,7 @@ RSpec.describe Gitlab::SidekiqLogging::StructuredLogger do
let(:scheduling_latency_s) { 7200.0 }
it 'logs with scheduling latency' do
Timecop.freeze(timestamp) do
travel_to(timestamp) do
expect(logger).to receive(:info).with(start_payload).ordered
expect(logger).to receive(:info).with(end_payload).ordered
expect(subject).to receive(:log_job_start).and_call_original
@ -194,6 +194,35 @@ RSpec.describe Gitlab::SidekiqLogging::StructuredLogger do
end
end
context 'with enqueue latency' do
let(:expected_start_payload) do
start_payload.merge(
'scheduled_at' => job['scheduled_at'],
'enqueue_latency_s' => 1.hour.to_f
)
end
let(:expected_end_payload) do
end_payload.merge('enqueue_latency_s' => 1.hour.to_f)
end
before do
# enqueued_at is set to created_at
job['scheduled_at'] = created_at - 1.hour
end
it 'logs with scheduling latency' do
travel_to(timestamp) do
expect(logger).to receive(:info).with(expected_start_payload).ordered
expect(logger).to receive(:info).with(expected_end_payload).ordered
expect(subject).to receive(:log_job_start).and_call_original
expect(subject).to receive(:log_job_done).and_call_original
call_subject(job, 'test_queue') { }
end
end
end
context 'with Gitaly, Rugged, and Redis calls' do
let(:timing_data) do
{
@ -218,7 +247,7 @@ RSpec.describe Gitlab::SidekiqLogging::StructuredLogger do
end
it 'logs with Gitaly and Rugged timing data', :aggregate_failures do
Timecop.freeze(timestamp) do
travel_to(timestamp) do
expect(logger).to receive(:info).with(start_payload).ordered
expect(logger).to receive(:info).with(expected_end_payload).ordered
@ -323,7 +352,7 @@ RSpec.describe Gitlab::SidekiqLogging::StructuredLogger do
end
it 'logs it in the done log' do
Timecop.freeze(timestamp) do
travel_to(timestamp) do
expect(logger).to receive(:info).with(expected_start_payload).ordered
expect(logger).to receive(:info).with(expected_end_payload).ordered
@ -365,7 +394,7 @@ RSpec.describe Gitlab::SidekiqLogging::StructuredLogger do
end
it 'logs it in the done log' do
Timecop.freeze(timestamp) do
travel_to(timestamp) do
expect(logger).to receive(:info).with(expected_start_payload).ordered
expect(logger).to receive(:info).with(expected_end_payload).ordered
@ -390,13 +419,13 @@ RSpec.describe Gitlab::SidekiqLogging::StructuredLogger do
'message' => 'my-message',
'job_status' => 'my-job-status',
'duration_s' => 0.123123,
'completed_at' => current_utc_time.to_f }
'completed_at' => current_utc_time.to_i }
end
subject { described_class.new }
it 'update payload correctly' do
Timecop.freeze(current_utc_time) do
travel_to(current_utc_time) do
subject.send(:add_time_keys!, time, payload)
expect(payload).to eq(payload_with_time_keys)

View file

@ -62,6 +62,14 @@ RSpec.describe Gitlab::SidekiqMiddleware::ClientMetrics do
Sidekiq::Testing.inline! { TestWorker.perform_in(1.second) }
end
it 'sets the scheduled_at field' do
job = { 'at' => Time.current }
subject.call('TestWorker', job, 'queue', nil) do
expect(job[:scheduled_at]).to eq(job['at'])
end
end
end
context 'when the worker class cannot be found' do

View file

@ -0,0 +1,12 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::Usage::Metrics::Instrumentations::CountUsersAssociatingMilestonesToReleasesMetric do
let_it_be(:release) { create(:release, created_at: 3.days.ago) }
let_it_be(:release_with_milestone) { create(:release, :with_milestones, created_at: 3.days.ago) }
it_behaves_like 'a correct instrumented metric value', { time_frame: '28d', data_source: 'database' } do
let(:expected_value) { 1 }
end
end

View file

@ -469,7 +469,8 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
for_defined_days_back do
user = create(:user)
create(:deployment, :failed, user: user)
create(:release, author: user)
release = create(:release, author: user)
create(:milestone, project: release.project, releases: [release])
create(:deployment, :success, user: user)
end
@ -477,13 +478,15 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
deployments: 2,
failed_deployments: 2,
releases: 2,
successful_deployments: 2
successful_deployments: 2,
releases_with_milestones: 2
)
expect(described_class.usage_activity_by_stage_release(described_class.monthly_time_range_db_params)).to include(
deployments: 1,
failed_deployments: 1,
releases: 1,
successful_deployments: 1
successful_deployments: 1,
releases_with_milestones: 1
)
end
end

View file

@ -22,6 +22,18 @@ RSpec.describe Issues::CloseService do
describe '#execute' do
let(:service) { described_class.new(project: project, current_user: user) }
context 'when skip_authorization is true' do
it 'does close the issue even if user is not authorized' do
non_authorized_user = create(:user)
service = described_class.new(project: project, current_user: non_authorized_user)
expect do
service.execute(issue, skip_authorization: true)
end.to change { issue.reload.state }.from('opened').to('closed')
end
end
it 'checks if the user is authorized to update the issue' do
expect(service).to receive(:can?).with(user, :update_issue, issue)
.and_call_original

View file

@ -8,17 +8,25 @@ RSpec.describe Issues::ReopenService do
describe '#execute' do
context 'when user is not authorized to reopen issue' do
before do
it 'does not reopen the issue' do
guest = create(:user)
project.add_guest(guest)
perform_enqueued_jobs do
described_class.new(project: project, current_user: guest).execute(issue)
end
described_class.new(project: project, current_user: guest).execute(issue)
expect(issue).to be_closed
end
it 'does not reopen the issue' do
expect(issue).to be_closed
context 'when skip_authorization is true' do
it 'does close the issue even if user is not authorized' do
non_authorized_user = create(:user)
service = described_class.new(project: project, current_user: non_authorized_user)
expect do
service.execute(issue, skip_authorization: true)
end.to change { issue.reload.state }.from('closed').to('opened')
end
end
end

View file

@ -293,53 +293,37 @@ RSpec.describe 'gitlab:db namespace rake task', :silence_stdout do
end
describe '#migrate_with_instrumentation' do
subject { run_rake_task('gitlab:db:migration_testing') }
describe '#up' do
subject { run_rake_task('gitlab:db:migration_testing:up') }
let(:ctx) { double('ctx', migrations: all_migrations, schema_migration: double, get_all_versions: existing_versions) }
let(:instrumentation) { instance_double(Gitlab::Database::Migrations::Instrumentation, observations: observations) }
let(:existing_versions) { [1] }
let(:all_migrations) { [double('migration1', version: 1, name: 'test'), pending_migration] }
let(:pending_migration) { double('migration2', version: 2, name: 'test') }
let(:filename) { Gitlab::Database::Migrations::Instrumentation::STATS_FILENAME }
let(:result_dir) { Dir.mktmpdir }
let(:observations) { %w[some data] }
it 'delegates to the migration runner' do
expect(::Gitlab::Database::Migrations::Runner).to receive_message_chain(:up, :run)
before do
allow(ActiveRecord::Base.connection).to receive(:migration_context).and_return(ctx)
allow(Gitlab::Database::Migrations::Instrumentation).to receive(:new).and_return(instrumentation)
allow(ActiveRecord::Migrator).to receive_message_chain('new.run').with(any_args).with(no_args)
allow(instrumentation).to receive(:observe).and_yield
stub_const('Gitlab::Database::Migrations::Instrumentation::RESULT_DIR', result_dir)
subject
end
end
after do
FileUtils.rm_rf(result_dir)
describe '#down' do
subject { run_rake_task('gitlab:db:migration_testing:down') }
it 'delegates to the migration runner' do
expect(::Gitlab::Database::Migrations::Runner).to receive_message_chain(:down, :run)
subject
end
end
it 'creates result directory when one does not exist' do
FileUtils.rm_rf(result_dir)
describe 'legacy rake task' do
subject { run_rake_task('gitlab:db:migration_testing') }
expect { subject }.to change { Dir.exist?(result_dir) }.from(false).to(true)
end
let(:runner) { double(Gitlab::Database::Migrations::Runner) }
it 'instruments the pending migration' do
expect(instrumentation).to receive(:observe).with(version: 2, name: 'test').and_yield
it 'delegates to the migration runner in legacy mode' do
expect(::Gitlab::Database::Migrations::Runner).to receive(:up).with(legacy_pipeline: true).and_return(runner)
expect(runner).to receive(:run)
subject
end
it 'executes the pending migration' do
expect(ActiveRecord::Migrator).to receive_message_chain('new.run').with(:up, ctx.migrations, ctx.schema_migration, pending_migration.version).with(no_args)
subject
end
it 'writes observations out to JSON file' do
subject
expect(File.read(File.join(result_dir, filename))).to eq(observations.to_json)
subject
end
end
end

View file

@ -1310,10 +1310,10 @@
dom-accessibility-api "^0.5.1"
pretty-format "^26.4.2"
"@tiptap/core@^2.0.0-beta.116":
version "2.0.0-beta.116"
resolved "https://registry.yarnpkg.com/@tiptap/core/-/core-2.0.0-beta.116.tgz#990b846cf4c4b4823a7a7ae44dd92bf96ee09403"
integrity sha512-5x3HkT71IxF56lPEHPSTqOqkm1fVfHgVfyBbiNA3Qsz47npgJCDelDr6PHY8qlzxLCaN4dMDQUXEsvDDAiRouw==
"@tiptap/core@^2.0.0-beta.118":
version "2.0.0-beta.118"
resolved "https://registry.yarnpkg.com/@tiptap/core/-/core-2.0.0-beta.118.tgz#17e990da525a81d957494171c0c33d17e2039823"
integrity sha512-TApketXliv2PZSTf5WP8j/svwzeK795fOf4Ff6A0gDwcVYFPHlny4ZlpawYthyqDoe1fEusyZokVgiDHnb+EzA==
dependencies:
"@types/prosemirror-commands" "^1.0.4"
"@types/prosemirror-inputrules" "^1.0.4"
@ -1344,10 +1344,10 @@
resolved "https://registry.yarnpkg.com/@tiptap/extension-bold/-/extension-bold-2.0.0-beta.15.tgz#cf9ddb3fc316be9707753ad4e497bfb8a3ebb0c2"
integrity sha512-jKyV6iiwhxwa0+7uuKD74jNDVNLNOS1GmU14MgaA95pY5e1fyaRBPPX8Gtt89niz2CLOY711AV17RPZTe/e60w==
"@tiptap/extension-bubble-menu@^2.0.0-beta.38":
version "2.0.0-beta.38"
resolved "https://registry.yarnpkg.com/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.0.0-beta.38.tgz#a3f85b44a28667da0fe80a4de3d97833bebefc87"
integrity sha512-XBQemM+0w2VBe72e9AH8fql0ZcrhoDThxbldgq0Cx6Nr49ZmzuYYcFKQvTGTkxEcWSfXIsisSRbkaqUB9QM+ZQ==
"@tiptap/extension-bubble-menu@^2.0.0-beta.39":
version "2.0.0-beta.39"
resolved "https://registry.yarnpkg.com/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.0.0-beta.39.tgz#8971feeac93e685fc38564173a83ad078b4e7f2a"
integrity sha512-hmA+ePR+MnRaTJ5MxoZ3yqOcK54cW2KQllZx16ZwSyM+yU9bXVhfMmyZwqRD7GGQFkrfnPm5QnedXDBYJD19OQ==
dependencies:
prosemirror-state "^1.3.4"
prosemirror-view "^1.20.1"
@ -1360,10 +1360,10 @@
dependencies:
prosemirror-inputrules "^1.1.3"
"@tiptap/extension-code-block-lowlight@2.0.0-beta.40":
version "2.0.0-beta.40"
resolved "https://registry.yarnpkg.com/@tiptap/extension-code-block-lowlight/-/extension-code-block-lowlight-2.0.0-beta.40.tgz#c2285dd472030f013dcbc86a211d4ec5838c2898"
integrity sha512-upMgyy2WxNjLFL2e6LUbc4JLCJIlBn1K8wHDwX7DFP5o9pH77W+0MeAayvF9j3fPaPLt6JgzAxfKlSGPkbX1rg==
"@tiptap/extension-code-block-lowlight@2.0.0-beta.41":
version "2.0.0-beta.41"
resolved "https://registry.yarnpkg.com/@tiptap/extension-code-block-lowlight/-/extension-code-block-lowlight-2.0.0-beta.41.tgz#3ec98f509bbd5df689de6282f2a3881229262ed7"
integrity sha512-2+D/SwcRjWThJ8uFWJT/6B7R+gTUlp4h13/EZqrFMm3YCSOx+bzgTVndog6UJkWyAoNDxkTVwcOyYI4HWdvCiQ==
dependencies:
"@tiptap/extension-code-block" "^2.0.0-beta.18"
"@types/lowlight" "^0.0.3"
@ -1397,27 +1397,27 @@
"@types/prosemirror-dropcursor" "^1.0.3"
prosemirror-dropcursor "^1.3.5"
"@tiptap/extension-floating-menu@^2.0.0-beta.32":
version "2.0.0-beta.32"
resolved "https://registry.yarnpkg.com/@tiptap/extension-floating-menu/-/extension-floating-menu-2.0.0-beta.32.tgz#6600fb0fd5b1ddc6d161d03c74e062c9133842cf"
integrity sha512-frroKOpE99fOdfJoWeM1ByWHCda+7Fe1n/Li6rdrmP9NdOS9FluG5lzzYf1qZ8wANfZhrXM8DOjXEXow/L3wkw==
"@tiptap/extension-floating-menu@^2.0.0-beta.33":
version "2.0.0-beta.33"
resolved "https://registry.yarnpkg.com/@tiptap/extension-floating-menu/-/extension-floating-menu-2.0.0-beta.33.tgz#11068488f10fce697df2a48f79039e9c1d10eb7b"
integrity sha512-8s8DPnHIzXg7E7S/DjuS1AAFZKVYXY0KBKaEd1f2V45YOkKwN9El46Ugk/4Ir3yrrllvnisbP9ol+BAQmI0bMg==
dependencies:
prosemirror-state "^1.3.4"
prosemirror-view "^1.20.1"
tippy.js "^6.3.1"
"@tiptap/extension-gapcursor@^2.0.0-beta.23":
version "2.0.0-beta.23"
resolved "https://registry.yarnpkg.com/@tiptap/extension-gapcursor/-/extension-gapcursor-2.0.0-beta.23.tgz#d6ea2a97392e0970642b7ea55d778562016ff77e"
integrity sha512-yav1pAZ6YTZ4GzRaY3x0KutghLfC0Z6g/KXFzdE5KD5nxBhLUhid9rZ/kQiVPmZx2R0M/O5C+mdScYS2UzMmqA==
"@tiptap/extension-gapcursor@^2.0.0-beta.24":
version "2.0.0-beta.24"
resolved "https://registry.yarnpkg.com/@tiptap/extension-gapcursor/-/extension-gapcursor-2.0.0-beta.24.tgz#dc42a3610cea611755c6521e14a6995555e0ad49"
integrity sha512-/6Ru0wNLIb3fo30Ar3z/rcakoUA2EIJL9sBFiuyHWTAIujeEaBzA6oG5L4PpP+daKd31JF0I6LjeWMSU9CBSFw==
dependencies:
"@types/prosemirror-gapcursor" "^1.0.4"
prosemirror-gapcursor "^1.2.0"
"@tiptap/extension-hard-break@^2.0.0-beta.20":
version "2.0.0-beta.20"
resolved "https://registry.yarnpkg.com/@tiptap/extension-hard-break/-/extension-hard-break-2.0.0-beta.20.tgz#308c8aaa935dcaf61296d8d0a8a0daed072d884b"
integrity sha512-tLZ53VMse2C1skj23tPFlq0wmdOCQ9vRsukz/KaR8VAFQBany0GOFmazu6QEFpC9+TI2gAckIGijEGFyP9QkMA==
"@tiptap/extension-hard-break@^2.0.0-beta.21":
version "2.0.0-beta.21"
resolved "https://registry.yarnpkg.com/@tiptap/extension-hard-break/-/extension-hard-break-2.0.0-beta.21.tgz#3b9108c7703f23ae186c1038033f0b1354f721bf"
integrity sha512-Ukl+wjfLhE0tW7lWRpSPPo2tajjGnEaSc/Irey1JineFf+x/azA9rREzQy0r2AhORTalH7lj/KDmSdG8IT6syA==
"@tiptap/extension-heading@^2.0.0-beta.15":
version "2.0.0-beta.15"
@ -1434,10 +1434,10 @@
"@types/prosemirror-history" "^1.0.3"
prosemirror-history "^1.2.0"
"@tiptap/extension-horizontal-rule@^2.0.0-beta.20":
version "2.0.0-beta.20"
resolved "https://registry.yarnpkg.com/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.0.0-beta.20.tgz#84260374812bf9fdac2f54869f10ffae772dc2b8"
integrity sha512-phvZy1ckl4FJ8k6cXce8wOXxv0c50HjU5sA7r6b8u7+Mj0Dc5DZE7enjiDGxoBLP6Yf2lzOgq1phH/r9EihDUQ==
"@tiptap/extension-horizontal-rule@^2.0.0-beta.21":
version "2.0.0-beta.21"
resolved "https://registry.yarnpkg.com/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.0.0-beta.21.tgz#1c73a8547611f53935117ed0079542d958ba37fa"
integrity sha512-fgvRGuNEGWAitbcoz6VZSR9gcVIHksTy2QpXPnQC+N9Mi7havaxreYdMZn+oePW/5kdZoZNRx+jsf5DjKomvoQ==
dependencies:
prosemirror-state "^1.3.4"
@ -1530,13 +1530,13 @@
resolved "https://registry.yarnpkg.com/@tiptap/extension-text/-/extension-text-2.0.0-beta.13.tgz#da0af8d9a3f149d20076e15d88c6af21fb6d940f"
integrity sha512-0EtAwuRldCAoFaL/iXgkRepEeOd55rPg5N4FQUN1xTwZT7PDofukP0DG/2jff/Uj17x4uTaJAa9qlFWuNnDvjw==
"@tiptap/vue-2@^2.0.0-beta.56":
version "2.0.0-beta.56"
resolved "https://registry.yarnpkg.com/@tiptap/vue-2/-/vue-2-2.0.0-beta.56.tgz#c339bb94d0ec6ea4b4a330bb0f9800468620780c"
integrity sha512-qU2rD6LHU5Xg30XVP8gdPPf/u3aEFhkQNOTk5m7rnRvqj8KXkZCTHLzBDgJfnoLfy7hVn62Iiq0KP7wKDda0Tw==
"@tiptap/vue-2@^2.0.0-beta.57":
version "2.0.0-beta.57"
resolved "https://registry.yarnpkg.com/@tiptap/vue-2/-/vue-2-2.0.0-beta.57.tgz#242f7aa47d3c99fdef0e66a05a193b6fef1a95a6"
integrity sha512-f8COWq84wOJeLwAmaYsHCqKVbGgMloW+1r4Rz/KhlFb1MNXYeDHibCiW/VtJe7bdae+iRyIwnfmnAp2u5s77hQ==
dependencies:
"@tiptap/extension-bubble-menu" "^2.0.0-beta.38"
"@tiptap/extension-floating-menu" "^2.0.0-beta.32"
"@tiptap/extension-bubble-menu" "^2.0.0-beta.39"
"@tiptap/extension-floating-menu" "^2.0.0-beta.33"
prosemirror-view "^1.20.1"
"@toast-ui/editor@^2.5.2":