data-migration 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -0
- data/README.md +4 -2
- data/data-migration.gemspec +1 -4
- data/lib/data-migration.rb +5 -2
- data/lib/data_migration/cli.rb +20 -0
- data/lib/data_migration/config.rb +2 -2
- data/lib/data_migration/job.rb +3 -4
- data/lib/data_migration/railtie.rb +9 -0
- data/lib/data_migration/selector.rb +48 -0
- data/lib/data_migration/task.rb +27 -8
- data/lib/data_migration/tasks/data_migration.rake +8 -0
- data/lib/generators/templates/data_migration.rb.tt +2 -2
- metadata +7 -45
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d0a50f98ed1ef1b0b960cc713e71d64d41219933e0f7071fa9462c8adf69bb18
|
|
4
|
+
data.tar.gz: d169ccd0f77204e3ce81a10d5c9d39f4b320572a8ca2e269c8502bfea6b8ecc1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b11fdc6a2879a38416e43b35bc2ea427c4396ca60977cfd919cd945f5563baaf07e398ce1d0cca42f71a936b01a6095ae971bf7320f8eced0059fa19a8c88eb8
|
|
7
|
+
data.tar.gz: d6d738963fe7473eb84984730f514cce3cebbf0a5ae2e4301734bca5e96c0cb8a5fdbe3998561136a27e1a0930c2162f2dbc713b19f9bded90b9f5dcdf463ac7
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# CHANGELOG
|
|
2
2
|
|
|
3
|
+
## 2.1.0 (2026-11-09)
|
|
4
|
+
|
|
5
|
+
- Ship `bin/rails db:migrate:data` with `VERSION=` / `NAME=` selection and a migration list when both are blank.
|
|
6
|
+
- Keep the data migration operator on the task when enqueueing and in monitoring context.
|
|
7
|
+
- Report failed data migration job exceptions to ActiveJob hosts instead of discarding them.
|
|
8
|
+
- Reach full line coverage of packaged `lib/` and bind a local pressure probe with ceilings.
|
|
9
|
+
|
|
10
|
+
## 2.0.1 (2026-10-28)
|
|
11
|
+
|
|
12
|
+
- Confirm gem release from a clean tree before tagging.
|
|
13
|
+
|
|
3
14
|
## 2.0.0 (2026-09-04)
|
|
4
15
|
|
|
5
16
|
- BREAKING: Require Ruby 3.4 or newer.
|
data/README.md
CHANGED
|
@@ -64,9 +64,11 @@ bin/rails g data_migration create_users
|
|
|
64
64
|
### Run data migrations
|
|
65
65
|
|
|
66
66
|
```sh
|
|
67
|
-
bin/rails db:migrate:data
|
|
67
|
+
bin/rails db:migrate:data VERSION=20241207120000
|
|
68
68
|
```
|
|
69
69
|
|
|
70
|
+
`VERSION` may be the timestamp or the full `timestamp_name` file stem. `NAME=create_users` also works. With neither set, the task lists available migrations and their latest task status.
|
|
71
|
+
|
|
70
72
|
## Configuration
|
|
71
73
|
|
|
72
74
|
### Set data migrations directory
|
|
@@ -104,7 +106,7 @@ end
|
|
|
104
106
|
- [x] User can generate data migration file under `db/data_migrations` directory with common format
|
|
105
107
|
- [x] User can generate data migration file with test script included
|
|
106
108
|
- [x] User can run specific data migration using Rails console
|
|
107
|
-
- [
|
|
109
|
+
- [x] User can run specific data migration using shell command
|
|
108
110
|
- [x] User can run data migration in background
|
|
109
111
|
- [x] User can run data migration in foreground
|
|
110
112
|
- [x] User can specify operator for data migration
|
data/data-migration.gemspec
CHANGED
|
@@ -37,12 +37,9 @@ Gem::Specification.new do |gem|
|
|
|
37
37
|
|
|
38
38
|
gem.add_development_dependency "bundler", ">= 2"
|
|
39
39
|
gem.add_development_dependency "rspec", "~> 3"
|
|
40
|
-
gem.add_development_dependency "polyrun", ">= 2.2.
|
|
40
|
+
gem.add_development_dependency "polyrun", ">= 2.2.9"
|
|
41
41
|
gem.add_development_dependency "prosopite", "~> 2.0"
|
|
42
42
|
gem.add_development_dependency "webmock", "~> 3"
|
|
43
|
-
gem.add_development_dependency "rspec_junit_formatter", "~> 0.6"
|
|
44
|
-
gem.add_development_dependency "simplecov", "~> 0.22"
|
|
45
|
-
gem.add_development_dependency "simplecov-cobertura", "~> 3"
|
|
46
43
|
gem.add_development_dependency "rbs", "~> 3"
|
|
47
44
|
gem.add_development_dependency "standard", "~> 1.52"
|
|
48
45
|
gem.add_development_dependency "standard-custom", "~> 1.0"
|
data/lib/data-migration.rb
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
require "data_migration/config"
|
|
2
2
|
require "data_migration/job"
|
|
3
3
|
require "data_migration/task"
|
|
4
|
+
require "data_migration/selector"
|
|
5
|
+
require "data_migration/cli"
|
|
6
|
+
require "data_migration/railtie" if defined?(Rails::Railtie)
|
|
4
7
|
|
|
5
8
|
module DataMigration
|
|
6
|
-
VERSION = "2.
|
|
9
|
+
VERSION = "2.1.0".freeze
|
|
7
10
|
|
|
8
11
|
module_function
|
|
9
12
|
|
|
@@ -12,7 +15,7 @@ module DataMigration
|
|
|
12
15
|
end
|
|
13
16
|
|
|
14
17
|
def configure
|
|
15
|
-
yield
|
|
18
|
+
yield(config)
|
|
16
19
|
end
|
|
17
20
|
|
|
18
21
|
def notify(message, context: {})
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module DataMigration
|
|
2
|
+
module Cli
|
|
3
|
+
module_function
|
|
4
|
+
|
|
5
|
+
def run!(env = ENV)
|
|
6
|
+
version = env["VERSION"]
|
|
7
|
+
name = env["NAME"]
|
|
8
|
+
|
|
9
|
+
if version.to_s.strip.empty? && name.to_s.strip.empty?
|
|
10
|
+
puts "Available data migrations:"
|
|
11
|
+
DataMigration::Selector.status_lines.each { |line| puts line }
|
|
12
|
+
return :listed
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
migration_name = DataMigration::Selector.resolve(version: version, name: name)
|
|
16
|
+
DataMigration.perform_now(migration_name)
|
|
17
|
+
:ran
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -60,10 +60,10 @@ module DataMigration
|
|
|
60
60
|
data_migration_operator_id: migration.operator&.try(:to_global_id) || migration.operator&.id
|
|
61
61
|
}
|
|
62
62
|
if Object.const_defined?(:ActionReporter)
|
|
63
|
-
ActionReporter.current_user
|
|
63
|
+
ActionReporter.current_user = migration.operator if migration.operator
|
|
64
64
|
ActionReporter.context(**context)
|
|
65
65
|
elsif Object.const_defined?(:Audited)
|
|
66
|
-
Audited.store[:audited_user]
|
|
66
|
+
Audited.store[:audited_user] = migration.operator if migration.operator
|
|
67
67
|
else
|
|
68
68
|
Rails.logger.info("Data migration context: #{context.inspect}")
|
|
69
69
|
end
|
data/lib/data_migration/job.rb
CHANGED
|
@@ -4,8 +4,6 @@ module DataMigration
|
|
|
4
4
|
class Job < ActiveJob::Base
|
|
5
5
|
queue_as { DataMigration.config.job_queue_name }
|
|
6
6
|
|
|
7
|
-
discard_on StandardError
|
|
8
|
-
|
|
9
7
|
def perform(task_id, *job_args, **job_kwargs)
|
|
10
8
|
checked_in = false
|
|
11
9
|
failed = false
|
|
@@ -45,13 +43,14 @@ module DataMigration
|
|
|
45
43
|
|
|
46
44
|
Thread.current[:data_migration_enqueue_called] ||= {}
|
|
47
45
|
Thread.current[:data_migration_enqueue_kwargs] ||= {}
|
|
48
|
-
migration_class.
|
|
46
|
+
migration = migration_class.new
|
|
47
|
+
migration.define_singleton_method(:enqueue) do |**enqueue_kwargs|
|
|
49
48
|
Thread.current[:data_migration_enqueue_called][migration_class.name] = true
|
|
50
49
|
Thread.current[:data_migration_enqueue_kwargs][migration_class.name] = enqueue_kwargs
|
|
51
50
|
end
|
|
52
51
|
|
|
53
52
|
task.update!(status: :performing, pause_minutes: 0)
|
|
54
|
-
|
|
53
|
+
migration.perform(**job_kwargs)
|
|
55
54
|
task.job_check_out!(job_id)
|
|
56
55
|
checked_in = false
|
|
57
56
|
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
module DataMigration
|
|
2
|
+
class Selector
|
|
3
|
+
class Error < StandardError; end
|
|
4
|
+
class NotFound < Error; end
|
|
5
|
+
class Ambiguous < Error; end
|
|
6
|
+
|
|
7
|
+
def self.resolve(version: nil, name: nil)
|
|
8
|
+
new(version: version, name: name).resolve
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.status_lines
|
|
12
|
+
DataMigration::Task.list.map do |migration_name|
|
|
13
|
+
task = DataMigration::Task.where(name: migration_name).order(id: :desc).first
|
|
14
|
+
status = task&.status || "not started"
|
|
15
|
+
"#{migration_name}\t#{status}"
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def initialize(version: nil, name: nil)
|
|
20
|
+
@version = version.to_s.strip.presence
|
|
21
|
+
@name = name.to_s.strip.presence
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def resolve
|
|
25
|
+
raise NotFound, "VERSION or NAME is required" if @version.nil? && @name.nil?
|
|
26
|
+
|
|
27
|
+
matches = matching_migrations
|
|
28
|
+
raise NotFound, "No data migration matches VERSION=#{@version.inspect} NAME=#{@name.inspect}" if matches.empty?
|
|
29
|
+
raise Ambiguous, "Multiple data migrations match: #{matches.join(", ")}" if matches.size > 1
|
|
30
|
+
|
|
31
|
+
matches.first
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def matching_migrations
|
|
37
|
+
DataMigration::Task.list.select { |migration_name| match?(migration_name) }
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def match?(migration_name)
|
|
41
|
+
return migration_name == @version || migration_name.start_with?("#{@version}_") if @version && @name.nil?
|
|
42
|
+
return migration_name == @name || migration_name.end_with?("_#{@name}") if @name && @version.nil?
|
|
43
|
+
|
|
44
|
+
combined = @version.include?("_") ? @version : "#{@version}_#{@name}"
|
|
45
|
+
migration_name == combined || migration_name == @version
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
data/lib/data_migration/task.rb
CHANGED
|
@@ -54,25 +54,27 @@ module DataMigration
|
|
|
54
54
|
end
|
|
55
55
|
|
|
56
56
|
def self.perform_now(name, **kwargs)
|
|
57
|
-
|
|
57
|
+
create_with_operator!(name, **kwargs).perform_now(**kwargs)
|
|
58
58
|
end
|
|
59
59
|
|
|
60
60
|
def perform_now(**perform_args)
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
job_kwargs = assign_operator_from!(perform_args)
|
|
62
|
+
update!(kwargs: job_kwargs)
|
|
63
|
+
self.class.job_class.perform_now(id, **job_kwargs)
|
|
63
64
|
end
|
|
64
65
|
|
|
65
66
|
def self.perform_later(name, **kwargs)
|
|
66
|
-
|
|
67
|
+
create_with_operator!(name, **kwargs).perform_later(**kwargs)
|
|
67
68
|
end
|
|
68
69
|
|
|
69
70
|
def perform_later(**perform_args)
|
|
70
|
-
|
|
71
|
-
|
|
71
|
+
job_kwargs = assign_operator_from!(perform_args)
|
|
72
|
+
update!(kwargs: job_kwargs)
|
|
73
|
+
self.class.job_class.perform_later(id, **job_kwargs)
|
|
72
74
|
end
|
|
73
75
|
|
|
74
|
-
def self.prepare(name, pause_minutes: nil, jobs_limit: nil)
|
|
75
|
-
create!(name: name, pause_minutes: pause_minutes, jobs_limit: jobs_limit)
|
|
76
|
+
def self.prepare(name, pause_minutes: nil, jobs_limit: nil, operator: nil)
|
|
77
|
+
create!(name: name, pause_minutes: pause_minutes, jobs_limit: jobs_limit, operator: operator)
|
|
76
78
|
end
|
|
77
79
|
|
|
78
80
|
def self.root_path
|
|
@@ -129,6 +131,23 @@ module DataMigration
|
|
|
129
131
|
|
|
130
132
|
private
|
|
131
133
|
|
|
134
|
+
def self.create_with_operator!(name, **kwargs)
|
|
135
|
+
attributes = {name: name}
|
|
136
|
+
attributes[:operator] = kwargs[:operator] if kwargs.key?(:operator)
|
|
137
|
+
create!(**attributes)
|
|
138
|
+
end
|
|
139
|
+
private_class_method :create_with_operator!
|
|
140
|
+
|
|
141
|
+
def assign_operator_from!(perform_args)
|
|
142
|
+
job_kwargs = perform_args.dup
|
|
143
|
+
if job_kwargs.key?(:operator)
|
|
144
|
+
self.operator = job_kwargs.delete(:operator)
|
|
145
|
+
else
|
|
146
|
+
self.operator ||= DataMigration.config.operator_resolver.call
|
|
147
|
+
end
|
|
148
|
+
job_kwargs
|
|
149
|
+
end
|
|
150
|
+
|
|
132
151
|
def file_should_exist
|
|
133
152
|
errors.add(:name, "is not found") unless file_exists?
|
|
134
153
|
end
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# USAGE: within remote console run one of the following commands
|
|
2
|
-
# - rails db:migrate:data
|
|
2
|
+
# - bin/rails db:migrate:data VERSION=<%= migration_number %>
|
|
3
3
|
# - DataMigration.perform_now("<%= migration_number %>_<%= migration_file_name %>")
|
|
4
4
|
# - DataMigration.perform_later("<%= migration_number %>_<%= migration_file_name %>")
|
|
5
|
-
# - DataMigration.prepare("<%= migration_number %>_<%= migration_file_name %>",
|
|
5
|
+
# - DataMigration.prepare("<%= migration_number %>_<%= migration_file_name %>", pause_minutes: 1, jobs_limit: 2).perform_later
|
|
6
6
|
|
|
7
7
|
class <%= migration_class_name %>
|
|
8
8
|
def perform(**kwargs)
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: data-migration
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andrei Makarov
|
|
@@ -99,14 +99,14 @@ dependencies:
|
|
|
99
99
|
requirements:
|
|
100
100
|
- - ">="
|
|
101
101
|
- !ruby/object:Gem::Version
|
|
102
|
-
version: 2.2.
|
|
102
|
+
version: 2.2.9
|
|
103
103
|
type: :development
|
|
104
104
|
prerelease: false
|
|
105
105
|
version_requirements: !ruby/object:Gem::Requirement
|
|
106
106
|
requirements:
|
|
107
107
|
- - ">="
|
|
108
108
|
- !ruby/object:Gem::Version
|
|
109
|
-
version: 2.2.
|
|
109
|
+
version: 2.2.9
|
|
110
110
|
- !ruby/object:Gem::Dependency
|
|
111
111
|
name: prosopite
|
|
112
112
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -135,48 +135,6 @@ dependencies:
|
|
|
135
135
|
- - "~>"
|
|
136
136
|
- !ruby/object:Gem::Version
|
|
137
137
|
version: '3'
|
|
138
|
-
- !ruby/object:Gem::Dependency
|
|
139
|
-
name: rspec_junit_formatter
|
|
140
|
-
requirement: !ruby/object:Gem::Requirement
|
|
141
|
-
requirements:
|
|
142
|
-
- - "~>"
|
|
143
|
-
- !ruby/object:Gem::Version
|
|
144
|
-
version: '0.6'
|
|
145
|
-
type: :development
|
|
146
|
-
prerelease: false
|
|
147
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
148
|
-
requirements:
|
|
149
|
-
- - "~>"
|
|
150
|
-
- !ruby/object:Gem::Version
|
|
151
|
-
version: '0.6'
|
|
152
|
-
- !ruby/object:Gem::Dependency
|
|
153
|
-
name: simplecov
|
|
154
|
-
requirement: !ruby/object:Gem::Requirement
|
|
155
|
-
requirements:
|
|
156
|
-
- - "~>"
|
|
157
|
-
- !ruby/object:Gem::Version
|
|
158
|
-
version: '0.22'
|
|
159
|
-
type: :development
|
|
160
|
-
prerelease: false
|
|
161
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
162
|
-
requirements:
|
|
163
|
-
- - "~>"
|
|
164
|
-
- !ruby/object:Gem::Version
|
|
165
|
-
version: '0.22'
|
|
166
|
-
- !ruby/object:Gem::Dependency
|
|
167
|
-
name: simplecov-cobertura
|
|
168
|
-
requirement: !ruby/object:Gem::Requirement
|
|
169
|
-
requirements:
|
|
170
|
-
- - "~>"
|
|
171
|
-
- !ruby/object:Gem::Version
|
|
172
|
-
version: '3'
|
|
173
|
-
type: :development
|
|
174
|
-
prerelease: false
|
|
175
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
176
|
-
requirements:
|
|
177
|
-
- - "~>"
|
|
178
|
-
- !ruby/object:Gem::Version
|
|
179
|
-
version: '3'
|
|
180
138
|
- !ruby/object:Gem::Dependency
|
|
181
139
|
name: rbs
|
|
182
140
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -330,9 +288,13 @@ files:
|
|
|
330
288
|
- README.md
|
|
331
289
|
- data-migration.gemspec
|
|
332
290
|
- lib/data-migration.rb
|
|
291
|
+
- lib/data_migration/cli.rb
|
|
333
292
|
- lib/data_migration/config.rb
|
|
334
293
|
- lib/data_migration/job.rb
|
|
294
|
+
- lib/data_migration/railtie.rb
|
|
295
|
+
- lib/data_migration/selector.rb
|
|
335
296
|
- lib/data_migration/task.rb
|
|
297
|
+
- lib/data_migration/tasks/data_migration.rake
|
|
336
298
|
- lib/generators/data_migration/install_generator.rb
|
|
337
299
|
- lib/generators/data_migration/templates/install_data_migration_tasks.rb.tt
|
|
338
300
|
- lib/generators/data_migration_generator.rb
|