diff options
47 files changed, 679 insertions, 434 deletions
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index 7bb21aff834..89eba2c5b85 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -0.102.0 +0.103.0 @@ -162,7 +162,7 @@ gem 'acts-as-taggable-on', '~> 5.0' # Background jobs gem 'sidekiq', '~> 5.1' gem 'sidekiq-cron', '~> 0.6.0' -gem 'redis-namespace', '~> 1.5.2' +gem 'redis-namespace', '~> 1.6.0' gem 'sidekiq-limit_fetch', '~> 3.4', require: false # Cron Parser @@ -412,7 +412,7 @@ group :ed25519 do end # Gitaly GRPC client -gem 'gitaly-proto', '~> 0.99.0', require: 'gitaly' +gem 'gitaly-proto', '~> 0.100.0', require: 'gitaly' gem 'grpc', '~> 1.11.0' # Locked until https://github.com/google/protobuf/issues/4210 is closed diff --git a/Gemfile.lock b/Gemfile.lock index 537c377dccc..b0b7bb537a8 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -281,7 +281,7 @@ GEM gettext_i18n_rails (>= 0.7.1) po_to_json (>= 1.0.0) rails (>= 3.2.0) - gitaly-proto (0.99.0) + gitaly-proto (0.100.0) google-protobuf (~> 3.1) grpc (~> 1.10) github-linguist (5.3.3) @@ -709,8 +709,8 @@ GEM redis-activesupport (5.0.4) activesupport (>= 3, < 6) redis-store (>= 1.3, < 2) - redis-namespace (1.5.2) - redis (~> 3.0, >= 3.0.4) + redis-namespace (1.6.0) + redis (>= 3.0.4) redis-rack (2.0.4) rack (>= 1.5, < 3) redis-store (>= 1.2, < 2) @@ -1036,7 +1036,7 @@ DEPENDENCIES gettext (~> 3.2.2) gettext_i18n_rails (~> 1.8.0) gettext_i18n_rails_js (~> 1.3) - gitaly-proto (~> 0.99.0) + gitaly-proto (~> 0.100.0) github-linguist (~> 5.3.3) gitlab-flowdock-git-hook (~> 1.0.1) gitlab-gollum-lib (~> 4.2) @@ -1129,7 +1129,7 @@ DEPENDENCIES recaptcha (~> 3.0) redcarpet (~> 3.4) redis (~> 3.2) - redis-namespace (~> 1.5.2) + redis-namespace (~> 1.6.0) redis-rails (~> 5.0.2) request_store (~> 1.3) responders (~> 2.0) diff --git a/app/controllers/admin/dashboard_controller.rb b/app/controllers/admin/dashboard_controller.rb index d6a6bc7d4a1..737942f3eb2 100644 --- a/app/controllers/admin/dashboard_controller.rb +++ b/app/controllers/admin/dashboard_controller.rb @@ -1,7 +1,11 @@ class Admin::DashboardController < Admin::ApplicationController include CountHelper + COUNTED_ITEMS = [Project, User, Group, ForkedProjectLink, Issue, MergeRequest, + Note, Snippet, Key, Milestone].freeze + def index + @counts = Gitlab::Database::Count.approximate_counts(COUNTED_ITEMS) @projects = Project.order_id_desc.without_deleted.with_route.limit(10) @users = User.order_id_desc.limit(10) @groups = Group.order_id_desc.with_route.limit(10) diff --git a/app/helpers/count_helper.rb b/app/helpers/count_helper.rb index 24ee62e68ba..5cd98f40f78 100644 --- a/app/helpers/count_helper.rb +++ b/app/helpers/count_helper.rb @@ -1,5 +1,9 @@ module CountHelper - def approximate_count_with_delimiters(model) - number_with_delimiter(Gitlab::Database::Count.approximate_count(model)) + def approximate_count_with_delimiters(count_data, model) + count = count_data[model] + + raise "Missing model #{model} from count data" unless count + + number_with_delimiter(count) end end diff --git a/app/models/concerns/diff_file.rb b/app/models/concerns/diff_file.rb new file mode 100644 index 00000000000..72332072012 --- /dev/null +++ b/app/models/concerns/diff_file.rb @@ -0,0 +1,9 @@ +module DiffFile + extend ActiveSupport::Concern + + def to_hash + keys = Gitlab::Git::Diff::SERIALIZE_KEYS - [:diff] + + as_json(only: keys).merge(diff: diff).with_indifferent_access + end +end diff --git a/app/models/diff_note.rb b/app/models/diff_note.rb index 616a626419b..d752d5bcdee 100644 --- a/app/models/diff_note.rb +++ b/app/models/diff_note.rb @@ -3,6 +3,7 @@ # A note of this type can be resolvable. class DiffNote < Note include NoteOnDiff + include Gitlab::Utils::StrongMemoize NOTEABLE_TYPES = %w(MergeRequest Commit).freeze @@ -12,7 +13,6 @@ class DiffNote < Note validates :original_position, presence: true validates :position, presence: true - validates :diff_line, presence: true, if: :on_text? validates :line_code, presence: true, line_code: true, if: :on_text? validates :noteable_type, inclusion: { in: NOTEABLE_TYPES } validate :positions_complete @@ -23,6 +23,7 @@ class DiffNote < Note before_validation :update_position, on: :create, if: :on_text? before_validation :set_line_code, if: :on_text? after_save :keep_around_commits + after_commit :create_diff_file, on: :create def discussion_class(*) DiffDiscussion @@ -53,21 +54,25 @@ class DiffNote < Note position.position_type == "image" end + def create_diff_file + return unless should_create_diff_file? + + diff_file = fetch_diff_file + diff_line = diff_file.line_for_position(self.original_position) + + creation_params = diff_file.diff.to_hash + .except(:too_large) + .merge(diff: diff_file.diff_hunk(diff_line)) + + create_note_diff_file(creation_params) + end + def diff_file - @diff_file ||= - begin - if created_at_diff?(noteable.diff_refs) - # We're able to use the already persisted diffs (Postgres) if we're - # presenting a "current version" of the MR discussion diff. - # So no need to make an extra Gitaly diff request for it. - # As an extra benefit, the returned `diff_file` already - # has `highlighted_diff_lines` data set from Redis on - # `Diff::FileCollection::MergeRequestDiff`. - noteable.diffs(paths: original_position.paths, expanded: true).diff_files.first - else - original_position.diff_file(self.project.repository) - end - end + strong_memoize(:diff_file) do + enqueue_diff_file_creation_job if should_create_diff_file? + + fetch_diff_file + end end def diff_line @@ -98,6 +103,38 @@ class DiffNote < Note private + def enqueue_diff_file_creation_job + # Avoid enqueuing multiple file creation jobs at once for a note (i.e. + # parallel calls to `DiffNote#diff_file`). + lease = Gitlab::ExclusiveLease.new("note_diff_file_creation:#{id}", timeout: 1.hour.to_i) + return unless lease.try_obtain + + CreateNoteDiffFileWorker.perform_async(id) + end + + def should_create_diff_file? + on_text? && note_diff_file.nil? && self == discussion.first_note + end + + def fetch_diff_file + if note_diff_file + diff = Gitlab::Git::Diff.new(note_diff_file.to_hash) + Gitlab::Diff::File.new(diff, + repository: project.repository, + diff_refs: original_position.diff_refs) + elsif created_at_diff?(noteable.diff_refs) + # We're able to use the already persisted diffs (Postgres) if we're + # presenting a "current version" of the MR discussion diff. + # So no need to make an extra Gitaly diff request for it. + # As an extra benefit, the returned `diff_file` already + # has `highlighted_diff_lines` data set from Redis on + # `Diff::FileCollection::MergeRequestDiff`. + noteable.diffs(paths: original_position.paths, expanded: true).diff_files.first + else + original_position.diff_file(self.project.repository) + end + end + def supported? for_commit? || self.noteable.has_complete_diff_refs? end diff --git a/app/models/merge_request_diff_file.rb b/app/models/merge_request_diff_file.rb index 1199ff5af22..cd8ba6b904d 100644 --- a/app/models/merge_request_diff_file.rb +++ b/app/models/merge_request_diff_file.rb @@ -1,5 +1,6 @@ class MergeRequestDiffFile < ActiveRecord::Base include Gitlab::EncodingHelper + include DiffFile belongs_to :merge_request_diff @@ -12,10 +13,4 @@ class MergeRequestDiffFile < ActiveRecord::Base def diff binary? ? super.unpack('m0').first : super end - - def to_hash - keys = Gitlab::Git::Diff::SERIALIZE_KEYS - [:diff] - - as_json(only: keys).merge(diff: diff).with_indifferent_access - end end diff --git a/app/models/note.rb b/app/models/note.rb index 109405d3f17..02f7a9b1e4f 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -63,6 +63,7 @@ class Note < ActiveRecord::Base has_many :todos has_many :events, as: :target, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_one :system_note_metadata + has_one :note_diff_file, inverse_of: :diff_note, foreign_key: :diff_note_id delegate :gfm_reference, :local_reference, to: :noteable delegate :name, to: :project, prefix: true @@ -100,7 +101,8 @@ class Note < ActiveRecord::Base scope :inc_author_project, -> { includes(:project, :author) } scope :inc_author, -> { includes(:author) } scope :inc_relations_for_view, -> do - includes(:project, :author, :updated_by, :resolved_by, :award_emoji, :system_note_metadata) + includes(:project, :author, :updated_by, :resolved_by, :award_emoji, + :system_note_metadata, :note_diff_file) end scope :diff_notes, -> { where(type: %w(LegacyDiffNote DiffNote)) } diff --git a/app/models/note_diff_file.rb b/app/models/note_diff_file.rb new file mode 100644 index 00000000000..e688018a6d9 --- /dev/null +++ b/app/models/note_diff_file.rb @@ -0,0 +1,7 @@ +class NoteDiffFile < ActiveRecord::Base + include DiffFile + + belongs_to :diff_note, inverse_of: :note_diff_file + + validates :diff_note, presence: true +end diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml index 3df4ce93fa8..3cdeb103bb8 100644 --- a/app/views/admin/dashboard/index.html.haml +++ b/app/views/admin/dashboard/index.html.haml @@ -12,7 +12,7 @@ = link_to admin_projects_path do %h3.text-center Projects: - = approximate_count_with_delimiters(Project) + = approximate_count_with_delimiters(@counts, Project) %hr = link_to('New project', new_project_path, class: "btn btn-new") .col-sm-4 @@ -21,7 +21,7 @@ = link_to admin_users_path do %h3.text-center Users: - = approximate_count_with_delimiters(User) + = approximate_count_with_delimiters(@counts, User) = render_if_exists 'users_statistics' %hr = link_to 'New user', new_admin_user_path, class: "btn btn-new" @@ -31,7 +31,7 @@ = link_to admin_groups_path do %h3.text-center Groups: - = approximate_count_with_delimiters(Group) + = approximate_count_with_delimiters(@counts, Group) %hr = link_to 'New group', new_admin_group_path, class: "btn btn-new" .row @@ -42,31 +42,31 @@ %p Forks %span.light.float-right - = approximate_count_with_delimiters(ForkedProjectLink) + = approximate_count_with_delimiters(@counts, ForkedProjectLink) %p Issues %span.light.float-right - = approximate_count_with_delimiters(Issue) + = approximate_count_with_delimiters(@counts, Issue) %p Merge Requests %span.light.float-right - = approximate_count_with_delimiters(MergeRequest) + = approximate_count_with_delimiters(@counts, MergeRequest) %p Notes %span.light.float-right - = approximate_count_with_delimiters(Note) + = approximate_count_with_delimiters(@counts, Note) %p Snippets %span.light.float-right - = approximate_count_with_delimiters(Snippet) + = approximate_count_with_delimiters(@counts, Snippet) %p SSH Keys %span.light.float-right - = approximate_count_with_delimiters(Key) + = approximate_count_with_delimiters(@counts, Key) %p Milestones %span.light.float-right - = approximate_count_with_delimiters(Milestone) + = approximate_count_with_delimiters(@counts, Milestone) %p Active Users %span.light.float-right diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml index b6433eb3eff..d07865a4043 100644 --- a/app/workers/all_queues.yml +++ b/app/workers/all_queues.yml @@ -115,3 +115,4 @@ - upload_checksum - web_hook - repository_update_remote_mirror +- create_note_diff_file diff --git a/app/workers/create_note_diff_file_worker.rb b/app/workers/create_note_diff_file_worker.rb new file mode 100644 index 00000000000..624b638a24e --- /dev/null +++ b/app/workers/create_note_diff_file_worker.rb @@ -0,0 +1,9 @@ +class CreateNoteDiffFileWorker + include ApplicationWorker + + def perform(diff_note_id) + diff_note = DiffNote.find(diff_note_id) + + diff_note.create_diff_file + end +end diff --git a/changelogs/unreleased/45190-create-notes-diff-files.yml b/changelogs/unreleased/45190-create-notes-diff-files.yml new file mode 100644 index 00000000000..efe322b682d --- /dev/null +++ b/changelogs/unreleased/45190-create-notes-diff-files.yml @@ -0,0 +1,5 @@ +--- +title: Persist truncated note diffs on a new table +merge_request: +author: +type: performance diff --git a/changelogs/unreleased/46846-update-redis-namespace-to-1-6-0.yml b/changelogs/unreleased/46846-update-redis-namespace-to-1-6-0.yml new file mode 100644 index 00000000000..3707ad74b8f --- /dev/null +++ b/changelogs/unreleased/46846-update-redis-namespace-to-1-6-0.yml @@ -0,0 +1,5 @@ +--- +title: Update redis-namespace to 1.6.0 +merge_request: 19166 +author: Takuya Noguchi +type: other diff --git a/changelogs/unreleased/bvl-add-username-to-terms-message.yml b/changelogs/unreleased/bvl-add-username-to-terms-message.yml new file mode 100644 index 00000000000..b95d87e9265 --- /dev/null +++ b/changelogs/unreleased/bvl-add-username-to-terms-message.yml @@ -0,0 +1,5 @@ +--- +title: Add username to terms message in git and API calls +merge_request: 19126 +author: +type: changed diff --git a/changelogs/unreleased/ignore-writing-trace-if-it-already-archived.yml b/changelogs/unreleased/ignore-writing-trace-if-it-already-archived.yml new file mode 100644 index 00000000000..5c342e2a131 --- /dev/null +++ b/changelogs/unreleased/ignore-writing-trace-if-it-already-archived.yml @@ -0,0 +1,5 @@ +--- +title: Disallow updating job status if the job is not running +merge_request: 19101 +author: +type: fixed diff --git a/changelogs/unreleased/sh-fix-admin-page-counts-take-2.yml b/changelogs/unreleased/sh-fix-admin-page-counts-take-2.yml new file mode 100644 index 00000000000..d9bd1af9380 --- /dev/null +++ b/changelogs/unreleased/sh-fix-admin-page-counts-take-2.yml @@ -0,0 +1,5 @@ +--- +title: Fix admin counters not working when PostgreSQL has secondaries +merge_request: +author: +type: fixed diff --git a/config/sidekiq_queues.yml b/config/sidekiq_queues.yml index e1e8f36b663..d16060e8f45 100644 --- a/config/sidekiq_queues.yml +++ b/config/sidekiq_queues.yml @@ -75,4 +75,5 @@ - [pipeline_background, 1] - [repository_update_remote_mirror, 1] - [repository_remove_remote, 1] + - [create_note_diff_file, 1] diff --git a/db/migrate/20180515121227_create_notes_diff_files.rb b/db/migrate/20180515121227_create_notes_diff_files.rb new file mode 100644 index 00000000000..7108bc1a64b --- /dev/null +++ b/db/migrate/20180515121227_create_notes_diff_files.rb @@ -0,0 +1,21 @@ +class CreateNotesDiffFiles < ActiveRecord::Migration + DOWNTIME = false + + disable_ddl_transaction! + + def change + create_table :note_diff_files do |t| + t.references :diff_note, references: :notes, null: false, index: { unique: true } + t.text :diff, null: false + t.boolean :new_file, null: false + t.boolean :renamed_file, null: false + t.boolean :deleted_file, null: false + t.string :a_mode, null: false + t.string :b_mode, null: false + t.text :new_path, null: false + t.text :old_path, null: false + end + + add_foreign_key :note_diff_files, :notes, column: :diff_note_id, on_delete: :cascade + end +end diff --git a/db/schema.rb b/db/schema.rb index 37d336b9928..884e333874c 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -1302,6 +1302,20 @@ ActiveRecord::Schema.define(version: 20180521171529) do add_index "namespaces", ["runners_token"], name: "index_namespaces_on_runners_token", unique: true, using: :btree add_index "namespaces", ["type"], name: "index_namespaces_on_type", using: :btree + create_table "note_diff_files", force: :cascade do |t| + t.integer "diff_note_id", null: false + t.text "diff", null: false + t.boolean "new_file", null: false + t.boolean "renamed_file", null: false + t.boolean "deleted_file", null: false + t.string "a_mode", null: false + t.string "b_mode", null: false + t.text "new_path", null: false + t.text "old_path", null: false + end + + add_index "note_diff_files", ["diff_note_id"], name: "index_note_diff_files_on_diff_note_id", unique: true, using: :btree + create_table "notes", force: :cascade do |t| t.text "note" t.string "noteable_type" @@ -2243,6 +2257,7 @@ ActiveRecord::Schema.define(version: 20180521171529) do add_foreign_key "merge_requests_closing_issues", "merge_requests", on_delete: :cascade add_foreign_key "milestones", "namespaces", column: "group_id", name: "fk_95650a40d4", on_delete: :cascade add_foreign_key "milestones", "projects", name: "fk_9bd0a0c791", on_delete: :cascade + add_foreign_key "note_diff_files", "notes", column: "diff_note_id", on_delete: :cascade add_foreign_key "notes", "projects", name: "fk_99e097b079", on_delete: :cascade add_foreign_key "oauth_openid_requests", "oauth_access_grants", column: "access_grant_id", name: "fk_oauth_openid_requests_oauth_access_grants_access_grant_id" add_foreign_key "pages_domains", "projects", name: "fk_ea2f6dfc6f", on_delete: :cascade diff --git a/doc/administration/high_availability/database.md b/doc/administration/high_availability/database.md index ca6d8d2de67..b5124b1d540 100644 --- a/doc/administration/high_availability/database.md +++ b/doc/administration/high_availability/database.md @@ -33,16 +33,7 @@ If you use a cloud-managed service, or provide your own PostgreSQL: external_url 'https://gitlab.example.com' # Disable all components except PostgreSQL - postgresql['enable'] = true - bootstrap['enable'] = false - nginx['enable'] = false - unicorn['enable'] = false - sidekiq['enable'] = false - redis['enable'] = false - prometheus['enable'] = false - gitaly['enable'] = false - gitlab_workhorse['enable'] = false - mailroom['enable'] = false + roles ['postgres_role'] # PostgreSQL configuration gitlab_rails['db_password'] = 'DB password' diff --git a/doc/administration/high_availability/gitlab.md b/doc/administration/high_availability/gitlab.md index e201848791c..b0348d1db10 100644 --- a/doc/administration/high_availability/gitlab.md +++ b/doc/administration/high_availability/gitlab.md @@ -75,25 +75,24 @@ for each GitLab application server in your environment. servers should point to the external url that users will use to access GitLab. In a typical HA setup, this will be the url of the load balancer which will route traffic to all GitLab application servers in the HA cluster. - -1. Run `sudo gitlab-ctl reconfigure` to compile the configuration. + + > **Note:** When you specify `https` in the `external_url`, as in the example + above, GitLab assumes you have SSL certificates in `/etc/gitlab/ssl/`. If + certificates are not present, Nginx will fail to start. See + [Nginx documentation](http://docs.gitlab.com/omnibus/settings/nginx.html#enable-https) + for more information. ## First GitLab application server -As a final step, run the setup rake task on the first GitLab application server. -It is not necessary to run this on additional application servers. +As a final step, run the setup rake task **only on** the first GitLab application server. +Do not run this on additional application servers. 1. Initialize the database by running `sudo gitlab-rake gitlab:setup`. +1. Run `sudo gitlab-ctl reconfigure` to compile the configuration. > **WARNING:** Only run this setup task on **NEW** GitLab instances because it will wipe any existing data. -> **Note:** When you specify `https` in the `external_url`, as in the example - above, GitLab assumes you have SSL certificates in `/etc/gitlab/ssl/`. If - certificates are not present, Nginx will fail to start. See - [Nginx documentation](http://docs.gitlab.com/omnibus/settings/nginx.html#enable-https) - for more information. - ## Extra configuration for additional GitLab application servers Additional GitLab servers (servers configured **after** the first GitLab server) @@ -101,8 +100,7 @@ need some extra configuration. 1. Configure shared secrets. These values can be obtained from the primary GitLab server in `/etc/gitlab/gitlab-secrets.json`. Add these to - `/etc/gitlab/gitlab.rb` **prior to** running the first `reconfigure` in - the steps above. + `/etc/gitlab/gitlab.rb` **prior to** running the first `reconfigure`. ```ruby gitlab_shell['secret_token'] = 'fbfb19c355066a9afb030992231c4a363357f77345edd0f2e772359e5be59b02538e1fa6cae8f93f7d23355341cea2b93600dab6d6c3edcdced558fc6d739860' @@ -115,6 +113,8 @@ need some extra configuration. from running on upgrade. Only the primary GitLab application server should handle migrations. +1. Run `sudo gitlab-ctl reconfigure` to compile the configuration. + ## Troubleshooting - `mount: wrong fs type, bad option, bad superblock on` diff --git a/lib/api/helpers/internal_helpers.rb b/lib/api/helpers/internal_helpers.rb index abe3d353984..83151be82ad 100644 --- a/lib/api/helpers/internal_helpers.rb +++ b/lib/api/helpers/internal_helpers.rb @@ -89,12 +89,6 @@ module API end end - # Return the repository full path so that gitlab-shell has it when - # handling ssh commands - def repository_path - repository.path_to_repo - end - # Return the Gitaly Address if it is enabled def gitaly_payload(action) return unless %w[git-receive-pack git-upload-pack git-upload-archive].include?(action) diff --git a/lib/api/internal.rb b/lib/api/internal.rb index a3dac36b8b6..a9803be9f69 100644 --- a/lib/api/internal.rb +++ b/lib/api/internal.rb @@ -59,7 +59,11 @@ module API status: true, gl_repository: gl_repository, gl_username: user&.username, - repository_path: repository_path, + + # This repository_path is a bogus value but gitlab-shell still requires + # its presence. https://gitlab.com/gitlab-org/gitlab-shell/issues/135 + repository_path: '/', + gitaly: gitaly_payload(params[:action]) } end diff --git a/lib/api/runner.rb b/lib/api/runner.rb index a7f1cb1131f..5b7ae89440c 100644 --- a/lib/api/runner.rb +++ b/lib/api/runner.rb @@ -123,6 +123,7 @@ module API end put '/:id' do job = authenticate_job! + forbidden!('Job is not running') unless job.running? job.trace.set(params[:trace]) if params[:trace] @@ -131,9 +132,9 @@ module API case params[:state].to_s when 'success' - job.success + job.success! when 'failed' - job.drop(params[:failure_reason] || :unknown_failure) + job.drop!(params[:failure_reason] || :unknown_failure) end end diff --git a/lib/backup/repository.rb b/lib/backup/repository.rb index c3360c391af..84670d6582e 100644 --- a/lib/backup/repository.rb +++ b/lib/backup/repository.rb @@ -73,29 +73,40 @@ module Backup end def prepare_directories - # TODO: Need to find a way to do this for gitaly - # Gitaly discussion issue: https://gitlab.com/gitlab-org/gitaly/issues/1194 - Gitlab.config.repositories.storages.each do |name, repository_storage| - path = repository_storage.legacy_disk_path - next unless File.exist?(path) - - # Move all files in the existing repos directory except . and .. to - # repositories.old.<timestamp> directory - bk_repos_path = File.join(Gitlab.config.backup.path, "tmp", "#{name}-repositories.old." + Time.now.to_i.to_s) - FileUtils.mkdir_p(bk_repos_path, mode: 0700) - files = Dir.glob(File.join(path, "*"), File::FNM_DOTMATCH) - [File.join(path, "."), File.join(path, "..")] - - begin - FileUtils.mv(files, bk_repos_path) - rescue Errno::EACCES - access_denied_error(path) - rescue Errno::EBUSY - resource_busy_error(path) + delete_all_repositories(name, repository_storage) + end + end + + def delete_all_repositories(name, repository_storage) + gitaly_migrate(:delete_all_repositories) do |is_enabled| + if is_enabled + Gitlab::GitalyClient::StorageService.new(name).delete_all_repositories + else + local_delete_all_repositories(name, repository_storage) end end end + def local_delete_all_repositories(name, repository_storage) + path = repository_storage.legacy_disk_path + return unless File.exist?(path) + + # Move all files in the existing repos directory except . and .. to + # repositories.old.<timestamp> directory + bk_repos_path = File.join(Gitlab.config.backup.path, "tmp", "#{name}-repositories.old." + Time.now.to_i.to_s) + FileUtils.mkdir_p(bk_repos_path, mode: 0700) + files = Dir.glob(File.join(path, "*"), File::FNM_DOTMATCH) - [File.join(path, "."), File.join(path, "..")] + + begin + FileUtils.mv(files, bk_repos_path) + rescue Errno::EACCES + access_denied_error(path) + rescue Errno::EBUSY + resource_busy_error(path) + end + end + def restore_custom_hooks(project) # TODO: Need to find a way to do this for gitaly # Gitaly migration issue: https://gitlab.com/gitlab-org/gitaly/issues/1195 @@ -113,6 +124,7 @@ module Backup def restore prepare_directories gitlab_shell = Gitlab::Shell.new + Project.find_each(batch_size: 1000) do |project| progress.print " * #{project.full_path} ... " path_to_project_bundle = path_to_bundle(project) @@ -121,7 +133,6 @@ module Backup restore_repo_success = nil if File.exist?(path_to_project_bundle) begin - gitlab_shell.remove_repository(project.repository_storage, project.disk_path) if project.repository_exists? project.repository.create_from_bundle path_to_project_bundle restore_repo_success = true rescue => e @@ -146,7 +157,6 @@ module Backup if File.exist?(path_to_wiki_bundle) progress.print " * #{wiki.full_path} ... " begin - gitlab_shell.remove_repository(wiki.repository_storage, wiki.disk_path) if wiki.repository_exists? wiki.repository.create_from_bundle(path_to_wiki_bundle) progress.puts "[DONE]".color(:green) rescue => e @@ -224,5 +234,11 @@ module Backup def display_repo_path(project) project.hashed_storage?(:repository) ? "#{project.full_path} (#{project.disk_path})" : project.full_path end + + def gitaly_migrate(method, status: Gitlab::GitalyClient::MigrationStatus::OPT_IN, &block) + Gitlab::GitalyClient.migrate(method, status: status, &block) + rescue GRPC::NotFound, GRPC::BadStatus => e + raise Error, e + end end end diff --git a/lib/gitlab/auth/user_access_denied_reason.rb b/lib/gitlab/auth/user_access_denied_reason.rb index af310aa12fc..1893cb001b2 100644 --- a/lib/gitlab/auth/user_access_denied_reason.rb +++ b/lib/gitlab/auth/user_access_denied_reason.rb @@ -8,12 +8,12 @@ module Gitlab def rejection_message case rejection_type when :internal - 'This action cannot be performed by internal users' + "This action cannot be performed by internal users" when :terms_not_accepted - 'You must accept the Terms of Service in order to perform this action. '\ - 'Please access GitLab from a web browser to accept these terms.' + "You (#{@user.to_reference}) must accept the Terms of Service in order to perform this action. "\ + "Please access GitLab from a web browser to accept these terms." else - 'Your account has been blocked.' + "Your account has been blocked." end end diff --git a/lib/gitlab/database/count.rb b/lib/gitlab/database/count.rb index 3374203960e..5f549ed2b3c 100644 --- a/lib/gitlab/database/count.rb +++ b/lib/gitlab/database/count.rb @@ -17,31 +17,69 @@ module Gitlab ].freeze end - def self.approximate_count(model) - return model.count unless Gitlab::Database.postgresql? + # Takes in an array of models and returns a Hash for the approximate + # counts for them. If the model's table has not been vacuumed or + # analyzed recently, simply run the Model.count to get the data. + # + # @param [Array] + # @return [Hash] of Model -> count mapping + def self.approximate_counts(models) + table_to_model_map = models.each_with_object({}) do |model, hash| + hash[model.table_name] = model + end - execute_estimate_if_updated_recently(model) || model.count - end + table_names = table_to_model_map.keys + counts_by_table_name = Gitlab::Database.postgresql? ? reltuples_from_recently_updated(table_names) : {} - def self.execute_estimate_if_updated_recently(model) - ActiveRecord::Base.connection.select_value(postgresql_estimate_query(model)).to_i if reltuples_updated_recently?(model) - rescue *CONNECTION_ERRORS + # Convert table -> count to Model -> count + counts_by_model = counts_by_table_name.each_with_object({}) do |pair, hash| + model = table_to_model_map[pair.first] + hash[model] = pair.second + end + + missing_tables = table_names - counts_by_table_name.keys + + missing_tables.each do |table| + model = table_to_model_map[table] + counts_by_model[model] = model.count + end + + counts_by_model end - def self.reltuples_updated_recently?(model) - time = "to_timestamp(#{1.hour.ago.to_i})" - query = <<~SQL - SELECT 1 FROM pg_stat_user_tables WHERE relname = '#{model.table_name}' AND - (last_vacuum > #{time} OR last_autovacuum > #{time} OR last_analyze > #{time} OR last_autoanalyze > #{time}) - SQL + # Returns a hash of the table names that have recently updated tuples. + # + # @param [Array] table names + # @returns [Hash] Table name to count mapping (e.g. { 'projects' => 5, 'users' => 100 }) + def self.reltuples_from_recently_updated(table_names) + query = postgresql_estimate_query(table_names) + rows = [] - ActiveRecord::Base.connection.select_all(query).count > 0 + # Querying tuple stats only works on the primary. Due to load + # balancing, we need to ensure this query hits the load balancer. The + # easiest way to do this is to start a transaction. + ActiveRecord::Base.transaction do + rows = ActiveRecord::Base.connection.select_all(query) + end + + rows.each_with_object({}) { |row, data| data[row['table_name']] = row['estimate'].to_i } rescue *CONNECTION_ERRORS - false + {} end - def self.postgresql_estimate_query(model) - "SELECT reltuples::bigint AS estimate FROM pg_class where relname = '#{model.table_name}'" + # Generates the PostgreSQL query to return the tuples for tables + # that have been vacuumed or analyzed in the last hour. + # + # @param [Array] table names + # @returns [Hash] Table name to count mapping (e.g. { 'projects' => 5, 'users' => 100 }) + def self.postgresql_estimate_query(table_names) + time = "to_timestamp(#{1.hour.ago.to_i})" + <<~SQL + SELECT pg_class.relname AS table_name, reltuples::bigint AS estimate FROM pg_class + LEFT JOIN pg_stat_user_tables ON pg_class.relname = pg_stat_user_tables.relname + WHERE pg_class.relname IN (#{table_names.map { |table| "'#{table}'" }.join(',')}) + AND (last_vacuum > #{time} OR last_autovacuum > #{time} OR last_analyze > #{time} OR last_autoanalyze > #{time}) + SQL end end end diff --git a/lib/gitlab/diff/file.rb b/lib/gitlab/diff/file.rb index 014854da55c..765fb0289a8 100644 --- a/lib/gitlab/diff/file.rb +++ b/lib/gitlab/diff/file.rb @@ -76,6 +76,13 @@ module Gitlab line_code(line) if line end + # Returns the raw diff content up to the given line index + def diff_hunk(diff_line) + # Adding 2 because of the @@ diff header and Enum#take should consider + # an extra line, because we're passing an index. + raw_diff.each_line.take(diff_line.index + 2).join + end + def old_sha diff_refs&.base_sha end diff --git a/lib/gitlab/gitaly_client/storage_service.rb b/lib/gitlab/gitaly_client/storage_service.rb new file mode 100644 index 00000000000..eb0e910665b --- /dev/null +++ b/lib/gitlab/gitaly_client/storage_service.rb @@ -0,0 +1,15 @@ +module Gitlab + module GitalyClient + class StorageService + def initialize(storage) + @storage = storage + end + + # Delete all repositories in the storage. This is a slow and VERY DESTRUCTIVE operation. + def delete_all_repositories + request = Gitaly::DeleteAllRepositoriesRequest.new(storage_name: @storage) + GitalyClient.call(@storage, :storage_service, :delete_all_repositories, request) + end + end + end +end diff --git a/spec/lib/backup/repository_spec.rb b/spec/lib/backup/repository_spec.rb index a44243ac82d..023bedaaebb 100644 --- a/spec/lib/backup/repository_spec.rb +++ b/spec/lib/backup/repository_spec.rb @@ -71,6 +71,40 @@ describe Backup::Repository do end end + describe '#delete_all_repositories', :seed_helper do + shared_examples('delete_all_repositories') do + before do + allow(FileUtils).to receive(:mkdir_p).and_call_original + allow(FileUtils).to receive(:mv).and_call_original + end + + after(:all) do + ensure_seeds + end + + it 'removes all repositories' do + # Sanity check: there should be something for us to delete + expect(list_repositories).to include(File.join(SEED_STORAGE_PATH, TEST_REPO_PATH)) + + subject.delete_all_repositories('default', Gitlab.config.repositories.storages['default']) + + expect(list_repositories).to be_empty + end + + def list_repositories + Dir[SEED_STORAGE_PATH + '/*.git'] + end + end + + context 'with gitaly' do + it_behaves_like 'delete_all_repositories' + end + + context 'without gitaly', :skip_gitaly_mock do + it_behaves_like 'delete_all_repositories' + end + end + describe '#empty_repo?' do context 'for a wiki' do let(:wiki) { create(:project_wiki) } diff --git a/spec/lib/gitlab/auth/user_access_denied_reason_spec.rb b/spec/lib/gitlab/auth/user_access_denied_reason_spec.rb index fa209bed74e..002ce776be9 100644 --- a/spec/lib/gitlab/auth/user_access_denied_reason_spec.rb +++ b/spec/lib/gitlab/auth/user_access_denied_reason_spec.rb @@ -22,7 +22,8 @@ describe Gitlab::Auth::UserAccessDeniedReason do enforce_terms end - it { is_expected.to match /You must accept the Terms of Service/ } + it { is_expected.to match /must accept the Terms of Service/ } + it { is_expected.to include(user.username) } end context 'when the user is internal' do diff --git a/spec/lib/gitlab/database/count_spec.rb b/spec/lib/gitlab/database/count_spec.rb index 9d9caaabe16..407d9470785 100644 --- a/spec/lib/gitlab/database/count_spec.rb +++ b/spec/lib/gitlab/database/count_spec.rb @@ -3,59 +3,68 @@ require 'spec_helper' describe Gitlab::Database::Count do before do create_list(:project, 3) + create(:identity) end - describe '.execute_estimate_if_updated_recently', :postgresql do - context 'when reltuples have not been updated' do - before do - expect(described_class).to receive(:reltuples_updated_recently?).and_return(false) - end + let(:models) { [Project, Identity] } - it 'returns nil' do - expect(described_class.execute_estimate_if_updated_recently(Project)).to be nil - end - end + describe '.approximate_counts' do + context 'with MySQL' do + context 'when reltuples have not been updated' do + it 'counts all models the normal way' do + expect(Gitlab::Database).to receive(:postgresql?).and_return(false) - context 'when reltuples have been updated' do - before do - ActiveRecord::Base.connection.execute('ANALYZE projects') - end + expect(Project).to receive(:count).and_call_original + expect(Identity).to receive(:count).and_call_original - it 'calls postgresql_estimate_query' do - expect(described_class).to receive(:postgresql_estimate_query).with(Project).and_call_original - expect(described_class.execute_estimate_if_updated_recently(Project)).to eq(3) + expect(described_class.approximate_counts(models)).to eq({ Project => 3, Identity => 1 }) + end end end - end - describe '.approximate_count' do - context 'when reltuples have not been updated' do - it 'counts all projects the normal way' do - allow(described_class).to receive(:reltuples_updated_recently?).and_return(false) + context 'with PostgreSQL', :postgresql do + describe 'when reltuples have not been updated' do + it 'counts all models the normal way' do + expect(described_class).to receive(:reltuples_from_recently_updated).with(%w(projects identities)).and_return({}) - expect(Project).to receive(:count).and_call_original - expect(described_class.approximate_count(Project)).to eq(3) + expect(Project).to receive(:count).and_call_original + expect(Identity).to receive(:count).and_call_original + expect(described_class.approximate_counts(models)).to eq({ Project => 3, Identity => 1 }) + end end - end - context 'no permission' do - it 'falls back to standard query' do - allow(described_class).to receive(:reltuples_updated_recently?).and_raise(PG::InsufficientPrivilege) + describe 'no permission' do + it 'falls back to standard query' do + allow(described_class).to receive(:postgresql_estimate_query).and_raise(PG::InsufficientPrivilege) - expect(Project).to receive(:count).and_call_original - expect(described_class.approximate_count(Project)).to eq(3) + expect(Project).to receive(:count).and_call_original + expect(Identity).to receive(:count).and_call_original + expect(described_class.approximate_counts(models)).to eq({ Project => 3, Identity => 1 }) + end end - end - describe 'when reltuples have been updated', :postgresql do - before do - ActiveRecord::Base.connection.execute('ANALYZE projects') + describe 'when some reltuples have been updated' do + it 'counts projects in the fast way' do + expect(described_class).to receive(:reltuples_from_recently_updated).with(%w(projects identities)).and_return({ 'projects' => 3 }) + + expect(Project).not_to receive(:count).and_call_original + expect(Identity).to receive(:count).and_call_original + expect(described_class.approximate_counts(models)).to eq({ Project => 3, Identity => 1 }) + end end - it 'counts all projects in the fast way' do - expect(described_class).to receive(:postgresql_estimate_query).with(Project).and_call_original + describe 'when all reltuples have been updated' do + before do + ActiveRecord::Base.connection.execute('ANALYZE projects') + ActiveRecord::Base.connection.execute('ANALYZE identities') + end + + it 'counts models with the standard way' do + expect(Project).not_to receive(:count) + expect(Identity).not_to receive(:count) - expect(described_class.approximate_count(Project)).to eq(3) + expect(described_class.approximate_counts(models)).to eq({ Project => 3, Identity => 1 }) + end end end end diff --git a/spec/lib/gitlab/diff/file_spec.rb b/spec/lib/gitlab/diff/file_spec.rb index 0c2e18c268a..0588fe935c3 100644 --- a/spec/lib/gitlab/diff/file_spec.rb +++ b/spec/lib/gitlab/diff/file_spec.rb @@ -468,4 +468,58 @@ describe Gitlab::Diff::File do end end end + + describe '#diff_hunk' do + let(:raw_diff) do + <<EOS +@@ -6,12 +6,18 @@ module Popen + + def popen(cmd, path=nil) + unless cmd.is_a?(Array) +- raise "System commands must be given as an array of strings" ++ raise RuntimeError, "System commands must be given as an array of strings" + end + + path ||= Dir.pwd +- vars = { "PWD" => path } +- options = { chdir: path } ++ ++ vars = { ++ "PWD" => path ++ } ++ ++ options = { ++ chdir: path ++ } + + unless File.directory?(path) + FileUtils.mkdir_p(path) +@@ -19,6 +25,7 @@ module Popen + + @cmd_output = "" + @cmd_status = 0 ++ + Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr| + @cmd_output << stdout.read + @cmd_output << stderr.read +EOS + end + + it 'returns raw diff up to given line index' do + allow(diff_file).to receive(:raw_diff) { raw_diff } + diff_line = instance_double(Gitlab::Diff::Line, index: 5) + + diff_hunk = <<EOS +@@ -6,12 +6,18 @@ module Popen + + def popen(cmd, path=nil) + unless cmd.is_a?(Array) +- raise "System commands must be given as an array of strings" ++ raise RuntimeError, "System commands must be given as an array of strings" + end +EOS + + expect(diff_file.diff_hunk(diff_line)).to eq(diff_hunk) + end + end end diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb index 317a932d5a6..dfffea7797f 100644 --- a/spec/lib/gitlab/git_access_spec.rb +++ b/spec/lib/gitlab/git_access_spec.rb @@ -1055,7 +1055,7 @@ describe Gitlab::GitAccess do it 'blocks access when the user did not accept terms', :aggregate_failures do actions.each do |action| - expect { action.call }.to raise_unauthorized(/You must accept the Terms of Service in order to perform this action/) + expect { action.call }.to raise_unauthorized(/must accept the Terms of Service in order to perform this action/) end end diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml index 8b46b04b8b5..fb5fd300dbb 100644 --- a/spec/lib/gitlab/import_export/all_models.yml +++ b/spec/lib/gitlab/import_export/all_models.yml @@ -35,6 +35,7 @@ notes: - todos - events - system_note_metadata +- note_diff_file label_links: - target - label diff --git a/spec/models/concerns/discussion_on_diff_spec.rb b/spec/models/concerns/discussion_on_diff_spec.rb index 30572ce9332..8cd129dc851 100644 --- a/spec/models/concerns/discussion_on_diff_spec.rb +++ b/spec/models/concerns/discussion_on_diff_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe DiscussionOnDiff do - subject { create(:diff_note_on_merge_request).to_discussion } + subject { create(:diff_note_on_merge_request, line_number: 18).to_discussion } describe "#truncated_diff_lines" do let(:truncated_lines) { subject.truncated_diff_lines } diff --git a/spec/models/diff_note_spec.rb b/spec/models/diff_note_spec.rb index fb51c0172ab..8624f0daa4d 100644 --- a/spec/models/diff_note_spec.rb +++ b/spec/models/diff_note_spec.rb @@ -84,7 +84,47 @@ describe DiffNote do end end - describe "#diff_file" do + describe '#create_diff_file callback' do + let(:noteable) { create(:merge_request) } + let(:project) { noteable.project } + + context 'merge request' do + let!(:diff_note) { create(:diff_note_on_merge_request, project: project, noteable: noteable) } + + it 'creates a diff note file' do + expect(diff_note.reload.note_diff_file).to be_present + end + + it 'does not create diff note file if it is a reply' do + expect { create(:diff_note_on_merge_request, noteable: noteable, in_reply_to: diff_note) } + .not_to change(NoteDiffFile, :count) + end + end + + context 'commit' do + let!(:diff_note) { create(:diff_note_on_commit, project: project) } + + it 'creates a diff note file' do + expect(diff_note.reload.note_diff_file).to be_present + end + + it 'does not create diff note file if it is a reply' do + expect { create(:diff_note_on_commit, in_reply_to: diff_note) } + .not_to change(NoteDiffFile, :count) + end + end + end + + describe '#diff_file', :clean_gitlab_redis_shared_state do + context 'when note_diff_file association exists' do + it 'returns persisted diff file data' do + diff_file = subject.diff_file + + expect(diff_file.diff.to_hash.with_indifferent_access) + .to include(subject.note_diff_file.to_hash) + end + end + context 'when the discussion was created in the diff' do it 'returns correct diff file' do diff_file = subject.diff_file @@ -115,6 +155,26 @@ describe DiffNote do expect(diff_file.diff_refs).to eq(position.diff_refs) end end + + context 'note diff file creation enqueuing' do + it 'enqueues CreateNoteDiffFileWorker if it is the first note of a discussion' do + subject.note_diff_file.destroy! + + expect(CreateNoteDiffFileWorker).to receive(:perform_async).with(subject.id) + + subject.reload.diff_file + end + + it 'does not enqueues CreateNoteDiffFileWorker if not first note of a discussion' do + mr = create(:merge_request) + diff_note = create(:diff_note_on_merge_request, project: mr.project, noteable: mr) + reply_diff_note = create(:diff_note_on_merge_request, in_reply_to: diff_note) + + expect(CreateNoteDiffFileWorker).not_to receive(:perform_async).with(reply_diff_note.id) + + reply_diff_note.reload.diff_file + end + end end describe "#diff_line" do diff --git a/spec/models/note_diff_file_spec.rb b/spec/models/note_diff_file_spec.rb new file mode 100644 index 00000000000..591c1a89748 --- /dev/null +++ b/spec/models/note_diff_file_spec.rb @@ -0,0 +1,11 @@ +require 'rails_helper' + +describe NoteDiffFile do + describe 'associations' do + it { is_expected.to belong_to(:diff_note) } + end + + describe 'validations' do + it { is_expected.to validate_presence_of(:diff_note) } + end +end diff --git a/spec/requests/api/helpers_spec.rb b/spec/requests/api/helpers_spec.rb index d3ab44c0d7e..d8a51f36dba 100644 --- a/spec/requests/api/helpers_spec.rb +++ b/spec/requests/api/helpers_spec.rb @@ -171,7 +171,7 @@ describe API::Helpers do end it 'returns a 403 when a user has not accepted the terms' do - expect { current_user }.to raise_error /You must accept the Terms of Service/ + expect { current_user }.to raise_error /must accept the Terms of Service/ end it 'sets the current user when the user accepted the terms' do diff --git a/spec/requests/api/internal_spec.rb b/spec/requests/api/internal_spec.rb index db8c5f963d6..5dc3ddd4b36 100644 --- a/spec/requests/api/internal_spec.rb +++ b/spec/requests/api/internal_spec.rb @@ -1,9 +1,9 @@ require 'spec_helper' describe API::Internal do - let(:user) { create(:user) } + set(:user) { create(:user) } let(:key) { create(:key, user: user) } - let(:project) { create(:project, :repository, :wiki_repo) } + set(:project) { create(:project, :repository, :wiki_repo) } let(:secret_token) { Gitlab::Shell.secret_token } let(:gl_repository) { "project-#{project.id}" } let(:reference_counter) { double('ReferenceCounter') } @@ -277,7 +277,7 @@ describe API::Internal do expect(response).to have_gitlab_http_status(200) expect(json_response["status"]).to be_truthy - expect(json_response["repository_path"]).to eq(project.wiki.repository.path_to_repo) + expect(json_response["repository_path"]).to eq('/') expect(json_response["gl_repository"]).to eq("wiki-#{project.id}") expect(user).not_to have_an_activity_record end @@ -289,7 +289,7 @@ describe API::Internal do expect(response).to have_gitlab_http_status(200) expect(json_response["status"]).to be_truthy - expect(json_response["repository_path"]).to eq(project.wiki.repository.path_to_repo) + expect(json_response["repository_path"]).to eq('/') expect(json_response["gl_repository"]).to eq("wiki-#{project.id}") expect(user).to have_an_activity_record end @@ -301,7 +301,7 @@ describe API::Internal do expect(response).to have_gitlab_http_status(200) expect(json_response["status"]).to be_truthy - expect(json_response["repository_path"]).to eq(project.repository.path_to_repo) + expect(json_response["repository_path"]).to eq('/') expect(json_response["gl_repository"]).to eq("project-#{project.id}") expect(json_response["gitaly"]).not_to be_nil expect(json_response["gitaly"]["repository"]).not_to be_nil @@ -320,7 +320,7 @@ describe API::Internal do expect(response).to have_gitlab_http_status(200) expect(json_response["status"]).to be_truthy - expect(json_response["repository_path"]).to eq(project.repository.path_to_repo) + expect(json_response["repository_path"]).to eq('/') expect(json_response["gl_repository"]).to eq("project-#{project.id}") expect(json_response["gitaly"]).not_to be_nil expect(json_response["gitaly"]["repository"]).not_to be_nil diff --git a/spec/requests/api/runner_spec.rb b/spec/requests/api/runner_spec.rb index efb9bddde44..827c6dd4af1 100644 --- a/spec/requests/api/runner_spec.rb +++ b/spec/requests/api/runner_spec.rb @@ -830,6 +830,21 @@ describe API::Runner, :clean_gitlab_redis_shared_state do end end + context 'when job has already been finished' do + before do + job.trace.set('Job failed') + job.drop!(:script_failure) + end + + it 'does not update job status and job trace' do + update_job(state: 'success', trace: 'BUILD TRACE UPDATED') + + expect(response).to have_gitlab_http_status(403) + expect(job.trace.raw).to eq 'Job failed' + expect(job).to be_failed + end + end + def update_job(token = job.token, **params) new_params = params.merge(token: token) put api("/jobs/#{job.id}"), new_params diff --git a/spec/services/notes/create_service_spec.rb b/spec/services/notes/create_service_spec.rb index f5cff66de6d..2b2b983494f 100644 --- a/spec/services/notes/create_service_spec.rb +++ b/spec/services/notes/create_service_spec.rb @@ -57,6 +57,88 @@ describe Notes::CreateService do end end + context 'note diff file' do + let(:project_with_repo) { create(:project, :repository) } + let(:merge_request) do + create(:merge_request, + source_project: project_with_repo, + target_project: project_with_repo) + end + let(:line_number) { 14 } + let(:position) do + Gitlab::Diff::Position.new(old_path: "files/ruby/popen.rb", + new_path: "files/ruby/popen.rb", + old_line: nil, + new_line: line_number, + diff_refs: merge_request.diff_refs) + end + let(:previous_note) do + create(:diff_note_on_merge_request, noteable: merge_request, project: project_with_repo) + end + + context 'when eligible to have a note diff file' do + let(:new_opts) do + opts.merge(in_reply_to_discussion_id: nil, + type: 'DiffNote', + noteable_type: 'MergeRequest', + noteable_id: merge_request.id, + position: position.to_h) + end + + it 'note is associated with a note diff file' do + note = described_class.new(project_with_repo, user, new_opts).execute + + expect(note).to be_persisted + expect(note.note_diff_file).to be_present + end + end + + context 'when DiffNote is a reply' do + let(:new_opts) do + opts.merge(in_reply_to_discussion_id: previous_note.discussion_id, + type: 'DiffNote', + noteable_type: 'MergeRequest', + noteable_id: merge_request.id, + position: position.to_h) + end + + it 'note is not associated with a note diff file' do + note = described_class.new(project_with_repo, user, new_opts).execute + + expect(note).to be_persisted + expect(note.note_diff_file).to be_nil + end + + context 'when DiffNote from an image' do + let(:image_position) do + Gitlab::Diff::Position.new(old_path: "files/images/6049019_460s.jpg", + new_path: "files/images/6049019_460s.jpg", + width: 100, + height: 100, + x: 1, + y: 100, + diff_refs: merge_request.diff_refs, + position_type: 'image') + end + + let(:new_opts) do + opts.merge(in_reply_to_discussion_id: nil, + type: 'DiffNote', + noteable_type: 'MergeRequest', + noteable_id: merge_request.id, + position: image_position.to_h) + end + + it 'note is not associated with a note diff file' do + note = described_class.new(project_with_repo, user, new_opts).execute + + expect(note).to be_persisted + expect(note.note_diff_file).to be_nil + end + end + end + end + context 'note with commands' do context 'as a user who can update the target' do context '/close, /label, /assign & /milestone' do diff --git a/spec/views/admin/dashboard/index.html.haml_spec.rb b/spec/views/admin/dashboard/index.html.haml_spec.rb index 59c777ea338..0e8b7c82d3a 100644 --- a/spec/views/admin/dashboard/index.html.haml_spec.rb +++ b/spec/views/admin/dashboard/index.html.haml_spec.rb @@ -4,6 +4,11 @@ describe 'admin/dashboard/index.html.haml' do include Devise::Test::ControllerHelpers before do + counts = Admin::DashboardController::COUNTED_ITEMS.each_with_object({}) do |item, hash| + hash[item] = 100 + end + + assign(:counts, counts) assign(:projects, create_list(:project, 1)) assign(:users, create_list(:user, 1)) assign(:groups, create_list(:group, 1)) diff --git a/spec/workers/create_note_diff_file_worker_spec.rb b/spec/workers/create_note_diff_file_worker_spec.rb new file mode 100644 index 00000000000..0ac946a1232 --- /dev/null +++ b/spec/workers/create_note_diff_file_worker_spec.rb @@ -0,0 +1,16 @@ +require 'spec_helper' + +describe CreateNoteDiffFileWorker do + describe '#perform' do + let(:diff_note) { create(:diff_note_on_merge_request) } + + it 'creates diff file' do + diff_note.note_diff_file.destroy! + + expect_any_instance_of(DiffNote).to receive(:create_diff_file) + .and_call_original + + described_class.new.perform(diff_note.id) + end + end +end diff --git a/yarn.lock b/yarn.lock index af8785bbc66..4925fa8f373 100644 --- a/yarn.lock +++ b/yarn.lock @@ -385,14 +385,10 @@ ast-types@0.10.1: version "0.10.1" resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.10.1.tgz#f52fca9715579a14f841d67d7f8d25432ab6a3dd" -ast-types@0.11.3: +ast-types@0.11.3, ast-types@0.x.x: version "0.11.3" resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.11.3.tgz#c20757fe72ee71278ea0ff3d87e5c2ca30d9edf8" -ast-types@0.x.x: - version "0.11.1" - resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.11.1.tgz#5bb3a8d5ba292c3f4ae94d46df37afc30300b990" - async-each@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" @@ -405,18 +401,12 @@ async@1.x, async@^1.4.0, async@^1.5.0, async@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" -async@^2.0.0, async@^2.6.0: +async@^2.0.0, async@^2.1.4, async@^2.6.0: version "2.6.0" resolved "https://registry.yarnpkg.com/async/-/async-2.6.0.tgz#61a29abb6fcc026fea77e56d1c6ec53a795951f4" dependencies: lodash "^4.14.0" -async@^2.1.4: - version "2.4.1" - resolved "https://registry.yarnpkg.com/async/-/async-2.4.1.tgz#62a56b279c98a11d0987096a01cc3eeb8eb7bbd7" - dependencies: - lodash "^4.14.0" - async@~2.1.2: version "2.1.5" resolved "https://registry.yarnpkg.com/async/-/async-2.1.5.tgz#e587c68580994ac67fc56ff86d3ac56bdbe810bc" @@ -1581,7 +1571,7 @@ chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" -chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3.1, chalk@^2.4.1: +chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3.2, chalk@^2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" dependencies: @@ -1589,14 +1579,6 @@ chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3.1, chalk@^2.4 escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^2.3.2: - version "2.4.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.0.tgz#a060a297a6b57e15b61ca63ce84995daa0fe6e52" - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - chalk@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/chalk/-/chalk-0.4.0.tgz#5199a3ddcd0c1efe23bc08c1b027b06176e0c64f" @@ -1775,14 +1757,10 @@ clone-stats@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-1.0.0.tgz#b3782dff8bb5474e18b9b6bf0fdfe782f8777680" -clone@^1.0.0: +clone@^1.0.0, clone@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.3.tgz#298d7e2231660f40c003c2ed3140decf3f53085f" -clone@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.2.tgz#260b7a99ebb1edfe247538175f783243cb19d149" - clone@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.1.tgz#d217d1e961118e3ac9a4b8bba3285553bf647cdb" @@ -2572,11 +2550,7 @@ di@^0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/di/-/di-0.0.1.tgz#806649326ceaa7caa3306d75d985ea2748ba913c" -diff@^3.3.1, diff@^3.4.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/diff/-/diff-3.4.0.tgz#b1d85507daf3964828de54b37d0d73ba67dda56c" - -diff@^3.5.0: +diff@^3.3.1, diff@^3.4.0, diff@^3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" @@ -2716,11 +2690,7 @@ ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" -ejs@^2.5.7: - version "2.5.7" - resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.5.7.tgz#cc872c168880ae3c7189762fd5ffc00896c9518a" - -ejs@^2.5.9: +ejs@^2.5.7, ejs@^2.5.9: version "2.5.9" resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.5.9.tgz#7ba254582a560d267437109a68354112475b0ce5" @@ -3319,14 +3289,6 @@ extend@3, extend@^3.0.0, extend@~3.0.0, extend@~3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" -external-editor@^2.0.4: - version "2.1.0" - resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.1.0.tgz#3d026a21b7f95b5726387d4200ac160d372c3b48" - dependencies: - chardet "^0.4.0" - iconv-lite "^0.4.17" - tmp "^0.0.33" - external-editor@^2.1.0: version "2.2.0" resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.2.0.tgz#045511cfd8d133f3846673d1047c154e214ad3d5" @@ -3779,18 +3741,7 @@ glob@^5.0.15: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.0.0, glob@^7.0.3: - version "7.1.1" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8" - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.2" - once "^1.3.0" - path-is-absolute "^1.0.0" - -glob@^7.0.5, glob@^7.1.1, glob@^7.1.2: +glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2: version "7.1.2" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" dependencies: @@ -3865,7 +3816,7 @@ globby@^7.1.1: pify "^3.0.0" slash "^1.0.0" -globby@^8.0.0: +globby@^8.0.0, globby@^8.0.1: version "8.0.1" resolved "https://registry.yarnpkg.com/globby/-/globby-8.0.1.tgz#b5ad48b8aa80b35b814fc1281ecc851f1d2b5b50" dependencies: @@ -4032,10 +3983,6 @@ has-flag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" -has-flag@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51" - has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" @@ -4404,26 +4351,7 @@ inquirer@^0.12.0: strip-ansi "^3.0.0" through "^2.3.6" -inquirer@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.3.0.tgz#9dd2f2ad765dcab1ff0443b491442a20ba227dc9" - dependencies: - ansi-escapes "^3.0.0" - chalk "^2.0.0" - cli-cursor "^2.1.0" - cli-width "^2.0.0" - external-editor "^2.0.4" - figures "^2.0.0" - lodash "^4.3.0" - mute-stream "0.0.7" - run-async "^2.2.0" - rx-lite "^4.0.8" - rx-lite-aggregates "^4.0.8" - string-width "^2.1.0" - strip-ansi "^4.0.0" - through "^2.3.6" - -inquirer@^5.1.0: +inquirer@^5.1.0, inquirer@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-5.2.0.tgz#db350c2b73daca77ff1243962e9f22f099685726" dependencies: @@ -4447,11 +4375,7 @@ internal-ip@1.2.0: dependencies: meow "^3.3.0" -interpret@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.1.tgz#d579fb7f693b858004947af39fa0db49f795602c" - -interpret@^1.0.4: +interpret@^1.0.0, interpret@^1.0.4: version "1.1.0" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.1.0.tgz#7ed1b1410c6a0e0f78cf95d3b8440c63f78b8614" @@ -4868,7 +4792,7 @@ istanbul-lib-hook@^1.1.0: dependencies: append-transform "^0.4.0" -istanbul-lib-instrument@^1.10.1: +istanbul-lib-instrument@^1.10.1, istanbul-lib-instrument@^1.9.1: version "1.10.1" resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-1.10.1.tgz#724b4b6caceba8692d3f1f9d0727e279c401af7b" dependencies: @@ -4880,18 +4804,6 @@ istanbul-lib-instrument@^1.10.1: istanbul-lib-coverage "^1.2.0" semver "^5.3.0" -istanbul-lib-instrument@^1.9.1: - version "1.9.1" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-1.9.1.tgz#250b30b3531e5d3251299fdd64b0b2c9db6b558e" - dependencies: - babel-generator "^6.18.0" - babel-template "^6.16.0" - babel-traverse "^6.18.0" - babel-types "^6.18.0" - babylon "^6.18.0" - istanbul-lib-coverage "^1.1.1" - semver "^5.3.0" - istanbul-lib-report@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-1.1.2.tgz#922be27c13b9511b979bd1587359f69798c1d425" @@ -5455,11 +5367,7 @@ lodash@4.17.4: version "4.17.4" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" -lodash@^4.0.0, lodash@^4.11.1, lodash@^4.13.1, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.2, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.0, lodash@^4.3.0, lodash@^4.5.0: - version "4.17.5" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.5.tgz#99a92d65c0272debe8c96b6057bc8fbfa3bed511" - -lodash@^4.17.10: +lodash@^4.0.0, lodash@^4.11.1, lodash@^4.13.1, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.2, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.0, lodash@^4.3.0, lodash@^4.5.0: version "4.17.10" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" @@ -5469,13 +5377,7 @@ log-symbols@^1.0.2: dependencies: chalk "^1.0.0" -log-symbols@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.1.0.tgz#f35fa60e278832b538dc4dddcbb478a45d3e3be6" - dependencies: - chalk "^2.0.1" - -log-symbols@^2.2.0: +log-symbols@^2.1.0, log-symbols@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" dependencies: @@ -5548,14 +5450,7 @@ lru-cache@2.2.x: version "2.2.4" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.2.4.tgz#6c658619becf14031d0d0b594b16042ce4dc063d" -lru-cache@^4.0.1, lru-cache@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.1.tgz#622e32e82488b49279114a4f9ecf45e7cd6bba55" - dependencies: - pseudomap "^1.0.2" - yallist "^2.1.2" - -lru-cache@^4.1.2: +lru-cache@^4.0.1, lru-cache@^4.1.1, lru-cache@^4.1.2: version "4.1.3" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.3.tgz#a1175cf3496dfc8436c156c334b4955992bce69c" dependencies: @@ -5591,13 +5486,7 @@ mailgun-js@^0.7.0: q "~1.4.0" tsscmp "~1.0.0" -make-dir@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.0.0.tgz#97a011751e91dd87cfadef58832ebb04936de978" - dependencies: - pify "^2.3.0" - -make-dir@^1.1.0: +make-dir@^1.0.0, make-dir@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.2.0.tgz#6d6a49eead4aae296c53bbf3a1a008bd6c89469b" dependencies: @@ -5736,7 +5625,7 @@ micromatch@^2.1.5, micromatch@^2.3.7: parse-glob "^3.0.4" regex-cache "^0.4.2" -micromatch@^3.1.10, micromatch@^3.1.9: +micromatch@^3.1.10, micromatch@^3.1.4, micromatch@^3.1.8, micromatch@^3.1.9: version "3.1.10" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" dependencies: @@ -5754,42 +5643,6 @@ micromatch@^3.1.10, micromatch@^3.1.9: snapdragon "^0.8.1" to-regex "^3.0.2" -micromatch@^3.1.4: - version "3.1.6" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.6.tgz#8d7c043b48156f408ca07a4715182b79b99420bf" - dependencies: - arr-diff "^4.0.0" - array-unique "^0.3.2" - braces "^2.3.1" - define-property "^2.0.2" - extend-shallow "^3.0.2" - extglob "^2.0.4" - fragment-cache "^0.2.1" - kind-of "^6.0.2" - nanomatch "^1.2.9" - object.pick "^1.3.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - -micromatch@^3.1.8: - version "3.1.9" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.9.tgz#15dc93175ae39e52e93087847096effc73efcf89" - dependencies: - arr-diff "^4.0.0" - array-unique "^0.3.2" - braces "^2.3.1" - define-property "^2.0.2" - extend-shallow "^3.0.2" - extglob "^2.0.4" - fragment-cache "^0.2.1" - kind-of "^6.0.2" - nanomatch "^1.2.9" - object.pick "^1.3.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - miller-rabin@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" @@ -5815,14 +5668,10 @@ mime@^1.3.4: version "1.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" -mime@^2.0.3: +mime@^2.0.3, mime@^2.1.0: version "2.3.1" resolved "https://registry.yarnpkg.com/mime/-/mime-2.3.1.tgz#b1621c54d63b97c47d3cfe7f7215f7d64517c369" -mime@^2.1.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/mime/-/mime-2.2.0.tgz#161e541965551d3b549fa1114391e3a3d55b923b" - mimic-fn@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.1.0.tgz#e667783d92e89dbd342818b5230b9d62a672ad18" @@ -6926,15 +6775,7 @@ postcss@^5.0.10, postcss@^5.0.11, postcss@^5.0.12, postcss@^5.0.13, postcss@^5.0 source-map "^0.5.6" supports-color "^3.2.3" -postcss@^6.0.1, postcss@^6.0.14: - version "6.0.19" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.19.tgz#76a78386f670b9d9494a655bf23ac012effd1555" - dependencies: - chalk "^2.3.1" - source-map "^0.6.1" - supports-color "^5.2.0" - -postcss@^6.0.20: +postcss@^6.0.1, postcss@^6.0.14, postcss@^6.0.20: version "6.0.22" resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.22.tgz#e23b78314905c3b90cbd61702121e7a78848f2a3" dependencies: @@ -6962,14 +6803,10 @@ prettier@1.11.1: version "1.11.1" resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.11.1.tgz#61e43fc4cd44e68f2b0dfc2c38cd4bb0fccdcc75" -prettier@^1.11.1: +prettier@^1.11.1, prettier@^1.5.3: version "1.12.1" resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.12.1.tgz#c1ad20e803e7749faf905a409d2367e06bbe7325" -prettier@^1.5.3: - version "1.10.2" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.10.2.tgz#1af8356d1842276a99a5b5529c82dd9e9ad3cc93" - pretty-bytes@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-4.0.2.tgz#b2bf82e7350d65c6c33aa95aaa5a4f6327f61cd9" @@ -7615,18 +7452,12 @@ right-align@^0.1.1: dependencies: align-text "^0.1.1" -rimraf@2, rimraf@^2.5.1, rimraf@^2.5.4, rimraf@^2.6.0, rimraf@^2.6.1, rimraf@^2.6.2: +rimraf@2, rimraf@^2.2.8, rimraf@^2.5.1, rimraf@^2.5.4, rimraf@^2.6.0, rimraf@^2.6.1, rimraf@^2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" dependencies: glob "^7.0.5" -rimraf@^2.2.8: - version "2.6.1" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.1.tgz#c2338ec643df7a1b7fe5c54fa86f57428a55f33d" - dependencies: - glob "^7.0.5" - rimraf@~2.2.6: version "2.2.8" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.2.8.tgz#e439be2aaee327321952730f99a8929e4fc50582" @@ -7656,16 +7487,6 @@ run-queue@^1.0.0, run-queue@^1.0.3: dependencies: aproba "^1.1.1" -rx-lite-aggregates@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz#753b87a89a11c95467c4ac1626c4efc4e05c67be" - dependencies: - rx-lite "*" - -rx-lite@*, rx-lite@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444" - rx-lite@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102" @@ -7737,11 +7558,7 @@ semver-diff@^2.0.0: dependencies: semver "^5.0.3" -"semver@2 || 3 || 4 || 5", semver@^5.0.3: - version "5.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" - -semver@^5.1.0, semver@^5.3.0, semver@^5.5.0: +"semver@2 || 3 || 4 || 5", semver@^5.0.3, semver@^5.1.0, semver@^5.3.0, semver@^5.5.0: version "5.5.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" @@ -8075,11 +7892,7 @@ source-map@^0.4.4: dependencies: amdefine ">=0.0.4" -source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.1: - version "0.5.6" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" - -source-map@^0.5.7, source-map@~0.5.3, source-map@~0.5.6: +source-map@^0.5.3, source-map@^0.5.6, source-map@^0.5.7, source-map@~0.5.1, source-map@~0.5.6: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" @@ -8346,19 +8159,7 @@ supports-color@^3.1.0, supports-color@^3.1.2, supports-color@^3.2.3: dependencies: has-flag "^1.0.0" -supports-color@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.1.0.tgz#058a021d1b619f7ddf3980d712ea3590ce7de3d5" - dependencies: - has-flag "^2.0.0" - -supports-color@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.2.0.tgz#b0d5333b1184dd3666cbe5aa0b45c5ac7ac17a4a" - dependencies: - has-flag "^3.0.0" - -supports-color@^5.3.0, supports-color@^5.4.0: +supports-color@^5.1.0, supports-color@^5.2.0, supports-color@^5.3.0, supports-color@^5.4.0: version "5.4.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54" dependencies: @@ -8555,15 +8356,7 @@ to-regex-range@^2.1.0: is-number "^3.0.0" repeat-string "^1.6.1" -to-regex@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.1.tgz#15358bee4a2c83bd76377ba1dc049d0f18837aae" - dependencies: - define-property "^0.2.5" - extend-shallow "^2.0.1" - regex-not "^1.0.0" - -to-regex@^3.0.2: +to-regex@^3.0.1, to-regex@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" dependencies: @@ -9149,7 +8942,7 @@ webpack-dev-server@^3.1.4: webpack-log "^1.1.2" yargs "11.0.0" -webpack-log@^1.0.1: +webpack-log@^1.0.1, webpack-log@^1.1.2: version "1.2.0" resolved "https://registry.yarnpkg.com/webpack-log/-/webpack-log-1.2.0.tgz#a4b34cda6b22b518dbb0ab32e567962d5c72a43d" dependencies: @@ -9158,23 +8951,7 @@ webpack-log@^1.0.1: loglevelnext "^1.0.1" uuid "^3.1.0" -webpack-log@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/webpack-log/-/webpack-log-1.1.2.tgz#cdc76016537eed24708dc6aa3d1e52189efee107" - dependencies: - chalk "^2.1.0" - log-symbols "^2.1.0" - loglevelnext "^1.0.1" - uuid "^3.1.0" - -webpack-sources@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.0.1.tgz#c7356436a4d13123be2e2426a05d1dad9cbe65cf" - dependencies: - source-list-map "^2.0.0" - source-map "~0.5.3" - -webpack-sources@^1.1.0: +webpack-sources@^1.0.1, webpack-sources@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.1.0.tgz#a101ebae59d6507354d71d8013950a3a8b7a5a54" dependencies: @@ -9415,39 +9192,23 @@ yeast@0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419" -yeoman-environment@^2.0.0: - version "2.0.5" - resolved "https://registry.yarnpkg.com/yeoman-environment/-/yeoman-environment-2.0.5.tgz#84f22bafa84088971fe99ea85f654a3a3dd2b693" - dependencies: - chalk "^2.1.0" - debug "^3.1.0" - diff "^3.3.1" - escape-string-regexp "^1.0.2" - globby "^6.1.0" - grouped-queue "^0.3.3" - inquirer "^3.3.0" - is-scoped "^1.0.0" - lodash "^4.17.4" - log-symbols "^2.1.0" - mem-fs "^1.1.0" - text-table "^0.2.0" - untildify "^3.0.2" - -yeoman-environment@^2.0.5: - version "2.0.6" - resolved "https://registry.yarnpkg.com/yeoman-environment/-/yeoman-environment-2.0.6.tgz#ae1b21d826b363f3d637f88a7fc9ea7414cb5377" +yeoman-environment@^2.0.0, yeoman-environment@^2.0.5: + version "2.1.1" + resolved "https://registry.yarnpkg.com/yeoman-environment/-/yeoman-environment-2.1.1.tgz#10a045f7fc4397873764882eae055a33e56ee1c5" dependencies: chalk "^2.1.0" + cross-spawn "^6.0.5" debug "^3.1.0" diff "^3.3.1" escape-string-regexp "^1.0.2" - globby "^6.1.0" + globby "^8.0.1" grouped-queue "^0.3.3" - inquirer "^3.3.0" + inquirer "^5.2.0" is-scoped "^1.0.0" - lodash "^4.17.4" + lodash "^4.17.10" log-symbols "^2.1.0" mem-fs "^1.1.0" + strip-ansi "^4.0.0" text-table "^0.2.0" untildify "^3.0.2" |