pronto-rails_migrations_annotated 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +19 -1
- data/Gemfile +3 -0
- data/Gemfile.lock +11 -1
- data/README.md +2 -5
- data/lib/pronto/rails_migrations_annotated/version.rb +1 -1
- data/lib/pronto/rails_migrations_annotated.rb +135 -94
- data/pronto-rails_migrations_annotated.gemspec +2 -2
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 88430fc4dc1457f81ee58ddbb791363370cb6e85fcd6ffe17b504717a637d5de
|
4
|
+
data.tar.gz: e7f1c20a1f124f27506cb1494d756d1ab314cd9217ce075e68e7a42842380030
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0f56e324b56c92ef3e2957bd39a6dba969e6fa4748bf8acadc1e077288cf0abb882b6e30b9c7c1f9a6d4d0a73b2be713a344e66c8c2d5aee667e7e6dd379f21d
|
7
|
+
data.tar.gz: a9f3ebffd0dd50bc1549d669069cc27a0f3cd67dc0064d428c03f1adffcd0d58df885485e4b9458dbb0c95a8a244e4dc479fbffbb7b2ea169742a73e7b7db5ad
|
data/.rubocop.yml
CHANGED
@@ -1,5 +1,13 @@
|
|
1
|
+
require:
|
2
|
+
- rubocop-rake
|
3
|
+
- rubocop-rspec
|
4
|
+
- rubocop-performance
|
5
|
+
|
1
6
|
AllCops:
|
2
|
-
TargetRubyVersion: 2.
|
7
|
+
TargetRubyVersion: 2.5
|
8
|
+
NewCops: enable
|
9
|
+
Exclude:
|
10
|
+
- spec/fixtures/somerepo/**/*.rb
|
3
11
|
|
4
12
|
Style/StringLiterals:
|
5
13
|
Enabled: true
|
@@ -11,3 +19,13 @@ Style/StringLiteralsInInterpolation:
|
|
11
19
|
|
12
20
|
Layout/LineLength:
|
13
21
|
Max: 120
|
22
|
+
|
23
|
+
Metrics/BlockLength:
|
24
|
+
Exclude:
|
25
|
+
- spec/**/*.rb
|
26
|
+
Metrics/ClassLength:
|
27
|
+
Max: 180
|
28
|
+
Metrics/AbcSize:
|
29
|
+
Max: 25
|
30
|
+
Metrics/CyclomaticComplexity:
|
31
|
+
Max: 8
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
pronto-rails_migrations_annotated (0.0.
|
4
|
+
pronto-rails_migrations_annotated (0.0.3)
|
5
5
|
pronto (~> 0.11)
|
6
6
|
|
7
7
|
GEM
|
@@ -84,6 +84,13 @@ GEM
|
|
84
84
|
unicode-display_width (>= 1.4.0, < 3.0)
|
85
85
|
rubocop-ast (1.13.0)
|
86
86
|
parser (>= 3.0.1.1)
|
87
|
+
rubocop-performance (1.12.0)
|
88
|
+
rubocop (>= 1.7.0, < 2.0)
|
89
|
+
rubocop-ast (>= 0.4.0)
|
90
|
+
rubocop-rake (0.6.0)
|
91
|
+
rubocop (~> 1.0)
|
92
|
+
rubocop-rspec (2.6.0)
|
93
|
+
rubocop (~> 1.19)
|
87
94
|
ruby-progressbar (1.11.0)
|
88
95
|
ruby2_keywords (0.0.5)
|
89
96
|
rugged (1.0.1)
|
@@ -103,6 +110,9 @@ DEPENDENCIES
|
|
103
110
|
rake (~> 13.0)
|
104
111
|
rspec (~> 3.0)
|
105
112
|
rubocop (~> 1.21)
|
113
|
+
rubocop-performance
|
114
|
+
rubocop-rake
|
115
|
+
rubocop-rspec
|
106
116
|
|
107
117
|
BUNDLED WITH
|
108
118
|
2.2.31
|
data/README.md
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# Pronto::RailsMigrationsAnnotated
|
2
|
+
[![Gem Version](https://badge.fury.io/rb/pronto-rails_migrations_annotated.svg)](https://badge.fury.io/rb/pronto-rails_migrations_annotated)
|
2
3
|
|
3
4
|
Pronto runner to enforce migrations to be in separate PRs, but allow schema annotations.
|
4
5
|
Also check for obvious things like adding migration number that is not present in PR to structure.sql.
|
@@ -8,17 +9,13 @@ Also check for obvious things like adding migration number that is not present i
|
|
8
9
|
Add this line to your application's Gemfile:
|
9
10
|
|
10
11
|
```ruby
|
11
|
-
gem 'pronto-rails_migrations_annotated'
|
12
|
+
gem 'pronto-rails_migrations_annotated', require: false
|
12
13
|
```
|
13
14
|
|
14
15
|
And then execute:
|
15
16
|
|
16
17
|
$ bundle install
|
17
18
|
|
18
|
-
Or install it yourself as:
|
19
|
-
|
20
|
-
$ gem install pronto-rails_migrations_annotated
|
21
|
-
|
22
19
|
## Usage
|
23
20
|
|
24
21
|
When in gemfile - pronto should pick up this runner automatically.
|
@@ -1,9 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "pronto"
|
4
4
|
require_relative "rails_migrations_annotated/version"
|
5
5
|
|
6
6
|
module Pronto
|
7
|
+
# Runner that detects migration smells
|
7
8
|
class RailsMigrationsAnnotated < Runner
|
8
9
|
VERSION = Pronto::RailsMigrationsAnnotatedVersion::VERSION
|
9
10
|
|
@@ -11,13 +12,15 @@ module Pronto
|
|
11
12
|
@messages = []
|
12
13
|
|
13
14
|
check_migrations_mixed_with_code
|
14
|
-
|
15
|
+
check_migration_schema_version
|
16
|
+
check_structure_migration_version_numbers
|
15
17
|
check_large_schema_diff
|
18
|
+
check_large_structure_diff
|
16
19
|
|
17
20
|
@messages
|
18
21
|
end
|
19
22
|
|
20
|
-
#
|
23
|
+
# TODO: override def self.title ?
|
21
24
|
|
22
25
|
private
|
23
26
|
|
@@ -25,19 +28,11 @@ module Pronto
|
|
25
28
|
patches = [patches] unless patches.is_a?(Array)
|
26
29
|
patches.each do |patch|
|
27
30
|
target_line = case line
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
@messages << Message.new(
|
34
|
-
patch.delta.new_file[:path],
|
35
|
-
target_line,
|
36
|
-
level,
|
37
|
-
message,
|
38
|
-
nil,
|
39
|
-
self.class
|
40
|
-
)
|
31
|
+
when :first then patch.added_lines.first || patch.lines.first
|
32
|
+
when :last then patch.added_lines.last || patch.lines.last
|
33
|
+
else line
|
34
|
+
end
|
35
|
+
@messages << Message.new(patch.delta.new_file[:path], target_line, level, message, nil, self.class)
|
41
36
|
end
|
42
37
|
end
|
43
38
|
|
@@ -48,14 +43,18 @@ module Pronto
|
|
48
43
|
|
49
44
|
def check_large_schema_diff
|
50
45
|
return unless diff_threshold
|
46
|
+
return unless schema_patches.sum(&:additions) >= diff_threshold ||
|
47
|
+
schema_patches.sum(&:deletions) >= diff_threshold
|
51
48
|
|
52
|
-
|
53
|
-
|
54
|
-
end
|
49
|
+
add_message_at_patch(schema_patches.first, "Large schema diff, pay attention")
|
50
|
+
end
|
55
51
|
|
56
|
-
|
57
|
-
|
58
|
-
|
52
|
+
def check_large_structure_diff
|
53
|
+
return unless diff_threshold
|
54
|
+
return unless structure_patches.sum(&:additions) >= diff_threshold ||
|
55
|
+
structure_patches.sum(&:deletions) >= diff_threshold
|
56
|
+
|
57
|
+
add_message_at_patch(structure_patches.first, "Large structure diff, pay attention")
|
59
58
|
end
|
60
59
|
|
61
60
|
def check_migrations_mixed_with_code
|
@@ -64,82 +63,109 @@ module Pronto
|
|
64
63
|
add_message_at_patch(migration_patches, "Do not mix migrations with other stuff", :fatal)
|
65
64
|
end
|
66
65
|
|
67
|
-
def
|
68
|
-
return
|
66
|
+
def check_migration_schema_version
|
67
|
+
return if migration_patches.none? || !File.exist?(schema_file_name) || gitignored?(schema_file_name)
|
69
68
|
|
70
|
-
|
71
|
-
|
72
|
-
schema_file_name = 'db/schema.rb'
|
73
|
-
if File.exist?(schema_file_name) && !gitignored?(schema_file_name)
|
74
|
-
if schema_patches.none?
|
75
|
-
add_message_at_patch(migration_patches, "Migration file detected, but no changes in schema.rb", :error)
|
76
|
-
else
|
77
|
-
match = File.read(schema_file_name).match(%r{ActiveRecord::Schema.define\(version:\s*(?<version>[0-9_]+)\s*\)})
|
78
|
-
if match
|
79
|
-
schema_migration_version = match[:version]
|
80
|
-
schema_migration_version_clean = schema_migration_version.gsub(/[^0-9]/, '')
|
81
|
-
version_numbers.select { |version| version > schema_migration_version_clean }.each do |wrong_version|
|
82
|
-
add_message_at_patch(
|
83
|
-
migration_patches.first { |patch| patch.delta.new_file[:path].include?(wrong_version) },
|
84
|
-
"Migration version #{wrong_version} is above schema.rb version #{schema_migration_version}"
|
85
|
-
)
|
86
|
-
end
|
87
|
-
else
|
88
|
-
add_message_at_patch(migration_patches.first, "Cannot detect schema migration version", :warning)
|
89
|
-
end
|
90
|
-
end
|
69
|
+
if schema_patches.none?
|
70
|
+
return add_message_at_patch(migration_patches, "Migration file detected, but no changes in schema.rb", :error)
|
91
71
|
end
|
92
72
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
version_numbers.select { |version| versions_from_schema.none? { |strct_ver| strct_ver.include?(version) } }
|
101
|
-
.each do |wrong_version|
|
102
|
-
missing_from_structure = true
|
103
|
-
add_message_at_patch(
|
104
|
-
migration_patches.first { |patch| patch.delta.new_file[:path].include?(wrong_version) },
|
105
|
-
"Migration #{wrong_version} is missing from structure.sql", :error
|
106
|
-
)
|
107
|
-
end
|
73
|
+
migration_version_numbers.select { |version| schema_migration_version&.<(version) }.each do |wrong_version|
|
74
|
+
add_message_at_patch(
|
75
|
+
migration_patches.first { |patch| patch.delta.new_file[:path].include?(wrong_version) },
|
76
|
+
"Migration version #{wrong_version} is above schema.rb version #{schema_migration_version}"
|
77
|
+
)
|
78
|
+
end
|
79
|
+
end
|
108
80
|
|
109
|
-
|
110
|
-
|
111
|
-
end
|
81
|
+
def schema_migration_version
|
82
|
+
return @schema_migration_version if defined? @schema_migration_version
|
112
83
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
84
|
+
@schema_migration_version ||= File.read(schema_file_name)
|
85
|
+
.match(/ActiveRecord::Schema.define\(version:\s*(?<version>[0-9_]+)\s*\)/)
|
86
|
+
&.[](:version)&.gsub(/[^0-9]/, "") ||
|
87
|
+
(add_message_at_patch(migration_patches.first,
|
88
|
+
"Cannot detect migration version in schema.rb. "\
|
89
|
+
"Migration versions are not checked.",
|
90
|
+
:warning) && nil)
|
91
|
+
end
|
120
92
|
|
121
|
-
|
122
|
-
|
123
|
-
|
93
|
+
def migration_version_numbers
|
94
|
+
@migration_version_numbers ||= migration_patches.map do |patch|
|
95
|
+
patch.delta.new_file[:path].sub(/^[^0-9]*([0-9]+).+/, '\1')
|
96
|
+
end
|
97
|
+
end
|
124
98
|
|
125
|
-
|
126
|
-
|
99
|
+
def check_structure_migration_version_numbers
|
100
|
+
return unless migration_patches.any?
|
127
101
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
102
|
+
structure_file_name = "db/structure.sql"
|
103
|
+
return if !File.exist?(structure_file_name) || gitignored?(structure_file_name)
|
104
|
+
|
105
|
+
structure_file_lines = File.readlines(structure_file_name)
|
106
|
+
versions_from_schema = structure_file_lines.grep(migration_line_regex)
|
107
|
+
|
108
|
+
check_structure_versions_present(versions_from_schema)
|
109
|
+
check_structure_migration_missing
|
110
|
+
|
111
|
+
check_structure_versions_syntax(versions_from_schema)
|
112
|
+
check_structure_versions_sorted(versions_from_schema)
|
113
|
+
check_structure_ending(structure_file_lines)
|
114
|
+
end
|
115
|
+
|
116
|
+
def check_structure_versions_present(versions_from_schema)
|
117
|
+
found_missing_migration = false
|
118
|
+
migration_version_numbers.select { |version| versions_from_schema.none? { |ver| ver.include?(version) } }
|
119
|
+
.each do |wrong_version|
|
120
|
+
found_missing_migration = true
|
121
|
+
add_message_at_patch(migration_patches.first { |patch| patch.delta.new_file[:path].include?(wrong_version) },
|
122
|
+
"Migration #{wrong_version} is missing from structure.sql", :error)
|
123
|
+
end
|
124
|
+
|
125
|
+
return if structure_patches.any? || found_missing_migration
|
126
|
+
|
127
|
+
add_message_at_patch(migration_patches, "Migration file detected, but no changes in structure.sql")
|
128
|
+
end
|
134
129
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
130
|
+
def check_structure_migration_missing
|
131
|
+
structure_patches.each do |patch|
|
132
|
+
patch.added_lines.select { |line| line.content.match?(migration_line_regex) }.each do |line|
|
133
|
+
version = line.content.match(migration_line_regex)[:version]
|
134
|
+
next if migration_version_numbers.include?(version)
|
135
|
+
next if line.content.end_with?(",") &&
|
136
|
+
patch.deleted_lines.any? { |del| del.content.include?("#{version}');") }
|
137
|
+
|
138
|
+
add_message_at_patch(patch, "Migration #{version} is not present in this changeset", :error, line: line)
|
140
139
|
end
|
141
140
|
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def check_structure_versions_sorted(versions_from_schema)
|
144
|
+
return if versions_from_schema == versions_from_schema.sort
|
145
|
+
|
146
|
+
add_message_at_patch(
|
147
|
+
structure_patches.first { |patch| patch.lines.any? { |line| line.content.match?(migration_line_regex) } },
|
148
|
+
"Migration versions must be sorted and have correct syntax"
|
149
|
+
)
|
150
|
+
end
|
151
|
+
|
152
|
+
def check_structure_versions_syntax(versions_from_schema)
|
153
|
+
return if versions_from_schema.last.end_with?("');\n") &&
|
154
|
+
versions_from_schema.all? { |line| line.end_with?("'),\n", "');\n") }
|
142
155
|
|
156
|
+
add_message_at_patch(
|
157
|
+
structure_patches.first { |patch| patch.lines.any? { |line| line.content.match?(migration_line_regex) } },
|
158
|
+
"Migration version lines must be separated by comma and end with semicolon"
|
159
|
+
)
|
160
|
+
end
|
161
|
+
|
162
|
+
def check_structure_ending(structure_file_lines)
|
163
|
+
return if structure_patches.none?
|
164
|
+
return if structure_file_lines.last(2) == %W[\n \n] && structure_file_lines[-3] != "\n"
|
165
|
+
return if structure_file_lines.last.match?(/\A\s*[^\s]+\s*\n/)
|
166
|
+
|
167
|
+
add_message_at_patch(structure_patches.last,
|
168
|
+
"structure.sql must end with a newline or 2 empty lines", line: :last)
|
143
169
|
end
|
144
170
|
|
145
171
|
def gitignored?(path)
|
@@ -149,6 +175,22 @@ module Pronto
|
|
149
175
|
nil
|
150
176
|
end
|
151
177
|
|
178
|
+
def schema_file_name
|
179
|
+
"db/schema.rb"
|
180
|
+
end
|
181
|
+
|
182
|
+
def migration_line_regex
|
183
|
+
/\A\s*\('(?<version>[0-9]{14})'\)/
|
184
|
+
end
|
185
|
+
|
186
|
+
def migration_related_files
|
187
|
+
Regexp.union(
|
188
|
+
%r{db/migrate/.*[0-9]+_\w+.rb},
|
189
|
+
%r{db/schema.rb},
|
190
|
+
%r{db/structure.sql}
|
191
|
+
)
|
192
|
+
end
|
193
|
+
|
152
194
|
def migration_patches
|
153
195
|
# nb: there may be engines added to migrations_paths in config or database.yml
|
154
196
|
# but cannot check for this without more knowledge into the particular app
|
@@ -159,14 +201,14 @@ module Pronto
|
|
159
201
|
end
|
160
202
|
|
161
203
|
def non_migration_related_patches
|
162
|
-
@patches.
|
204
|
+
@patches.select do |patch|
|
163
205
|
path = patch.delta.new_file[:path]
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
206
|
+
next false if path.match?(migration_related_files)
|
207
|
+
|
208
|
+
# allow edits in comments and blank lines (model annotations usually come there)
|
209
|
+
!path.end_with?(".rb") || patch.lines.any? do |line|
|
210
|
+
(line.addition? || line.deletion?) && !line.content.match?(/\A\s*(#|\z)/)
|
211
|
+
end
|
170
212
|
end
|
171
213
|
end
|
172
214
|
|
@@ -177,6 +219,5 @@ module Pronto
|
|
177
219
|
def structure_patches
|
178
220
|
@structure_patches = @patches.select { |patch| patch.delta.new_file[:path] =~ %r{db/structure.sql} }
|
179
221
|
end
|
180
|
-
|
181
222
|
end
|
182
223
|
end
|
@@ -15,7 +15,7 @@ Gem::Specification.new do |spec|
|
|
15
15
|
TEXT
|
16
16
|
spec.homepage = "https://github.com/Vasfed/pronto-rails_migrations_annotated"
|
17
17
|
spec.license = "MIT"
|
18
|
-
spec.required_ruby_version = ">= 2.
|
18
|
+
spec.required_ruby_version = ">= 2.5.0"
|
19
19
|
|
20
20
|
spec.metadata["homepage_uri"] = spec.homepage
|
21
21
|
spec.metadata["source_code_uri"] = "https://github.com/Vasfed/pronto-rails_migrations_annotated"
|
@@ -30,5 +30,5 @@ Gem::Specification.new do |spec|
|
|
30
30
|
end
|
31
31
|
spec.require_paths = ["lib"]
|
32
32
|
|
33
|
-
spec.add_dependency "pronto",
|
33
|
+
spec.add_dependency "pronto", "~>0.11"
|
34
34
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pronto-rails_migrations_annotated
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Vasily Fedoseyev
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-12-
|
11
|
+
date: 2021-12-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: pronto
|
@@ -57,7 +57,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
57
57
|
requirements:
|
58
58
|
- - ">="
|
59
59
|
- !ruby/object:Gem::Version
|
60
|
-
version: 2.
|
60
|
+
version: 2.5.0
|
61
61
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
62
62
|
requirements:
|
63
63
|
- - ">="
|