migsupo 0.1.0 → 0.3.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/README.md +107 -0
- data/lib/migsupo/coherent.rb +71 -0
- data/lib/migsupo/generator/migration_builder.rb +7 -1
- data/lib/migsupo/generator/schemafile_dumper.rb +18 -0
- data/lib/migsupo/squash.rb +44 -0
- data/lib/migsupo/tasks/migsupo.rake +157 -0
- data/lib/migsupo/version.rb +1 -1
- data/lib/migsupo.rb +10 -8
- metadata +6 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 46769b47cf0ccd1fddb7f3391c43cdec1cbe20df56cb3d18efdafc22c9427c09
|
|
4
|
+
data.tar.gz: a8edc433d3472182ddcfd5be264704c6d21ad58d70e491934c3f92cbf53cd3ed
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 03a340761dfb39ff21b8d80d98f1901b2e553c6b57b8178e0927fdb78db6f0247034c98d2e159379b754700d8ecd7e46ac45d24966794344a920dd93d1a49aae
|
|
7
|
+
data.tar.gz: cbeba7b4ec1da9528edec1ef4efd7c52d25101d3e6f8f071f4bddde68d23a30a059740b902a99475b620cea0f39aa12aa17a67bdc752fd6813045c6262b6b9cf
|
data/README.md
CHANGED
|
@@ -122,6 +122,113 @@ Exit with code 1 if the Schemafile and the current database are not in sync. Use
|
|
|
122
122
|
rails db:generate_migration:check
|
|
123
123
|
```
|
|
124
124
|
|
|
125
|
+
### `rails db:coherent` / `rails db:coherent:apply`
|
|
126
|
+
|
|
127
|
+
Adopt a hand-made database change as the truth. See [coherent](#coherent--when-the-db-was-changed-by-hand).
|
|
128
|
+
|
|
129
|
+
### `rails db:squash` / `rails db:squash:apply`
|
|
130
|
+
|
|
131
|
+
Collapse the whole migration history into a single `Init` migration. See [squash](#squash--collapse-the-history-into-one-init-migration).
|
|
132
|
+
|
|
133
|
+
## `coherent` — when the DB was changed by hand
|
|
134
|
+
|
|
135
|
+
Sometimes someone edits the database directly — an emergency `ALTER TABLE` in production, say — and that state is the one you want to keep. The normal flow cannot express this: the Schemafile is behind, and running a migration to catch up would touch columns that are already correct.
|
|
136
|
+
|
|
137
|
+
`coherent` takes the **DB as the source of truth** and moves everything else up to it: it writes the migration file that *would* have produced the change (so every other environment gets it through the normal `rails db:migrate`), updates the Schemafile, and then records the migration as applied on this database **without executing it**.
|
|
138
|
+
|
|
139
|
+
```
|
|
140
|
+
DB (hand-edited, = truth)
|
|
141
|
+
├─ db:coherent → db/migrate/*.rb (generated) + Schemafile (updated)
|
|
142
|
+
└─ db:coherent:apply → INSERT INTO schema_migrations ← columns untouched
|
|
143
|
+
db/schema.rb re-dumped
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### 1. Generate
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
rails db:coherent
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
```
|
|
153
|
+
Adopting the following from the database:
|
|
154
|
+
add_column users.nickname (string)
|
|
155
|
+
|
|
156
|
+
Generated migration(s):
|
|
157
|
+
db/migrate/20260901120000_add_columns_to_users.rb
|
|
158
|
+
Updated Schemafile: Schemafile
|
|
159
|
+
|
|
160
|
+
Review them, then record the history without touching the database:
|
|
161
|
+
rails db:coherent:apply VERSION=20260901120000
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
The diff runs in the opposite direction from `db:generate_migration`: the DB is the desired state and the Schemafile is the current one, so a column that exists only in the DB comes out as `add_column`, not `remove_column`.
|
|
165
|
+
|
|
166
|
+
### 2. Review, then rewrite history only
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
rails db:coherent:apply VERSION=20260901120000
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
This inserts the version into `schema_migrations` and re-dumps `db/schema.rb`. No DDL is executed — the columns are already where they should be.
|
|
173
|
+
|
|
174
|
+
`VERSION` is required (comma-separate several). Run without it to list the pending migrations. Before writing anything, `apply` re-checks that the DB and the Schemafile match: if they do not, the migration genuinely needs to run and the task aborts.
|
|
175
|
+
|
|
176
|
+
### On other environments
|
|
177
|
+
|
|
178
|
+
Nothing special. `db/migrate/*.rb` is a normal migration, so staging and production pick it up with `rails db:migrate`.
|
|
179
|
+
|
|
180
|
+
### Caveats
|
|
181
|
+
|
|
182
|
+
- `db:coherent` regenerates the whole Schemafile from the DB, so hand-written comments and ordering in it are lost. Review the diff (`git diff Schemafile`) before committing.
|
|
183
|
+
- `rename_hints` are not applied — a column renamed by hand comes out as `remove_column` + `add_column`.
|
|
184
|
+
- Only what migsupo models is tracked: tables, columns, indexes. Foreign keys, extensions and check constraints are neither diffed nor written to the Schemafile.
|
|
185
|
+
- Primary key options (`id: :uuid`, `primary_key: "uid"`) are not read back from the database, so the regenerated Schemafile drops them. Diffs are unaffected — table options are not compared — but re-add them by hand if you ever recreate the table from the Schemafile.
|
|
186
|
+
|
|
187
|
+
## `squash` — collapse the history into one Init migration
|
|
188
|
+
|
|
189
|
+
When `db/migrate` has grown long, `squash` replaces every migration file with a single `<timestamp>_init.rb` that creates the whole schema as declared in the Schemafile, and then rewrites `schema_migrations` to hold only that version. No DDL is executed.
|
|
190
|
+
|
|
191
|
+
```
|
|
192
|
+
db/migrate/*.rb (N files)
|
|
193
|
+
├─ db:squash → db/migrate/<ts>_init.rb (all other files deleted)
|
|
194
|
+
└─ db:squash:apply → schema_migrations = [<ts>] ← tables untouched
|
|
195
|
+
db/schema.rb re-dumped
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### 1. Generate
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
rails db:squash
|
|
202
|
+
rails db:squash DRY_RUN=true # print the Init migration, change nothing
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
It aborts unless both hold:
|
|
206
|
+
|
|
207
|
+
- every migration file has been applied (pending ones may carry data migrations the Schemafile cannot express)
|
|
208
|
+
- the DB matches the Schemafile (the Init migration must reproduce this exact DB)
|
|
209
|
+
|
|
210
|
+
### 2. Review, then rewrite history
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
rails db:squash:apply VERSION=20261103120000
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
`VERSION` can be omitted when `db/migrate` holds exactly one file (right after `db:squash`); that one is used.
|
|
217
|
+
|
|
218
|
+
### On other environments
|
|
219
|
+
|
|
220
|
+
Unlike `coherent`, this is **not** picked up by plain `rails db:migrate` — the Init migration would try to create tables that already exist. On every existing environment:
|
|
221
|
+
|
|
222
|
+
1. Deploy the pre-squash code and run `rails db:migrate` so the DB matches the Schemafile.
|
|
223
|
+
2. Deploy the squashed code and run `rails db:squash:apply VERSION=<ts>` **before** `rails db:migrate`.
|
|
224
|
+
|
|
225
|
+
New environments just run `rails db:migrate` (or `db:schema:load`).
|
|
226
|
+
|
|
227
|
+
### Caveats
|
|
228
|
+
|
|
229
|
+
- Data migrations and raw SQL in the old files are gone. Keep them elsewhere if you still need them.
|
|
230
|
+
- Only what migsupo models is emitted: tables, columns, indexes. Foreign keys, extensions and check constraints are not in the Init migration.
|
|
231
|
+
|
|
125
232
|
## Environment Variables
|
|
126
233
|
|
|
127
234
|
| Variable | Default | Description |
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
require_relative "parser/schemafile_parser"
|
|
2
|
+
require_relative "loader/active_record_loader"
|
|
3
|
+
require_relative "differ/diff_calculator"
|
|
4
|
+
require_relative "generator/schemafile_dumper"
|
|
5
|
+
|
|
6
|
+
module Migsupo
|
|
7
|
+
# For the case where the DB was changed by hand and that state is now the
|
|
8
|
+
# truth. Coherent brings the Schemafile and the migration history up to the
|
|
9
|
+
# DB without touching a single column.
|
|
10
|
+
module Coherent
|
|
11
|
+
module_function
|
|
12
|
+
|
|
13
|
+
# desired/current are swapped on purpose: the migration has to move the
|
|
14
|
+
# declared state up to what the DB already looks like.
|
|
15
|
+
# ponytail: rename_hints point the other way here, so they are dropped -
|
|
16
|
+
# a hand-renamed column comes out as remove + add.
|
|
17
|
+
def diff(schemafile_path: nil)
|
|
18
|
+
schemafile_path ||= Migsupo.configuration.schemafile_path
|
|
19
|
+
declared = Parser::SchemafileParser.parse(schemafile_path)
|
|
20
|
+
|
|
21
|
+
Differ::DiffCalculator.new.calculate(desired: actual_schema, current: declared)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def dump_schemafile(path: nil)
|
|
25
|
+
path ||= Migsupo.configuration.schemafile_path
|
|
26
|
+
File.write(path, Generator::SchemafileDumper.new.dump(actual_schema))
|
|
27
|
+
path
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Writes history only - no DDL is executed. Returns the versions inserted.
|
|
31
|
+
def mark_applied(versions)
|
|
32
|
+
applied = applied_versions
|
|
33
|
+
inserted = versions.map(&:to_s) - applied
|
|
34
|
+
|
|
35
|
+
table = connection.quote_table_name("schema_migrations")
|
|
36
|
+
inserted.each do |version|
|
|
37
|
+
connection.execute("INSERT INTO #{table} (version) VALUES (#{connection.quote(version)})")
|
|
38
|
+
end
|
|
39
|
+
inserted
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def applied_versions
|
|
43
|
+
connection.select_values(
|
|
44
|
+
"SELECT version FROM #{connection.quote_table_name('schema_migrations')}"
|
|
45
|
+
).map(&:to_s)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# [version, filename] for every migration file not yet in schema_migrations.
|
|
49
|
+
def pending(migrations_dir = nil)
|
|
50
|
+
migrations_dir ||= Migsupo.configuration.migrations_dir
|
|
51
|
+
applied = applied_versions
|
|
52
|
+
|
|
53
|
+
migration_files(migrations_dir).reject { |version, _| applied.include?(version) }
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def migration_files(migrations_dir)
|
|
57
|
+
Dir.glob(File.join(migrations_dir, "*.rb")).sort.filter_map do |path|
|
|
58
|
+
version = File.basename(path)[/\A\d+/]
|
|
59
|
+
[version, File.basename(path)] if version
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def actual_schema
|
|
64
|
+
Loader::ActiveRecordLoader.new(ignored_tables: Migsupo.configuration.ignored_tables).load_schema
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def connection
|
|
68
|
+
ActiveRecord::Base.connection
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -32,6 +32,12 @@ module Migsupo
|
|
|
32
32
|
RUBY
|
|
33
33
|
end
|
|
34
34
|
|
|
35
|
+
# Renders one create_table block (plus its add_index lines) - the same shape
|
|
36
|
+
# a Schemafile uses.
|
|
37
|
+
def build_table(table)
|
|
38
|
+
render_create_table(Differ::Operations::CreateTable.new(table))
|
|
39
|
+
end
|
|
40
|
+
|
|
35
41
|
private
|
|
36
42
|
|
|
37
43
|
def version_suffix
|
|
@@ -102,7 +108,7 @@ module Migsupo
|
|
|
102
108
|
|
|
103
109
|
def render_drop_table(op, direction:)
|
|
104
110
|
if direction == :down
|
|
105
|
-
render_create_table(Operations::CreateTable.new(op.table))
|
|
111
|
+
render_create_table(Differ::Operations::CreateTable.new(op.table))
|
|
106
112
|
else
|
|
107
113
|
"drop_table #{op.table_name.inspect}"
|
|
108
114
|
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
require_relative "migration_builder"
|
|
2
|
+
|
|
3
|
+
module Migsupo
|
|
4
|
+
module Generator
|
|
5
|
+
# Renders a SchemaDefinition back out as Schemafile DSL.
|
|
6
|
+
# Only what migsupo models (tables / columns / indexes) is emitted, so the
|
|
7
|
+
# output always round-trips through SchemafileParser.
|
|
8
|
+
class SchemafileDumper
|
|
9
|
+
def initialize
|
|
10
|
+
@builder = MigrationBuilder.new
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def dump(schema)
|
|
14
|
+
"#{schema.tables.values.map { |table| @builder.build_table(table) }.join("\n\n")}\n"
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
require_relative "parser/schemafile_parser"
|
|
2
|
+
require_relative "generator/migration_builder"
|
|
3
|
+
require_relative "differ/operations/create_table"
|
|
4
|
+
require_relative "coherent"
|
|
5
|
+
|
|
6
|
+
module Migsupo
|
|
7
|
+
# Collapses the whole migration history into a single Init migration built
|
|
8
|
+
# from the Schemafile, then rewrites schema_migrations to match - no DDL.
|
|
9
|
+
module Squash
|
|
10
|
+
module_function
|
|
11
|
+
|
|
12
|
+
# Built from the Schemafile, not the DB: the DB loader drops primary key
|
|
13
|
+
# options (id: :uuid etc.), the Schemafile keeps them.
|
|
14
|
+
def build(schemafile_path: nil, rails_version: nil)
|
|
15
|
+
schemafile_path ||= Migsupo.configuration.schemafile_path
|
|
16
|
+
schema = Parser::SchemafileParser.parse(schemafile_path)
|
|
17
|
+
ops = schema.tables.values.map { |table| Differ::Operations::CreateTable.new(table) }
|
|
18
|
+
|
|
19
|
+
Generator::MigrationBuilder.new(rails_version: rails_version).build(ops, class_name: "Init")
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Deletes every migration file and writes <timestamp>_init.rb. Returns its path.
|
|
23
|
+
def write(content, migrations_dir: nil)
|
|
24
|
+
migrations_dir ||= Migsupo.configuration.migrations_dir
|
|
25
|
+
Dir.glob(File.join(migrations_dir, "*.rb")).each { |path| File.delete(path) }
|
|
26
|
+
|
|
27
|
+
path = File.join(migrations_dir, "#{Time.now.strftime('%Y%m%d%H%M%S')}_init.rb")
|
|
28
|
+
File.write(path, content)
|
|
29
|
+
path
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Replaces all history with the single version. Atomic so a failure
|
|
33
|
+
# cannot leave schema_migrations empty.
|
|
34
|
+
def rewrite_history(version)
|
|
35
|
+
connection = Coherent.connection
|
|
36
|
+
table = connection.quote_table_name("schema_migrations")
|
|
37
|
+
|
|
38
|
+
connection.transaction do
|
|
39
|
+
connection.execute("DELETE FROM #{table}")
|
|
40
|
+
connection.execute("INSERT INTO #{table} (version) VALUES (#{connection.quote(version.to_s)})")
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -54,3 +54,160 @@ namespace :db do
|
|
|
54
54
|
end
|
|
55
55
|
end
|
|
56
56
|
end
|
|
57
|
+
|
|
58
|
+
namespace :db do
|
|
59
|
+
desc "Take the current DB as truth: generate migrations for hand-made changes and update the Schemafile"
|
|
60
|
+
task coherent: :environment do
|
|
61
|
+
schemafile_path = ENV.fetch("SCHEMAFILE", Migsupo.configuration.schemafile_path)
|
|
62
|
+
output_dir = ENV.fetch("OUTPUT_DIR", Migsupo.configuration.migrations_dir)
|
|
63
|
+
dry_run = ENV["DRY_RUN"] == "true"
|
|
64
|
+
|
|
65
|
+
diff = Migsupo::Coherent.diff(schemafile_path: schemafile_path)
|
|
66
|
+
|
|
67
|
+
if diff.empty?
|
|
68
|
+
puts "No differences between the database and the Schemafile. Nothing for coherent to do."
|
|
69
|
+
next
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
puts "Adopting the following from the database:"
|
|
73
|
+
puts diff.to_s
|
|
74
|
+
puts
|
|
75
|
+
|
|
76
|
+
files = Migsupo.generate_migrations(diff, output_dir: output_dir, dry_run: dry_run)
|
|
77
|
+
next if dry_run
|
|
78
|
+
|
|
79
|
+
Migsupo::Coherent.dump_schemafile(path: schemafile_path)
|
|
80
|
+
versions = files.map { |f| File.basename(f)[/\A\d+/] }
|
|
81
|
+
|
|
82
|
+
puts "Generated migration(s):"
|
|
83
|
+
files.each { |f| puts " #{f}" }
|
|
84
|
+
puts "Updated Schemafile: #{schemafile_path}"
|
|
85
|
+
puts
|
|
86
|
+
puts "Review them, then record the history without touching the database:"
|
|
87
|
+
puts " rails db:coherent:apply VERSION=#{versions.join(',')}"
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
namespace :coherent do
|
|
91
|
+
desc "Mark the given migrations as applied without running them (VERSION=ts[,ts...])"
|
|
92
|
+
task apply: :environment do
|
|
93
|
+
schemafile_path = ENV.fetch("SCHEMAFILE", Migsupo.configuration.schemafile_path)
|
|
94
|
+
output_dir = ENV.fetch("OUTPUT_DIR", Migsupo.configuration.migrations_dir)
|
|
95
|
+
versions = ENV["VERSION"].to_s.split(",").map(&:strip).reject(&:empty?)
|
|
96
|
+
|
|
97
|
+
if versions.empty?
|
|
98
|
+
puts "VERSION is required (e.g. VERSION=20260901120000)."
|
|
99
|
+
pending = Migsupo::Coherent.pending(output_dir)
|
|
100
|
+
if pending.empty?
|
|
101
|
+
puts "No pending migrations."
|
|
102
|
+
else
|
|
103
|
+
puts "Pending migrations:"
|
|
104
|
+
pending.each { |version, name| puts " #{version} #{name}" }
|
|
105
|
+
end
|
|
106
|
+
exit 1
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
known = Migsupo::Coherent.migration_files(output_dir).to_h
|
|
110
|
+
unknown = versions.reject { |v| known.key?(v) }
|
|
111
|
+
unless unknown.empty?
|
|
112
|
+
puts "No migration found in #{output_dir} for: #{unknown.join(', ')}"
|
|
113
|
+
exit 1
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Recording history only is sound when the DB already matches the
|
|
117
|
+
# Schemafile. A remaining diff means the migration genuinely needs to run.
|
|
118
|
+
diff = Migsupo::Coherent.diff(schemafile_path: schemafile_path)
|
|
119
|
+
unless diff.empty?
|
|
120
|
+
puts "The database and the Schemafile do not match. Run rails db:coherent first:"
|
|
121
|
+
puts diff.to_s
|
|
122
|
+
exit 1
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
inserted = Migsupo::Coherent.mark_applied(versions)
|
|
126
|
+
skipped = versions - inserted
|
|
127
|
+
|
|
128
|
+
inserted.each { |v| puts "Recorded as applied: #{v} #{known[v]}" }
|
|
129
|
+
skipped.each { |v| puts "Already recorded, skipped: #{v} #{known[v]}" }
|
|
130
|
+
|
|
131
|
+
Rake::Task["db:schema:dump"].invoke
|
|
132
|
+
puts "Updated db/schema.rb."
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
namespace :db do
|
|
138
|
+
desc "Collapse every migration into a single Init migration built from the Schemafile"
|
|
139
|
+
task squash: :environment do
|
|
140
|
+
schemafile_path = ENV.fetch("SCHEMAFILE", Migsupo.configuration.schemafile_path)
|
|
141
|
+
output_dir = ENV.fetch("OUTPUT_DIR", Migsupo.configuration.migrations_dir)
|
|
142
|
+
dry_run = ENV["DRY_RUN"] == "true"
|
|
143
|
+
|
|
144
|
+
# Pending files may hold data migrations that the Schemafile cannot express.
|
|
145
|
+
pending = Migsupo::Coherent.pending(output_dir)
|
|
146
|
+
unless pending.empty?
|
|
147
|
+
puts "Run the pending migrations before squashing:"
|
|
148
|
+
pending.each { |version, name| puts " #{version} #{name}" }
|
|
149
|
+
exit 1
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Init replaces the history only if it reproduces this exact DB.
|
|
153
|
+
diff = Migsupo.calculate_diff(schemafile_path: schemafile_path, loader: :active_record)
|
|
154
|
+
unless diff.empty?
|
|
155
|
+
puts "The database and the Schemafile do not match. Bring them in sync before squashing:"
|
|
156
|
+
puts diff.to_s
|
|
157
|
+
exit 1
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
content = Migsupo::Squash.build(schemafile_path: schemafile_path, rails_version: Migsupo.detect_rails_version)
|
|
161
|
+
if dry_run
|
|
162
|
+
puts content
|
|
163
|
+
next
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
removed = Migsupo::Coherent.migration_files(output_dir).size
|
|
167
|
+
path = Migsupo::Squash.write(content, migrations_dir: output_dir)
|
|
168
|
+
version = File.basename(path)[/\A\d+/]
|
|
169
|
+
|
|
170
|
+
puts "Removed #{removed} migration file(s) and generated:"
|
|
171
|
+
puts " #{path}"
|
|
172
|
+
puts
|
|
173
|
+
puts "Review it, then replace the history on every environment (no DDL is executed):"
|
|
174
|
+
puts " rails db:squash:apply VERSION=#{version}"
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
namespace :squash do
|
|
178
|
+
desc "Replace schema_migrations with the single Init version (VERSION=ts)"
|
|
179
|
+
task apply: :environment do
|
|
180
|
+
schemafile_path = ENV.fetch("SCHEMAFILE", Migsupo.configuration.schemafile_path)
|
|
181
|
+
output_dir = ENV.fetch("OUTPUT_DIR", Migsupo.configuration.migrations_dir)
|
|
182
|
+
version = ENV["VERSION"].to_s.strip
|
|
183
|
+
|
|
184
|
+
if version.empty?
|
|
185
|
+
files = Migsupo::Coherent.migration_files(output_dir)
|
|
186
|
+
unless files.size == 1
|
|
187
|
+
puts "VERSION is required when #{output_dir} does not hold exactly one migration (found #{files.size})."
|
|
188
|
+
exit 1
|
|
189
|
+
end
|
|
190
|
+
version = files.first.first
|
|
191
|
+
puts "Using #{files.first.last}"
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
unless Migsupo::Coherent.migration_files(output_dir).to_h[version]
|
|
195
|
+
puts "No migration found in #{output_dir} for: #{version}"
|
|
196
|
+
exit 1
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
diff = Migsupo.calculate_diff(schemafile_path: schemafile_path, loader: :active_record)
|
|
200
|
+
unless diff.empty?
|
|
201
|
+
puts "The database and the Schemafile do not match. Run rails db:migrate on the pre-squash code first:"
|
|
202
|
+
puts diff.to_s
|
|
203
|
+
exit 1
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
Migsupo::Squash.rewrite_history(version)
|
|
207
|
+
puts "schema_migrations now holds only: #{version}"
|
|
208
|
+
|
|
209
|
+
Rake::Task["db:schema:dump"].invoke
|
|
210
|
+
puts "Updated db/schema.rb."
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
end
|
data/lib/migsupo/version.rb
CHANGED
data/lib/migsupo.rb
CHANGED
|
@@ -9,6 +9,8 @@ require_relative "migsupo/loader/active_record_loader"
|
|
|
9
9
|
require_relative "migsupo/loader/schema_rb_loader"
|
|
10
10
|
require_relative "migsupo/differ/diff_calculator"
|
|
11
11
|
require_relative "migsupo/generator/migration_generator"
|
|
12
|
+
require_relative "migsupo/coherent"
|
|
13
|
+
require_relative "migsupo/squash"
|
|
12
14
|
|
|
13
15
|
module Migsupo
|
|
14
16
|
class << self
|
|
@@ -38,6 +40,14 @@ module Migsupo
|
|
|
38
40
|
.generate(diff, output_dir: output_dir, dry_run: dry_run)
|
|
39
41
|
end
|
|
40
42
|
|
|
43
|
+
def detect_rails_version
|
|
44
|
+
return configuration.migration_version if configuration.migration_version
|
|
45
|
+
return nil unless defined?(Rails)
|
|
46
|
+
|
|
47
|
+
major_minor = Rails::VERSION::STRING.split(".").first(2).join(".")
|
|
48
|
+
major_minor
|
|
49
|
+
end
|
|
50
|
+
|
|
41
51
|
private
|
|
42
52
|
|
|
43
53
|
def build_loader(loader)
|
|
@@ -61,14 +71,6 @@ module Migsupo
|
|
|
61
71
|
"db/schema.rb"
|
|
62
72
|
end
|
|
63
73
|
end
|
|
64
|
-
|
|
65
|
-
def detect_rails_version
|
|
66
|
-
return configuration.migration_version if configuration.migration_version
|
|
67
|
-
return nil unless defined?(Rails)
|
|
68
|
-
|
|
69
|
-
major_minor = Rails::VERSION::STRING.split(".").first(2).join(".")
|
|
70
|
-
major_minor
|
|
71
|
-
end
|
|
72
74
|
end
|
|
73
75
|
end
|
|
74
76
|
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: migsupo
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- masak1yu
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 1980-01-01 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activerecord
|
|
@@ -103,6 +103,7 @@ extra_rdoc_files: []
|
|
|
103
103
|
files:
|
|
104
104
|
- README.md
|
|
105
105
|
- lib/migsupo.rb
|
|
106
|
+
- lib/migsupo/coherent.rb
|
|
106
107
|
- lib/migsupo/configuration.rb
|
|
107
108
|
- lib/migsupo/differ/diff.rb
|
|
108
109
|
- lib/migsupo/differ/diff_calculator.rb
|
|
@@ -117,6 +118,7 @@ files:
|
|
|
117
118
|
- lib/migsupo/generator/migration_builder.rb
|
|
118
119
|
- lib/migsupo/generator/migration_generator.rb
|
|
119
120
|
- lib/migsupo/generator/naming.rb
|
|
121
|
+
- lib/migsupo/generator/schemafile_dumper.rb
|
|
120
122
|
- lib/migsupo/loader/active_record_loader.rb
|
|
121
123
|
- lib/migsupo/loader/schema_rb_loader.rb
|
|
122
124
|
- lib/migsupo/parser/dsl_context.rb
|
|
@@ -127,6 +129,7 @@ files:
|
|
|
127
129
|
- lib/migsupo/schema/index_definition.rb
|
|
128
130
|
- lib/migsupo/schema/schema_definition.rb
|
|
129
131
|
- lib/migsupo/schema/table_definition.rb
|
|
132
|
+
- lib/migsupo/squash.rb
|
|
130
133
|
- lib/migsupo/tasks/migsupo.rake
|
|
131
134
|
- lib/migsupo/version.rb
|
|
132
135
|
- migsupo.gemspec
|
|
@@ -149,7 +152,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
149
152
|
- !ruby/object:Gem::Version
|
|
150
153
|
version: '0'
|
|
151
154
|
requirements: []
|
|
152
|
-
rubygems_version: 3.
|
|
155
|
+
rubygems_version: 3.4.19
|
|
153
156
|
signing_key:
|
|
154
157
|
specification_version: 4
|
|
155
158
|
summary: Generate Rails migrations from a Schemafile diff
|