actual_db_schema 0.4.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2f452edc8daa863317285713926ed63c23141523bb4072d5d92efaf512c95023
4
- data.tar.gz: 21d4189566b38353dc93f5f58acec5a343ef8553736f8e119263b4606672082f
3
+ metadata.gz: 116a62e0bbc1d2f43ad5e7654f732807bb6807520f26f33bfde84889601bb895
4
+ data.tar.gz: 63c36388ed0efa1d19b9daec936206b6580e22b1b04b25a0db3f058047c34b56
5
5
  SHA512:
6
- metadata.gz: a591d53c12d6d26993ff7ffaebf12ee4fc2cfd183a04bae10f32d1f7b514dcbdc0b012349f0b2f6fc392d724fcba1ecc6981a6f8f5f0c69222eb0c91290f1397
7
- data.tar.gz: c9251df1909fbd42a722ae7f62b3dfbcf40ff5cc669fc3a6eac992a322d91fd96c31d476a6adac0324bf21d9420e2f91bd5f08c910fb833dfb57841af7e84e73
6
+ metadata.gz: ef27b62051c6183b68a35018b0ed7f402f25f8de2f3d3da8f842b914c13516a49b00ab57687a513bca04b664fc682b40c93c75929e414dfea514c2e0065a68b9
7
+ data.tar.gz: 7a7f0ce3ef0ce09a5a0ecf90ff0aec292ae6f9ab58050ce946dd849123b36162398592a9249ea9ec4acecbe9cfe2031737cbe16c5c0b5ef89989ebb48237fee9
data/.rubocop.yml CHANGED
@@ -1,5 +1,9 @@
1
1
  AllCops:
2
- TargetRubyVersion: 2.6
2
+ TargetRubyVersion: 2.7
3
+ Exclude:
4
+ - gemfiles/*
5
+ - test/dummy_app/**/*
6
+ - vendor/**/*
3
7
 
4
8
  Style/StringLiterals:
5
9
  Enabled: true
@@ -11,3 +15,12 @@ Style/StringLiteralsInInterpolation:
11
15
 
12
16
  Layout/LineLength:
13
17
  Max: 120
18
+
19
+ Metrics/BlockLength:
20
+ Exclude:
21
+ - test/**/*
22
+ - actual_db_schema.gemspec
23
+
24
+ Metrics/MethodLength:
25
+ Exclude:
26
+ - test/**/*
data/Appraisals ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ %w[6.0 6.1 7.0 7.1].each do |version|
4
+ appraise "rails.#{version}" do
5
+ gem "activerecord", "~> #{version}.0"
6
+ gem "activesupport", "~> #{version}.0"
7
+ end
8
+ end
data/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ ## [0.6.0] - 2024-01-03
2
+
3
+ - Added db:phantom_migrations task to display phantom migrations
4
+ - Updated README
5
+
6
+ ## [0.5.0] - 2023-11-06
7
+
8
+ - Rails 7.1 support added
9
+
1
10
  ## [0.4.0] - 2023-07-05
2
11
 
3
12
  - rollback migrations in the reversed order
data/Gemfile CHANGED
@@ -5,8 +5,8 @@ source "https://rubygems.org"
5
5
  # Specify your gem's dependencies in actual_db_schema.gemspec
6
6
  gemspec
7
7
 
8
- gem "rake", "~> 13.0"
9
-
8
+ gem "activerecord", "~> 7.1.0"
9
+ gem "activesupport", "~> 7.1.0"
10
10
  gem "minitest", "~> 5.0"
11
-
11
+ gem "rake"
12
12
  gem "rubocop", "~> 1.21"
data/Gemfile.lock CHANGED
@@ -1,61 +1,237 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- actual_db_schema (0.4.0)
5
- activerecord
4
+ actual_db_schema (0.6.0)
5
+ activerecord (>= 6.0.0)
6
+ activesupport (>= 6.0.0)
6
7
 
7
8
  GEM
8
9
  remote: https://rubygems.org/
9
10
  specs:
