From 0ea3f7b052ea13063c894fcab75c9f373b9dbc23 Mon Sep 17 00:00:00 2001 From: Drew Blessing Date: Mon, 23 Apr 2018 12:10:58 -0500 Subject: Fix charlock --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index aed9f1d6b30..f52432d98f0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -120,7 +120,7 @@ GEM activesupport (>= 4.0.0) mime-types (>= 1.16) cause (0.1) - charlock_holmes (0.7.5) + charlock_holmes (0.7.6) childprocess (0.7.0) ffi (~> 1.0, >= 1.0.11) chronic (0.10.2) -- cgit v1.2.1 From 50abbd3e53d4f5e3e67543650a13aca9a54b37c2 Mon Sep 17 00:00:00 2001 From: gfyoung Date: Wed, 25 Jul 2018 02:30:33 -0700 Subject: Enable frozen string in app/models/*.rb Partially addresses #47424. --- app/controllers/admin/services_controller.rb | 4 +++- app/models/ability.rb | 2 ++ app/models/abuse_report.rb | 2 ++ app/models/active_session.rb | 2 ++ app/models/appearance.rb | 2 ++ app/models/application_setting.rb | 2 ++ app/models/audit_event.rb | 2 ++ app/models/award_emoji.rb | 2 ++ app/models/badge.rb | 2 ++ app/models/blob.rb | 2 ++ app/models/board.rb | 2 ++ app/models/broadcast_message.rb | 2 ++ app/models/chat_name.rb | 2 ++ app/models/chat_team.rb | 2 ++ app/models/commit.rb | 10 ++++++---- app/models/commit_range.rb | 2 ++ app/models/commit_status.rb | 2 ++ app/models/compare.rb | 2 ++ app/models/container_repository.rb | 2 ++ app/models/cycle_analytics.rb | 2 ++ app/models/dashboard_milestone.rb | 2 ++ app/models/deploy_key.rb | 2 ++ app/models/deploy_keys_project.rb | 2 ++ app/models/deploy_token.rb | 2 ++ app/models/deployment.rb | 2 ++ app/models/diff_discussion.rb | 2 ++ app/models/diff_note.rb | 2 ++ app/models/directly_addressed_user.rb | 2 ++ app/models/discussion.rb | 2 ++ app/models/discussion_note.rb | 2 ++ app/models/email.rb | 2 ++ app/models/environment.rb | 4 +++- app/models/epic.rb | 2 ++ app/models/event.rb | 2 ++ app/models/event_collection.rb | 2 ++ app/models/external_issue.rb | 2 ++ app/models/fork_network.rb | 2 ++ app/models/fork_network_member.rb | 2 ++ app/models/forked_project_link.rb | 2 ++ app/models/generic_commit_status.rb | 2 ++ app/models/global_label.rb | 2 ++ app/models/global_milestone.rb | 2 ++ app/models/gpg_key.rb | 2 ++ app/models/gpg_key_subkey.rb | 2 ++ app/models/gpg_signature.rb | 2 ++ app/models/group.rb | 2 ++ app/models/group_custom_attribute.rb | 2 ++ app/models/group_label.rb | 2 ++ app/models/group_milestone.rb | 2 ++ app/models/guest.rb | 2 ++ app/models/identity.rb | 2 ++ app/models/import_export_upload.rb | 2 ++ app/models/individual_note_discussion.rb | 2 ++ app/models/instance_configuration.rb | 2 ++ app/models/internal_id.rb | 2 ++ app/models/issue.rb | 2 ++ app/models/issue_assignee.rb | 2 ++ app/models/issue_collection.rb | 2 ++ app/models/key.rb | 2 ++ app/models/label.rb | 2 ++ app/models/label_link.rb | 2 ++ app/models/label_priority.rb | 2 ++ app/models/legacy_diff_discussion.rb | 2 ++ app/models/legacy_diff_note.rb | 2 ++ app/models/lfs_file_lock.rb | 2 ++ app/models/lfs_object.rb | 2 ++ app/models/lfs_objects_project.rb | 2 ++ app/models/list.rb | 2 ++ app/models/member.rb | 2 ++ app/models/merge_request.rb | 2 ++ app/models/merge_request_diff.rb | 2 ++ app/models/merge_request_diff_commit.rb | 2 ++ app/models/merge_request_diff_file.rb | 2 ++ app/models/merge_requests_closing_issues.rb | 2 ++ app/models/milestone.rb | 2 ++ app/models/namespace.rb | 2 ++ app/models/note.rb | 2 ++ app/models/note_diff_file.rb | 2 ++ app/models/notification_reason.rb | 2 ++ app/models/notification_recipient.rb | 2 ++ app/models/notification_setting.rb | 2 ++ app/models/oauth_access_grant.rb | 2 ++ app/models/oauth_access_token.rb | 2 ++ app/models/out_of_context_discussion.rb | 2 ++ app/models/pages_domain.rb | 2 ++ app/models/personal_access_token.rb | 2 ++ app/models/personal_snippet.rb | 2 ++ app/models/project.rb | 2 ++ app/models/project_authorization.rb | 2 ++ app/models/project_auto_devops.rb | 2 ++ app/models/project_ci_cd_setting.rb | 2 ++ app/models/project_custom_attribute.rb | 2 ++ app/models/project_deploy_token.rb | 2 ++ app/models/project_feature.rb | 2 ++ app/models/project_group_link.rb | 2 ++ app/models/project_import_data.rb | 2 ++ app/models/project_import_state.rb | 2 ++ app/models/project_label.rb | 2 ++ app/models/project_snippet.rb | 2 ++ app/models/project_statistics.rb | 2 ++ app/models/project_team.rb | 2 ++ app/models/protectable_dropdown.rb | 2 ++ app/models/protected_branch.rb | 2 ++ app/models/protected_ref_matcher.rb | 2 ++ app/models/protected_tag.rb | 2 ++ app/models/push_event.rb | 2 ++ app/models/push_event_payload.rb | 2 ++ app/models/readme_blob.rb | 2 ++ app/models/redirect_route.rb | 2 ++ app/models/release.rb | 2 ++ app/models/remote_mirror.rb | 2 ++ app/models/repository.rb | 2 ++ app/models/route.rb | 2 ++ app/models/security_event.rb | 2 ++ app/models/sent_notification.rb | 2 ++ app/models/service.rb | 2 ++ app/models/snippet.rb | 2 ++ app/models/snippet_blob.rb | 2 ++ app/models/spam_log.rb | 2 ++ app/models/subscription.rb | 2 ++ app/models/system_note_metadata.rb | 2 ++ app/models/term_agreement.rb | 2 ++ app/models/timelog.rb | 2 ++ app/models/todo.rb | 2 ++ app/models/tree.rb | 2 ++ app/models/trending_project.rb | 2 ++ app/models/u2f_registration.rb | 2 ++ app/models/upload.rb | 2 ++ app/models/user.rb | 2 ++ app/models/user_agent_detail.rb | 2 ++ app/models/user_callout.rb | 2 ++ app/models/user_custom_attribute.rb | 2 ++ app/models/user_interacted_project.rb | 2 ++ app/models/user_synced_attributes_metadata.rb | 2 ++ app/models/users_star_project.rb | 2 ++ app/models/wiki_directory.rb | 2 ++ app/models/wiki_page.rb | 2 ++ changelogs/unreleased/frozen-string-enable-app-models.yml | 5 +++++ spec/controllers/admin/services_controller_spec.rb | 2 +- 139 files changed, 286 insertions(+), 7 deletions(-) create mode 100644 changelogs/unreleased/frozen-string-enable-app-models.yml diff --git a/app/controllers/admin/services_controller.rb b/app/controllers/admin/services_controller.rb index e70aa549140..91a36af34f3 100644 --- a/app/controllers/admin/services_controller.rb +++ b/app/controllers/admin/services_controller.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class Admin::ServicesController < Admin::ApplicationController include ServiceParams @@ -30,7 +32,7 @@ class Admin::ServicesController < Admin::ApplicationController def services_templates Service.available_services_names.map do |service_name| - service_template = service_name.concat("_service").camelize.constantize + service_template = "#{service_name}_service".camelize.constantize service_template.where(template: true).first_or_create end end diff --git a/app/models/ability.rb b/app/models/ability.rb index bb600eaccba..a853106e5bd 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require_dependency 'declarative_policy' class Ability diff --git a/app/models/abuse_report.rb b/app/models/abuse_report.rb index 4cbd90c5817..1b78fd04ebb 100644 --- a/app/models/abuse_report.rb +++ b/app/models/abuse_report.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class AbuseReport < ActiveRecord::Base include CacheMarkdownField diff --git a/app/models/active_session.rb b/app/models/active_session.rb index b4a86dbb331..0d9c6a4a1f0 100644 --- a/app/models/active_session.rb +++ b/app/models/active_session.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ActiveSession include ActiveModel::Model diff --git a/app/models/appearance.rb b/app/models/appearance.rb index b770aadef0e..bffba3e13fa 100644 --- a/app/models/appearance.rb +++ b/app/models/appearance.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class Appearance < ActiveRecord::Base include CacheableAttributes include CacheMarkdownField diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index f770b219422..bc31e548a09 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ApplicationSetting < ActiveRecord::Base include CacheableAttributes include CacheMarkdownField diff --git a/app/models/audit_event.rb b/app/models/audit_event.rb index 112a8778b4e..8508c88d406 100644 --- a/app/models/audit_event.rb +++ b/app/models/audit_event.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class AuditEvent < ActiveRecord::Base serialize :details, Hash # rubocop:disable Cop/ActiveRecordSerialize diff --git a/app/models/award_emoji.rb b/app/models/award_emoji.rb index 4d1a15c53aa..99c7866d636 100644 --- a/app/models/award_emoji.rb +++ b/app/models/award_emoji.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class AwardEmoji < ActiveRecord::Base DOWNVOTE_NAME = "thumbsdown".freeze UPVOTE_NAME = "thumbsup".freeze diff --git a/app/models/badge.rb b/app/models/badge.rb index 265c5d872d4..7e3b6b659e4 100644 --- a/app/models/badge.rb +++ b/app/models/badge.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class Badge < ActiveRecord::Base # This structure sets the placeholders that the urls # can have. This hash also sets which action to ask when diff --git a/app/models/blob.rb b/app/models/blob.rb index 71c974b4c09..acc64ffca67 100644 --- a/app/models/blob.rb +++ b/app/models/blob.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Blob is a Rails-specific wrapper around Gitlab::Git::Blob objects class Blob < SimpleDelegator CACHE_TIME = 60 # Cache raw blobs referred to by a (mutable) ref for 1 minute diff --git a/app/models/board.rb b/app/models/board.rb index bb6bb753daf..a137863456c 100644 --- a/app/models/board.rb +++ b/app/models/board.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class Board < ActiveRecord::Base belongs_to :group belongs_to :project diff --git a/app/models/broadcast_message.rb b/app/models/broadcast_message.rb index 4aa236555cb..baf8adb318b 100644 --- a/app/models/broadcast_message.rb +++ b/app/models/broadcast_message.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class BroadcastMessage < ActiveRecord::Base include CacheMarkdownField include Sortable diff --git a/app/models/chat_name.rb b/app/models/chat_name.rb index fbd0f123341..03b0af53046 100644 --- a/app/models/chat_name.rb +++ b/app/models/chat_name.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ChatName < ActiveRecord::Base LAST_USED_AT_INTERVAL = 1.hour diff --git a/app/models/chat_team.rb b/app/models/chat_team.rb index 25ecf2d5937..4e724f9adf7 100644 --- a/app/models/chat_team.rb +++ b/app/models/chat_team.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ChatTeam < ActiveRecord::Base validates :team_id, presence: true validates :namespace, uniqueness: true diff --git a/app/models/commit.rb b/app/models/commit.rb index 56d4c86774e..8b9f4490ffa 100644 --- a/app/models/commit.rb +++ b/app/models/commit.rb @@ -1,4 +1,6 @@ # coding: utf-8 +# frozen_string_literal: true + class Commit extend ActiveModel::Naming extend Gitlab::Cache::RequestCache @@ -339,21 +341,21 @@ class Commit end def cherry_pick_description(user) - message_body = "(cherry picked from commit #{sha})" + message_body = ["(cherry picked from commit #{sha})"] if merged_merge_request?(user) commits_in_merge_request = merged_merge_request(user).commits if commits_in_merge_request.present? - message_body << "\n" + message_body << "" commits_in_merge_request.reverse.each do |commit_in_merge| - message_body << "\n#{commit_in_merge.short_id} #{commit_in_merge.title}" + message_body << "#{commit_in_merge.short_id} #{commit_in_merge.title}" end end end - message_body + message_body.join("\n") end def cherry_pick_message(user) diff --git a/app/models/commit_range.rb b/app/models/commit_range.rb index b93c111dabc..094747ee48d 100644 --- a/app/models/commit_range.rb +++ b/app/models/commit_range.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # CommitRange makes it easier to work with commit ranges # # Examples: diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb index 97516079b66..4163fe31477 100644 --- a/app/models/commit_status.rb +++ b/app/models/commit_status.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class CommitStatus < ActiveRecord::Base include HasStatus include Importable diff --git a/app/models/compare.rb b/app/models/compare.rb index feb4b89c781..b2d46ada831 100644 --- a/app/models/compare.rb +++ b/app/models/compare.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class Compare include Gitlab::Utils::StrongMemoize diff --git a/app/models/container_repository.rb b/app/models/container_repository.rb index d0c94d3b694..41413854d5c 100644 --- a/app/models/container_repository.rb +++ b/app/models/container_repository.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ContainerRepository < ActiveRecord::Base belongs_to :project diff --git a/app/models/cycle_analytics.rb b/app/models/cycle_analytics.rb index b34d1382d43..d0f5b6970b1 100644 --- a/app/models/cycle_analytics.rb +++ b/app/models/cycle_analytics.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class CycleAnalytics STAGES = %i[issue plan code test review staging production].freeze diff --git a/app/models/dashboard_milestone.rb b/app/models/dashboard_milestone.rb index 86eb4ec76fc..96bc8090b81 100644 --- a/app/models/dashboard_milestone.rb +++ b/app/models/dashboard_milestone.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class DashboardMilestone < GlobalMilestone def issues_finder_params { authorized_only: true } diff --git a/app/models/deploy_key.rb b/app/models/deploy_key.rb index 89a74b7dcb1..fd5d7726fb6 100644 --- a/app/models/deploy_key.rb +++ b/app/models/deploy_key.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class DeployKey < Key include IgnorableColumn diff --git a/app/models/deploy_keys_project.rb b/app/models/deploy_keys_project.rb index 6eef12c4373..71fd02fac86 100644 --- a/app/models/deploy_keys_project.rb +++ b/app/models/deploy_keys_project.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class DeployKeysProject < ActiveRecord::Base belongs_to :project belongs_to :deploy_key, inverse_of: :deploy_keys_projects diff --git a/app/models/deploy_token.rb b/app/models/deploy_token.rb index 7ab647abe93..446c4576678 100644 --- a/app/models/deploy_token.rb +++ b/app/models/deploy_token.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class DeployToken < ActiveRecord::Base include Expirable include TokenAuthenticatable diff --git a/app/models/deployment.rb b/app/models/deployment.rb index 687246b47b2..6962b54441b 100644 --- a/app/models/deployment.rb +++ b/app/models/deployment.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class Deployment < ActiveRecord::Base include AtomicInternalId include IidRoutes diff --git a/app/models/diff_discussion.rb b/app/models/diff_discussion.rb index bd6af622bfb..93e3ebf7896 100644 --- a/app/models/diff_discussion.rb +++ b/app/models/diff_discussion.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # A discussion on merge request or commit diffs consisting of `DiffNote` notes. # # A discussion of this type can be resolvable. diff --git a/app/models/diff_note.rb b/app/models/diff_note.rb index d752d5bcdee..58d949315e0 100644 --- a/app/models/diff_note.rb +++ b/app/models/diff_note.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # A note on merge request or commit diffs # # A note of this type can be resolvable. diff --git a/app/models/directly_addressed_user.rb b/app/models/directly_addressed_user.rb index 0d519c6ac22..06df2d6c012 100644 --- a/app/models/directly_addressed_user.rb +++ b/app/models/directly_addressed_user.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class DirectlyAddressedUser class << self def reference_pattern diff --git a/app/models/discussion.rb b/app/models/discussion.rb index 35a0ef00856..dbc7b6e67be 100644 --- a/app/models/discussion.rb +++ b/app/models/discussion.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # A non-diff discussion on an issue, merge request, commit, or snippet, consisting of `DiscussionNote` notes. # # A discussion of this type can be resolvable. diff --git a/app/models/discussion_note.rb b/app/models/discussion_note.rb index e660b024083..89d86aaed66 100644 --- a/app/models/discussion_note.rb +++ b/app/models/discussion_note.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # A note in a non-diff discussion on an issue, merge request, commit, or snippet. # # A note of this type can be resolvable. diff --git a/app/models/email.rb b/app/models/email.rb index 15bdedeac33..b6a977dfa22 100644 --- a/app/models/email.rb +++ b/app/models/email.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class Email < ActiveRecord::Base include Sortable include Gitlab::SQL::Pattern diff --git a/app/models/environment.rb b/app/models/environment.rb index 4856d313318..c8d1d378ae0 100644 --- a/app/models/environment.rb +++ b/app/models/environment.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class Environment < ActiveRecord::Base # Used to generate random suffixes for the slug LETTERS = 'a'..'z' @@ -173,7 +175,7 @@ class Environment < ActiveRecord::Base # * cannot end with `-` def generate_slug # Lowercase letters and numbers only - slugified = name.to_s.downcase.gsub(/[^a-z0-9]/, '-') + slugified = +name.to_s.downcase.gsub(/[^a-z0-9]/, '-') # Must start with a letter slugified = 'env-' + slugified unless LETTERS.cover?(slugified[0]) diff --git a/app/models/epic.rb b/app/models/epic.rb index 286b855de3f..f027993376c 100644 --- a/app/models/epic.rb +++ b/app/models/epic.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Placeholder class for model that is implemented in EE # It reserves '&' as a reference prefix, but the table does not exists in CE class Epic < ActiveRecord::Base diff --git a/app/models/event.rb b/app/models/event.rb index ac0b1c7b27c..ba28866e8e6 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class Event < ActiveRecord::Base include Sortable include IgnorableColumn diff --git a/app/models/event_collection.rb b/app/models/event_collection.rb index 8b8244314af..a4c69b11781 100644 --- a/app/models/event_collection.rb +++ b/app/models/event_collection.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # A collection of events to display in an event list. # # An EventCollection is meant to be used for displaying events to a user (e.g. diff --git a/app/models/external_issue.rb b/app/models/external_issue.rb index 282fd7edcb7..4f73beaafc5 100644 --- a/app/models/external_issue.rb +++ b/app/models/external_issue.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ExternalIssue include Referable diff --git a/app/models/fork_network.rb b/app/models/fork_network.rb index 7f1728e8c77..1b9bf93cbbc 100644 --- a/app/models/fork_network.rb +++ b/app/models/fork_network.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ForkNetwork < ActiveRecord::Base belongs_to :root_project, class_name: 'Project' has_many :fork_network_members diff --git a/app/models/fork_network_member.rb b/app/models/fork_network_member.rb index eb9417dc34f..36c66f21b0b 100644 --- a/app/models/fork_network_member.rb +++ b/app/models/fork_network_member.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ForkNetworkMember < ActiveRecord::Base belongs_to :fork_network belongs_to :project diff --git a/app/models/forked_project_link.rb b/app/models/forked_project_link.rb index 8d35864eff6..0f7067238cd 100644 --- a/app/models/forked_project_link.rb +++ b/app/models/forked_project_link.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ForkedProjectLink < ActiveRecord::Base belongs_to :forked_to_project, -> { where.not(pending_delete: true) }, class_name: 'Project' belongs_to :forked_from_project, -> { where.not(pending_delete: true) }, class_name: 'Project' diff --git a/app/models/generic_commit_status.rb b/app/models/generic_commit_status.rb index 5ac8bde44cd..3028bf21301 100644 --- a/app/models/generic_commit_status.rb +++ b/app/models/generic_commit_status.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class GenericCommitStatus < CommitStatus before_validation :set_default_values diff --git a/app/models/global_label.rb b/app/models/global_label.rb index 2a1b7564962..c5b2492bbf6 100644 --- a/app/models/global_label.rb +++ b/app/models/global_label.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class GlobalLabel attr_accessor :title, :labels alias_attribute :name, :title diff --git a/app/models/global_milestone.rb b/app/models/global_milestone.rb index dc2f6817190..2ddad9b6b0b 100644 --- a/app/models/global_milestone.rb +++ b/app/models/global_milestone.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class GlobalMilestone include Milestoneish diff --git a/app/models/gpg_key.rb b/app/models/gpg_key.rb index 44eda741679..077afffd358 100644 --- a/app/models/gpg_key.rb +++ b/app/models/gpg_key.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class GpgKey < ActiveRecord::Base KEY_PREFIX = '-----BEGIN PGP PUBLIC KEY BLOCK-----'.freeze KEY_SUFFIX = '-----END PGP PUBLIC KEY BLOCK-----'.freeze diff --git a/app/models/gpg_key_subkey.rb b/app/models/gpg_key_subkey.rb index b57922aba30..440b588bc78 100644 --- a/app/models/gpg_key_subkey.rb +++ b/app/models/gpg_key_subkey.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class GpgKeySubkey < ActiveRecord::Base include ShaAttribute diff --git a/app/models/gpg_signature.rb b/app/models/gpg_signature.rb index bf88d75246f..0816778deae 100644 --- a/app/models/gpg_signature.rb +++ b/app/models/gpg_signature.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class GpgSignature < ActiveRecord::Base include ShaAttribute diff --git a/app/models/group.rb b/app/models/group.rb index ddebaff50b0..cd548fc0061 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'carrierwave/orm/activerecord' class Group < Namespace diff --git a/app/models/group_custom_attribute.rb b/app/models/group_custom_attribute.rb index 8157d602d67..22f14885657 100644 --- a/app/models/group_custom_attribute.rb +++ b/app/models/group_custom_attribute.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class GroupCustomAttribute < ActiveRecord::Base belongs_to :group diff --git a/app/models/group_label.rb b/app/models/group_label.rb index 92c83b54861..ff14529c6e6 100644 --- a/app/models/group_label.rb +++ b/app/models/group_label.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class GroupLabel < Label belongs_to :group diff --git a/app/models/group_milestone.rb b/app/models/group_milestone.rb index 98135ee3c8b..d6ab32ea7c8 100644 --- a/app/models/group_milestone.rb +++ b/app/models/group_milestone.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class GroupMilestone < GlobalMilestone attr_accessor :group diff --git a/app/models/guest.rb b/app/models/guest.rb index df287c277a7..9c8097e1ac8 100644 --- a/app/models/guest.rb +++ b/app/models/guest.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class Guest class << self def can?(action, subject = :global) diff --git a/app/models/identity.rb b/app/models/identity.rb index 3fd0c5e751d..f5a13dbd6f2 100644 --- a/app/models/identity.rb +++ b/app/models/identity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class Identity < ActiveRecord::Base def self.uniqueness_scope :provider diff --git a/app/models/import_export_upload.rb b/app/models/import_export_upload.rb index 60d53d6c2c8..40795d7c42f 100644 --- a/app/models/import_export_upload.rb +++ b/app/models/import_export_upload.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ImportExportUpload < ActiveRecord::Base include WithUploads include ObjectStorage::BackgroundMove diff --git a/app/models/individual_note_discussion.rb b/app/models/individual_note_discussion.rb index 6be8ca45739..07ee7470ea2 100644 --- a/app/models/individual_note_discussion.rb +++ b/app/models/individual_note_discussion.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # A discussion to wrap a single `Note` note on the root of an issue, merge request, # commit, or snippet, that is not displayed as a discussion. # diff --git a/app/models/instance_configuration.rb b/app/models/instance_configuration.rb index b30b707e5fe..7d8ce0bbd05 100644 --- a/app/models/instance_configuration.rb +++ b/app/models/instance_configuration.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'resolv' class InstanceConfiguration diff --git a/app/models/internal_id.rb b/app/models/internal_id.rb index f50f28deffe..6a3714f1f10 100644 --- a/app/models/internal_id.rb +++ b/app/models/internal_id.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # An InternalId is a strictly monotone sequence of integers # generated for a given scope and usage. # diff --git a/app/models/issue.rb b/app/models/issue.rb index 4715d942c8d..bf3cc63968d 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'carrierwave/orm/activerecord' class Issue < ActiveRecord::Base diff --git a/app/models/issue_assignee.rb b/app/models/issue_assignee.rb index 326b9eb7ad5..400c0256945 100644 --- a/app/models/issue_assignee.rb +++ b/app/models/issue_assignee.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class IssueAssignee < ActiveRecord::Base belongs_to :issue belongs_to :assignee, class_name: "User", foreign_key: :user_id diff --git a/app/models/issue_collection.rb b/app/models/issue_collection.rb index 49f011c113f..05607fc3a08 100644 --- a/app/models/issue_collection.rb +++ b/app/models/issue_collection.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # IssueCollection can be used to reduce a list of issues down to a subset. # # IssueCollection is not meant to be some sort of Enumerable, instead it's meant diff --git a/app/models/key.rb b/app/models/key.rb index ae5769c0627..3bb0d2f6f9c 100644 --- a/app/models/key.rb +++ b/app/models/key.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'digest/md5' class Key < ActiveRecord::Base diff --git a/app/models/label.rb b/app/models/label.rb index 7bbcaa121ca..f5c60177d52 100644 --- a/app/models/label.rb +++ b/app/models/label.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class Label < ActiveRecord::Base include CacheMarkdownField include Referable diff --git a/app/models/label_link.rb b/app/models/label_link.rb index d68e1f54317..779657b25d5 100644 --- a/app/models/label_link.rb +++ b/app/models/label_link.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class LabelLink < ActiveRecord::Base include Importable diff --git a/app/models/label_priority.rb b/app/models/label_priority.rb index 5b85e0b6533..8ed8bb7577f 100644 --- a/app/models/label_priority.rb +++ b/app/models/label_priority.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class LabelPriority < ActiveRecord::Base belongs_to :project belongs_to :label diff --git a/app/models/legacy_diff_discussion.rb b/app/models/legacy_diff_discussion.rb index 80fc6304fd4..7d78c580fa2 100644 --- a/app/models/legacy_diff_discussion.rb +++ b/app/models/legacy_diff_discussion.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # A discussion on merge request or commit diffs consisting of `LegacyDiffNote` notes. # # All new diff discussions are of the type `DiffDiscussion`, but any diff discussions created diff --git a/app/models/legacy_diff_note.rb b/app/models/legacy_diff_note.rb index d90cafd14b4..20f9b18e4ca 100644 --- a/app/models/legacy_diff_note.rb +++ b/app/models/legacy_diff_note.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # A note on merge request or commit diffs, using the legacy implementation. # # All new diff notes are of the type `DiffNote`, but any diff notes created diff --git a/app/models/lfs_file_lock.rb b/app/models/lfs_file_lock.rb index 50bb6ca382d..431d37e12e9 100644 --- a/app/models/lfs_file_lock.rb +++ b/app/models/lfs_file_lock.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class LfsFileLock < ActiveRecord::Base belongs_to :project belongs_to :user diff --git a/app/models/lfs_object.rb b/app/models/lfs_object.rb index 84487031ee5..2a1a4ef48b7 100644 --- a/app/models/lfs_object.rb +++ b/app/models/lfs_object.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class LfsObject < ActiveRecord::Base include AfterCommitQueue include ObjectStorage::BackgroundMove diff --git a/app/models/lfs_objects_project.rb b/app/models/lfs_objects_project.rb index b0625c52b62..353602800d7 100644 --- a/app/models/lfs_objects_project.rb +++ b/app/models/lfs_objects_project.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class LfsObjectsProject < ActiveRecord::Base belongs_to :project belongs_to :lfs_object diff --git a/app/models/list.rb b/app/models/list.rb index 4edcfa78835..eabe3ffccbb 100644 --- a/app/models/list.rb +++ b/app/models/list.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class List < ActiveRecord::Base belongs_to :board belongs_to :label diff --git a/app/models/member.rb b/app/models/member.rb index 00a13a279a9..05c0bc8cb97 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class Member < ActiveRecord::Base include AfterCommitQueue include Sortable diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index b4090fd8baf..cf4ee17d2b0 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class MergeRequest < ActiveRecord::Base include AtomicInternalId include IidRoutes diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb index a073bbfad20..b18a8623ce4 100644 --- a/app/models/merge_request_diff.rb +++ b/app/models/merge_request_diff.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class MergeRequestDiff < ActiveRecord::Base include Sortable include Importable diff --git a/app/models/merge_request_diff_commit.rb b/app/models/merge_request_diff_commit.rb index 1c2e57bb01f..4ad3690512d 100644 --- a/app/models/merge_request_diff_commit.rb +++ b/app/models/merge_request_diff_commit.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class MergeRequestDiffCommit < ActiveRecord::Base include ShaAttribute diff --git a/app/models/merge_request_diff_file.rb b/app/models/merge_request_diff_file.rb index cd8ba6b904d..a9f110bec5c 100644 --- a/app/models/merge_request_diff_file.rb +++ b/app/models/merge_request_diff_file.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class MergeRequestDiffFile < ActiveRecord::Base include Gitlab::EncodingHelper include DiffFile diff --git a/app/models/merge_requests_closing_issues.rb b/app/models/merge_requests_closing_issues.rb index 7f7c114803d..242b65bedc0 100644 --- a/app/models/merge_requests_closing_issues.rb +++ b/app/models/merge_requests_closing_issues.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class MergeRequestsClosingIssues < ActiveRecord::Base belongs_to :merge_request belongs_to :issue diff --git a/app/models/milestone.rb b/app/models/milestone.rb index 14cc12b38a5..1f3b3fda1eb 100644 --- a/app/models/milestone.rb +++ b/app/models/milestone.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class Milestone < ActiveRecord::Base # Represents a "No Milestone" state used for filtering Issues and Merge # Requests that have no milestone assigned. diff --git a/app/models/namespace.rb b/app/models/namespace.rb index 7034c633268..75341cab59b 100644 --- a/app/models/namespace.rb +++ b/app/models/namespace.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class Namespace < ActiveRecord::Base include CacheMarkdownField include Sortable diff --git a/app/models/note.rb b/app/models/note.rb index fe3507adcb3..67a507a5685 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # A note on the root of an issue, merge request, commit, or snippet. # # A note of this type is never resolvable. diff --git a/app/models/note_diff_file.rb b/app/models/note_diff_file.rb index e688018a6d9..27aef7adc48 100644 --- a/app/models/note_diff_file.rb +++ b/app/models/note_diff_file.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class NoteDiffFile < ActiveRecord::Base include DiffFile diff --git a/app/models/notification_reason.rb b/app/models/notification_reason.rb index c3965565022..0a13487574f 100644 --- a/app/models/notification_reason.rb +++ b/app/models/notification_reason.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Holds reasons for a notification to have been sent as well as a priority list to select which reason to use # above the rest class NotificationReason diff --git a/app/models/notification_recipient.rb b/app/models/notification_recipient.rb index 1a03dd9df56..9f16eefe074 100644 --- a/app/models/notification_recipient.rb +++ b/app/models/notification_recipient.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class NotificationRecipient include Gitlab::Utils::StrongMemoize diff --git a/app/models/notification_setting.rb b/app/models/notification_setting.rb index 1933c46ee44..1df3a51a7fc 100644 --- a/app/models/notification_setting.rb +++ b/app/models/notification_setting.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class NotificationSetting < ActiveRecord::Base include IgnorableColumn diff --git a/app/models/oauth_access_grant.rb b/app/models/oauth_access_grant.rb index 3a997406565..d5a8a1a25b6 100644 --- a/app/models/oauth_access_grant.rb +++ b/app/models/oauth_access_grant.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class OauthAccessGrant < Doorkeeper::AccessGrant belongs_to :resource_owner, class_name: 'User' belongs_to :application, class_name: 'Doorkeeper::Application' diff --git a/app/models/oauth_access_token.rb b/app/models/oauth_access_token.rb index e8595b13d6d..0aa920fa828 100644 --- a/app/models/oauth_access_token.rb +++ b/app/models/oauth_access_token.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class OauthAccessToken < Doorkeeper::AccessToken belongs_to :resource_owner, class_name: 'User' belongs_to :application, class_name: 'Doorkeeper::Application' diff --git a/app/models/out_of_context_discussion.rb b/app/models/out_of_context_discussion.rb index 4227c40b69a..4de717e2c51 100644 --- a/app/models/out_of_context_discussion.rb +++ b/app/models/out_of_context_discussion.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # When notes on a commit are displayed in the context of a merge request that # contains that commit, they are displayed as if they were a discussion. # diff --git a/app/models/pages_domain.rb b/app/models/pages_domain.rb index bfea64c3759..7739a3894d3 100644 --- a/app/models/pages_domain.rb +++ b/app/models/pages_domain.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class PagesDomain < ActiveRecord::Base VERIFICATION_KEY = 'gitlab-pages-verification-code'.freeze VERIFICATION_THRESHOLD = 3.days.freeze diff --git a/app/models/personal_access_token.rb b/app/models/personal_access_token.rb index 063dc521324..207146479c0 100644 --- a/app/models/personal_access_token.rb +++ b/app/models/personal_access_token.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class PersonalAccessToken < ActiveRecord::Base include Expirable include TokenAuthenticatable diff --git a/app/models/personal_snippet.rb b/app/models/personal_snippet.rb index 355624fd552..1b5be8698b1 100644 --- a/app/models/personal_snippet.rb +++ b/app/models/personal_snippet.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class PersonalSnippet < Snippet include WithUploads end diff --git a/app/models/project.rb b/app/models/project.rb index 32315dfaa01..cb10b06c233 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'carrierwave/orm/activerecord' class Project < ActiveRecord::Base diff --git a/app/models/project_authorization.rb b/app/models/project_authorization.rb index 73302207e6b..746bb4584c9 100644 --- a/app/models/project_authorization.rb +++ b/app/models/project_authorization.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ProjectAuthorization < ActiveRecord::Base belongs_to :user belongs_to :project diff --git a/app/models/project_auto_devops.rb b/app/models/project_auto_devops.rb index faa831b1949..155400d1a43 100644 --- a/app/models/project_auto_devops.rb +++ b/app/models/project_auto_devops.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ProjectAutoDevops < ActiveRecord::Base belongs_to :project diff --git a/app/models/project_ci_cd_setting.rb b/app/models/project_ci_cd_setting.rb index 588cced5781..1dad235cc2b 100644 --- a/app/models/project_ci_cd_setting.rb +++ b/app/models/project_ci_cd_setting.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ProjectCiCdSetting < ActiveRecord::Base belongs_to :project, inverse_of: :ci_cd_settings diff --git a/app/models/project_custom_attribute.rb b/app/models/project_custom_attribute.rb index 3f1a7b86a82..4e767cb3b26 100644 --- a/app/models/project_custom_attribute.rb +++ b/app/models/project_custom_attribute.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ProjectCustomAttribute < ActiveRecord::Base belongs_to :project diff --git a/app/models/project_deploy_token.rb b/app/models/project_deploy_token.rb index ab4482f0c0b..719c492a1ff 100644 --- a/app/models/project_deploy_token.rb +++ b/app/models/project_deploy_token.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ProjectDeployToken < ActiveRecord::Base belongs_to :project belongs_to :deploy_token, inverse_of: :project_deploy_tokens diff --git a/app/models/project_feature.rb b/app/models/project_feature.rb index 9c768b13f78..d74cb2506ba 100644 --- a/app/models/project_feature.rb +++ b/app/models/project_feature.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ProjectFeature < ActiveRecord::Base # == Project features permissions # diff --git a/app/models/project_group_link.rb b/app/models/project_group_link.rb index cf8fc41e870..bc3759142ae 100644 --- a/app/models/project_group_link.rb +++ b/app/models/project_group_link.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ProjectGroupLink < ActiveRecord::Base include Expirable diff --git a/app/models/project_import_data.rb b/app/models/project_import_data.rb index 1d7089ccfc7..2c3080c6d8d 100644 --- a/app/models/project_import_data.rb +++ b/app/models/project_import_data.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'carrierwave/orm/activerecord' class ProjectImportData < ActiveRecord::Base diff --git a/app/models/project_import_state.rb b/app/models/project_import_state.rb index 1605317ae14..89ed09af96a 100644 --- a/app/models/project_import_state.rb +++ b/app/models/project_import_state.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ProjectImportState < ActiveRecord::Base include AfterCommitQueue diff --git a/app/models/project_label.rb b/app/models/project_label.rb index 313815e5869..d0b16cc98b4 100644 --- a/app/models/project_label.rb +++ b/app/models/project_label.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ProjectLabel < Label MAX_NUMBER_OF_PRIORITIES = 1 diff --git a/app/models/project_snippet.rb b/app/models/project_snippet.rb index 25b5d777641..b3585c4cf4c 100644 --- a/app/models/project_snippet.rb +++ b/app/models/project_snippet.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ProjectSnippet < Snippet belongs_to :project belongs_to :author, class_name: "User" diff --git a/app/models/project_statistics.rb b/app/models/project_statistics.rb index 5d4e3c34b39..206d6ac5e88 100644 --- a/app/models/project_statistics.rb +++ b/app/models/project_statistics.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ProjectStatistics < ActiveRecord::Base belongs_to :project belongs_to :namespace diff --git a/app/models/project_team.rb b/app/models/project_team.rb index c7d0f49d837..33bc6a561f9 100644 --- a/app/models/project_team.rb +++ b/app/models/project_team.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ProjectTeam include BulkMemberAccessLoad diff --git a/app/models/protectable_dropdown.rb b/app/models/protectable_dropdown.rb index c96edc5a259..25e70ab406c 100644 --- a/app/models/protectable_dropdown.rb +++ b/app/models/protectable_dropdown.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ProtectableDropdown REF_TYPES = %i[branches tags].freeze diff --git a/app/models/protected_branch.rb b/app/models/protected_branch.rb index dff99cfca35..6c1073265a1 100644 --- a/app/models/protected_branch.rb +++ b/app/models/protected_branch.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ProtectedBranch < ActiveRecord::Base include Gitlab::ShellAdapter include ProtectedRef diff --git a/app/models/protected_ref_matcher.rb b/app/models/protected_ref_matcher.rb index d970f2b01fc..bfa9180ac93 100644 --- a/app/models/protected_ref_matcher.rb +++ b/app/models/protected_ref_matcher.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ProtectedRefMatcher def initialize(protected_ref) @protected_ref = protected_ref diff --git a/app/models/protected_tag.rb b/app/models/protected_tag.rb index 42a9bcf7723..a36f0d36262 100644 --- a/app/models/protected_tag.rb +++ b/app/models/protected_tag.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ProtectedTag < ActiveRecord::Base include Gitlab::ShellAdapter include ProtectedRef diff --git a/app/models/push_event.rb b/app/models/push_event.rb index 90c085c888e..9c0267c3140 100644 --- a/app/models/push_event.rb +++ b/app/models/push_event.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class PushEvent < Event # This validation exists so we can't accidentally use PushEvent with a # different "action" value. diff --git a/app/models/push_event_payload.rb b/app/models/push_event_payload.rb index 6cdb1cd4fe9..c7769edf055 100644 --- a/app/models/push_event_payload.rb +++ b/app/models/push_event_payload.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class PushEventPayload < ActiveRecord::Base include ShaAttribute diff --git a/app/models/readme_blob.rb b/app/models/readme_blob.rb index 1863a08f1de..7b49fa632f6 100644 --- a/app/models/readme_blob.rb +++ b/app/models/readme_blob.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ReadmeBlob < SimpleDelegator attr_reader :repository diff --git a/app/models/redirect_route.rb b/app/models/redirect_route.rb index 31de204d824..c6bd4bb6dfa 100644 --- a/app/models/redirect_route.rb +++ b/app/models/redirect_route.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class RedirectRoute < ActiveRecord::Base belongs_to :source, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations diff --git a/app/models/release.rb b/app/models/release.rb index c936899799e..cba80ad30ca 100644 --- a/app/models/release.rb +++ b/app/models/release.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class Release < ActiveRecord::Base include CacheMarkdownField diff --git a/app/models/remote_mirror.rb b/app/models/remote_mirror.rb index 976b501e297..2cc5e521e3d 100644 --- a/app/models/remote_mirror.rb +++ b/app/models/remote_mirror.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class RemoteMirror < ActiveRecord::Base include AfterCommitQueue diff --git a/app/models/repository.rb b/app/models/repository.rb index e248f94cbd8..192865dfd61 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'securerandom' class Repository diff --git a/app/models/route.rb b/app/models/route.rb index 2d609920051..4b23dfa5778 100644 --- a/app/models/route.rb +++ b/app/models/route.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class Route < ActiveRecord::Base include CaseSensitivity diff --git a/app/models/security_event.rb b/app/models/security_event.rb index d131c11cb6c..3fe4cc99c9b 100644 --- a/app/models/security_event.rb +++ b/app/models/security_event.rb @@ -1,2 +1,4 @@ +# frozen_string_literal: true + class SecurityEvent < AuditEvent end diff --git a/app/models/sent_notification.rb b/app/models/sent_notification.rb index 3da7c301d28..e65b3df0fb6 100644 --- a/app/models/sent_notification.rb +++ b/app/models/sent_notification.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class SentNotification < ActiveRecord::Base serialize :position, Gitlab::Diff::Position # rubocop:disable Cop/ActiveRecordSerialize diff --git a/app/models/service.rb b/app/models/service.rb index cbfe0c6eedd..140058771ee 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # To add new service you should build a class inherited from Service # and implement a set of methods class Service < ActiveRecord::Base diff --git a/app/models/snippet.rb b/app/models/snippet.rb index 644120453cf..f82d3a5d00f 100644 --- a/app/models/snippet.rb +++ b/app/models/snippet.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class Snippet < ActiveRecord::Base include Gitlab::VisibilityLevel include CacheMarkdownField diff --git a/app/models/snippet_blob.rb b/app/models/snippet_blob.rb index fa5fa151607..cf1ab089829 100644 --- a/app/models/snippet_blob.rb +++ b/app/models/snippet_blob.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class SnippetBlob include BlobLike diff --git a/app/models/spam_log.rb b/app/models/spam_log.rb index 56a115d1db4..ef3f974b959 100644 --- a/app/models/spam_log.rb +++ b/app/models/spam_log.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class SpamLog < ActiveRecord::Base belongs_to :user diff --git a/app/models/subscription.rb b/app/models/subscription.rb index 2f0c9640744..0f6ee0ddf7e 100644 --- a/app/models/subscription.rb +++ b/app/models/subscription.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class Subscription < ActiveRecord::Base belongs_to :user belongs_to :project diff --git a/app/models/system_note_metadata.rb b/app/models/system_note_metadata.rb index 1c2161accc4..c5c77bc8333 100644 --- a/app/models/system_note_metadata.rb +++ b/app/models/system_note_metadata.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class SystemNoteMetadata < ActiveRecord::Base # These notes's action text might contain a reference that is external. # We should always force a deep validation upon references that are found diff --git a/app/models/term_agreement.rb b/app/models/term_agreement.rb index c317bd0c90b..9b3c8ac68bd 100644 --- a/app/models/term_agreement.rb +++ b/app/models/term_agreement.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class TermAgreement < ActiveRecord::Base belongs_to :term, class_name: 'ApplicationSetting::Term' belongs_to :user diff --git a/app/models/timelog.rb b/app/models/timelog.rb index 659146f43e4..e04c644a53a 100644 --- a/app/models/timelog.rb +++ b/app/models/timelog.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class Timelog < ActiveRecord::Base validates :time_spent, :user, presence: true validate :issuable_id_is_present diff --git a/app/models/todo.rb b/app/models/todo.rb index a2ab405fdbe..5f5c2f9073d 100644 --- a/app/models/todo.rb +++ b/app/models/todo.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class Todo < ActiveRecord::Base include Sortable diff --git a/app/models/tree.rb b/app/models/tree.rb index 4c1856b67a8..3641c33254c 100644 --- a/app/models/tree.rb +++ b/app/models/tree.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class Tree include Gitlab::MarkupHelper diff --git a/app/models/trending_project.rb b/app/models/trending_project.rb index 27e3732da17..7b22e8cb760 100644 --- a/app/models/trending_project.rb +++ b/app/models/trending_project.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class TrendingProject < ActiveRecord::Base belongs_to :project diff --git a/app/models/u2f_registration.rb b/app/models/u2f_registration.rb index 808acec098f..37598173fd1 100644 --- a/app/models/u2f_registration.rb +++ b/app/models/u2f_registration.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Registration information for U2F (universal 2nd factor) devices, like Yubikeys class U2fRegistration < ActiveRecord::Base diff --git a/app/models/upload.rb b/app/models/upload.rb index cf71a7b76fc..23bc9ca42fc 100644 --- a/app/models/upload.rb +++ b/app/models/upload.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class Upload < ActiveRecord::Base # Upper limit for foreground checksum processing CHECKSUM_THRESHOLD = 100.megabytes diff --git a/app/models/user.rb b/app/models/user.rb index 58429f8d607..0de8a6d057f 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'carrierwave/orm/activerecord' class User < ActiveRecord::Base diff --git a/app/models/user_agent_detail.rb b/app/models/user_agent_detail.rb index 2d05fdd3e54..e2b2e7f1df9 100644 --- a/app/models/user_agent_detail.rb +++ b/app/models/user_agent_detail.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class UserAgentDetail < ActiveRecord::Base belongs_to :subject, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations diff --git a/app/models/user_callout.rb b/app/models/user_callout.rb index 9d461c6750a..97e955ace36 100644 --- a/app/models/user_callout.rb +++ b/app/models/user_callout.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class UserCallout < ActiveRecord::Base belongs_to :user diff --git a/app/models/user_custom_attribute.rb b/app/models/user_custom_attribute.rb index eff25b31f9b..e0ffe8ebbfd 100644 --- a/app/models/user_custom_attribute.rb +++ b/app/models/user_custom_attribute.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class UserCustomAttribute < ActiveRecord::Base belongs_to :user diff --git a/app/models/user_interacted_project.rb b/app/models/user_interacted_project.rb index dd55a6acb79..ae6778e49be 100644 --- a/app/models/user_interacted_project.rb +++ b/app/models/user_interacted_project.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class UserInteractedProject < ActiveRecord::Base belongs_to :user belongs_to :project diff --git a/app/models/user_synced_attributes_metadata.rb b/app/models/user_synced_attributes_metadata.rb index 688432a9d67..7115262942d 100644 --- a/app/models/user_synced_attributes_metadata.rb +++ b/app/models/user_synced_attributes_metadata.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class UserSyncedAttributesMetadata < ActiveRecord::Base belongs_to :user diff --git a/app/models/users_star_project.rb b/app/models/users_star_project.rb index 0dfe597317e..bdaf58ae1c1 100644 --- a/app/models/users_star_project.rb +++ b/app/models/users_star_project.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class UsersStarProject < ActiveRecord::Base belongs_to :project, counter_cache: :star_count, touch: true belongs_to :user diff --git a/app/models/wiki_directory.rb b/app/models/wiki_directory.rb index 9340fc2dbbe..712ba79bbd2 100644 --- a/app/models/wiki_directory.rb +++ b/app/models/wiki_directory.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class WikiDirectory include ActiveModel::Validations diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb index 55243136140..33790afc35e 100644 --- a/app/models/wiki_page.rb +++ b/app/models/wiki_page.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # rubocop:disable Rails/ActiveRecordAliases class WikiPage PageChangedError = Class.new(StandardError) diff --git a/changelogs/unreleased/frozen-string-enable-app-models.yml b/changelogs/unreleased/frozen-string-enable-app-models.yml new file mode 100644 index 00000000000..4c149ea55ef --- /dev/null +++ b/changelogs/unreleased/frozen-string-enable-app-models.yml @@ -0,0 +1,5 @@ +--- +title: Enable frozen string in app/models/*.rb +merge_request: 20851 +author: gfyoung +type: performance diff --git a/spec/controllers/admin/services_controller_spec.rb b/spec/controllers/admin/services_controller_spec.rb index 701211c2586..4439ea4a533 100644 --- a/spec/controllers/admin/services_controller_spec.rb +++ b/spec/controllers/admin/services_controller_spec.rb @@ -13,7 +13,7 @@ describe Admin::ServicesController do Service.available_services_names.each do |service_name| context "#{service_name}" do let!(:service) do - service_template = service_name.concat("_service").camelize.constantize + service_template = "#{service_name}_service".camelize.constantize service_template.where(template: true).first_or_create end -- cgit v1.2.1 From d7afed34c4238d637ce71fe8dbea41a64e7a3f1f Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Thu, 26 Jul 2018 10:05:32 +0200 Subject: Remove feature flags from lib/backup Moved to OPT_OUT in 7d14b725a0da41d1ae7c0a8496b5e66832023e3b, Now, by removing the feature gates, this is an mandatory feature. Related issues: - https://gitlab.com/gitlab-org/gitaly/issues/526 - https://gitlab.com/gitlab-org/gitaly/issues/1194 Closes https://gitlab.com/gitlab-org/gitaly/issues/749 Closes https://gitlab.com/gitlab-org/gitaly/issues/1212 Closes https://gitlab.com/gitlab-org/gitaly/issues/1195 --- lib/backup/repository.rb | 171 +++------------------ spec/lib/backup/repository_spec.rb | 40 ++--- .../gitlab/gitaly_client/storage_service_spec.rb | 13 ++ spec/tasks/gitlab/backup_rake_spec.rb | 21 +++ 4 files changed, 70 insertions(+), 175 deletions(-) create mode 100644 spec/lib/gitlab/gitaly_client/storage_service_spec.rb diff --git a/lib/backup/repository.rb b/lib/backup/repository.rb index af762db517c..906ed498026 100644 --- a/lib/backup/repository.rb +++ b/lib/backup/repository.rb @@ -1,10 +1,7 @@ require 'yaml' -require_relative 'helper' module Backup class Repository - include Backup::Helper - attr_reader :progress def initialize(progress) @@ -42,131 +39,36 @@ module Backup end def prepare_directories - Gitlab.config.repositories.storages.each do |name, repository_storage| - delete_all_repositories(name, repository_storage) + Gitlab.config.repositories.storages.each do |name, _repository_storage| + Gitlab::GitalyClient::StorageService.new(name).delete_all_repositories end end def backup_project(project) - gitaly_migrate(:repository_backup, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled| - if is_enabled - backup_project_gitaly(project) - else - backup_project_local(project) - end - end - - backup_custom_hooks(project) - rescue => e - progress_warn(project, e, 'Failed to backup repo') - end - - def backup_project_gitaly(project) path_to_project_bundle = path_to_bundle(project) Gitlab::GitalyClient::RepositoryService.new(project.repository) .create_bundle(path_to_project_bundle) - end - - def backup_project_local(project) - path_to_project_repo = Gitlab::GitalyClient::StorageSettings.allow_disk_access do - path_to_repo(project) - end - - path_to_project_bundle = path_to_bundle(project) - - cmd = %W(#{Gitlab.config.git.bin_path} --git-dir=#{path_to_project_repo} bundle create #{path_to_project_bundle} --all) - output, status = Gitlab::Popen.popen(cmd) - progress_warn(project, cmd.join(' '), output) unless status.zero? - end - - def delete_all_repositories(name, repository_storage) - gitaly_migrate(:delete_all_repositories, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) 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) - - 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 local_restore_custom_hooks(project, dir) - path_to_project_repo = Gitlab::GitalyClient::StorageSettings.allow_disk_access do - path_to_repo(project) - end - cmd = %W(tar -xf #{path_to_tars(project, dir)} -C #{path_to_project_repo} #{dir}) - output, status = Gitlab::Popen.popen(cmd) - unless status.zero? - progress_warn(project, cmd.join(' '), output) - end - end - - def gitaly_restore_custom_hooks(project, dir) - custom_hooks_path = path_to_tars(project, dir) - Gitlab::GitalyClient::RepositoryService.new(project.repository) - .restore_custom_hooks(custom_hooks_path) + backup_custom_hooks(project) + rescue => e + progress_warn(project, e, 'Failed to backup repo') end - def local_backup_custom_hooks(project) - in_path(path_to_tars(project)) do |dir| - path_to_project_repo = Gitlab::GitalyClient::StorageSettings.allow_disk_access do - path_to_repo(project) - end - break unless File.exist?(File.join(path_to_project_repo, dir)) - - FileUtils.mkdir_p(path_to_tars(project)) - cmd = %W(tar -cf #{path_to_tars(project, dir)} -c #{path_to_project_repo} #{dir}) - output, status = Gitlab::Popen.popen(cmd) - - unless status.zero? - progress_warn(project, cmd.join(' '), output) - end - end - end + def backup_custom_hooks(project) + FileUtils.mkdir_p(project_backup_path(project)) - def gitaly_backup_custom_hooks(project) - FileUtils.mkdir_p(path_to_tars(project)) - custom_hooks_path = path_to_tars(project, 'custom_hooks') + custom_hooks_path = custom_hooks_tar(project) Gitlab::GitalyClient::RepositoryService.new(project.repository) .backup_custom_hooks(custom_hooks_path) end - def backup_custom_hooks(project) - gitaly_migrate(:backup_custom_hooks, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled| - if is_enabled - gitaly_backup_custom_hooks(project) - else - local_backup_custom_hooks(project) - end - end - end - def restore_custom_hooks(project) - in_path(path_to_tars(project)) do |dir| - gitaly_migrate(:restore_custom_hooks, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled| - if is_enabled - gitaly_restore_custom_hooks(project, dir) - else - local_restore_custom_hooks(project, dir) - end - end - end + return unless Dir.exist?(project_backup_path(project)) + return if Dir.glob("#{project_backup_path(project)}/custom_hooks*").none? + + custom_hooks_path = custom_hooks_tar(project) + Gitlab::GitalyClient::RepositoryService.new(project.repository) + .restore_custom_hooks(custom_hooks_path) end def restore @@ -181,7 +83,8 @@ module Backup restore_repo_success = nil if File.exist?(path_to_project_bundle) begin - project.repository.create_from_bundle path_to_project_bundle + project.repository.create_from_bundle(path_to_project_bundle) + restore_custom_hooks(project) restore_repo_success = true rescue => e restore_repo_success = false @@ -197,8 +100,6 @@ module Backup progress.puts "[Failed] restoring #{project.full_path} repository".color(:red) end - restore_custom_hooks(project) - wiki = ProjectWiki.new(project) path_to_wiki_bundle = path_to_bundle(wiki) @@ -219,48 +120,28 @@ module Backup protected - def path_to_repo(project) - project.repository.path_to_repo - end - def path_to_bundle(project) File.join(backup_repos_path, project.disk_path + '.bundle') end - def path_to_tars(project, dir = nil) - path = File.join(backup_repos_path, project.disk_path) + def project_backup_path(project) + File.join(backup_repos_path, project.disk_path) + end - if dir - File.join(path, "#{dir}.tar") - else - path - end + def custom_hooks_tar(project) + File.join(project_backup_path(project), "custom_hooks.tar") end def backup_repos_path File.join(Gitlab.config.backup.path, 'repositories') end - def in_path(path) - return unless Dir.exist?(path) - - dir_entries = Dir.entries(path) - - if dir_entries.include?('custom_hooks') || dir_entries.include?('custom_hooks.tar') - yield('custom_hooks') - end - end - def prepare FileUtils.rm_rf(backup_repos_path) FileUtils.mkdir_p(Gitlab.config.backup.path) FileUtils.mkdir(backup_repos_path, mode: 0700) end - def silent - { err: '/dev/null', out: '/dev/null' } - end - private def progress_warn(project, cmd, output) @@ -273,18 +154,8 @@ module Backup project_or_wiki.repository.empty? end - def repository_storage_paths_args - Gitlab.config.repositories.storages.values.map { |rs| rs.legacy_disk_path } - end - 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/spec/lib/backup/repository_spec.rb b/spec/lib/backup/repository_spec.rb index 92a27e308d2..c5a854b5660 100644 --- a/spec/lib/backup/repository_spec.rb +++ b/spec/lib/backup/repository_spec.rb @@ -73,37 +73,27 @@ 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)) + describe '#prepare_directories', :seed_helper do + before do + allow(FileUtils).to receive(:mkdir_p).and_call_original + allow(FileUtils).to receive(:mv).and_call_original + end - subject.delete_all_repositories('default', Gitlab.config.repositories.storages['default']) + after(:all) do + ensure_seeds + end - expect(list_repositories).to be_empty - 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)) - def list_repositories - Dir[File.join(SEED_STORAGE_PATH, '*.git')] - end - end + subject.prepare_directories - context 'with gitaly' do - it_behaves_like 'delete_all_repositories' + expect(list_repositories).to be_empty end - context 'without gitaly', :skip_gitaly_mock do - it_behaves_like 'delete_all_repositories' + def list_repositories + Dir[File.join(SEED_STORAGE_PATH, '*.git')] end end diff --git a/spec/lib/gitlab/gitaly_client/storage_service_spec.rb b/spec/lib/gitlab/gitaly_client/storage_service_spec.rb new file mode 100644 index 00000000000..6c25e2d6ebd --- /dev/null +++ b/spec/lib/gitlab/gitaly_client/storage_service_spec.rb @@ -0,0 +1,13 @@ +require 'spec_helper' + +describe Gitlab::GitalyClient::StorageService do + describe '#delete_all_repositories' do + let!(:project) { create(:project, :repository) } + + it 'removes all repositories' do + described_class.new(project.repository_storage).delete_all_repositories + + expect(project.repository.exists?).to be(false) + end + end +end diff --git a/spec/tasks/gitlab/backup_rake_spec.rb b/spec/tasks/gitlab/backup_rake_spec.rb index 93a436cb2b5..3ba6caf1337 100644 --- a/spec/tasks/gitlab/backup_rake_spec.rb +++ b/spec/tasks/gitlab/backup_rake_spec.rb @@ -87,6 +87,27 @@ describe 'gitlab:app namespace rake task' do expect { run_rake_task('gitlab:backup:restore') }.to output.to_stdout end end + + context 'when the restore directory is not empty' do + before do + # We only need a backup of the repositories for this test + stub_env('SKIP', 'db,uploads,builds,artifacts,lfs,registry') + end + + it 'removes stale data' do + expect { run_rake_task('gitlab:backup:create') }.to output.to_stdout + + excluded_project = create(:project, :repository, name: 'mepmep') + + expect { run_rake_task('gitlab:backup:restore') }.to output.to_stdout + + raw_repo = excluded_project.repository.raw + + # The restore will not find the repository in the backup, but will create + # an empty one in its place + expect(raw_repo.empty?).to be(true) + end + end end # backup_restore task describe 'backup' do -- cgit v1.2.1 From 01de2b5df89c4eaca92408c18203050604a4e94f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Rodr=C3=ADguez?= Date: Thu, 26 Jul 2018 21:18:28 -0400 Subject: Refactor gitlab:import:repos task to remove direct disk access --- lib/gitlab/bare_repository_import/importer.rb | 36 +++++++++++---- lib/gitlab/bare_repository_import/repository.rb | 2 - lib/gitlab/git/repository.rb | 13 ------ .../gitlab/bare_repository_import/importer_spec.rb | 54 +++++++++++----------- .../gitlab/git/attributes_at_ref_parser_spec.rb | 2 +- spec/lib/gitlab/git/attributes_parser_spec.rb | 2 +- spec/lib/gitlab/git/blame_spec.rb | 2 +- spec/lib/gitlab/git/blob_snippet_spec.rb | 2 +- spec/lib/gitlab/git/blob_spec.rb | 2 +- spec/lib/gitlab/git/branch_spec.rb | 2 +- spec/lib/gitlab/git/commit_spec.rb | 2 +- spec/lib/gitlab/git/committer_with_hooks_spec.rb | 2 +- spec/lib/gitlab/git/compare_spec.rb | 2 +- spec/lib/gitlab/git/diff_collection_spec.rb | 2 +- spec/lib/gitlab/git/diff_spec.rb | 2 +- spec/lib/gitlab/git/hooks_service_spec.rb | 2 +- spec/lib/gitlab/git/index_spec.rb | 2 +- spec/lib/gitlab/git/remote_repository_spec.rb | 2 +- spec/lib/gitlab/git/repository_spec.rb | 2 +- spec/lib/gitlab/git/tag_spec.rb | 2 +- spec/lib/gitlab/git/tree_spec.rb | 2 +- spec/support/helpers/seed_helper.rb | 6 --- spec/support/helpers/test_env.rb | 8 ++++ spec/support/stored_repositories.rb | 4 -- 24 files changed, 77 insertions(+), 80 deletions(-) diff --git a/lib/gitlab/bare_repository_import/importer.rb b/lib/gitlab/bare_repository_import/importer.rb index 4ca5a78e068..04aa6aab771 100644 --- a/lib/gitlab/bare_repository_import/importer.rb +++ b/lib/gitlab/bare_repository_import/importer.rb @@ -26,6 +26,12 @@ module Gitlab end end + # This is called from within a rake task only used by Admins, so allow writing + # to STDOUT + def self.log(message) + puts message # rubocop:disable Rails/Output + end + attr_reader :user, :project_name, :bare_repo delegate :log, to: :class @@ -59,11 +65,10 @@ module Gitlab import_type: 'bare_repository', namespace_id: group&.id).execute - if project.persisted? && mv_repo(project) + if project.persisted? && mv_repositories(project) log " * Created #{project.name} (#{project_full_path})".color(:green) project.write_repository_config - Gitlab::Git::Repository.create_hooks(project.repository.path_to_repo, Gitlab.config.gitlab_shell.hooks_path) ProjectCacheWorker.perform_async(project.id) else @@ -74,12 +79,11 @@ module Gitlab project end - def mv_repo(project) - storage_path = storage_path_for_shard(project.repository_storage) - FileUtils.mv(repo_path, project.repository.path_to_repo) + def mv_repositories(project) + mv_repo(bare_repo.repo_path, project.repository) if bare_repo.wiki_exists? - FileUtils.mv(wiki_path, File.join(storage_path, project.disk_path + '.wiki.git')) + mv_repo(bare_repo.wiki_path, project.wiki.repository) end true @@ -89,6 +93,11 @@ module Gitlab false end + def mv_repo(path, repository) + repository.create_from_bundle(bundle(path)) + FileUtils.rm_rf(path) + end + def storage_path_for_shard(shard) Gitlab.config.repositories.storages[shard].legacy_disk_path end @@ -101,10 +110,17 @@ module Gitlab Groups::NestedCreateService.new(user, group_path: group_path).execute end - # This is called from within a rake task only used by Admins, so allow writing - # to STDOUT - def self.log(message) - puts message # rubocop:disable Rails/Output + def bundle(repo_path) + # TODO: we could save some time and disk space by using + # `git bundle create - --all` and streaming the bundle directly to + # Gitaly, rather than writing it on disk first + bundle_path = "#{repo_path}.bundle" + cmd = %W(#{Gitlab.config.git.bin_path} --git-dir=#{repo_path} bundle create #{bundle_path} --all) + output, status = Gitlab::Popen.popen(cmd) + + raise output unless status.zero? + + bundle_path end end end diff --git a/lib/gitlab/bare_repository_import/repository.rb b/lib/gitlab/bare_repository_import/repository.rb index fe267248275..c0c666dfb7b 100644 --- a/lib/gitlab/bare_repository_import/repository.rb +++ b/lib/gitlab/bare_repository_import/repository.rb @@ -1,5 +1,3 @@ -# Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/953 -# module Gitlab module BareRepositoryImport class Repository diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb index eb02c7ac8e1..73151e4a4c5 100644 --- a/lib/gitlab/git/repository.rb +++ b/lib/gitlab/git/repository.rb @@ -39,19 +39,6 @@ module Gitlab ChecksumError = Class.new(StandardError) class << self - # Unlike `new`, `create` takes the repository path - def create(repo_path, bare: true, symlink_hooks_to: nil) - FileUtils.mkdir_p(repo_path, mode: 0770) - - # Equivalent to `git --git-path=#{repo_path} init [--bare]` - repo = Rugged::Repository.init_at(repo_path, bare) - repo.close - - create_hooks(repo_path, symlink_hooks_to) if symlink_hooks_to.present? - - true - end - def create_hooks(repo_path, global_hooks_path) local_hooks_path = File.join(repo_path, 'hooks') real_local_hooks_path = :not_found diff --git a/spec/lib/gitlab/bare_repository_import/importer_spec.rb b/spec/lib/gitlab/bare_repository_import/importer_spec.rb index 468f6ff6d24..6e21c846c0a 100644 --- a/spec/lib/gitlab/bare_repository_import/importer_spec.rb +++ b/spec/lib/gitlab/bare_repository_import/importer_spec.rb @@ -1,10 +1,11 @@ require 'spec_helper' -describe Gitlab::BareRepositoryImport::Importer, repository: true do +describe Gitlab::BareRepositoryImport::Importer, :seed_helper do let!(:admin) { create(:admin) } let!(:base_dir) { Dir.mktmpdir + '/' } let(:bare_repository) { Gitlab::BareRepositoryImport::Repository.new(base_dir, File.join(base_dir, "#{project_path}.git")) } let(:gitlab_shell) { Gitlab::Shell.new } + let(:source_project) { TEST_REPO_PATH } subject(:importer) { described_class.new(admin, bare_repository) } @@ -17,16 +18,11 @@ describe Gitlab::BareRepositoryImport::Importer, repository: true do after do FileUtils.rm_rf(base_dir) + TestEnv.clean_test_path + ensure_seeds Rainbow.enabled = @rainbow end - around do |example| - # TODO migrate BareRepositoryImport https://gitlab.com/gitlab-org/gitaly/issues/953 - Gitlab::GitalyClient::StorageSettings.allow_disk_access do - example.run - end - end - shared_examples 'importing a repository' do describe '.execute' do it 'creates a project for a repository in storage' do @@ -86,8 +82,8 @@ describe Gitlab::BareRepositoryImport::Importer, repository: true do importer.create_project_if_needed end - it 'creates the Git repo on disk with the proper symlink for hooks' do - create_bare_repository("#{project_path}.git") + it 'creates the Git repo on disk' do + prepare_repository("#{project_path}.git", source_project) importer.create_project_if_needed @@ -97,9 +93,6 @@ describe Gitlab::BareRepositoryImport::Importer, repository: true do expect(gitlab_shell.exists?(project.repository_storage, repo_path)).to be(true) expect(gitlab_shell.exists?(project.repository_storage, hook_path)).to be(true) - - full_hook_path = File.join(project.repository.path_to_repo, 'hooks') - expect(File.readlink(full_hook_path)).to eq(Gitlab.config.gitlab_shell.hooks_path) end context 'hashed storage enabled' do @@ -148,7 +141,7 @@ describe Gitlab::BareRepositoryImport::Importer, repository: true do end it 'creates the Git repo in disk' do - create_bare_repository("#{project_path}.git") + prepare_repository("#{project_path}.git", source_project) importer.create_project_if_needed @@ -158,23 +151,23 @@ describe Gitlab::BareRepositoryImport::Importer, repository: true do expect(gitlab_shell.exists?(project.repository_storage, project.disk_path + '.wiki.git')).to be(true) end - it 'moves an existing project to the correct path' do + context 'with a repository already on disk' do + let!(:base_dir) { TestEnv.repos_path } # This is a quick way to get a valid repository instead of copying an # existing one. Since it's not persisted, the importer will try to # create the project. - project = build(:project, :legacy_storage, :repository) - original_commit_count = project.repository.commit_count - - legacy_path = Gitlab.config.repositories.storages[project.repository_storage].legacy_disk_path + let(:project) { build(:project, :legacy_storage, :repository) } + let(:project_path) { project.full_path } - bare_repo = Gitlab::BareRepositoryImport::Repository.new(legacy_path, project.repository.path) - gitlab_importer = described_class.new(admin, bare_repo) + it 'moves an existing project to the correct path' do + original_commit_count = project.repository.commit_count - expect(gitlab_importer).to receive(:create_project).and_call_original + expect(importer).to receive(:create_project).and_call_original - new_project = gitlab_importer.create_project_if_needed + new_project = importer.create_project_if_needed - expect(new_project.repository.commit_count).to eq(original_commit_count) + expect(new_project.repository.commit_count).to eq(original_commit_count) + end end end @@ -185,8 +178,8 @@ describe Gitlab::BareRepositoryImport::Importer, repository: true do it_behaves_like 'importing a repository' it 'creates the Wiki git repo in disk' do - create_bare_repository("#{project_path}.git") - create_bare_repository("#{project_path}.wiki.git") + prepare_repository("#{project_path}.git", source_project) + prepare_repository("#{project_path}.wiki.git", source_project) expect(Projects::CreateService).to receive(:new).with(admin, hash_including(skip_wiki: true, import_type: 'bare_repository')).and_call_original @@ -213,8 +206,13 @@ describe Gitlab::BareRepositoryImport::Importer, repository: true do end end - def create_bare_repository(project_path) + def prepare_repository(project_path, source_project) repo_path = File.join(base_dir, project_path) - Gitlab::Git::Repository.create(repo_path, bare: true) + + return create_bare_repository(repo_path) unless source_project + + cmd = %W(#{Gitlab.config.git.bin_path} clone --bare #{source_project} #{repo_path}) + + system(git_env, *cmd, chdir: SEED_STORAGE_PATH, out: '/dev/null', err: '/dev/null') end end diff --git a/spec/lib/gitlab/git/attributes_at_ref_parser_spec.rb b/spec/lib/gitlab/git/attributes_at_ref_parser_spec.rb index 5d22dcfb508..ca067a29174 100644 --- a/spec/lib/gitlab/git/attributes_at_ref_parser_spec.rb +++ b/spec/lib/gitlab/git/attributes_at_ref_parser_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Git::AttributesAtRefParser, seed_helper: true do +describe Gitlab::Git::AttributesAtRefParser, :seed_helper do let(:project) { create(:project, :repository) } let(:repository) { project.repository } diff --git a/spec/lib/gitlab/git/attributes_parser_spec.rb b/spec/lib/gitlab/git/attributes_parser_spec.rb index 2d103123998..18ebfef38f0 100644 --- a/spec/lib/gitlab/git/attributes_parser_spec.rb +++ b/spec/lib/gitlab/git/attributes_parser_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Git::AttributesParser, seed_helper: true do +describe Gitlab::Git::AttributesParser, :seed_helper do let(:attributes_path) { File.join(SEED_STORAGE_PATH, 'with-git-attributes.git', 'info', 'attributes') } let(:data) { File.read(attributes_path) } diff --git a/spec/lib/gitlab/git/blame_spec.rb b/spec/lib/gitlab/git/blame_spec.rb index ba790b717ae..e704d1c673c 100644 --- a/spec/lib/gitlab/git/blame_spec.rb +++ b/spec/lib/gitlab/git/blame_spec.rb @@ -1,7 +1,7 @@ # coding: utf-8 require "spec_helper" -describe Gitlab::Git::Blame, seed_helper: true do +describe Gitlab::Git::Blame, :seed_helper do let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') } let(:blame) do Gitlab::Git::Blame.new(repository, SeedRepo::Commit::ID, "CONTRIBUTING.md") diff --git a/spec/lib/gitlab/git/blob_snippet_spec.rb b/spec/lib/gitlab/git/blob_snippet_spec.rb index d6d365f6492..6effec8295c 100644 --- a/spec/lib/gitlab/git/blob_snippet_spec.rb +++ b/spec/lib/gitlab/git/blob_snippet_spec.rb @@ -2,7 +2,7 @@ require "spec_helper" -describe Gitlab::Git::BlobSnippet, seed_helper: true do +describe Gitlab::Git::BlobSnippet, :seed_helper do describe '#data' do context 'empty lines' do let(:snippet) { Gitlab::Git::BlobSnippet.new('master', nil, nil, nil) } diff --git a/spec/lib/gitlab/git/blob_spec.rb b/spec/lib/gitlab/git/blob_spec.rb index 034b89a46fa..ea49502ae2e 100644 --- a/spec/lib/gitlab/git/blob_spec.rb +++ b/spec/lib/gitlab/git/blob_spec.rb @@ -2,7 +2,7 @@ require "spec_helper" -describe Gitlab::Git::Blob, seed_helper: true do +describe Gitlab::Git::Blob, :seed_helper do let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') } describe 'initialize' do diff --git a/spec/lib/gitlab/git/branch_spec.rb b/spec/lib/gitlab/git/branch_spec.rb index a8c5627e678..79ccbb79966 100644 --- a/spec/lib/gitlab/git/branch_spec.rb +++ b/spec/lib/gitlab/git/branch_spec.rb @@ -1,6 +1,6 @@ require "spec_helper" -describe Gitlab::Git::Branch, seed_helper: true do +describe Gitlab::Git::Branch, :seed_helper do let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') } let(:rugged) do Gitlab::GitalyClient::StorageSettings.allow_disk_access do diff --git a/spec/lib/gitlab/git/commit_spec.rb b/spec/lib/gitlab/git/commit_spec.rb index 0adb684765d..2718a3c5e49 100644 --- a/spec/lib/gitlab/git/commit_spec.rb +++ b/spec/lib/gitlab/git/commit_spec.rb @@ -1,6 +1,6 @@ require "spec_helper" -describe Gitlab::Git::Commit, seed_helper: true do +describe Gitlab::Git::Commit, :seed_helper do let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') } let(:commit) { described_class.find(repository, SeedRepo::Commit::ID) } let(:rugged_commit) do diff --git a/spec/lib/gitlab/git/committer_with_hooks_spec.rb b/spec/lib/gitlab/git/committer_with_hooks_spec.rb index 2100690f873..c7626058acd 100644 --- a/spec/lib/gitlab/git/committer_with_hooks_spec.rb +++ b/spec/lib/gitlab/git/committer_with_hooks_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Git::CommitterWithHooks, seed_helper: true do +describe Gitlab::Git::CommitterWithHooks, :seed_helper do # TODO https://gitlab.com/gitlab-org/gitaly/issues/1234 skip 'needs to be moved to gitaly-ruby test suite' do shared_examples 'calling wiki hooks' do diff --git a/spec/lib/gitlab/git/compare_spec.rb b/spec/lib/gitlab/git/compare_spec.rb index b6a42e422b5..7cc6f52f8ee 100644 --- a/spec/lib/gitlab/git/compare_spec.rb +++ b/spec/lib/gitlab/git/compare_spec.rb @@ -1,6 +1,6 @@ require "spec_helper" -describe Gitlab::Git::Compare, seed_helper: true do +describe Gitlab::Git::Compare, :seed_helper do let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') } let(:compare) { Gitlab::Git::Compare.new(repository, SeedRepo::BigCommit::ID, SeedRepo::Commit::ID, straight: false) } let(:compare_straight) { Gitlab::Git::Compare.new(repository, SeedRepo::BigCommit::ID, SeedRepo::Commit::ID, straight: true) } diff --git a/spec/lib/gitlab/git/diff_collection_spec.rb b/spec/lib/gitlab/git/diff_collection_spec.rb index 65edc750f39..81658874be7 100644 --- a/spec/lib/gitlab/git/diff_collection_spec.rb +++ b/spec/lib/gitlab/git/diff_collection_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Git::DiffCollection, seed_helper: true do +describe Gitlab::Git::DiffCollection, :seed_helper do subject do Gitlab::Git::DiffCollection.new( iterator, diff --git a/spec/lib/gitlab/git/diff_spec.rb b/spec/lib/gitlab/git/diff_spec.rb index 11ab376ab8f..87d9fcee39e 100644 --- a/spec/lib/gitlab/git/diff_spec.rb +++ b/spec/lib/gitlab/git/diff_spec.rb @@ -1,6 +1,6 @@ require "spec_helper" -describe Gitlab::Git::Diff, seed_helper: true do +describe Gitlab::Git::Diff, :seed_helper do let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') } before do diff --git a/spec/lib/gitlab/git/hooks_service_spec.rb b/spec/lib/gitlab/git/hooks_service_spec.rb index 9337aa39e13..55ffced36ac 100644 --- a/spec/lib/gitlab/git/hooks_service_spec.rb +++ b/spec/lib/gitlab/git/hooks_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Git::HooksService, seed_helper: true do +describe Gitlab::Git::HooksService, :seed_helper do let(:gl_id) { 'user-456' } let(:gl_username) { 'janedoe' } let(:user) { Gitlab::Git::User.new(gl_username, 'Jane Doe', 'janedoe@example.com', gl_id) } diff --git a/spec/lib/gitlab/git/index_spec.rb b/spec/lib/gitlab/git/index_spec.rb index e51b875be11..c4edd6961e1 100644 --- a/spec/lib/gitlab/git/index_spec.rb +++ b/spec/lib/gitlab/git/index_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Git::Index, seed_helper: true do +describe Gitlab::Git::Index, :seed_helper do let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') } let(:index) { described_class.new(repository) } diff --git a/spec/lib/gitlab/git/remote_repository_spec.rb b/spec/lib/gitlab/git/remote_repository_spec.rb index eb148cc3804..53ed7c5a13a 100644 --- a/spec/lib/gitlab/git/remote_repository_spec.rb +++ b/spec/lib/gitlab/git/remote_repository_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Git::RemoteRepository, seed_helper: true do +describe Gitlab::Git::RemoteRepository, :seed_helper do let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') } subject { described_class.new(repository) } diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index 62396af1ebe..35a6fc94753 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -1,7 +1,7 @@ # coding: utf-8 require "spec_helper" -describe Gitlab::Git::Repository, seed_helper: true do +describe Gitlab::Git::Repository, :seed_helper do include Gitlab::EncodingHelper using RSpec::Parameterized::TableSyntax diff --git a/spec/lib/gitlab/git/tag_spec.rb b/spec/lib/gitlab/git/tag_spec.rb index be2f5bfb819..2d9db576a6c 100644 --- a/spec/lib/gitlab/git/tag_spec.rb +++ b/spec/lib/gitlab/git/tag_spec.rb @@ -1,6 +1,6 @@ require "spec_helper" -describe Gitlab::Git::Tag, seed_helper: true do +describe Gitlab::Git::Tag, :seed_helper do let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') } shared_examples 'Gitlab::Git::Repository#tags' do diff --git a/spec/lib/gitlab/git/tree_spec.rb b/spec/lib/gitlab/git/tree_spec.rb index 001e406a930..3792d6bf67b 100644 --- a/spec/lib/gitlab/git/tree_spec.rb +++ b/spec/lib/gitlab/git/tree_spec.rb @@ -1,6 +1,6 @@ require "spec_helper" -describe Gitlab::Git::Tree, seed_helper: true do +describe Gitlab::Git::Tree, :seed_helper do let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') } context :repo do diff --git a/spec/support/helpers/seed_helper.rb b/spec/support/helpers/seed_helper.rb index 8fd107260cc..25781f5e679 100644 --- a/spec/support/helpers/seed_helper.rb +++ b/spec/support/helpers/seed_helper.rb @@ -101,10 +101,4 @@ bla/bla.txt handle.write('# hello'.encode(enc)) end end - - # Prevent developer git configurations from being persisted to test - # repositories - def git_env - { 'GIT_TEMPLATE_DIR' => '' } - end end diff --git a/spec/support/helpers/test_env.rb b/spec/support/helpers/test_env.rb index e531495d917..8e1d4cfe269 100644 --- a/spec/support/helpers/test_env.rb +++ b/spec/support/helpers/test_env.rb @@ -243,6 +243,14 @@ module TestEnv set_repo_refs(target_repo_path, refs) end + def create_bare_repository(path) + FileUtils.mkdir_p(path) + + system(git_env, *%W(#{Gitlab.config.git.bin_path} -C #{path} init --bare), + out: '/dev/null', + err: '/dev/null') + end + def repos_path @repos_path ||= Gitlab.config.repositories.storages[REPOS_STORAGE].legacy_disk_path end diff --git a/spec/support/stored_repositories.rb b/spec/support/stored_repositories.rb index 21995c89a6e..26f823cb6ef 100644 --- a/spec/support/stored_repositories.rb +++ b/spec/support/stored_repositories.rb @@ -1,8 +1,4 @@ RSpec.configure do |config| - config.before(:each, :repository) do - TestEnv.clean_test_path - end - config.before(:all, :broken_storage) do FileUtils.rm_rf Gitlab.config.repositories.storages.broken.legacy_disk_path end -- cgit v1.2.1 From 4402e6080551d21870a61178a9a4a291b67faa94 Mon Sep 17 00:00:00 2001 From: John Burak Date: Tue, 31 Jul 2018 20:45:40 +0000 Subject: Update two_factor_authentication.md --- doc/user/profile/account/two_factor_authentication.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/user/profile/account/two_factor_authentication.md b/doc/user/profile/account/two_factor_authentication.md index d3a2a7dcd14..e25e1e19b13 100644 --- a/doc/user/profile/account/two_factor_authentication.md +++ b/doc/user/profile/account/two_factor_authentication.md @@ -88,7 +88,7 @@ storage in a safe place. **Each code can be used only once** to log in to your account. If you lose the recovery codes or just want to generate new ones, you can do so -from the **Profile settings âž” Account** page where you first enabled 2FA. +[using SSH](#generate-new-recovery-codes-using-ssh). ## Logging in with 2FA Enabled -- cgit v1.2.1 From fc4a0cd055643d6d66ed0dc8f0ba3ad5f09f6a95 Mon Sep 17 00:00:00 2001 From: Oswaldo Ferreira Date: Tue, 31 Jul 2018 12:47:22 -0300 Subject: Only serializes diff files found by paths query --- app/models/merge_request_diff.rb | 8 +++----- changelogs/unreleased/48246-osw-load-diffs-improvement.yml | 5 +++++ spec/models/merge_request_diff_spec.rb | 7 +++++++ 3 files changed, 15 insertions(+), 5 deletions(-) create mode 100644 changelogs/unreleased/48246-osw-load-diffs-improvement.yml diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb index a073bbfad20..dbc072c19a9 100644 --- a/app/models/merge_request_diff.rb +++ b/app/models/merge_request_diff.rb @@ -249,15 +249,13 @@ class MergeRequestDiff < ActiveRecord::Base end def load_diffs(options) - raw = merge_request_diff_files.map(&:to_hash) + collection = merge_request_diff_files if paths = options[:paths] - raw = raw.select do |diff| - paths.include?(diff[:old_path]) || paths.include?(diff[:new_path]) - end + collection = collection.where('old_path IN (?) OR new_path IN (?)', paths, paths) end - Gitlab::Git::DiffCollection.new(raw, options) + Gitlab::Git::DiffCollection.new(collection.map(&:to_hash), options) end def load_commits diff --git a/changelogs/unreleased/48246-osw-load-diffs-improvement.yml b/changelogs/unreleased/48246-osw-load-diffs-improvement.yml new file mode 100644 index 00000000000..c4292ab0d29 --- /dev/null +++ b/changelogs/unreleased/48246-osw-load-diffs-improvement.yml @@ -0,0 +1,5 @@ +--- +title: Improve performance when fetching collapsed diffs and commenting in merge requests +merge_request: 20940 +author: +type: performance diff --git a/spec/models/merge_request_diff_spec.rb b/spec/models/merge_request_diff_spec.rb index 0aee78ac12d..90cce826b6c 100644 --- a/spec/models/merge_request_diff_spec.rb +++ b/spec/models/merge_request_diff_spec.rb @@ -127,6 +127,13 @@ describe MergeRequestDiff do expect(diffs.map(&:new_path)).to contain_exactly('files/ruby/popen.rb') end + it 'only serializes diff files found by query' do + expect(diff_with_commits.merge_request_diff_files.count).to be > 10 + expect_any_instance_of(MergeRequestDiffFile).to receive(:to_hash).once + + diffs + end + it 'uses the diffs from the DB' do expect(diff_with_commits).to receive(:load_diffs) -- cgit v1.2.1 From d60f7d0d46a931c92b5d7b50991009a7d9b5612e Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Wed, 1 Aug 2018 20:11:42 +0100 Subject: Automatically expand runner's settings block when linking to the runner's settings page by using page anchor on the link --- app/views/projects/jobs/show.html.haml | 2 +- app/views/projects/settings/ci_cd/show.html.haml | 2 +- changelogs/unreleased/49851-link-to-runners.yml | 6 ++++++ 3 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 changelogs/unreleased/49851-link-to-runners.yml diff --git a/app/views/projects/jobs/show.html.haml b/app/views/projects/jobs/show.html.haml index 1f33bb3a129..078f40c4477 100644 --- a/app/views/projects/jobs/show.html.haml +++ b/app/views/projects/jobs/show.html.haml @@ -22,7 +22,7 @@ %br Go to - = link_to project_runners_path(@build.project) do + = link_to project_runners_path(@build.project, anchor: 'js-runners-settings') do Runners page - if @build.starts_environment? diff --git a/app/views/projects/settings/ci_cd/show.html.haml b/app/views/projects/settings/ci_cd/show.html.haml index be22bbd7a9b..e9f5e62f5e7 100644 --- a/app/views/projects/settings/ci_cd/show.html.haml +++ b/app/views/projects/settings/ci_cd/show.html.haml @@ -28,7 +28,7 @@ .settings-content = render 'autodevops_form' -%section.qa-runners-settings.settings.no-animate{ class: ('expanded' if expanded) } +%section.qa-runners-settings.settings.no-animate#js-runners-settings{ class: ('expanded' if expanded) } .settings-header %h4 = _("Runners") diff --git a/changelogs/unreleased/49851-link-to-runners.yml b/changelogs/unreleased/49851-link-to-runners.yml new file mode 100644 index 00000000000..89fd6853bc8 --- /dev/null +++ b/changelogs/unreleased/49851-link-to-runners.yml @@ -0,0 +1,6 @@ +--- +title: Automatically expand runner's settings block when linking to the runner's settings + page +merge_request: +author: +type: other -- cgit v1.2.1 From 0c14042af3ade3d653201029c9b946fb60da83e1 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Wed, 1 Aug 2018 12:56:53 -0700 Subject: Rework performance section to allow for string freezing Relates to #47424 --- doc/development/performance.md | 54 ++++++++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 23 deletions(-) diff --git a/doc/development/performance.md b/doc/development/performance.md index c4162a05b77..6b4cb6d72d1 100644 --- a/doc/development/performance.md +++ b/doc/development/performance.md @@ -347,13 +347,7 @@ def expire_first_branch_cache end ``` -## Anti-Patterns - -This is a collection of [anti-patterns][anti-pattern] that should be avoided -unless these changes have a measurable, significant and positive impact on -production environments. - -### String Freezing +## String Freezing In recent Ruby versions calling `freeze` on a String leads to it being allocated only once and re-used. For example, on Ruby 2.3 this will only allocate the @@ -365,17 +359,38 @@ only once and re-used. For example, on Ruby 2.3 this will only allocate the end ``` -Blindly adding a `.freeze` call to every String is an anti-pattern that should -be avoided unless one can prove (using production data) the call actually has a -positive impact on performance. +Depending on the size of the String and how frequently it would be allocated +(before the `.freeze` call was added), this _may_ make things faster, but +there's no guarantee it will. + +Strings will be frozen by default in Ruby 3.0. To prepare our code base for +this eventuality, it's a good practice to add the following header to all +Ruby files: + +```ruby +# frozen_string_literal: true +``` + +This may cause test failures in the code that expects to be able to manipulate +strings. Instead of using `dup`, use the unary plus to get an unfrozen string: + +```ruby +test = +"hello" +test += " world" +``` + +## Anti-Patterns -This feature of Ruby wasn't really meant to make things faster directly, instead -it was meant to reduce the number of allocations. Depending on the size of the -String and how frequently it would be allocated (before the `.freeze` call was -added), this _may_ make things faster, but there's no guarantee it will. +This is a collection of [anti-patterns][anti-pattern] that should be avoided +unless these changes have a measurable, significant and positive impact on +production environments. -Another common flavour of this is to not only freeze a String, but also assign -it to a constant, for example: +### Moving Allocations to Constants + +Storing an object as a constant so you only allocate it once _may_ improve +performance, but there's no guarantee this will. Looking up constants has an +impact on runtime performance, and as such, using a constant instead of +referencing an object directly may even slow code down. For example: ```ruby SOME_CONSTANT = 'foo'.freeze @@ -393,13 +408,6 @@ there's nothing stopping somebody from doing this elsewhere in the code: SOME_CONSTANT = 'bar' ``` -### Moving Allocations to Constants - -Storing an object as a constant so you only allocate it once _may_ improve -performance, but there's no guarantee this will. Looking up constants has an -impact on runtime performance, and as such, using a constant instead of -referencing an object directly may even slow code down. - [#15607]: https://gitlab.com/gitlab-org/gitlab-ce/issues/15607 [yorickpeterse]: https://gitlab.com/yorickpeterse [anti-pattern]: https://en.wikipedia.org/wiki/Anti-pattern -- cgit v1.2.1 From 0cd76190deb01db8ff080186f3daa5b6067a27f6 Mon Sep 17 00:00:00 2001 From: Thong Kuah Date: Sun, 22 Jul 2018 22:48:53 +1200 Subject: Lock helm charts to the VERSION already specified for each application. Fix up VERSION for each of the applications * There is no 0.0.1 helm version for jupyterhub. Use the latest version instead * `:nginx` is not a valid chart version. Lock the ingress application GitLab installs to the latest chart version. * Use the latest gitlab-runner chart to prevent GitLab installing older versions when users have been installing the lastest version Always install from the VERSION and not the database `version` column. This should fix cases like https://gitlab.com/gitlab-org/gitlab-ee/issues/6795 in the instances where an install command failed previously, which locked the version in the database to an older version. Also, ensure that the version column is updated to the version we are installing. Add specs to show how previously failed appplications will be handled when the helm installation is run again Add changelog entry --- app/models/clusters/applications/ingress.rb | 6 +++++- app/models/clusters/applications/jupyter.rb | 4 +++- app/models/clusters/applications/prometheus.rb | 3 ++- app/models/clusters/applications/runner.rb | 4 +++- .../clusters/concerns/application_version.rb | 17 +++++++++++++++ ...ck-install-buttons-should-be-version-locked.yml | 6 ++++++ .../gitlab/kubernetes/helm/install_command_spec.rb | 6 +++--- spec/models/clusters/applications/ingress_spec.rb | 24 +++++++++++++++++++++- spec/models/clusters/applications/jupyter_spec.rb | 24 +++++++++++++++++++++- .../clusters/applications/prometheus_spec.rb | 22 ++++++++++++++++++++ spec/models/clusters/applications/runner_spec.rb | 24 +++++++++++++++++++++- 11 files changed, 130 insertions(+), 10 deletions(-) create mode 100644 app/models/clusters/concerns/application_version.rb create mode 100644 changelogs/unreleased/48834-chart-versions-for-applications-installed-by-one-click-install-buttons-should-be-version-locked.yml diff --git a/app/models/clusters/applications/ingress.rb b/app/models/clusters/applications/ingress.rb index 27fc3b85465..4a8fd9a0b8c 100644 --- a/app/models/clusters/applications/ingress.rb +++ b/app/models/clusters/applications/ingress.rb @@ -1,15 +1,18 @@ module Clusters module Applications class Ingress < ActiveRecord::Base + VERSION = '0.23.0'.freeze + self.table_name = 'clusters_applications_ingress' include ::Clusters::Concerns::ApplicationCore include ::Clusters::Concerns::ApplicationStatus + include ::Clusters::Concerns::ApplicationVersion include ::Clusters::Concerns::ApplicationData include AfterCommitQueue default_value_for :ingress_type, :nginx - default_value_for :version, :nginx + default_value_for :version, VERSION enum ingress_type: { nginx: 1 @@ -33,6 +36,7 @@ module Clusters def install_command Gitlab::Kubernetes::Helm::InstallCommand.new( name, + version: VERSION, chart: chart, values: values ) diff --git a/app/models/clusters/applications/jupyter.rb b/app/models/clusters/applications/jupyter.rb index 975d434e1a4..72dd734246b 100644 --- a/app/models/clusters/applications/jupyter.rb +++ b/app/models/clusters/applications/jupyter.rb @@ -1,12 +1,13 @@ module Clusters module Applications class Jupyter < ActiveRecord::Base - VERSION = '0.0.1'.freeze + VERSION = 'v0.6'.freeze self.table_name = 'clusters_applications_jupyter' include ::Clusters::Concerns::ApplicationCore include ::Clusters::Concerns::ApplicationStatus + include ::Clusters::Concerns::ApplicationVersion include ::Clusters::Concerns::ApplicationData belongs_to :oauth_application, class_name: 'Doorkeeper::Application' @@ -36,6 +37,7 @@ module Clusters def install_command Gitlab::Kubernetes::Helm::InstallCommand.new( name, + version: VERSION, chart: chart, values: values, repository: repository diff --git a/app/models/clusters/applications/prometheus.rb b/app/models/clusters/applications/prometheus.rb index ea6ec4d6b03..53ac9199ae2 100644 --- a/app/models/clusters/applications/prometheus.rb +++ b/app/models/clusters/applications/prometheus.rb @@ -9,6 +9,7 @@ module Clusters include ::Clusters::Concerns::ApplicationCore include ::Clusters::Concerns::ApplicationStatus + include ::Clusters::Concerns::ApplicationVersion include ::Clusters::Concerns::ApplicationData default_value_for :version, VERSION @@ -44,8 +45,8 @@ module Clusters def install_command Gitlab::Kubernetes::Helm::InstallCommand.new( name, + version: VERSION, chart: chart, - version: version, values: values ) end diff --git a/app/models/clusters/applications/runner.rb b/app/models/clusters/applications/runner.rb index e6f795f3e0b..6d97dd1448a 100644 --- a/app/models/clusters/applications/runner.rb +++ b/app/models/clusters/applications/runner.rb @@ -1,12 +1,13 @@ module Clusters module Applications class Runner < ActiveRecord::Base - VERSION = '0.1.13'.freeze + VERSION = '0.1.31'.freeze self.table_name = 'clusters_applications_runners' include ::Clusters::Concerns::ApplicationCore include ::Clusters::Concerns::ApplicationStatus + include ::Clusters::Concerns::ApplicationVersion include ::Clusters::Concerns::ApplicationData belongs_to :runner, class_name: 'Ci::Runner', foreign_key: :runner_id @@ -29,6 +30,7 @@ module Clusters def install_command Gitlab::Kubernetes::Helm::InstallCommand.new( name, + version: VERSION, chart: chart, values: values, repository: repository diff --git a/app/models/clusters/concerns/application_version.rb b/app/models/clusters/concerns/application_version.rb new file mode 100644 index 00000000000..ccad74dc35a --- /dev/null +++ b/app/models/clusters/concerns/application_version.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +module Clusters + module Concerns + module ApplicationVersion + extend ActiveSupport::Concern + + included do + state_machine :status do + after_transition any => [:installing] do |application| + application.update(version: application.class.const_get(:VERSION)) + end + end + end + end + end +end diff --git a/changelogs/unreleased/48834-chart-versions-for-applications-installed-by-one-click-install-buttons-should-be-version-locked.yml b/changelogs/unreleased/48834-chart-versions-for-applications-installed-by-one-click-install-buttons-should-be-version-locked.yml new file mode 100644 index 00000000000..d79b95411aa --- /dev/null +++ b/changelogs/unreleased/48834-chart-versions-for-applications-installed-by-one-click-install-buttons-should-be-version-locked.yml @@ -0,0 +1,6 @@ +--- +title: Chart versions for applications installed by one click install buttons should + be version locked +merge_request: 20765 +author: +type: fixed diff --git a/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb b/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb index 25c6fa3b9a3..cd456a45287 100644 --- a/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb +++ b/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb @@ -14,7 +14,7 @@ describe Gitlab::Kubernetes::Helm::InstallCommand do let(:commands) do <<~EOS helm init --client-only >/dev/null - helm install #{application.chart} --name #{application.name} --namespace #{namespace} -f /data/helm/#{application.name}/config/values.yaml >/dev/null + helm install #{application.chart} --name #{application.name} --version #{application.version} --namespace #{namespace} -f /data/helm/#{application.name}/config/values.yaml >/dev/null EOS end end @@ -42,7 +42,7 @@ describe Gitlab::Kubernetes::Helm::InstallCommand do <<~EOS helm init --client-only >/dev/null helm repo add #{application.name} #{application.repository} - helm install #{application.chart} --name #{application.name} --namespace #{namespace} -f /data/helm/#{application.name}/config/values.yaml >/dev/null + helm install #{application.chart} --name #{application.name} --version #{application.version} --namespace #{namespace} -f /data/helm/#{application.name}/config/values.yaml >/dev/null EOS end end @@ -56,7 +56,7 @@ describe Gitlab::Kubernetes::Helm::InstallCommand do <<~EOS helm init --client-only >/dev/null helm repo add #{application.name} #{application.repository} - helm install #{application.chart} --name #{application.name} --namespace #{namespace} -f /data/helm/#{application.name}/config/values.yaml >/dev/null + helm install #{application.chart} --name #{application.name} --version #{application.version} --namespace #{namespace} -f /data/helm/#{application.name}/config/values.yaml >/dev/null EOS end end diff --git a/spec/models/clusters/applications/ingress_spec.rb b/spec/models/clusters/applications/ingress_spec.rb index bb5b2ef3a47..d378248d5d6 100644 --- a/spec/models/clusters/applications/ingress_spec.rb +++ b/spec/models/clusters/applications/ingress_spec.rb @@ -23,6 +23,20 @@ describe Clusters::Applications::Ingress do it { is_expected.to contain_exactly(cluster) } end + describe '#make_installing!' do + before do + application.make_installing! + end + + context 'application install previously errored with older version' do + let(:application) { create(:clusters_applications_ingress, :scheduled, version: '0.22.0') } + + it 'updates the application version' do + expect(application.reload.version).to eq('0.23.0') + end + end + end + describe '#make_installed!' do before do application.make_installed! @@ -73,9 +87,17 @@ describe Clusters::Applications::Ingress do it 'should be initialized with ingress arguments' do expect(subject.name).to eq('ingress') expect(subject.chart).to eq('stable/nginx-ingress') - expect(subject.version).to be_nil + expect(subject.version).to eq('0.23.0') expect(subject.values).to eq(ingress.values) end + + context 'application failed to install previously' do + let(:ingress) { create(:clusters_applications_ingress, :errored, version: 'nginx') } + + it 'should be initialized with the locked version' do + expect(subject.version).to eq('0.23.0') + end + end end describe '#values' do diff --git a/spec/models/clusters/applications/jupyter_spec.rb b/spec/models/clusters/applications/jupyter_spec.rb index 65750141e65..e0d57ac65f7 100644 --- a/spec/models/clusters/applications/jupyter_spec.rb +++ b/spec/models/clusters/applications/jupyter_spec.rb @@ -25,6 +25,20 @@ describe Clusters::Applications::Jupyter do end end + describe '#make_installing!' do + before do + application.make_installing! + end + + context 'application install previously errored with older version' do + let(:application) { create(:clusters_applications_jupyter, :scheduled, version: 'v0.5') } + + it 'updates the application version' do + expect(application.reload.version).to eq('v0.6') + end + end + end + describe '#install_command' do let!(:ingress) { create(:clusters_applications_ingress, :installed, external_ip: '127.0.0.1') } let!(:jupyter) { create(:clusters_applications_jupyter, cluster: ingress.cluster) } @@ -36,10 +50,18 @@ describe Clusters::Applications::Jupyter do it 'should be initialized with 4 arguments' do expect(subject.name).to eq('jupyter') expect(subject.chart).to eq('jupyter/jupyterhub') - expect(subject.version).to be_nil + expect(subject.version).to eq('v0.6') expect(subject.repository).to eq('https://jupyterhub.github.io/helm-chart/') expect(subject.values).to eq(jupyter.values) end + + context 'application failed to install previously' do + let(:jupyter) { create(:clusters_applications_jupyter, :errored, version: '0.0.1') } + + it 'should be initialized with the locked version' do + expect(subject.version).to eq('v0.6') + end + end end describe '#values' do diff --git a/spec/models/clusters/applications/prometheus_spec.rb b/spec/models/clusters/applications/prometheus_spec.rb index e4b61552033..3812c65b3b6 100644 --- a/spec/models/clusters/applications/prometheus_spec.rb +++ b/spec/models/clusters/applications/prometheus_spec.rb @@ -16,6 +16,20 @@ describe Clusters::Applications::Prometheus do it { is_expected.to contain_exactly(cluster) } end + describe '#make_installing!' do + before do + application.make_installing! + end + + context 'application install previously errored with older version' do + let(:application) { create(:clusters_applications_prometheus, :scheduled, version: '6.7.2') } + + it 'updates the application version' do + expect(application.reload.version).to eq('6.7.3') + end + end + end + describe 'transition to installed' do let(:project) { create(:project) } let(:cluster) { create(:cluster, projects: [project]) } @@ -155,6 +169,14 @@ describe Clusters::Applications::Prometheus do expect(command.version).to eq('6.7.3') expect(command.values).to eq(prometheus.values) end + + context 'application failed to install previously' do + let(:prometheus) { create(:clusters_applications_prometheus, :errored, version: '2.0.0') } + + it 'should be initialized with the locked version' do + expect(subject.version).to eq('6.7.3') + end + end end describe '#values' do diff --git a/spec/models/clusters/applications/runner_spec.rb b/spec/models/clusters/applications/runner_spec.rb index b12500d0acd..526300755b5 100644 --- a/spec/models/clusters/applications/runner_spec.rb +++ b/spec/models/clusters/applications/runner_spec.rb @@ -8,6 +8,20 @@ describe Clusters::Applications::Runner do it { is_expected.to belong_to(:runner) } + describe '#make_installing!' do + before do + application.make_installing! + end + + context 'application install previously errored with older version' do + let(:application) { create(:clusters_applications_runner, :scheduled, version: '0.1.30') } + + it 'updates the application version' do + expect(application.reload.version).to eq('0.1.31') + end + end + end + describe '.installed' do subject { described_class.installed } @@ -31,10 +45,18 @@ describe Clusters::Applications::Runner do it 'should be initialized with 4 arguments' do expect(subject.name).to eq('runner') expect(subject.chart).to eq('runner/gitlab-runner') - expect(subject.version).to be_nil + expect(subject.version).to eq('0.1.31') expect(subject.repository).to eq('https://charts.gitlab.io') expect(subject.values).to eq(gitlab_runner.values) end + + context 'application failed to install previously' do + let(:gitlab_runner) { create(:clusters_applications_runner, :errored, runner: ci_runner, version: '0.1.13') } + + it 'should be initialized with the locked version' do + expect(subject.version).to eq('0.1.31') + end + end end describe '#values' do -- cgit v1.2.1 From 05c7c7e0ef0639fde2d98ad3fe9f09b2297ffa4b Mon Sep 17 00:00:00 2001 From: Thong Kuah Date: Wed, 1 Aug 2018 22:43:27 +1200 Subject: Update Helm Tiller used by gitlab-managed-apps to 2.7.2 --- changelogs/unreleased/49830-use-helm-272.yml | 5 +++++ lib/gitlab/kubernetes/helm.rb | 2 +- spec/support/shared_examples/helm_generated_script.rb | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 changelogs/unreleased/49830-use-helm-272.yml diff --git a/changelogs/unreleased/49830-use-helm-272.yml b/changelogs/unreleased/49830-use-helm-272.yml new file mode 100644 index 00000000000..f6ecc12dbfa --- /dev/null +++ b/changelogs/unreleased/49830-use-helm-272.yml @@ -0,0 +1,5 @@ +--- +title: Use Helm 2.7.2 for GitLab Managed Apps +merge_request: 20956 +author: +type: changed diff --git a/lib/gitlab/kubernetes/helm.rb b/lib/gitlab/kubernetes/helm.rb index 0f0588b8b23..530ccf88053 100644 --- a/lib/gitlab/kubernetes/helm.rb +++ b/lib/gitlab/kubernetes/helm.rb @@ -1,7 +1,7 @@ module Gitlab module Kubernetes module Helm - HELM_VERSION = '2.7.0'.freeze + HELM_VERSION = '2.7.2'.freeze NAMESPACE = 'gitlab-managed-apps'.freeze end end diff --git a/spec/support/shared_examples/helm_generated_script.rb b/spec/support/shared_examples/helm_generated_script.rb index 05d53a13fd3..ef9bb7f5533 100644 --- a/spec/support/shared_examples/helm_generated_script.rb +++ b/spec/support/shared_examples/helm_generated_script.rb @@ -7,7 +7,7 @@ shared_examples 'helm commands' do echo http://mirror.clarkson.edu/alpine/v$ALPINE_VERSION/main >> /etc/apk/repositories echo http://mirror1.hs-esslingen.de/pub/Mirrors/alpine/v$ALPINE_VERSION/main >> /etc/apk/repositories apk add -U wget ca-certificates openssl >/dev/null - wget -q -O - https://kubernetes-helm.storage.googleapis.com/helm-v2.7.0-linux-amd64.tar.gz | tar zxC /tmp >/dev/null + wget -q -O - https://kubernetes-helm.storage.googleapis.com/helm-v2.7.2-linux-amd64.tar.gz | tar zxC /tmp >/dev/null mv /tmp/linux-amd64/helm /usr/bin/ EOS end -- cgit v1.2.1 From b5f4f41252e158874c55df074e84361a36e71501 Mon Sep 17 00:00:00 2001 From: Adam Liter Date: Wed, 1 Aug 2018 21:28:44 -0400 Subject: Fix indentation for artifacts example in docs --- doc/ci/yaml/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index d95f8c7c8cc..95d705d3a3d 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -962,8 +962,8 @@ the binaries directory: ```yaml job: - artifacts: - name: "$CI_COMMIT_REF_NAME" + artifacts: + name: "$CI_COMMIT_REF_NAME" paths: - binaries/ ``` -- cgit v1.2.1 From 283635240a513812a3f164838c492367f0bcbb65 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Thu, 2 Aug 2018 07:59:15 +0000 Subject: Update GITLAB_SHELL_VERSION to 8.1.0 --- GITLAB_SHELL_VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION index ae9a76b9249..8104cabd36f 100644 --- a/GITLAB_SHELL_VERSION +++ b/GITLAB_SHELL_VERSION @@ -1 +1 @@ -8.0.0 +8.1.0 -- cgit v1.2.1 From a0a36a9994fcae93da850a679e475d501442041f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9F=99=88=20=20jacopo=20beschi=20=F0=9F=99=89?= Date: Thu, 2 Aug 2018 09:34:44 +0000 Subject: Resolve "Remove ghost notification settings for groups and projects" --- app/models/user.rb | 2 +- ...notification-settings-for-group-and-project.yml | 5 +++ ...eign_key_from_notification_settings_to_users.rb | 30 ++++++++++++++++++ db/schema.rb | 1 + ...key_from_notification_settings_to_users_spec.rb | 36 ++++++++++++++++++++++ 5 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/44824-remove-ghost-notification-settings-for-group-and-project.yml create mode 100644 db/migrate/20180710162338_add_foreign_key_from_notification_settings_to_users.rb create mode 100644 spec/migrations/add_foreign_key_from_notification_settings_to_users_spec.rb diff --git a/app/models/user.rb b/app/models/user.rb index fdf3618b4dc..37f2e8b680e 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -130,7 +130,7 @@ class User < ActiveRecord::Base has_many :builds, dependent: :nullify, class_name: 'Ci::Build' # rubocop:disable Cop/ActiveRecordDependent has_many :pipelines, dependent: :nullify, class_name: 'Ci::Pipeline' # rubocop:disable Cop/ActiveRecordDependent has_many :todos - has_many :notification_settings, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent + has_many :notification_settings has_many :award_emoji, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :triggers, dependent: :destroy, class_name: 'Ci::Trigger', foreign_key: :owner_id # rubocop:disable Cop/ActiveRecordDependent diff --git a/changelogs/unreleased/44824-remove-ghost-notification-settings-for-group-and-project.yml b/changelogs/unreleased/44824-remove-ghost-notification-settings-for-group-and-project.yml new file mode 100644 index 00000000000..ddc878ee710 --- /dev/null +++ b/changelogs/unreleased/44824-remove-ghost-notification-settings-for-group-and-project.yml @@ -0,0 +1,5 @@ +--- +title: Adds foreign key to notification_settings.user_id +merge_request: 20567 +author: Jacopo Beschi @jacopo-beschi +type: added diff --git a/db/migrate/20180710162338_add_foreign_key_from_notification_settings_to_users.rb b/db/migrate/20180710162338_add_foreign_key_from_notification_settings_to_users.rb new file mode 100644 index 00000000000..91656f194e5 --- /dev/null +++ b/db/migrate/20180710162338_add_foreign_key_from_notification_settings_to_users.rb @@ -0,0 +1,30 @@ +class AddForeignKeyFromNotificationSettingsToUsers < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + class NotificationSetting < ActiveRecord::Base + self.table_name = 'notification_settings' + + include EachBatch + end + + class User < ActiveRecord::Base + self.table_name = 'users' + end + + DOWNTIME = false + + disable_ddl_transaction! + + def up + NotificationSetting.each_batch(of: 1000) do |batch| + batch.where('NOT EXISTS (?)', User.select(1).where('users.id = notification_settings.user_id')) + .delete_all + end + + add_concurrent_foreign_key(:notification_settings, :users, column: :user_id, on_delete: :cascade) + end + + def down + remove_foreign_key(:notification_settings, column: :user_id) + end +end diff --git a/db/schema.rb b/db/schema.rb index 769baa825a5..2bef2971f29 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -2350,6 +2350,7 @@ ActiveRecord::Schema.define(version: 20180726172057) do 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 "notification_settings", "users", name: "fk_0c95e91db7", 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 add_foreign_key "personal_access_tokens", "users" diff --git a/spec/migrations/add_foreign_key_from_notification_settings_to_users_spec.rb b/spec/migrations/add_foreign_key_from_notification_settings_to_users_spec.rb new file mode 100644 index 00000000000..656d4f75e3b --- /dev/null +++ b/spec/migrations/add_foreign_key_from_notification_settings_to_users_spec.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +require 'spec_helper' +require Rails.root.join('db', 'migrate', '20180710162338_add_foreign_key_from_notification_settings_to_users.rb') + +describe AddForeignKeyFromNotificationSettingsToUsers, :migration do + let(:notification_settings) { table(:notification_settings) } + let(:users) { table(:users) } + let(:projects) { table(:projects) } + + before do + users.create!(email: 'email@email.com', name: 'foo', username: 'foo', projects_limit: 0) + projects.create!(name: 'gitlab', path: 'gitlab-org/gitlab-ce', namespace_id: 1) + end + + describe 'removal of orphans without user' do + let!(:notification_setting_without_user) { create_notification_settings!(user_id: 123) } + let!(:notification_setting_with_user) { create_notification_settings!(user_id: users.last.id) } + + it 'removes orphaned notification_settings without user' do + expect { migrate! }.to change { notification_settings.count }.by(-1) + end + + it "doesn't remove notification_settings with valid user" do + expect { migrate! }.not_to change { notification_setting_with_user.reload } + end + end + + def create_notification_settings!(**opts) + notification_settings.create!( + source_id: projects.last.id, + source_type: 'Project', + user_id: users.last.id, + **opts) + end +end -- cgit v1.2.1 From 07009a1f4893652e152794ae8160a2f46e00772c Mon Sep 17 00:00:00 2001 From: James Lopez Date: Thu, 26 Jul 2018 12:55:21 +0200 Subject: Add Object Storage to GitLab project import - Refactor uploads manager - Refactor importer, update import spec - Add more object storage specs --- app/models/import_export_upload.rb | 1 + .../projects/gitlab_projects_import_service.rb | 3 +- app/uploaders/import_export_uploader.rb | 2 +- ...ab-project-import-should-use-object-storage.yml | 5 + lib/gitlab/import_export/command_line_util.rb | 15 +++ lib/gitlab/import_export/file_importer.rb | 24 ++++- lib/gitlab/import_export/importer.rb | 12 ++- lib/gitlab/import_export/uploads_manager.rb | 5 +- lib/gitlab/template_helper.rb | 14 ++- .../import_file_object_storage_spec.rb | 103 ++++++++++++++++++ .../projects/import_export/import_file_spec.rb | 1 + .../file_importer_object_storage_spec.rb | 89 ++++++++++++++++ .../lib/gitlab/import_export/file_importer_spec.rb | 4 +- .../import_export/importer_object_storage_spec.rb | 115 +++++++++++++++++++++ spec/lib/gitlab/import_export/importer_spec.rb | 5 +- spec/requests/api/project_import_spec.rb | 2 + .../gitlab_projects_import_service_spec.rb | 2 +- 17 files changed, 380 insertions(+), 22 deletions(-) create mode 100644 changelogs/unreleased/48773-gitlab-project-import-should-use-object-storage.yml create mode 100644 spec/features/projects/import_export/import_file_object_storage_spec.rb create mode 100644 spec/lib/gitlab/import_export/file_importer_object_storage_spec.rb create mode 100644 spec/lib/gitlab/import_export/importer_object_storage_spec.rb diff --git a/app/models/import_export_upload.rb b/app/models/import_export_upload.rb index 40795d7c42f..f0cc5aafcd4 100644 --- a/app/models/import_export_upload.rb +++ b/app/models/import_export_upload.rb @@ -6,6 +6,7 @@ class ImportExportUpload < ActiveRecord::Base belongs_to :project + # These hold the project Import/Export archives (.tar.gz files) mount_uploader :import_file, ImportExportUploader mount_uploader :export_file, ImportExportUploader diff --git a/app/services/projects/gitlab_projects_import_service.rb b/app/services/projects/gitlab_projects_import_service.rb index 615dccc4685..044afa1d5e1 100644 --- a/app/services/projects/gitlab_projects_import_service.rb +++ b/app/services/projects/gitlab_projects_import_service.rb @@ -15,7 +15,7 @@ module Projects end def execute - prepare_template_environment(template_file&.path) + prepare_template_environment(template_file) prepare_import_params @@ -61,7 +61,6 @@ module Projects if template_file params[:import_type] = 'gitlab_project' - params[:import_source] = import_upload_path end params[:import_data] = { data: data } if data.present? diff --git a/app/uploaders/import_export_uploader.rb b/app/uploaders/import_export_uploader.rb index 7c45ba5ca95..716922bc017 100644 --- a/app/uploaders/import_export_uploader.rb +++ b/app/uploaders/import_export_uploader.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class ImportExportUploader < AttachmentUploader - EXTENSION_WHITELIST = %w[tar.gz].freeze + EXTENSION_WHITELIST = %w[tar.gz gz].freeze def extension_whitelist EXTENSION_WHITELIST diff --git a/changelogs/unreleased/48773-gitlab-project-import-should-use-object-storage.yml b/changelogs/unreleased/48773-gitlab-project-import-should-use-object-storage.yml new file mode 100644 index 00000000000..f298380b920 --- /dev/null +++ b/changelogs/unreleased/48773-gitlab-project-import-should-use-object-storage.yml @@ -0,0 +1,5 @@ +--- +title: Add object storage logic to project import +merge_request: 20773 +author: +type: added diff --git a/lib/gitlab/import_export/command_line_util.rb b/lib/gitlab/import_export/command_line_util.rb index 2f163db936b..3adc44f8044 100644 --- a/lib/gitlab/import_export/command_line_util.rb +++ b/lib/gitlab/import_export/command_line_util.rb @@ -18,6 +18,21 @@ module Gitlab private + def download_or_copy_upload(uploader, upload_path) + if uploader.upload.local? + copy_files(uploader.path, upload_path) + else + download(uploader.url, upload_path) + end + end + + def download(url, upload_path) + File.open(upload_path, 'w') do |file| + # Download (stream) file from the uploader's location + IO.copy_stream(URI.parse(url).open, file) + end + end + def tar_with_options(archive:, dir:, options:) execute(%W(tar -#{options} #{archive} -C #{dir} .)) end diff --git a/lib/gitlab/import_export/file_importer.rb b/lib/gitlab/import_export/file_importer.rb index 4c411f4847e..7fd66b4e244 100644 --- a/lib/gitlab/import_export/file_importer.rb +++ b/lib/gitlab/import_export/file_importer.rb @@ -10,15 +10,18 @@ module Gitlab new(*args).import end - def initialize(archive_file:, shared:) + def initialize(project:, archive_file:, shared:) + @project = project @archive_file = archive_file @shared = shared end def import mkdir_p(@shared.export_path) + mkdir_p(@shared.archive_path) - remove_symlinks! + remove_symlinks + copy_archive wait_for_archived_file do decompress_archive @@ -27,7 +30,8 @@ module Gitlab @shared.error(e) false ensure - remove_symlinks! + remove_import_file + remove_symlinks end private @@ -51,7 +55,15 @@ module Gitlab result end - def remove_symlinks! + def copy_archive + return if @archive_file + + @archive_file = File.join(@shared.archive_path, Gitlab::ImportExport.export_filename(project: @project)) + + download_or_copy_upload(@project.import_export_upload.import_file, @archive_file) + end + + def remove_symlinks extracted_files.each do |path| FileUtils.rm(path) if File.lstat(path).symlink? end @@ -59,6 +71,10 @@ module Gitlab true end + def remove_import_file + FileUtils.rm_rf(@archive_file) + end + def extracted_files Dir.glob("#{@shared.export_path}/**/*", File::FNM_DOTMATCH).reject { |f| IGNORED_FILENAMES.include?(File.basename(f)) } end diff --git a/lib/gitlab/import_export/importer.rb b/lib/gitlab/import_export/importer.rb index 63cab07324a..4e179f63d8c 100644 --- a/lib/gitlab/import_export/importer.rb +++ b/lib/gitlab/import_export/importer.rb @@ -35,7 +35,8 @@ module Gitlab end def import_file - Gitlab::ImportExport::FileImporter.import(archive_file: @archive_file, + Gitlab::ImportExport::FileImporter.import(project: @project, + archive_file: @archive_file, shared: @shared) end @@ -91,7 +92,14 @@ module Gitlab end def remove_import_file - FileUtils.rm_rf(@archive_file) + return unless Gitlab::ImportExport.object_storage? + + upload = @project.import_export_upload + + return unless upload&.import_file&.file + + upload.remove_import_file! + upload.save! end def overwrite_project diff --git a/lib/gitlab/import_export/uploads_manager.rb b/lib/gitlab/import_export/uploads_manager.rb index 1110149712d..07875ebb56a 100644 --- a/lib/gitlab/import_export/uploads_manager.rb +++ b/lib/gitlab/import_export/uploads_manager.rb @@ -91,10 +91,7 @@ module Gitlab mkdir_p(File.join(uploads_export_path, secret)) - File.open(upload_path, 'w') do |file| - # Download (stream) file from the uploader's location - IO.copy_stream(URI.parse(upload.file.url).open, file) - end + download_or_copy_upload(upload, upload_path) end end end diff --git a/lib/gitlab/template_helper.rb b/lib/gitlab/template_helper.rb index 3b8e45e0688..3918abaab45 100644 --- a/lib/gitlab/template_helper.rb +++ b/lib/gitlab/template_helper.rb @@ -2,11 +2,17 @@ module Gitlab module TemplateHelper include Gitlab::Utils::StrongMemoize - def prepare_template_environment(file_path) - return unless file_path.present? + def prepare_template_environment(file) + return unless file&.path.present? - FileUtils.mkdir_p(File.dirname(import_upload_path)) - FileUtils.copy_entry(file_path, import_upload_path) + if Gitlab::ImportExport.object_storage? + params[:import_export_upload] = ImportExportUpload.new(import_file: file) + else + FileUtils.mkdir_p(File.dirname(import_upload_path)) + FileUtils.copy_entry(file.path, import_upload_path) + + params[:import_source] = import_upload_path + end end def import_upload_path diff --git a/spec/features/projects/import_export/import_file_object_storage_spec.rb b/spec/features/projects/import_export/import_file_object_storage_spec.rb new file mode 100644 index 00000000000..0d364543916 --- /dev/null +++ b/spec/features/projects/import_export/import_file_object_storage_spec.rb @@ -0,0 +1,103 @@ +require 'spec_helper' + +describe 'Import/Export - project import integration test', :js do + include Select2Helper + + let(:user) { create(:user) } + let(:file) { File.join(Rails.root, 'spec', 'features', 'projects', 'import_export', 'test_project_export.tar.gz') } + let(:export_path) { "#{Dir.tmpdir}/import_file_spec" } + + before do + stub_feature_flags(import_export_object_storage: true) + stub_uploads_object_storage(FileUploader) + allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path) + gitlab_sign_in(user) + end + + after do + FileUtils.rm_rf(export_path, secure: true) + end + + context 'when selecting the namespace' do + let(:user) { create(:admin) } + let!(:namespace) { user.namespace } + let(:project_path) { 'test-project-path' + SecureRandom.hex } + + context 'prefilled the path' do + it 'user imports an exported project successfully' do + visit new_project_path + + select2(namespace.id, from: '#project_namespace_id') + fill_in :project_path, with: project_path, visible: true + click_import_project_tab + click_link 'GitLab export' + + expect(page).to have_content('Import an exported GitLab project') + expect(URI.parse(current_url).query).to eq("namespace_id=#{namespace.id}&path=#{project_path}") + + attach_file('file', file) + click_on 'Import project' + + expect(Project.count).to eq(1) + + project = Project.last + expect(project).not_to be_nil + expect(project.description).to eq("Foo Bar") + expect(project.issues).not_to be_empty + expect(project.merge_requests).not_to be_empty + expect(project_hook_exists?(project)).to be true + expect(wiki_exists?(project)).to be true + expect(project.import_state.status).to eq('finished') + end + end + + context 'path is not prefilled' do + it 'user imports an exported project successfully' do + visit new_project_path + click_import_project_tab + click_link 'GitLab export' + + fill_in :path, with: 'test-project-path', visible: true + attach_file('file', file) + + expect { click_on 'Import project' }.to change { Project.count }.by(1) + + project = Project.last + expect(project).not_to be_nil + expect(page).to have_content("Project 'test-project-path' is being imported") + end + end + end + + it 'invalid project' do + project = create(:project, namespace: user.namespace) + + visit new_project_path + + select2(user.namespace.id, from: '#project_namespace_id') + fill_in :project_path, with: project.name, visible: true + click_import_project_tab + click_link 'GitLab export' + attach_file('file', file) + click_on 'Import project' + + page.within('.flash-container') do + expect(page).to have_content('Project could not be imported') + end + end + + def wiki_exists?(project) + wiki = ProjectWiki.new(project) + wiki.repository.exists? && !wiki.repository.empty? + end + + def project_hook_exists?(project) + Gitlab::GitalyClient::StorageSettings.allow_disk_access do + Gitlab::Git::Hook.new('post-receive', project.repository.raw_repository).exists? + end + end + + def click_import_project_tab + find('#import-project-tab').click + end +end diff --git a/spec/features/projects/import_export/import_file_spec.rb b/spec/features/projects/import_export/import_file_spec.rb index 9cbfb62d872..2d86115de12 100644 --- a/spec/features/projects/import_export/import_file_spec.rb +++ b/spec/features/projects/import_export/import_file_spec.rb @@ -8,6 +8,7 @@ describe 'Import/Export - project import integration test', :js do let(:export_path) { "#{Dir.tmpdir}/import_file_spec" } before do + stub_feature_flags(import_export_object_storage: false) allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path) gitlab_sign_in(user) end diff --git a/spec/lib/gitlab/import_export/file_importer_object_storage_spec.rb b/spec/lib/gitlab/import_export/file_importer_object_storage_spec.rb new file mode 100644 index 00000000000..287745eb40e --- /dev/null +++ b/spec/lib/gitlab/import_export/file_importer_object_storage_spec.rb @@ -0,0 +1,89 @@ +require 'spec_helper' + +describe Gitlab::ImportExport::FileImporter do + let(:shared) { Gitlab::ImportExport::Shared.new(nil) } + let(:storage_path) { "#{Dir.tmpdir}/file_importer_spec" } + let(:valid_file) { "#{shared.export_path}/valid.json" } + let(:symlink_file) { "#{shared.export_path}/invalid.json" } + let(:hidden_symlink_file) { "#{shared.export_path}/.hidden" } + let(:subfolder_symlink_file) { "#{shared.export_path}/subfolder/invalid.json" } + let(:evil_symlink_file) { "#{shared.export_path}/.\nevil" } + + before do + stub_const('Gitlab::ImportExport::FileImporter::MAX_RETRIES', 0) + stub_feature_flags(import_export_object_storage: true) + stub_uploads_object_storage(FileUploader) + + allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(storage_path) + allow_any_instance_of(Gitlab::ImportExport::CommandLineUtil).to receive(:untar_zxf).and_return(true) + allow_any_instance_of(Gitlab::ImportExport::Shared).to receive(:relative_archive_path).and_return('test') + allow(SecureRandom).to receive(:hex).and_return('abcd') + setup_files + end + + after do + FileUtils.rm_rf(storage_path) + end + + context 'normal run' do + before do + described_class.import(project: build(:project), archive_file: '', shared: shared) + end + + it 'removes symlinks in root folder' do + expect(File.exist?(symlink_file)).to be false + end + + it 'removes hidden symlinks in root folder' do + expect(File.exist?(hidden_symlink_file)).to be false + end + + it 'removes evil symlinks in root folder' do + expect(File.exist?(evil_symlink_file)).to be false + end + + it 'removes symlinks in subfolders' do + expect(File.exist?(subfolder_symlink_file)).to be false + end + + it 'does not remove a valid file' do + expect(File.exist?(valid_file)).to be true + end + + it 'creates the file in the right subfolder' do + expect(shared.export_path).to include('test/abcd') + end + end + + context 'error' do + before do + allow_any_instance_of(described_class).to receive(:wait_for_archived_file).and_raise(StandardError) + described_class.import(project: build(:project), archive_file: '', shared: shared) + end + + it 'removes symlinks in root folder' do + expect(File.exist?(symlink_file)).to be false + end + + it 'removes hidden symlinks in root folder' do + expect(File.exist?(hidden_symlink_file)).to be false + end + + it 'removes symlinks in subfolders' do + expect(File.exist?(subfolder_symlink_file)).to be false + end + + it 'does not remove a valid file' do + expect(File.exist?(valid_file)).to be true + end + end + + def setup_files + FileUtils.mkdir_p("#{shared.export_path}/subfolder/") + FileUtils.touch(valid_file) + FileUtils.ln_s(valid_file, symlink_file) + FileUtils.ln_s(valid_file, subfolder_symlink_file) + FileUtils.ln_s(valid_file, hidden_symlink_file) + FileUtils.ln_s(valid_file, evil_symlink_file) + end +end diff --git a/spec/lib/gitlab/import_export/file_importer_spec.rb b/spec/lib/gitlab/import_export/file_importer_spec.rb index 265937f899e..78fccdf1dfc 100644 --- a/spec/lib/gitlab/import_export/file_importer_spec.rb +++ b/spec/lib/gitlab/import_export/file_importer_spec.rb @@ -24,7 +24,7 @@ describe Gitlab::ImportExport::FileImporter do context 'normal run' do before do - described_class.import(archive_file: '', shared: shared) + described_class.import(project: nil, archive_file: '', shared: shared) end it 'removes symlinks in root folder' do @@ -55,7 +55,7 @@ describe Gitlab::ImportExport::FileImporter do context 'error' do before do allow_any_instance_of(described_class).to receive(:wait_for_archived_file).and_raise(StandardError) - described_class.import(archive_file: '', shared: shared) + described_class.import(project: nil, archive_file: '', shared: shared) end it 'removes symlinks in root folder' do diff --git a/spec/lib/gitlab/import_export/importer_object_storage_spec.rb b/spec/lib/gitlab/import_export/importer_object_storage_spec.rb new file mode 100644 index 00000000000..24a994b3611 --- /dev/null +++ b/spec/lib/gitlab/import_export/importer_object_storage_spec.rb @@ -0,0 +1,115 @@ +require 'spec_helper' + +describe Gitlab::ImportExport::Importer do + let(:user) { create(:user) } + let(:test_path) { "#{Dir.tmpdir}/importer_spec" } + let(:shared) { project.import_export_shared } + let(:project) { create(:project) } + let(:import_file) { fixture_file_upload('spec/features/projects/import_export/test_project_export.tar.gz') } + + subject(:importer) { described_class.new(project) } + + before do + allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(test_path) + allow_any_instance_of(Gitlab::ImportExport::FileImporter).to receive(:remove_import_file) + stub_feature_flags(import_export_object_storage: true) + stub_uploads_object_storage(FileUploader) + + FileUtils.mkdir_p(shared.export_path) + ImportExportUpload.create(project: project, import_file: import_file) + end + + after do + FileUtils.rm_rf(test_path) + end + + describe '#execute' do + it 'succeeds' do + importer.execute + + expect(shared.errors).to be_empty + end + + it 'extracts the archive' do + expect(Gitlab::ImportExport::FileImporter).to receive(:import).and_call_original + + importer.execute + end + + it 'checks the version' do + expect(Gitlab::ImportExport::VersionChecker).to receive(:check!).and_call_original + + importer.execute + end + + context 'all restores are executed' do + [ + Gitlab::ImportExport::AvatarRestorer, + Gitlab::ImportExport::RepoRestorer, + Gitlab::ImportExport::WikiRestorer, + Gitlab::ImportExport::UploadsRestorer, + Gitlab::ImportExport::LfsRestorer, + Gitlab::ImportExport::StatisticsRestorer + ].each do |restorer| + it "calls the #{restorer}" do + fake_restorer = double(restorer.to_s) + + expect(fake_restorer).to receive(:restore).and_return(true).at_least(1) + expect(restorer).to receive(:new).and_return(fake_restorer).at_least(1) + + importer.execute + end + end + + it 'restores the ProjectTree' do + expect(Gitlab::ImportExport::ProjectTreeRestorer).to receive(:new).and_call_original + + importer.execute + end + + it 'removes the import file' do + expect(importer).to receive(:remove_import_file).and_call_original + + importer.execute + + expect(project.import_export_upload.import_file&.file).to be_nil + end + end + + context 'when project successfully restored' do + let!(:existing_project) { create(:project, namespace: user.namespace) } + let(:project) { create(:project, namespace: user.namespace, name: 'whatever', path: 'whatever') } + + before do + restorers = double(:restorers, all?: true) + + allow(subject).to receive(:import_file).and_return(true) + allow(subject).to receive(:check_version!).and_return(true) + allow(subject).to receive(:restorers).and_return(restorers) + allow(project).to receive(:import_data).and_return(double(data: { 'original_path' => existing_project.path })) + end + + context 'when import_data' do + context 'has original_path' do + it 'overwrites existing project' do + expect_any_instance_of(::Projects::OverwriteProjectService).to receive(:execute).with(existing_project) + + subject.execute + end + end + + context 'has not original_path' do + before do + allow(project).to receive(:import_data).and_return(double(data: {})) + end + + it 'does not call the overwrite service' do + expect_any_instance_of(::Projects::OverwriteProjectService).not_to receive(:execute).with(existing_project) + + subject.execute + end + end + end + end + end +end diff --git a/spec/lib/gitlab/import_export/importer_spec.rb b/spec/lib/gitlab/import_export/importer_spec.rb index c074e61da26..f07946824c4 100644 --- a/spec/lib/gitlab/import_export/importer_spec.rb +++ b/spec/lib/gitlab/import_export/importer_spec.rb @@ -10,9 +10,10 @@ describe Gitlab::ImportExport::Importer do before do allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(test_path) + allow_any_instance_of(Gitlab::ImportExport::FileImporter).to receive(:remove_import_file) + FileUtils.mkdir_p(shared.export_path) FileUtils.cp(Rails.root.join('spec/features/projects/import_export/test_project_export.tar.gz'), test_path) - allow(subject).to receive(:remove_import_file) end after do @@ -69,7 +70,7 @@ describe Gitlab::ImportExport::Importer do let(:project) { create(:project, namespace: user.namespace, name: 'whatever', path: 'whatever') } before do - restorers = double + restorers = double(:restorers, all?: true) allow(subject).to receive(:import_file).and_return(true) allow(subject).to receive(:check_version!).and_return(true) diff --git a/spec/requests/api/project_import_spec.rb b/spec/requests/api/project_import_spec.rb index 55332f56508..e3fb6cecce9 100644 --- a/spec/requests/api/project_import_spec.rb +++ b/spec/requests/api/project_import_spec.rb @@ -7,6 +7,8 @@ describe API::ProjectImport do let(:namespace) { create(:group) } before do allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path) + stub_feature_flags(import_export_object_storage: true) + stub_uploads_object_storage(FileUploader) namespace.add_owner(user) end diff --git a/spec/services/projects/gitlab_projects_import_service_spec.rb b/spec/services/projects/gitlab_projects_import_service_spec.rb index a2061127698..b5f2c826c97 100644 --- a/spec/services/projects/gitlab_projects_import_service_spec.rb +++ b/spec/services/projects/gitlab_projects_import_service_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Projects::GitlabProjectsImportService do set(:namespace) { create(:namespace) } let(:path) { 'test-path' } - let(:file) { fixture_file_upload('spec/fixtures/doc_sample.txt', 'text/plain') } + let(:file) { fixture_file_upload('spec/fixtures/project_export.tar.gz') } let(:overwrite) { false } let(:import_params) { { namespace_id: namespace.id, path: path, file: file, overwrite: overwrite } } -- cgit v1.2.1 From 069b2a8e1ead257484125f4137d8519b43236135 Mon Sep 17 00:00:00 2001 From: Lukas Eipert Date: Thu, 2 Aug 2018 13:47:14 +0200 Subject: move mandatory vue entry point from optional path --- app/views/projects/pipelines/_info.html.haml | 1 - app/views/projects/pipelines/show.html.haml | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/views/projects/pipelines/_info.html.haml b/app/views/projects/pipelines/_info.html.haml index bc247460d28..bb85cec1978 100644 --- a/app/views/projects/pipelines/_info.html.haml +++ b/app/views/projects/pipelines/_info.html.haml @@ -1,4 +1,3 @@ -#js-pipeline-header-vue.pipeline-header-container - if @commit.present? .commit-box diff --git a/app/views/projects/pipelines/show.html.haml b/app/views/projects/pipelines/show.html.haml index a7d7c923957..be4837346e2 100644 --- a/app/views/projects/pipelines/show.html.haml +++ b/app/views/projects/pipelines/show.html.haml @@ -4,6 +4,9 @@ - page_title "Pipeline" .js-pipeline-container{ class: container_class, data: { controller_action: "#{controller.action_name}" } } + + #js-pipeline-header-vue.pipeline-header-container + - if @commit = render "projects/pipelines/info" -- cgit v1.2.1 From 86d29506d1aa6fdfc17d4f2f5a07c6fa22075f5a Mon Sep 17 00:00:00 2001 From: Lukas Eipert Date: Thu, 2 Aug 2018 13:48:59 +0200 Subject: remove unnecessary if clause and move logic to optional partial --- app/views/projects/pipelines/_info.html.haml | 66 ++++++++++++++-------------- app/views/projects/pipelines/show.html.haml | 3 +- 2 files changed, 35 insertions(+), 34 deletions(-) diff --git a/app/views/projects/pipelines/_info.html.haml b/app/views/projects/pipelines/_info.html.haml index bb85cec1978..cdbef98df29 100644 --- a/app/views/projects/pipelines/_info.html.haml +++ b/app/views/projects/pipelines/_info.html.haml @@ -1,36 +1,38 @@ +- commit = local_assigns.fetch(:commit) -- if @commit.present? - .commit-box - %h3.commit-title - = markdown(@commit.title, pipeline: :single_line) - - if @commit.description.present? - .commit-description< - = preserve(markdown(@commit.description, pipeline: :single_line)) +- return unless commit&.present? - .info-well - - if @commit.status - .well-segment.pipeline-info - .icon-container - = icon('clock-o') - = pluralize @pipeline.total_size, "job" - - if @pipeline.ref - from - = link_to @pipeline.ref, project_ref_path(@project, @pipeline.ref), class: "ref-name" - - if @pipeline.duration - in - = time_interval_in_words(@pipeline.duration) - - if @pipeline.queued_duration - = "(queued for #{time_interval_in_words(@pipeline.queued_duration)})" +.commit-box + %h3.commit-title + = markdown(commit.title, pipeline: :single_line) + - if commit.description.present? + .commit-description< + = preserve(markdown(commit.description, pipeline: :single_line)) - .well-segment.branch-info - .icon-container.commit-icon - = custom_icon("icon_commit") - = link_to @commit.short_id, project_commit_path(@project, @pipeline.sha), class: "commit-sha js-details-short" - = link_to("#", class: "js-details-expand d-none d-sm-none d-md-inline") do - %span.text-expander - = sprite_icon('ellipsis_h', size: 12) - %span.js-details-content.hide - = link_to @pipeline.sha, project_commit_path(@project, @pipeline.sha), class: "commit-sha commit-hash-full" - = clipboard_button(text: @pipeline.sha, title: "Copy commit SHA to clipboard") +.info-well + - if commit.status + .well-segment.pipeline-info + .icon-container + = icon('clock-o') + = pluralize @pipeline.total_size, "job" + - if @pipeline.ref + from + = link_to @pipeline.ref, project_ref_path(@project, @pipeline.ref), class: "ref-name" + - if @pipeline.duration + in + = time_interval_in_words(@pipeline.duration) + - if @pipeline.queued_duration + = "(queued for #{time_interval_in_words(@pipeline.queued_duration)})" - = render_if_exists "projects/pipelines/info_extension", pipeline: @pipeline + .well-segment.branch-info + .icon-container.commit-icon + = custom_icon("icon_commit") + = link_to commit.short_id, project_commit_path(@project, @pipeline.sha), class: "commit-sha js-details-short" + = link_to("#", class: "js-details-expand d-none d-sm-none d-md-inline") do + %span.text-expander + = sprite_icon('ellipsis_h', size: 12) + %span.js-details-content.hide + = link_to @pipeline.sha, project_commit_path(@project, @pipeline.sha), class: "commit-sha commit-hash-full" + = clipboard_button(text: @pipeline.sha, title: "Copy commit SHA to clipboard") + + = render_if_exists "projects/pipelines/info_extension", pipeline: @pipeline diff --git a/app/views/projects/pipelines/show.html.haml b/app/views/projects/pipelines/show.html.haml index be4837346e2..4066e2c590d 100644 --- a/app/views/projects/pipelines/show.html.haml +++ b/app/views/projects/pipelines/show.html.haml @@ -7,8 +7,7 @@ #js-pipeline-header-vue.pipeline-header-container - - if @commit - = render "projects/pipelines/info" + = render "projects/pipelines/info", commit: @pipeline.commit = render "projects/pipelines/with_tabs", pipeline: @pipeline -- cgit v1.2.1 From 8cd80da8bb5c4f67b08cb2334472fac54d083ab3 Mon Sep 17 00:00:00 2001 From: Lukas Eipert Date: Thu, 2 Aug 2018 13:49:39 +0200 Subject: remove unneeded `before_action` --- app/controllers/projects/pipelines_controller.rb | 5 ----- 1 file changed, 5 deletions(-) diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb index 1d5d1ee5d5a..b5db646bf57 100644 --- a/app/controllers/projects/pipelines_controller.rb +++ b/app/controllers/projects/pipelines_controller.rb @@ -1,7 +1,6 @@ class Projects::PipelinesController < Projects::ApplicationController before_action :whitelist_query_limiting, only: [:create, :retry] before_action :pipeline, except: [:index, :new, :create, :charts] - before_action :commit, only: [:show, :builds, :failures] before_action :authorize_read_pipeline! before_action :authorize_create_pipeline!, only: [:new, :create] before_action :authorize_update_pipeline!, only: [:retry, :cancel] @@ -168,10 +167,6 @@ class Projects::PipelinesController < Projects::ApplicationController .present(current_user: current_user) end - def commit - @commit ||= @pipeline.commit - end - def whitelist_query_limiting # Also see https://gitlab.com/gitlab-org/gitlab-ce/issues/42343 Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-ce/issues/42339') -- cgit v1.2.1 From 14e88565c68de056c83ddc53a68ade4c3f82b531 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 2 Aug 2018 15:12:07 +0300 Subject: Refactor pipline info box view Signed-off-by: Dmitriy Zaporozhets --- app/views/projects/pipelines/_info.html.haml | 4 ---- app/views/projects/pipelines/show.html.haml | 4 ++-- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/app/views/projects/pipelines/_info.html.haml b/app/views/projects/pipelines/_info.html.haml index cdbef98df29..78e3b209686 100644 --- a/app/views/projects/pipelines/_info.html.haml +++ b/app/views/projects/pipelines/_info.html.haml @@ -1,7 +1,3 @@ -- commit = local_assigns.fetch(:commit) - -- return unless commit&.present? - .commit-box %h3.commit-title = markdown(commit.title, pipeline: :single_line) diff --git a/app/views/projects/pipelines/show.html.haml b/app/views/projects/pipelines/show.html.haml index 4066e2c590d..ff0ed3ed30d 100644 --- a/app/views/projects/pipelines/show.html.haml +++ b/app/views/projects/pipelines/show.html.haml @@ -4,10 +4,10 @@ - page_title "Pipeline" .js-pipeline-container{ class: container_class, data: { controller_action: "#{controller.action_name}" } } - #js-pipeline-header-vue.pipeline-header-container - = render "projects/pipelines/info", commit: @pipeline.commit + - if @pipeline.commit.present? + = render "projects/pipelines/info", commit: @pipeline.commit = render "projects/pipelines/with_tabs", pipeline: @pipeline -- cgit v1.2.1 From 9e5bc599688950e4c1d2c65358fe780b77dff863 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 2 Aug 2018 13:47:23 +0100 Subject: Fixed Web IDE row dropdowns scrolling list incorrectly Closes #49892 --- app/assets/javascripts/ide/components/new_dropdown/index.vue | 4 +++- spec/javascripts/ide/components/new_dropdown/index_spec.js | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/ide/components/new_dropdown/index.vue b/app/assets/javascripts/ide/components/new_dropdown/index.vue index 440e480d596..8eddfa5e455 100644 --- a/app/assets/javascripts/ide/components/new_dropdown/index.vue +++ b/app/assets/javascripts/ide/components/new_dropdown/index.vue @@ -35,7 +35,9 @@ export default { watch: { dropdownOpen() { this.$nextTick(() => { - this.$refs.dropdownMenu.scrollIntoView(); + this.$refs.dropdownMenu.scrollIntoView({ + block: 'nearest', + }); }); }, mouseOver() { diff --git a/spec/javascripts/ide/components/new_dropdown/index_spec.js b/spec/javascripts/ide/components/new_dropdown/index_spec.js index 092c405a70b..5cb8b177fc9 100644 --- a/spec/javascripts/ide/components/new_dropdown/index_spec.js +++ b/spec/javascripts/ide/components/new_dropdown/index_spec.js @@ -62,7 +62,9 @@ describe('new dropdown component', () => { vm.dropdownOpen = true; setTimeout(() => { - expect(vm.$refs.dropdownMenu.scrollIntoView).toHaveBeenCalled(); + expect(vm.$refs.dropdownMenu.scrollIntoView).toHaveBeenCalledWith({ + block: 'nearest', + }); done(); }); -- cgit v1.2.1 From 52e5250b42b04ea07f0ba033912de9142d12b8cb Mon Sep 17 00:00:00 2001 From: Dennis Tang Date: Thu, 2 Aug 2018 14:18:17 +0000 Subject: Resolve "Top nav search bar produces console error when unauthenticated" --- app/assets/javascripts/search_autocomplete.js | 6 +++++- ...p-nav-search-bar-produces-console-error-when-unauthenticated.yml | 5 +++++ 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/49861-top-nav-search-bar-produces-console-error-when-unauthenticated.yml diff --git a/app/assets/javascripts/search_autocomplete.js b/app/assets/javascripts/search_autocomplete.js index 5b2e0468784..72a2c7ca101 100644 --- a/app/assets/javascripts/search_autocomplete.js +++ b/app/assets/javascripts/search_autocomplete.js @@ -137,7 +137,11 @@ export default class SearchAutocomplete { if (!term) { const contents = this.getCategoryContents(); if (contents) { - this.searchInput.data('glDropdown').filter.options.callback(contents); + const glDropdownInstance = this.searchInput.data('glDropdown'); + + if (glDropdownInstance) { + glDropdownInstance.filter.options.callback(contents); + } this.enableAutocomplete(); } return; diff --git a/changelogs/unreleased/49861-top-nav-search-bar-produces-console-error-when-unauthenticated.yml b/changelogs/unreleased/49861-top-nav-search-bar-produces-console-error-when-unauthenticated.yml new file mode 100644 index 00000000000..30f5002c5b5 --- /dev/null +++ b/changelogs/unreleased/49861-top-nav-search-bar-produces-console-error-when-unauthenticated.yml @@ -0,0 +1,5 @@ +--- +title: fix error caused when using the search bar while unauthenticated +merge_request: 20970 +author: +type: fixed -- cgit v1.2.1 From a6268d302379258b1a2bf50f7db87b40e84ffe94 Mon Sep 17 00:00:00 2001 From: Bob Van Landuyt Date: Thu, 2 Aug 2018 19:43:36 +0200 Subject: Fix deploy tokens without `expire_at` crashes --- app/models/deploy_token.rb | 8 +++++++- spec/models/deploy_token_spec.rb | 8 ++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/app/models/deploy_token.rb b/app/models/deploy_token.rb index c466304a827..0b2eedf3631 100644 --- a/app/models/deploy_token.rb +++ b/app/models/deploy_token.rb @@ -30,7 +30,7 @@ class DeployToken < ActiveRecord::Base end def active? - !revoked && expires_at > Date.today + !revoked && !expired? end def scopes @@ -63,6 +63,12 @@ class DeployToken < ActiveRecord::Base private + def expired? + return false unless expires_at + + expires_at < Date.today + end + def ensure_at_least_one_scope errors.add(:base, "Scopes can't be blank") unless read_repository || read_registry end diff --git a/spec/models/deploy_token_spec.rb b/spec/models/deploy_token_spec.rb index cd84a684fec..3435f93c999 100644 --- a/spec/models/deploy_token_spec.rb +++ b/spec/models/deploy_token_spec.rb @@ -74,6 +74,14 @@ describe DeployToken do expect(deploy_token.active?).to be_falsy end end + + context "when it hasn't been revoked and has no expiry" do + let(:deploy_token) { create(:deploy_token, expires_at: nil) } + + it 'should return true' do + expect(deploy_token.active?).to be_truthy + end + end end describe '#username' do -- cgit v1.2.1 From 1436423a490fe9f4c1ee1ccb8ecaa6240eed2906 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 2 Aug 2018 11:17:00 -0700 Subject: Fix failing 500 errors when deploy tokens are used to clone A DeployToken responds to `:username`, but it returns the username for the token, not a User object. Don't attempt to log user activity in this case. Closes gitlab-org/gitlab-ee#7080 --- app/services/users/activity_service.rb | 1 + spec/services/users/activity_service_spec.rb | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/app/services/users/activity_service.rb b/app/services/users/activity_service.rb index 822df6c646a..db03ba8756f 100644 --- a/app/services/users/activity_service.rb +++ b/app/services/users/activity_service.rb @@ -11,6 +11,7 @@ module Users author.user end + @user = nil unless @user.is_a?(User) @activity = activity end diff --git a/spec/services/users/activity_service_spec.rb b/spec/services/users/activity_service_spec.rb index f20849e6924..719b4adf212 100644 --- a/spec/services/users/activity_service_spec.rb +++ b/spec/services/users/activity_service_spec.rb @@ -28,6 +28,18 @@ describe Users::ActivityService do end end + context 'when a bad object is passed' do + let(:fake_object) { double(username: 'hello') } + + it 'does not record activity' do + service = described_class.new(fake_object, 'pull') + + expect(service).not_to receive(:record_activity) + + service.execute + end + end + context 'when last activity is today' do let(:last_activity_on) { Date.today } -- cgit v1.2.1 From 8fb4b243c2ef675c330047335b15412dd2e20b98 Mon Sep 17 00:00:00 2001 From: Drew Blessing Date: Thu, 2 Aug 2018 12:42:12 -0500 Subject: Update Rouge to 3.2.0 --- Gemfile.lock | 2 +- changelogs/unreleased/rouge-3-2-0.yml | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/rouge-3-2-0.yml diff --git a/Gemfile.lock b/Gemfile.lock index 77c255a7464..d8f878875f3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -745,7 +745,7 @@ GEM retriable (3.1.1) rinku (2.0.0) rotp (2.1.2) - rouge (3.1.1) + rouge (3.2.0) rqrcode (0.7.0) chunky_png rqrcode-rails3 (0.1.7) diff --git a/changelogs/unreleased/rouge-3-2-0.yml b/changelogs/unreleased/rouge-3-2-0.yml new file mode 100644 index 00000000000..15ac4cc1e76 --- /dev/null +++ b/changelogs/unreleased/rouge-3-2-0.yml @@ -0,0 +1,5 @@ +--- +title: Update to Rouge 3.2.0, including Terraform and Crystal lexer and bug fixes +merge_request: 20991 +author: +type: changed -- cgit v1.2.1 From 3fda119ea35a76bc07accfdacee55cb5b6c880cf Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 2 Aug 2018 14:22:18 -0700 Subject: Make CreateGpgSignatureWorker backwards compatible with original method signature Older versions of GitPushService push a single commit SHA string to the queue, but Gitaly requires that the parameters sent by CreateGpgSignatureWorker are an array. It's possible to have old workers using this original signature or jobs in the retry queue that would fail if CreateGpgSignatureWorker can't handle the string form. --- app/workers/create_gpg_signature_worker.rb | 4 ++++ spec/workers/create_gpg_signature_worker_spec.rb | 13 +++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/app/workers/create_gpg_signature_worker.rb b/app/workers/create_gpg_signature_worker.rb index 91833941070..a1aeeb7c4fc 100644 --- a/app/workers/create_gpg_signature_worker.rb +++ b/app/workers/create_gpg_signature_worker.rb @@ -4,6 +4,10 @@ class CreateGpgSignatureWorker include ApplicationWorker def perform(commit_shas, project_id) + # Older versions of GitPushService may push a single commit ID on the stack. + # We need this to be backwards compatible. + commit_shas = Array(commit_shas) + return if commit_shas.empty? project = Project.find_by(id: project_id) diff --git a/spec/workers/create_gpg_signature_worker_spec.rb b/spec/workers/create_gpg_signature_worker_spec.rb index 502765e9786..f5479e57260 100644 --- a/spec/workers/create_gpg_signature_worker_spec.rb +++ b/spec/workers/create_gpg_signature_worker_spec.rb @@ -4,10 +4,9 @@ describe CreateGpgSignatureWorker do let(:project) { create(:project, :repository) } let(:commits) { project.repository.commits('HEAD', limit: 3).commits } let(:commit_shas) { commits.map(&:id) } + let(:gpg_commit) { instance_double(Gitlab::Gpg::Commit) } context 'when GpgKey is found' do - let(:gpg_commit) { instance_double(Gitlab::Gpg::Commit) } - before do allow(Project).to receive(:find_by).with(id: project.id).and_return(project) allow(project).to receive(:commits_by).with(oids: commit_shas).and_return(commits) @@ -36,6 +35,16 @@ describe CreateGpgSignatureWorker do end end + context 'handles when a string is passed in for the commit SHA' do + it 'creates a signature once' do + allow(Gitlab::Gpg::Commit).to receive(:new).with(commits.first).and_return(gpg_commit) + + expect(gpg_commit).to receive(:signature).once + + described_class.new.perform(commit_shas.first, project.id) + end + end + context 'when Commit is not found' do let(:nonexisting_commit_sha) { '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a34' } -- cgit v1.2.1 From a4351ac077b9f266b4bcfa8f1c4867a837870a27 Mon Sep 17 00:00:00 2001 From: Michael Kozono Date: Fri, 3 Aug 2018 04:36:43 +0000 Subject: Add object storage related tests for `gitlab:cleanup:project_uploads` task --- lib/gitlab/cleanup/project_uploads.rb | 2 +- spec/lib/gitlab/cleanup/project_uploads_spec.rb | 278 ++++++++++++++++++++ spec/tasks/gitlab/cleanup_rake_spec.rb | 321 ++++-------------------- 3 files changed, 324 insertions(+), 277 deletions(-) create mode 100644 spec/lib/gitlab/cleanup/project_uploads_spec.rb diff --git a/lib/gitlab/cleanup/project_uploads.rb b/lib/gitlab/cleanup/project_uploads.rb index b88e00311d5..f55ab535efe 100644 --- a/lib/gitlab/cleanup/project_uploads.rb +++ b/lib/gitlab/cleanup/project_uploads.rb @@ -40,7 +40,7 @@ module Gitlab # Accepts a path in the form of "#{hex_secret}/#{filename}" def find_correct_path(upload_path) upload = Upload.find_by(uploader: 'FileUploader', path: upload_path) - return unless upload && upload.local? + return unless upload && upload.local? && upload.model upload.absolute_path rescue => e diff --git a/spec/lib/gitlab/cleanup/project_uploads_spec.rb b/spec/lib/gitlab/cleanup/project_uploads_spec.rb new file mode 100644 index 00000000000..37b38776775 --- /dev/null +++ b/spec/lib/gitlab/cleanup/project_uploads_spec.rb @@ -0,0 +1,278 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::Cleanup::ProjectUploads do + subject { described_class.new(logger: logger) } + let(:logger) { double(:logger) } + + before do + allow(logger).to receive(:info).at_least(1).times + allow(logger).to receive(:debug).at_least(1).times + end + + describe '#run!' do + shared_examples_for 'moves the file' do + shared_examples_for 'a real run' do + let(:args) { [dry_run: false] } + + it 'moves the file to its proper location' do + subject.run!(*args) + + expect(File.exist?(path)).to be_falsey + expect(File.exist?(new_path)).to be_truthy + end + + it 'logs action as done' do + expect(logger).to receive(:info).with("Looking for orphaned project uploads to clean up...") + expect(logger).to receive(:info).with("Did #{action}") + + subject.run!(*args) + end + end + + shared_examples_for 'a dry run' do + it 'does not move the file' do + subject.run!(*args) + + expect(File.exist?(path)).to be_truthy + expect(File.exist?(new_path)).to be_falsey + end + + it 'logs action as able to be done' do + expect(logger).to receive(:info).with("Looking for orphaned project uploads to clean up. Dry run...") + expect(logger).to receive(:info).with("Can #{action}") + + subject.run!(*args) + end + end + + context 'when dry_run is false' do + let(:args) { [dry_run: false] } + + it_behaves_like 'a real run' + end + + context 'when dry_run is nil' do + let(:args) { [dry_run: nil] } + + it_behaves_like 'a real run' + end + + context 'when dry_run is true' do + let(:args) { [dry_run: true] } + + it_behaves_like 'a dry run' + end + + context 'with dry_run not specified' do + let(:args) { [] } + + it_behaves_like 'a dry run' + end + end + + shared_examples_for 'moves the file to lost and found' do + let(:action) { "move to lost and found #{path} -> #{new_path}" } + + it_behaves_like 'moves the file' + end + + shared_examples_for 'fixes the file' do + let(:action) { "fix #{path} -> #{new_path}" } + + it_behaves_like 'moves the file' + end + + context 'orphaned project upload file' do + context 'when an upload record matching the secret and filename is found' do + context 'when the project is still in legacy storage' do + let(:orphaned) { create(:upload, :issuable_upload, :with_file, model: create(:project, :legacy_storage)) } + let(:new_path) { orphaned.absolute_path } + let(:path) { File.join(FileUploader.root, 'some', 'wrong', 'location', orphaned.path) } + + before do + FileUtils.mkdir_p(File.dirname(path)) + FileUtils.mv(new_path, path) + end + + it_behaves_like 'fixes the file' + end + + context 'when the project was moved to hashed storage' do + let(:orphaned) { create(:upload, :issuable_upload, :with_file) } + let(:new_path) { orphaned.absolute_path } + let(:path) { File.join(FileUploader.root, 'some', 'wrong', 'location', orphaned.path) } + + before do + FileUtils.mkdir_p(File.dirname(path)) + FileUtils.mv(new_path, path) + end + + it_behaves_like 'fixes the file' + end + + context 'when the project is missing (the upload *record* is an orphan)' do + let(:orphaned) { create(:upload, :issuable_upload, :with_file, model: build(:project, :legacy_storage)) } + let!(:path) { orphaned.absolute_path } + let!(:new_path) { File.join(FileUploader.root, '-', 'project-lost-found', orphaned.model.full_path, orphaned.path) } + + before do + orphaned.model.delete + end + + it_behaves_like 'moves the file to lost and found' + end + + # We will probably want to add logic (Reschedule background upload) to + # cover Case 2 in https://gitlab.com/gitlab-org/gitlab-ce/issues/46535#note_75355104 + context 'when the file should be in object storage' do + context 'when the file otherwise has the correct local path' do + let!(:orphaned) { create(:upload, :issuable_upload, :object_storage, model: build(:project, :legacy_storage)) } + let!(:path) { File.join(FileUploader.root, orphaned.model.full_path, orphaned.path) } + + before do + stub_feature_flags(import_export_object_storage: true) + stub_uploads_object_storage(FileUploader) + + FileUtils.mkdir_p(File.dirname(path)) + FileUtils.touch(path) + end + + it 'does not move the file' do + expect(File.exist?(path)).to be_truthy + + subject.run!(dry_run: false) + + expect(File.exist?(path)).to be_truthy + end + end + + # E.g. the upload file was orphaned, and then uploads were migrated to + # object storage + context 'when the file has the wrong local path' do + let!(:orphaned) { create(:upload, :issuable_upload, :object_storage, model: build(:project, :legacy_storage)) } + let!(:path) { File.join(FileUploader.root, 'wrong', orphaned.path) } + let!(:new_path) { File.join(FileUploader.root, '-', 'project-lost-found', 'wrong', orphaned.path) } + + before do + stub_feature_flags(import_export_object_storage: true) + stub_uploads_object_storage(FileUploader) + + FileUtils.mkdir_p(File.dirname(path)) + FileUtils.touch(path) + end + + it_behaves_like 'moves the file to lost and found' + end + end + end + + context 'when a matching upload record can not be found' do + context 'when the file path fits the known pattern' do + let!(:orphaned) { create(:upload, :issuable_upload, :with_file, model: build(:project, :legacy_storage)) } + let!(:path) { orphaned.absolute_path } + let!(:new_path) { File.join(FileUploader.root, '-', 'project-lost-found', orphaned.model.full_path, orphaned.path) } + + before do + orphaned.delete + end + + it_behaves_like 'moves the file to lost and found' + end + + context 'when the file path does not fit the known pattern' do + let!(:invalid_path) { File.join('group', 'file.jpg') } + let!(:path) { File.join(FileUploader.root, invalid_path) } + let!(:new_path) { File.join(FileUploader.root, '-', 'project-lost-found', invalid_path) } + + before do + FileUtils.mkdir_p(File.dirname(path)) + FileUtils.touch(path) + end + + after do + File.delete(path) if File.exist?(path) + end + + it_behaves_like 'moves the file to lost and found' + end + end + end + + context 'non-orphaned project upload file' do + it 'does not move the file' do + tracked = create(:upload, :issuable_upload, :with_file, model: build(:project, :legacy_storage)) + tracked_path = tracked.absolute_path + + expect(logger).not_to receive(:info).with(/move|fix/i) + expect(File.exist?(tracked_path)).to be_truthy + + subject.run!(dry_run: false) + + expect(File.exist?(tracked_path)).to be_truthy + end + end + + context 'ignorable cases' do + # Because we aren't concerned about these, and can save a lot of + # processing time by ignoring them. If we wish to cleanup hashed storage + # directories, it should simply require removing this test and modifying + # the find command. + context 'when the file is already in hashed storage' do + let(:project) { create(:project) } + + before do + expect(logger).not_to receive(:info).with(/move|fix/i) + end + + it 'does not move even an orphan file' do + orphaned = create(:upload, :issuable_upload, :with_file, model: project) + path = orphaned.absolute_path + orphaned.delete + + expect(File.exist?(path)).to be_truthy + + subject.run!(dry_run: false) + + expect(File.exist?(path)).to be_truthy + end + end + + it 'does not move any non-project (FileUploader) uploads' do + paths = [] + orphaned1 = create(:upload, :personal_snippet_upload, :with_file) + orphaned2 = create(:upload, :namespace_upload, :with_file) + orphaned3 = create(:upload, :attachment_upload, :with_file) + paths << orphaned1.absolute_path + paths << orphaned2.absolute_path + paths << orphaned3.absolute_path + Upload.delete_all + + expect(logger).not_to receive(:info).with(/move|fix/i) + paths.each do |path| + expect(File.exist?(path)).to be_truthy + end + + subject.run!(dry_run: false) + + paths.each do |path| + expect(File.exist?(path)).to be_truthy + end + end + + it 'does not move any uploads in tmp (which would interfere with ongoing upload activity)' do + path = File.join(FileUploader.root, 'tmp', 'foo.jpg') + FileUtils.mkdir_p(File.dirname(path)) + FileUtils.touch(path) + + expect(logger).not_to receive(:info).with(/move|fix/i) + expect(File.exist?(path)).to be_truthy + + subject.run!(dry_run: false) + + expect(File.exist?(path)).to be_truthy + end + end + end +end diff --git a/spec/tasks/gitlab/cleanup_rake_spec.rb b/spec/tasks/gitlab/cleanup_rake_spec.rb index ba08ece1b4b..cc2cca10f58 100644 --- a/spec/tasks/gitlab/cleanup_rake_spec.rb +++ b/spec/tasks/gitlab/cleanup_rake_spec.rb @@ -68,317 +68,86 @@ describe 'gitlab:cleanup rake tasks' do end end + # A single integration test that is redundant with one part of the + # Gitlab::Cleanup::ProjectUploads spec. + # + # Additionally, this tests DRY_RUN env var values, and the extra line of + # output that says you can disable DRY_RUN if it's enabled. describe 'cleanup:project_uploads' do - context 'orphaned project upload file' do - context 'when an upload record matching the secret and filename is found' do - context 'when the project is still in legacy storage' do - let!(:orphaned) { create(:upload, :issuable_upload, :with_file, model: build(:project, :legacy_storage)) } - let!(:correct_path) { orphaned.absolute_path } - let!(:other_project) { create(:project, :legacy_storage) } - let!(:orphaned_path) { correct_path.sub(/#{orphaned.model.full_path}/, other_project.full_path) } + let!(:logger) { double(:logger) } - before do - FileUtils.mkdir_p(File.dirname(orphaned_path)) - FileUtils.mv(correct_path, orphaned_path) - end - - it 'moves the file to its proper location' do - expect(Rails.logger).to receive(:info).twice - expect(Rails.logger).to receive(:info).with("Did fix #{orphaned_path} -> #{correct_path}") - - expect(File.exist?(orphaned_path)).to be_truthy - expect(File.exist?(correct_path)).to be_falsey - - stub_env('DRY_RUN', 'false') - run_rake_task('gitlab:cleanup:project_uploads') - - expect(File.exist?(orphaned_path)).to be_falsey - expect(File.exist?(correct_path)).to be_truthy - end - - it 'a dry run does not move the file' do - expect(Rails.logger).to receive(:info).twice - expect(Rails.logger).to receive(:info).with("Can fix #{orphaned_path} -> #{correct_path}") - expect(Rails.logger).to receive(:info) - - expect(File.exist?(orphaned_path)).to be_truthy - expect(File.exist?(correct_path)).to be_falsey - - run_rake_task('gitlab:cleanup:project_uploads') - - expect(File.exist?(orphaned_path)).to be_truthy - expect(File.exist?(correct_path)).to be_falsey - end - - context 'when the project record is missing (Upload#absolute_path raises error)' do - let!(:lost_and_found_path) { File.join(FileUploader.root, '-', 'project-lost-found', other_project.full_path, orphaned.path) } - - before do - orphaned.model.delete - end - - it 'moves the file to lost and found' do - expect(Rails.logger).to receive(:info).twice - expect(Rails.logger).to receive(:info).with("Did move to lost and found #{orphaned_path} -> #{lost_and_found_path}") - - expect(File.exist?(orphaned_path)).to be_truthy - expect(File.exist?(lost_and_found_path)).to be_falsey - - stub_env('DRY_RUN', 'false') - run_rake_task('gitlab:cleanup:project_uploads') - - expect(File.exist?(orphaned_path)).to be_falsey - expect(File.exist?(lost_and_found_path)).to be_truthy - end - - it 'a dry run does not move the file' do - expect(Rails.logger).to receive(:info).twice - expect(Rails.logger).to receive(:info).with("Can move to lost and found #{orphaned_path} -> #{lost_and_found_path}") - expect(Rails.logger).to receive(:info) - - expect(File.exist?(orphaned_path)).to be_truthy - expect(File.exist?(lost_and_found_path)).to be_falsey - - run_rake_task('gitlab:cleanup:project_uploads') - - expect(File.exist?(orphaned_path)).to be_truthy - expect(File.exist?(lost_and_found_path)).to be_falsey - end - end - end - - context 'when the project was moved to hashed storage' do - let!(:orphaned) { create(:upload, :issuable_upload, :with_file) } - let!(:correct_path) { orphaned.absolute_path } - let!(:orphaned_path) { File.join(FileUploader.root, 'foo', 'bar', orphaned.path) } - - before do - FileUtils.mkdir_p(File.dirname(orphaned_path)) - FileUtils.mv(correct_path, orphaned_path) - end - - it 'moves the file to its proper location' do - expect(Rails.logger).to receive(:info).twice - expect(Rails.logger).to receive(:info).with("Did fix #{orphaned_path} -> #{correct_path}") - - expect(File.exist?(orphaned_path)).to be_truthy - expect(File.exist?(correct_path)).to be_falsey - - stub_env('DRY_RUN', 'false') - run_rake_task('gitlab:cleanup:project_uploads') - - expect(File.exist?(orphaned_path)).to be_falsey - expect(File.exist?(correct_path)).to be_truthy - end - - it 'a dry run does not move the file' do - expect(Rails.logger).to receive(:info).twice - expect(Rails.logger).to receive(:info).with("Can fix #{orphaned_path} -> #{correct_path}") - expect(Rails.logger).to receive(:info) + before do + expect(main_object).to receive(:logger).and_return(logger).at_least(1).times - expect(File.exist?(orphaned_path)).to be_truthy - expect(File.exist?(correct_path)).to be_falsey + allow(logger).to receive(:info).at_least(1).times + allow(logger).to receive(:debug).at_least(1).times + end - run_rake_task('gitlab:cleanup:project_uploads') + context 'with a fixable orphaned project upload file' do + let(:orphaned) { create(:upload, :issuable_upload, :with_file, model: build(:project, :legacy_storage)) } + let(:new_path) { orphaned.absolute_path } + let(:path) { File.join(FileUploader.root, 'some', 'wrong', 'location', orphaned.path) } - expect(File.exist?(orphaned_path)).to be_truthy - expect(File.exist?(correct_path)).to be_falsey - end - end + before do + FileUtils.mkdir_p(File.dirname(path)) + FileUtils.mv(new_path, path) end - context 'when a matching upload record can not be found' do - context 'when the file path fits the known pattern' do - let!(:orphaned) { create(:upload, :issuable_upload, :with_file, model: build(:project, :legacy_storage)) } - let!(:orphaned_path) { orphaned.absolute_path } - let!(:lost_and_found_path) { File.join(FileUploader.root, '-', 'project-lost-found', orphaned.model.full_path, orphaned.path) } - - before do - orphaned.delete - end - - it 'moves the file to lost and found' do - expect(Rails.logger).to receive(:info).twice - expect(Rails.logger).to receive(:info).with("Did move to lost and found #{orphaned_path} -> #{lost_and_found_path}") - - expect(File.exist?(orphaned_path)).to be_truthy - expect(File.exist?(lost_and_found_path)).to be_falsey - - stub_env('DRY_RUN', 'false') - run_rake_task('gitlab:cleanup:project_uploads') - - expect(File.exist?(orphaned_path)).to be_falsey - expect(File.exist?(lost_and_found_path)).to be_truthy - end - - it 'a dry run does not move the file' do - expect(Rails.logger).to receive(:info).twice - expect(Rails.logger).to receive(:info).with("Can move to lost and found #{orphaned_path} -> #{lost_and_found_path}") - expect(Rails.logger).to receive(:info) - - expect(File.exist?(orphaned_path)).to be_truthy - expect(File.exist?(lost_and_found_path)).to be_falsey - - run_rake_task('gitlab:cleanup:project_uploads') - - expect(File.exist?(orphaned_path)).to be_truthy - expect(File.exist?(lost_and_found_path)).to be_falsey - end + context 'with DRY_RUN disabled' do + before do + stub_env('DRY_RUN', 'false') end - context 'when the file path does not fit the known pattern' do - let!(:invalid_path) { File.join('group', 'file.jpg') } - let!(:orphaned_path) { File.join(FileUploader.root, invalid_path) } - let!(:lost_and_found_path) { File.join(FileUploader.root, '-', 'project-lost-found', invalid_path) } - - before do - FileUtils.mkdir_p(File.dirname(orphaned_path)) - FileUtils.touch(orphaned_path) - end - - after do - File.delete(orphaned_path) if File.exist?(orphaned_path) - end - - it 'moves the file to lost and found' do - expect(Rails.logger).to receive(:info).twice - expect(Rails.logger).to receive(:info).with("Did move to lost and found #{orphaned_path} -> #{lost_and_found_path}") - - expect(File.exist?(orphaned_path)).to be_truthy - expect(File.exist?(lost_and_found_path)).to be_falsey - - stub_env('DRY_RUN', 'false') - run_rake_task('gitlab:cleanup:project_uploads') - - expect(File.exist?(orphaned_path)).to be_falsey - expect(File.exist?(lost_and_found_path)).to be_truthy - end - - it 'a dry run does not move the file' do - expect(Rails.logger).to receive(:info).twice - expect(Rails.logger).to receive(:info).with("Can move to lost and found #{orphaned_path} -> #{lost_and_found_path}") - expect(Rails.logger).to receive(:info) - - expect(File.exist?(orphaned_path)).to be_truthy - expect(File.exist?(lost_and_found_path)).to be_falsey - - run_rake_task('gitlab:cleanup:project_uploads') + it 'moves the file to its proper location' do + run_rake_task('gitlab:cleanup:project_uploads') - expect(File.exist?(orphaned_path)).to be_truthy - expect(File.exist?(lost_and_found_path)).to be_falsey - end + expect(File.exist?(path)).to be_falsey + expect(File.exist?(new_path)).to be_truthy end - end - end - - context 'non-orphaned project upload file' do - it 'does not move the file' do - tracked = create(:upload, :issuable_upload, :with_file, model: build(:project, :legacy_storage)) - tracked_path = tracked.absolute_path - expect(Rails.logger).not_to receive(:info).with(/move|fix/i) - expect(File.exist?(tracked_path)).to be_truthy - - stub_env('DRY_RUN', 'false') - run_rake_task('gitlab:cleanup:project_uploads') - - expect(File.exist?(tracked_path)).to be_truthy - end - end - - context 'ignorable cases' do - shared_examples_for 'does not move anything' do - it 'does not move even an orphan file' do - orphaned = create(:upload, :issuable_upload, :with_file, model: project) - orphaned_path = orphaned.absolute_path - orphaned.delete - - expect(File.exist?(orphaned_path)).to be_truthy + it 'logs action as done' do + expect(logger).to receive(:info).with("Looking for orphaned project uploads to clean up...") + expect(logger).to receive(:info).with("Did fix #{path} -> #{new_path}") run_rake_task('gitlab:cleanup:project_uploads') - - expect(File.exist?(orphaned_path)).to be_truthy end end - # Because we aren't concerned about these, and can save a lot of - # processing time by ignoring them. If we wish to cleanup hashed storage - # directories, it should simply require removing this test and modifying - # the find command. - context 'when the file is already in hashed storage' do - let(:project) { create(:project) } + shared_examples_for 'does not move the file' do + it 'does not move the file' do + run_rake_task('gitlab:cleanup:project_uploads') - before do - stub_env('DRY_RUN', 'false') - expect(Rails.logger).not_to receive(:info).with(/move|fix/i) + expect(File.exist?(path)).to be_truthy + expect(File.exist?(new_path)).to be_falsey end - it_behaves_like 'does not move anything' - end - - context 'when DRY_RUN env var is unset' do - let(:project) { create(:project, :legacy_storage) } + it 'logs action as able to be done' do + expect(logger).to receive(:info).with("Looking for orphaned project uploads to clean up. Dry run...") + expect(logger).to receive(:info).with("Can fix #{path} -> #{new_path}") + expect(logger).to receive(:info).with(/To clean up these files run this command with DRY_RUN=false/) - it_behaves_like 'does not move anything' + run_rake_task('gitlab:cleanup:project_uploads') + end end - context 'when DRY_RUN env var is true' do - let(:project) { create(:project, :legacy_storage) } - + context 'with DRY_RUN explicitly enabled' do before do stub_env('DRY_RUN', 'true') end - it_behaves_like 'does not move anything' + it_behaves_like 'does not move the file' end - context 'when DRY_RUN env var is foo' do - let(:project) { create(:project, :legacy_storage) } - + context 'with DRY_RUN set to an unknown value' do before do stub_env('DRY_RUN', 'foo') end - it_behaves_like 'does not move anything' + it_behaves_like 'does not move the file' end - it 'does not move any non-project (FileUploader) uploads' do - stub_env('DRY_RUN', 'false') - - paths = [] - orphaned1 = create(:upload, :personal_snippet_upload, :with_file) - orphaned2 = create(:upload, :namespace_upload, :with_file) - orphaned3 = create(:upload, :attachment_upload, :with_file) - paths << orphaned1.absolute_path - paths << orphaned2.absolute_path - paths << orphaned3.absolute_path - Upload.delete_all - - expect(Rails.logger).not_to receive(:info).with(/move|fix/i) - paths.each do |path| - expect(File.exist?(path)).to be_truthy - end - - run_rake_task('gitlab:cleanup:project_uploads') - - paths.each do |path| - expect(File.exist?(path)).to be_truthy - end - end - - it 'does not move any uploads in tmp (which would interfere with ongoing upload activity)' do - stub_env('DRY_RUN', 'false') - - path = File.join(FileUploader.root, 'tmp', 'foo.jpg') - FileUtils.mkdir_p(File.dirname(path)) - FileUtils.touch(path) - - expect(Rails.logger).not_to receive(:info).with(/move|fix/i) - expect(File.exist?(path)).to be_truthy - - run_rake_task('gitlab:cleanup:project_uploads') - - expect(File.exist?(path)).to be_truthy + context 'with DRY_RUN unset' do + it_behaves_like 'does not move the file' end end end -- cgit v1.2.1