summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYorick Peterse <yorickpeterse@gmail.com>2016-06-24 17:26:28 +0200
committerYorick Peterse <yorickpeterse@gmail.com>2016-06-29 14:14:02 +0200
commitc740445ad3825da4e5f7fea943be561ece982443 (patch)
treec5b99c0d3665c720b6c307e5b0806767f7b7b19d
parentd33991f8cc343cef704d04fd7b97c69887f3299b (diff)
downloadgitlab-ce-migration-cops.tar.gz
Added RuboCop cops for checking DB migrationsmigration-cops
There are currently two cops for this: * Migration/AddIndex: checks if indexes are added concurrently * Migration/ColumnWithDefault: checks if columns with default values are added in a concurrent manner Both cops are fairly simple and make no attempt at correcting the code as this is quite hard to do (e.g. modifications may need to be applied in various places in the same file). They however should provide enough to catch people ignoring the comments in generated migrations telling them to use add_concurrent_index or add_column_with_default.
-rw-r--r--.rubocop.yml4
-rw-r--r--rubocop/cop/migration/add_index.rb46
-rw-r--r--rubocop/cop/migration/column_with_default.rb50
-rw-r--r--rubocop/migration_helpers.rb10
-rw-r--r--rubocop/rubocop.rb3
5 files changed, 112 insertions, 1 deletions
diff --git a/.rubocop.yml b/.rubocop.yml
index dbdabbb9d4c..23ed0c82e8c 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -1,4 +1,6 @@
-require: rubocop-rspec
+require:
+ - rubocop-rspec
+ - ./rubocop/rubocop
AllCops:
TargetRubyVersion: 2.1
diff --git a/rubocop/cop/migration/add_index.rb b/rubocop/cop/migration/add_index.rb
new file mode 100644
index 00000000000..d9247a1f7ea
--- /dev/null
+++ b/rubocop/cop/migration/add_index.rb
@@ -0,0 +1,46 @@
+module RuboCop
+ module Cop
+ module Migration
+ # Cop that checks if indexes are added in a concurrent manner.
+ class AddIndex < RuboCop::Cop::Cop
+ include MigrationHelpers
+
+ MSG = 'add_index requires downtime, use add_concurrent_index instead'
+
+ def on_def(node)
+ return unless in_migration?(node)
+
+ new_tables = []
+
+ node.each_descendant(:send) do |send_node|
+ first_arg = first_argument(send_node)
+
+ # The first argument of "create_table" / "add_index" is the table
+ # name.
+ new_tables << first_arg if create_table?(send_node)
+
+ next if method_name(send_node) != :add_index
+
+ # Using "add_index" is fine for newly created tables as there's no
+ # data in these tables yet.
+ next if new_tables.include?(first_arg)
+
+ add_offense(send_node, :selector)
+ end
+ end
+
+ def create_table?(node)
+ method_name(node) == :create_table
+ end
+
+ def method_name(node)
+ node.children[1]
+ end
+
+ def first_argument(node)
+ node.children[2] ? node.children[0] : nil
+ end
+ end
+ end
+ end
+end
diff --git a/rubocop/cop/migration/column_with_default.rb b/rubocop/cop/migration/column_with_default.rb
new file mode 100644
index 00000000000..97ee8b11044
--- /dev/null
+++ b/rubocop/cop/migration/column_with_default.rb
@@ -0,0 +1,50 @@
+module RuboCop
+ module Cop
+ module Migration
+ # Cop that checks if columns are added in a way that doesn't require
+ # downtime.
+ class ColumnWithDefault < RuboCop::Cop::Cop
+ include MigrationHelpers
+
+ WHITELISTED_TABLES = [:application_settings]
+
+ MSG = 'add_column with a default value requires downtime, ' \
+ 'use add_column_with_default instead'
+
+ def on_send(node)
+ return unless in_migration?(node)
+
+ name = node.children[1]
+
+ return unless name == :add_column
+
+ # Ignore whitelisted tables.
+ return if table_whitelisted?(node.children[2])
+
+ opts = node.children.last
+
+ return unless opts && opts.type == :hash
+
+ opts.each_node(:pair) do |pair|
+ if hash_key_type(pair) == :sym && hash_key_name(pair) == :default
+ add_offense(node, :selector)
+ end
+ end
+ end
+
+ def table_whitelisted?(symbol)
+ symbol && symbol.type == :sym &&
+ WHITELISTED_TABLES.include?(symbol.children[0])
+ end
+
+ def hash_key_type(pair)
+ pair.children[0].type
+ end
+
+ def hash_key_name(pair)
+ pair.children[0].children[0]
+ end
+ end
+ end
+ end
+end
diff --git a/rubocop/migration_helpers.rb b/rubocop/migration_helpers.rb
new file mode 100644
index 00000000000..3160a784a04
--- /dev/null
+++ b/rubocop/migration_helpers.rb
@@ -0,0 +1,10 @@
+module RuboCop
+ # Module containing helper methods for writing migration cops.
+ module MigrationHelpers
+ # Returns true if the given node originated from the db/migrate directory.
+ def in_migration?(node)
+ File.dirname(node.location.expression.source_buffer.name).
+ end_with?('db/migrate')
+ end
+ end
+end
diff --git a/rubocop/rubocop.rb b/rubocop/rubocop.rb
new file mode 100644
index 00000000000..7922e19768b
--- /dev/null
+++ b/rubocop/rubocop.rb
@@ -0,0 +1,3 @@
+require_relative 'migration_helpers'
+require_relative 'cop/migration/add_index'
+require_relative 'cop/migration/column_with_default'