10
- activemodel (7.0.6)
11
- activesupport (= 7.0.6)
12
- activerecord (7.0.6)
13
- activemodel (= 7.0.6)
14
- activesupport (= 7.0.6)
15
- activesupport (7.0.6)
11
+ actioncable (7.1.1)
12
+ actionpack (= 7.1.1)
13
+ activesupport (= 7.1.1)
14
+ nio4r (~> 2.0)
15
+ websocket-driver (>= 0.6.1)
16
+ zeitwerk (~> 2.6)
17
+ actionmailbox (7.1.1)
18
+ actionpack (= 7.1.1)
19
+ activejob (= 7.1.1)
20
+ activerecord (= 7.1.1)
21
+ activestorage (= 7.1.1)
22
+ activesupport (= 7.1.1)
23
+ mail (>= 2.7.1)
24
+ net-imap
25
+ net-pop
26
+ net-smtp
27
+ actionmailer (7.1.1)
28
+ actionpack (= 7.1.1)
29
+ actionview (= 7.1.1)
30
+ activejob (= 7.1.1)
31
+ activesupport (= 7.1.1)
32
+ mail (~> 2.5, >= 2.5.4)
33
+ net-imap
34
+ net-pop
35
+ net-smtp
36
+ rails-dom-testing (~> 2.2)
37
+ actionpack (7.1.1)
38
+ actionview (= 7.1.1)
39
+ activesupport (= 7.1.1)
40
+ nokogiri (>= 1.8.5)
41
+ rack (>= 2.2.4)
42
+ rack-session (>= 1.0.1)
43
+ rack-test (>= 0.6.3)
44
+ rails-dom-testing (~> 2.2)
45
+ rails-html-sanitizer (~> 1.6)
46
+ actiontext (7.1.1)
47
+ actionpack (= 7.1.1)
48
+ activerecord (= 7.1.1)
49
+ activestorage (= 7.1.1)
50
+ activesupport (= 7.1.1)
51
+ globalid (>= 0.6.0)
52
+ nokogiri (>= 1.8.5)
53
+ actionview (7.1.1)
54
+ activesupport (= 7.1.1)
55
+ builder (~> 3.1)
56
+ erubi (~> 1.11)
57
+ rails-dom-testing (~> 2.2)
58
+ rails-html-sanitizer (~> 1.6)
59
+ activejob (7.1.1)
60
+ activesupport (= 7.1.1)
61
+ globalid (>= 0.3.6)
62
+ activemodel (7.1.1)
63
+ activesupport (= 7.1.1)
64
+ activerecord (7.1.1)
65
+ activemodel (= 7.1.1)
66
+ activesupport (= 7.1.1)
67
+ timeout (>= 0.4.0)
68
+ activestorage (7.1.1)
69
+ actionpack (= 7.1.1)
70
+ activejob (= 7.1.1)
71
+ activerecord (= 7.1.1)
72
+ activesupport (= 7.1.1)
73
+ marcel (~> 1.0)
74
+ activesupport (7.1.1)
75
+ base64
76
+ bigdecimal
16
77
  concurrent-ruby (~> 1.0, >= 1.0.2)
78
+ connection_pool (>= 2.2.5)
79
+ drb
17
80
  i18n (>= 1.6, < 2)
18
81
  minitest (>= 5.1)
82
+ mutex_m
19
83
  tzinfo (~> 2.0)
84
+ appraisal (2.5.0)
85
+ bundler
86
+ rake
87
+ thor (>= 0.14.0)
20
88
  ast (2.4.2)
89
+ base64 (0.1.1)
90
+ bigdecimal (3.1.4)
91
+ builder (3.2.4)
21
92
  concurrent-ruby (1.2.2)
93
+ connection_pool (2.4.1)
94
+ crass (1.0.6)
95
+ date (3.3.3)
96
+ debug (1.8.0)
97
+ irb (>= 1.5.0)
98
+ reline (>= 0.3.1)
99
+ drb (2.1.1)
100
+ ruby2_keywords
101
+ erubi (1.12.0)
102
+ globalid (1.2.1)
103
+ activesupport (>= 6.1)
22
104
  i18n (1.14.1)
23
105
  concurrent-ruby (~> 1.0)
