Add cop for use of remove_column

remove_column should only be used in the up (or change) step of a migration if
it's a post-deployment migration. Otherwise there will be downtime due to the
ActiveRecord column cache, which we can avoid by using the IgnorableColumn
concern in combination with a post-deployment migration.
This commit is contained in:
Sean McGivern 2017-12-11 16:34:51 +00:00
parent 8ff63039f1
commit 1ab33b15d1
17 changed files with 118 additions and 0 deletions

View file

@ -1,3 +1,4 @@
# rubocop:disable Migration/RemoveColumn
class RemoveDeprecatedIssuesTrackerColumnsFromProjects < ActiveRecord::Migration
def change
remove_column :projects, :issues_tracker, :string, default: 'gitlab', null: false

View file

@ -1,3 +1,4 @@
# rubocop:disable Migration/RemoveColumn
class RemoveNotificationLevelFromUsers < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers

View file

@ -1,3 +1,4 @@
# rubocop:disable Migration/RemoveColumn
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.

View file

@ -1,3 +1,4 @@
# rubocop:disable Migration/RemoveColumn
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.

View file

@ -1,3 +1,4 @@
# rubocop:disable Migration/RemoveColumn
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.

View file

@ -1,3 +1,4 @@
# rubocop:disable Migration/RemoveColumn
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.

View file

@ -1,3 +1,4 @@
# rubocop:disable Migration/RemoveColumn
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.

View file

@ -1,3 +1,4 @@
# rubocop:disable Migration/RemoveColumn
class RemovePriorityFromLabels < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers

View file

@ -1,3 +1,4 @@
# rubocop:disable Migration/RemoveColumn
class MigrateProjectStatistics < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers

View file

@ -1,3 +1,4 @@
# rubocop:disable Migration/RemoveColumn
# rubocop:disable RemoveIndex
class RemoveOldProjectIdColumns < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers

View file

@ -1,3 +1,4 @@
# rubocop:disable Migration/RemoveColumn
# rubocop:disable Migration/Datetime
class RemoveUnusedCiTablesAndColumns < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers

View file

@ -1,3 +1,4 @@
# rubocop:disable Migration/RemoveColumn
# rubocop:disable Migration/UpdateLargeTable
class RevertAddNotifiedOfOwnActivityToUsers < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers

View file

@ -1,3 +1,4 @@
# rubocop:disable Migration/RemoveColumn
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.

View file

@ -0,0 +1,30 @@
require_relative '../../migration_helpers'
module RuboCop
module Cop
module Migration
# Cop that checks if remove_column is used in a regular (not
# post-deployment) migration.
class RemoveColumn < RuboCop::Cop::Cop
include MigrationHelpers
MSG = '`remove_column` must only be used in post-deployment migrations'.freeze
def on_def(node)
def_method = node.children[0]
return unless in_migration?(node) && !in_post_deployment_migration?(node)
return unless def_method == :change || def_method == :up
node.each_descendant(:send) do |send_node|
send_method = send_node.children[1]
if send_method == :remove_column
add_offense(send_node, :selector)
end
end
end
end
end
end
end

View file

@ -7,5 +7,11 @@ module RuboCop
dirname.end_with?('db/migrate', 'db/post_migrate')
end
def in_post_deployment_migration?(node)
dirname = File.dirname(node.location.expression.source_buffer.name)
dirname.end_with?('db/post_migrate')
end
end
end

View file

@ -14,6 +14,7 @@ require_relative 'cop/migration/add_index'
require_relative 'cop/migration/add_timestamps'
require_relative 'cop/migration/datetime'
require_relative 'cop/migration/hash_index'
require_relative 'cop/migration/remove_column'
require_relative 'cop/migration/remove_concurrent_index'
require_relative 'cop/migration/remove_index'
require_relative 'cop/migration/reversible_add_column_with_default'

View file

@ -0,0 +1,68 @@
require 'spec_helper'
require 'rubocop'
require 'rubocop/rspec/support'
require_relative '../../../../rubocop/cop/migration/remove_column'
describe RuboCop::Cop::Migration::RemoveColumn do
include CopHelper
subject(:cop) { described_class.new }
def source(meth = 'change')
"def #{meth}; remove_column :table, :column; end"
end
context 'in a regular migration' do
before do
allow(cop).to receive(:in_migration?).and_return(true)
allow(cop).to receive(:in_post_deployment_migration?).and_return(false)
end
it 'registers an offense when remove_column is used in the change method' do
inspect_source(cop, source('change'))
aggregate_failures do
expect(cop.offenses.size).to eq(1)
expect(cop.offenses.map(&:line)).to eq([1])
end
end
it 'registers an offense when remove_column is used in the up method' do
inspect_source(cop, source('up'))
aggregate_failures do
expect(cop.offenses.size).to eq(1)
expect(cop.offenses.map(&:line)).to eq([1])
end
end
it 'registers no offense when remove_column is used in the down method' do
inspect_source(cop, source('down'))
expect(cop.offenses.size).to eq(0)
end
end
context 'in a post-deployment migration' do
before do
allow(cop).to receive(:in_migration?).and_return(true)
allow(cop).to receive(:in_post_deployment_migration?).and_return(true)
end
it 'registers no offense' do
inspect_source(cop, source)
expect(cop.offenses.size).to eq(0)
end
end
context 'outside of a migration' do
it 'registers no offense' do
inspect_source(cop, source)
expect(cop.offenses.size).to eq(0)
end
end
end