24
- json (2.6.2)
25
- minitest (5.16.3)
26
- parallel (1.22.1)
27
- parser (3.1.2.1)
106
+ io-console (0.6.0)
107
+ irb (1.8.3)
108
+ rdoc
109
+ reline (>= 0.3.8)
110
+ json (2.6.3)
111
+ language_server-protocol (3.17.0.3)
112
+ loofah (2.21.4)
113
+ crass (~> 1.0.2)
114
+ nokogiri (>= 1.12.0)
115
+ mail (2.8.1)
116
+ mini_mime (>= 0.1.1)
117
+ net-imap
118
+ net-pop
119
+ net-smtp
120
+ marcel (1.0.2)
121
+ mini_mime (1.1.5)
122
+ minitest (5.20.0)
123
+ mutex_m (0.1.2)
124
+ net-imap (0.4.4)
125
+ date
126
+ net-protocol
127
+ net-pop (0.1.2)
128
+ net-protocol
129
+ net-protocol (0.2.1)
130
+ timeout
131
+ net-smtp (0.4.0)
132
+ net-protocol
133
+ nio4r (2.5.9)
134
+ nokogiri (1.15.4-x86_64-darwin)
135
+ racc (~> 1.4)
136
+ nokogiri (1.15.4-x86_64-linux)
137
+ racc (~> 1.4)
138
+ parallel (1.23.0)
139
+ parser (3.2.2.4)
28
140
  ast (~> 2.4.1)
141
+ racc
142
+ psych (5.1.1.1)
143
+ stringio
144
+ racc (1.7.3)
145
+ rack (3.0.8)
146
+ rack-session (2.0.0)
147
+ rack (>= 3.0.0)
148
+ rack-test (2.1.0)
149
+ rack (>= 1.3)
150
+ rackup (2.1.0)
151
+ rack (>= 3)
152
+ webrick (~> 1.8)
153
+ rails (7.1.1)
154
+ actioncable (= 7.1.1)
155
+ actionmailbox (= 7.1.1)
156
+ actionmailer (= 7.1.1)
157
+ actionpack (= 7.1.1)
158
+ actiontext (= 7.1.1)
159
+ actionview (= 7.1.1)
160
+ activejob (= 7.1.1)
161
+ activemodel (= 7.1.1)
162
+ activerecord (= 7.1.1)
163
+ activestorage (= 7.1.1)
164
+ activesupport (= 7.1.1)
165
+ bundler (>= 1.15.0)
166
+ railties (= 7.1.1)
167
+ rails-dom-testing (2.2.0)
168
+ activesupport (>= 5.0.0)
169
+ minitest
170
+ nokogiri (>= 1.6)
171
+ rails-html-sanitizer (1.6.0)
172
+ loofah (~> 2.21)
173
+ nokogiri (~> 1.14)
174
+ railties (7.1.1)
175
+ actionpack (= 7.1.1)
176
+ activesupport (= 7.1.1)
177
+ irb
178
+ rackup (>= 1.0.0)
179
+ rake (>= 12.2)
180
+ thor (~> 1.0, >= 1.2.2)
181
+ zeitwerk (~> 2.6)
29
182
  rainbow (3.1.1)
30
- rake (13.0.6)
31
- regexp_parser (2.6.0)
32
- rexml (3.2.5)
33
- rubocop (1.36.0)
183
+ rake (13.1.0)
184
+ rdoc (6.5.0)
185
+ psych (>= 4.0.0)
186
+ regexp_parser (2.8.2)
187
+ reline (0.3.9)
188
+ io-console (~> 0.5)
189
+ rexml (3.2.6)
190
+ rubocop (1.57.2)
34
191
  json (~> 2.3)
192
+ language_server-protocol (>= 3.17.0)
35
193
  parallel (~> 1.10)
36
- parser (>= 3.1.2.1)
194
+ parser (>= 3.2.2.4)
37
195
  rainbow (>= 2.2.2, < 4.0)
38
196
  regexp_parser (>= 1.8, < 3.0)
39
197
  rexml (>= 3.2.5, < 4.0)
40
- rubocop-ast (>= 1.20.1, < 2.0)
198
+ rubocop-ast (>= 1.28.1, < 2.0)
41
199
  ruby-progressbar (~> 1.7)
42
- unicode-display_width (>= 1.4.0, < 3.0)
43
- rubocop-ast (1.21.0)
44
- parser (>= 3.1.1.0)
45
- ruby-progressbar (1.11.0)
200
+ unicode-display_width (>= 2.4.0, < 3.0)
201
+ rubocop-ast (1.30.0)
202
+ parser (>= 3.2.1.0)
203
+ ruby-progressbar (1.13.0)
204
+ ruby2_keywords (0.0.5)
205
+ sqlite3 (1.6.8-x86_64-darwin)
206
+ sqlite3 (1.6.8-x86_64-linux)
207
+ stringio (3.0.8)
208
+ thor (1.3.0)
209
+ timeout (0.4.0)
46
210
  tzinfo (2.0.6)
47
211
  concurrent-ruby (~> 1.0)
48
- unicode-display_width (2.3.0)
212
+ unicode-display_width (2.5.0)
213
+ webrick (1.8.1)
214
+ websocket-driver (0.7.6)
215
+ websocket-extensions (>= 0.1.0)
216
+ websocket-extensions (0.1.5)
217
+ zeitwerk (2.6.12)
49
218
 
50
219
  PLATFORMS
51
220
  x86_64-darwin-20
221
+ x86_64-darwin-22
52
222
  x86_64-linux
53
223
 
54
224
  DEPENDENCIES
225
+ activerecord (~> 7.1.0)
226
+ activesupport (~> 7.1.0)
55
227
  actual_db_schema!
228
+ appraisal
229
+ debug
56
230
  minitest (~> 5.0)
57
- rake (~> 13.0)
231
+ rails
232
+ rake
58
233
  rubocop (~> 1.21)
234
+ sqlite3
59
235
 
60
236
  BUNDLED WITH
61
237
  2.3.7
data/README.md CHANGED
@@ -2,7 +2,37 @@
2
2
 
3
3
  # ActualDbSchema
4
4
 
5
- Keep Rails DB schema consistent while switching between branches with no additional actions.
5
+ Does switching between branches in your Rails app mess up the DB schema?
6
+
7
+ Keep the DB schema actual across branches in your Rails project. Just install `actual_db_schema` gem and run `db:migrate` in branches as usual. It automatically rolls back the *phantom migrations* (non-relevant to the current branch). No additional steps are needed.
8
+
9
+ ## Why ActualDbSchema
10
+
11
+ Still not clear why it's needed? To grasp the purpose of this gem and the issue it addresses, review the problem definition outlined below.
12
+
13
+ ### The problem definition
14
+
15
+ Imagine you're working on **branch A**. You add a not-null column to a database table with a migration. You run the migration. Then you switch to **branch B**. The code in **branch B** isn't aware of this newly added field. When it tries to write data to the table, it fails with an error `null value provided for non-null field`. Why? The existing code is writing a null value into the column with a not-null constraint.
16
+
17
+ Here's an example of this error:
18
+
19
+ ActiveRecord::NotNullViolation:
20
+ PG::NotNullViolation: ERROR: null value in column "log" of relation "check_results" violates not-null constraint
21
+ DETAIL: Failing row contains (8, 46, success, 2022-10-16 21:47:21.07212, 2022-10-16 21:47:21.07212, null).
22
+
23
+ Furthermore, the `db:migrate` task on **branch B** generates an irrelevant diff on the `schema.rb` file, reflecting the new column added in **branch A**.
24
+
25
+ To fix this, you need to switch back to **branch A**, find the migration that added the problematic field, and roll it back. We'll call it a *phantom migration*. It's a pain, especially if you have a lot of branches in your project because you have to remember which branch the *phantom migration* is in and then manually roll it back.
26
+
27
+ With `actual_db_schema` gem you don't need to care about that anymore. It saves you time by handling all this dirty work behind the scenes automatically.
28
+
29
+ ### How it solves the issue
30
+
31
+ This gem stores all run migrations with their code in the `tmp/migrations` folder. Whenever you perform a schema dump, it rolls back the *phantom migrations*.
32
+
33
+ The *phantom migrations* list is the difference between the migrations you've executed (in the `tmp/migrations` folder) and the current ones (in the `db/migrate` folder).
34
+
35
+ Therefore, all you do is run rails `db:migrate` in your current branch. `actual_db_schema` will ensure the DB schema is up-to-date. You'll never have an inaccurate `schema.rb` file again.
6
36
 
7
37
  ## Installation
8
38
 
@@ -20,28 +50,14 @@ And then execute:
20
50
 
21
51
  ## Usage
22
52
 
23
- TLTR; Just run `rails db:migrate` inside the current branch.
24
-
25
- In **branch A** I add a mandatory (not null) field into DB via migration and run it.
26
- Then I switch to another **branch B**. This branch's code is not aware of that field.
27
- As the result, the code is failing with an error "null value provided for non-null field".
28
- Moreover, a DB rake task generates a diff on `schema.rb` that's not relevant to this branch.
29
- I can switch to **branch A** and roll back the migration, but I need to remember that branch or waste time on it.
30
-
31
- Some example of this error:
32
-
33
- ActiveRecord::NotNullViolation:
34
- PG::NotNullViolation: ERROR: null value in column "log" of relation "check_results" violates not-null constraint
35
- DETAIL: Failing row contains (8, 46, success, 2022-10-16 21:47:21.07212, 2022-10-16 21:47:21.07212, null).
36
-
37
- This gem saves all run migrations inside `tmp/migrations` folder.
38
- Every run of schema dump (that's a dependency of `db:migrate` task as well) rolls back the "unknown" for the current branch migrations
39
- looking into the `tmp/migrations` folder.
53
+ Just run `rails db:migrate` inside the current branch.
40
54
 
41
- You just need to run `rails db:migrate` in the current branch to actualize the DB schema. With its hand, you will never have wrongly generated `schema.rb`.
55
+ > [!WARNING]
56
+ > This solution implies that all migrations are reversible. The irreversible migrations should be solved manually. At the moment, the gem ignores them. You will see warnings in the terminal for each irreversible migrations.
42
57
 
43
- > **Warning**
44
- > This solution implies that all migrations are reversible. The cases with irreversible migrations should be solved manually. At the moment, these migrations are ignored by the gem and you will see a warning if some migrations can't roll back automatically.
58
+ The gem offers the following rake tasks that can be manually run according to your preferences:
59
+ - `rails db:rollback_branches` - run it to manually rolls back phantom migrations.
60
+ - `rails db:phantom_migrations` - displays a list of phantom migrations.
45
61
 
46
62
  ## Development
47
63
 
data/Rakefile CHANGED
@@ -6,11 +6,11 @@ require "rake/testtask"
6
6
  Rake::TestTask.new(:test) do |t|
7
7
  t.libs << "test"
8
8
  t.libs << "lib"
9
- t.test_files = FileList["test/**/test_*.rb"]
9
+ t.test_files = FileList["test/**/test_*.rb", "test/**/*_test.rb"]
10
10
  end
11
11
 
12
12
  require "rubocop/rake_task"
13
13
 
14
14
  RuboCop::RakeTask.new
15
15
 
16
- task default: %i[test rubocop]
16
+ task default: %i[test]
@@ -16,7 +16,7 @@ Gem::Specification.new do |spec|
16
16
  DESC
17
17
  spec.homepage = "https://github.com/widefix/actual_db_schema"
18
18
  spec.license = "MIT"
19
- spec.required_ruby_version = ">= 2.6.0"
19
+ spec.required_ruby_version = ">= 2.7.0"
20
20
 
21
21
  # spec.metadata["allowed_push_host"] = "TODO: Set to your gem server 'https://example.com'"
22
22
 
@@ -36,7 +36,13 @@ Gem::Specification.new do |spec|
36
36
  spec.require_paths = ["lib"]
37
37
 
38
38
  # Uncomment to register a new dependency of your gem
39
- spec.add_dependency "activerecord"
39
+ spec.add_runtime_dependency "activerecord", ">= 6.0.0"
40
+ spec.add_runtime_dependency "activesupport", ">= 6.0.0"
41
+
42
+ spec.add_development_dependency "appraisal"
43
+ spec.add_development_dependency "debug"
44
+ spec.add_development_dependency "rails"
45
+ spec.add_development_dependency "sqlite3"
40
46
 
41
47
  # For more information and examples about making a new gem, check out our
42
48
  # guide at: https://bundler.io/guides/creating_gem.html
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file was generated by Appraisal
4
+
5
+ source "https://rubygems.org"
6
+
7
+ gem "activerecord", "~> 6.0.0"
8
+ gem "activesupport", "~> 6.0.0"
9
+ gem "minitest", "~> 5.0"
10
+ gem "rake"
11
+ gem "rubocop", "~> 1.21"
12
+
13
+ gemspec path: "../"
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file was generated by Appraisal
4
+
5
+ source "https://rubygems.org"
6
+
7
+ gem "activerecord", "~> 6.1.0"
8
+ gem "activesupport", "~> 6.1.0"
9
+ gem "minitest", "~> 5.0"
10
+ gem "rake"
11
+ gem "rubocop", "~> 1.21"
12
+
13
+ gemspec path: "../"
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file was generated by Appraisal
4
+
5
+ source "https://rubygems.org"
6
+
7
+ gem "activerecord", "~> 7.0.0"
8
+ gem "activesupport", "~> 7.0.0"
9
+ gem "minitest", "~> 5.0"
10
+ gem "rake"
11
+ gem "rubocop", "~> 1.21"
12
+
13
+ gemspec path: "../"
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file was generated by Appraisal
4
+
5
+ source "https://rubygems.org"
6
+
7
+ gem "activerecord", "~> 7.1.0"
8
+ gem "activesupport", "~> 7.1.0"
9
+ gem "minitest", "~> 5.0"
10
+ gem "rake"
11
+ gem "rubocop", "~> 1.21"
12
+
13
+ gemspec path: "../"
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActualDbSchema
4
+ module Commands
5
+ # Base class for all commands
6
+ class Base
7
+ def call
8
+ unless ActualDbSchema.config.fetch(:enabled, true)
9
+ raise "ActualDbSchema is disabled. Set ActualDbSchema.config[:enabled] = true to enable it."
10
+ end
11
+
12
+ if ActiveRecord::Migration.current_version >= 6
13
+ ActiveRecord::Tasks::DatabaseTasks.raise_for_multi_db(command: "db:rollback_branches")
14
+ end
15
+
16
+ call_impl
17
+ end
18
+
19
+ private
20
+
21
+ def call_impl
22
+ raise NotImplementedError
23
+ end
24
+
25
+ def context
26
+ @context ||=
27
+ ActiveRecord::Base.connection.migration_context.tap do |c|
28
+ c.extend(ActualDbSchema::Patches::MigrationContext)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActualDbSchema
4
+ module Commands
5
+ # Shows the list of phantom migrations
6
+ class List < Base
7
+ private
8
+
9
+ def call_impl
10
+ preambule
11
+ table
12
+ end
13
+
14
+ def indexed_phantom_migrations
15
+ @indexed_phantom_migrations ||= context.migrations.index_by { |m| m.version.to_s }
16
+ end
17
+
18
+ def preambule
19
+ puts "\nPhantom migrations\n\n"
20
+ puts "Below is a list of irrelevant migrations executed in unmerged branches."
21
+ puts "To bring your database schema up to date, the migrations marked as \"up\" should be rolled back."
22
+ puts "\ndatabase: #{ActiveRecord::Base.connection_db_config.database}\n\n"
23
+ puts %(#{"Status".center(8)} #{"Migration ID".ljust(14)} Migration File)
24
+ puts "-" * 50
25
+ end
26
+
27
+ def table
28
+ context.migrations_status.each do |status, version|
29
+ migration = indexed_phantom_migrations[version]
30
+ next unless migration
31
+
32
+ puts %(#{status.center(8)} #{version.to_s.ljust(14)} #{migration.filename.gsub("#{Rails.root}/", "")})
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActualDbSchema
4
+ module Commands
5
+ # Rolls back all phantom migrations
6
+ class Rollback < Base
7
+ private
8
+
9
+ def call_impl
10
+ context.rollback_branches
11
+
12
+ return if ActualDbSchema.failed.empty?
13
+
14
+ puts ""
15
+ puts "[ActualDbSchema] Irreversible migrations were found from other branches. Roll them back or fix manually:"
16
+ puts ""
17
+ puts ActualDbSchema.failed.map { |migration| "- #{migration.filename}" }.join("\n")
18
+ puts ""
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActualDbSchema
4
+ module Patches
5
+ # Add new command to roll back the phantom migrations
6
+ module MigrationContext
7
+ def rollback_branches
8
+ ActualDbSchema.failed = []
9
+ migrations.reverse_each do |migration|
10
+ migrator = down_migrator_for(migration)
11
+ migrator.extend(ActualDbSchema::Patches::Migrator)
12
+ migrator.migrate
13
+ rescue StandardError => e
14
+ raise unless e.message.include?("ActiveRecord::IrreversibleMigration")
15
+
16
+ ActualDbSchema.failed << migration
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ def down_migrator_for(migration)
23
+ if ActiveRecord::Migration.current_version < 6
24
+ ActiveRecord::Migrator.new(:down, [migration], migration.version)
25
+ elsif ActiveRecord::Migration.current_version < 7.1
26
+ ActiveRecord::Migrator.new(:down, [migration], schema_migration, migration.version)
27
+ else
28
+ ActiveRecord::Migrator.new(:down, [migration], schema_migration, internal_metadata, migration.version)
29
+ end
30
+ end
31
+
32
+ def migration_files
33
+ paths = Array(migrations_paths)
34
+ current_branch_files = Dir[*paths.flat_map { |path| "#{path}/**/[0-9]*_*.rb" }]
35
+ other_branches_files = Dir["#{ActualDbSchema.migrated_folder}/**/[0-9]*_*.rb"]
36
+
37
+ current_branch_file_names = current_branch_files.map { |f| ActualDbSchema.migration_filename(f) }
38
+ other_branches_files.reject { |f| ActualDbSchema.migration_filename(f).in?(current_branch_file_names) }
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActualDbSchema
4
+ module Patches
5
+ # Records the migration file into the tmp folder after it's been migrated
6
+ module MigrationProxy
7
+ def migrate(direction)
8
+ super(direction)
9
+ FileUtils.copy(filename, ActualDbSchema.migrated_folder.join(basename)) if direction == :up
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActualDbSchema
4
+ module Patches
5
+ # Run only one migration that's being rolled back
6
+ module Migrator
7
+ def runnable
8
+ migration = migrations.first # there is only one migration, because we pass only one here
9
+ ran?(migration) ? [migration] : []
10
+ end
11
+ end
12
+ end
13
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActualDbSchema
4
- VERSION = "0.4.0"
4
+ VERSION = "0.6.0"
5
5
  end
@@ -1,8 +1,37 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_record/migration"
3
4
  require_relative "actual_db_schema/version"
5
+ require_relative "actual_db_schema/patches/migration_proxy"
6
+ require_relative "actual_db_schema/patches/migrator"
7
+ require_relative "actual_db_schema/patches/migration_context"
8
+
9
+ require_relative "actual_db_schema/commands/base"
10
+ require_relative "actual_db_schema/commands/rollback"
11
+ require_relative "actual_db_schema/commands/list"
4
12
 
5
13
  # The main module definition
6
14
  module ActualDbSchema
7
- require "railtie" if defined?(Rails)
15
+ raise NotImplementedError, "ActualDbSchema is only supported in Rails" unless defined?(Rails)
16
+
17
+ require "railtie"
18
+
19
+ class << self
20
+ attr_accessor :config, :failed
21
+ end
22
+
23
+ self.failed = []
24
+ self.config = {
25
+ enabled: Rails.env.development?
26
+ }
27
+
28
+ def self.migrated_folder
29
+ Rails.root.join("tmp", "migrated").tap { |folder| FileUtils.mkdir_p(folder) }
30
+ end
31
+
32
+ def self.migration_filename(fullpath)
33
+ fullpath.split("/").last
34
+ end
8
35
  end
36
+
37
+ ActiveRecord::MigrationProxy.prepend(ActualDbSchema::Patches::MigrationProxy)
data/lib/railtie.rb CHANGED
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "actual_db_schema"
4
3
  require "rails"
5
4
 
6
5
  module ActualDbSchema
data/lib/tasks/db.rake CHANGED
@@ -1,96 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- return unless Rails.env.development?
4
-
5
- require "active_record/migration"
6
-
7
- def migrated_folder
8
- Rails.root.join("tmp", "migrated").tap { |folder| FileUtils.mkdir_p(folder) }
9
- end
10
-
11
- def migration_filename(fullpath)
12
- fullpath.split("/").last
13
- end
14
-
15
- # All patches are namespaced into this module
16
- module ActualDbSchema
17
- class << self
18
- attr_accessor :failed
19
- end
20
-
21
- self.failed = []
22
-
23
- # Track migrated migrations inside the tmp folder
24
- module MigrationProxyPatch
25
- def migrate(direction)
26
- super(direction)
27
- FileUtils.copy(filename, migrated_folder.join(basename)) if direction == :up
28
- end
29
- end
30
-
31
- # Run only one migration that's being rolled back
32
- module MigratorPatch
33
- def runnable
34
- migration = migrations.first # there is only one migration, because we pass only one here
35
- ran?(migration) ? [migration] : []
36
- end
37
- end
38
-
39
- # Add new command to roll back the phantom migrations
40
- module MigrationContextPatch
41
- def rollback_branches
42
- migrations.reverse_each do |migration|
43
- migrator = down_migrator_for(migration)
44
- migrator.extend(ActualDbSchema::MigratorPatch)
45
- migrator.migrate
46
- rescue StandardError => e
47
- raise unless e.message.include?("ActiveRecord::IrreversibleMigration")
48
-
49
- ActualDbSchema.failed << migration
50
- end
51
- end
52
-
53
- private
54
-
55
- def down_migrator_for(migration)
56
- if ActiveRecord::Migration.current_version < 6
57
- ActiveRecord::Migrator.new(:down, [migration], migration.version)
58
- else
59
- ActiveRecord::Migrator.new(:down, [migration], schema_migration, migration.version)
60
- end
61
- end
62
-
63
- def migration_files
64
- paths = Array(migrations_paths)
65
- current_branch_files = Dir[*paths.flat_map { |path| "#{path}/**/[0-9]*_*.rb" }]
66
- other_branches_files = Dir["#{migrated_folder}/**/[0-9]*_*.rb"]
67
-
68
- current_branch_file_names = current_branch_files.map { |f| migration_filename(f) }
69
- other_branches_files.reject { |f| migration_filename(f).in?(current_branch_file_names) }
70
- end
71
- end
72
- end
73
-
74
- ActiveRecord::MigrationProxy.prepend(ActualDbSchema::MigrationProxyPatch)
75
-
76
3
  namespace :db do
77
4
  desc "Rollback migrations that were run inside not a merged branch."
78
5
  task rollback_branches: :load_config do
79
- if ActiveRecord::Migration.current_version >= 6
80
- ActiveRecord::Tasks::DatabaseTasks.raise_for_multi_db(command: "db:rollback_branches")
81
- end
6
+ ActualDbSchema::Commands::Rollback.new.call
7
+ end
82
8
 
83
- context = ActiveRecord::Base.connection.migration_context
84
- context.extend(ActualDbSchema::MigrationContextPatch)
85
- context.rollback_branches
86
- if ActualDbSchema.failed.any?
87
- puts ""
88
- puts "[ActualDbSchema] Irreversible migrations were found from other branches. Roll them back or fix manually:"
89
- puts ""
90
- puts ActualDbSchema.failed.map { |migration| "- #{migration.filename}" }.join("\n")
91
- puts ""
92
- end
93
- # raise ActualDbSchema.failed.inspect
9
+ desc "List all phantom migrations - non-relevant migrations that were run inside not a merged branch."
10
+ task phantom_migrations: :load_config do
11
+ ActualDbSchema::Commands::List.new.call
94
12
  end
95
13
 
96
14
  task _dump: :rollback_branches
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: actual_db_schema
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrei Kaleshka
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-07-05 00:00:00.000000000 Z
11
+ date: 2024-01-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -16,9 +16,79 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: 6.0.0
20
20
  type: :runtime
21
21
  prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 6.0.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: activesupport
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 6.0.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 6.0.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: appraisal
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: debug
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rails
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: sqlite3
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
22
92
  version_requirements: !ruby/object:Gem::Requirement
23
93
  requirements:
24
94
  - - ">="
@@ -35,6 +105,7 @@ extensions: []
35
105
  extra_rdoc_files: []
36
106
  files:
37
107
  - ".rubocop.yml"
108
+ - Appraisals
38
109
  - CHANGELOG.md
39
110
  - CODE_OF_CONDUCT.md
40
111
  - Gemfile
@@ -43,7 +114,17 @@ files:
43
114
  - README.md
44
115
  - Rakefile
45
116
  - actual_db_schema.gemspec
117
+ - gemfiles/rails.6.0.gemfile
118
+ - gemfiles/rails.6.1.gemfile
119
+ - gemfiles/rails.7.0.gemfile
120
+ - gemfiles/rails.7.1.gemfile
46
121
  - lib/actual_db_schema.rb
122
+ - lib/actual_db_schema/commands/base.rb
123
+ - lib/actual_db_schema/commands/list.rb
124
+ - lib/actual_db_schema/commands/rollback.rb
125
+ - lib/actual_db_schema/patches/migration_context.rb
126
+ - lib/actual_db_schema/patches/migration_proxy.rb
127
+ - lib/actual_db_schema/patches/migrator.rb
47
128
  - lib/actual_db_schema/version.rb
48
129
  - lib/railtie.rb
49
130
  - lib/tasks/db.rake
@@ -63,14 +144,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
63
144
  requirements:
64
145
  - - ">="
65
146
  - !ruby/object:Gem::Version
66
- version: 2.6.0
147
+ version: 2.7.0
67
148
  required_rubygems_version: !ruby/object:Gem::Requirement
68
149
  requirements:
69
150
  - - ">="
70
151
  - !ruby/object:Gem::Version
71
152
  version: '0'
72
153
  requirements: []
73
- rubygems_version: 3.3.7
154
+ rubygems_version: 3.4.21
74
155
  signing_key:
75
156
  specification_version: 4
76
157
  summary: Keep your DB clean and consistent between branches.