db-migrate 0.0.1
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 +7 -0
- data/.gitignore +3 -0
- data/.rspec +2 -0
- data/.travis.yml +6 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +46 -0
- data/LICENSE +20 -0
- data/README.md +60 -0
- data/bin/migrate +136 -0
- data/lib/migrate.rb +9 -0
- data/lib/migrate/config.rb +104 -0
- data/lib/migrate/errors.rb +4 -0
- data/lib/migrate/lang.rb +10 -0
- data/lib/migrate/lang/go.rb +37 -0
- data/lib/migrate/lang/javascript.rb +25 -0
- data/lib/migrate/lang/lang.rb +15 -0
- data/lib/migrate/lang/python.rb +25 -0
- data/lib/migrate/lang/ruby.rb +25 -0
- data/lib/migrate/lang/sql.rb +26 -0
- data/lib/migrate/logger.rb +39 -0
- data/lib/migrate/migrator.rb +144 -0
- data/lib/migrate/storage.rb +7 -0
- data/lib/migrate/storage/db.rb +150 -0
- data/lib/migrate/storage/mysql.rb +77 -0
- data/lib/migrate/storage/postgres.rb +74 -0
- data/migrate.gemspec +21 -0
- data/spec/lib/config_spec.rb +103 -0
- data/spec/lib/fixtures/go/down.go +7 -0
- data/spec/lib/fixtures/go/up.go +7 -0
- data/spec/lib/fixtures/js/down.js +1 -0
- data/spec/lib/fixtures/js/up.js +1 -0
- data/spec/lib/fixtures/py/down.py +1 -0
- data/spec/lib/fixtures/py/up.py +1 -0
- data/spec/lib/fixtures/rb/down.rb +1 -0
- data/spec/lib/fixtures/rb/up.rb +1 -0
- data/spec/lib/fixtures/sql/down.sql +1 -0
- data/spec/lib/fixtures/sql/up.sql +1 -0
- data/spec/lib/lang/lang_spec.rb +46 -0
- data/spec/lib/migrator_spec.rb +103 -0
- data/spec/lib/storage/db_spec.rb +104 -0
- data/spec/spec_helper.rb +109 -0
- metadata +184 -0
@@ -0,0 +1,25 @@
|
|
1
|
+
module Migrate
|
2
|
+
module Lang
|
3
|
+
class Javascript < Lang
|
4
|
+
def initialize
|
5
|
+
@ext = "js"
|
6
|
+
end
|
7
|
+
|
8
|
+
def create_migration(dir)
|
9
|
+
File.open("#{dir}/up.#{@ext}", "w") do |f|
|
10
|
+
f.puts "// Here goes JS for migration forward\n"
|
11
|
+
end
|
12
|
+
|
13
|
+
File.open("#{dir}/down.#{@ext}", "w") do |f|
|
14
|
+
f.puts "// Here goes JS for migration backward\n"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def exec_migration(dir, is_up)
|
19
|
+
script = "#{dir}/#{is_up ? "up" : "down"}.#{@ext}"
|
20
|
+
Log.info("Executing #{script}...")
|
21
|
+
`node #{script}`
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Migrate
|
2
|
+
module Lang
|
3
|
+
class Lang
|
4
|
+
attr_reader :ext
|
5
|
+
|
6
|
+
def create_migration(dir)
|
7
|
+
raise "Implementation for creating new migration not found."
|
8
|
+
end
|
9
|
+
|
10
|
+
def exec_migration(dir, is_up)
|
11
|
+
raise "Implementation for executing migration not found."
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Migrate
|
2
|
+
module Lang
|
3
|
+
class Python < Lang
|
4
|
+
def initialize
|
5
|
+
@ext = "py"
|
6
|
+
end
|
7
|
+
|
8
|
+
def create_migration(dir)
|
9
|
+
File.open("#{dir}/up.#{@ext}", "w") do |f|
|
10
|
+
f.puts "# Here goes Python code for migration forward\n"
|
11
|
+
end
|
12
|
+
|
13
|
+
File.open("#{dir}/down.#{@ext}", "w") do |f|
|
14
|
+
f.puts "# Here goes Python code for migration backward\n"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def exec_migration(dir, is_up)
|
19
|
+
script = "#{dir}/#{is_up ? "up" : "down"}.#{@ext}"
|
20
|
+
Log.info("Executing #{script}...")
|
21
|
+
`python #{script}`
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Migrate
|
2
|
+
module Lang
|
3
|
+
class Ruby < Lang
|
4
|
+
def initialize
|
5
|
+
@ext = "rb"
|
6
|
+
end
|
7
|
+
|
8
|
+
def create_migration(dir)
|
9
|
+
File.open("#{dir}/up.#{@ext}", "w") do |f|
|
10
|
+
f.puts "# Here goes Ruby code for migration forward\n"
|
11
|
+
end
|
12
|
+
|
13
|
+
File.open("#{dir}/down.#{@ext}", "w") do |f|
|
14
|
+
f.puts "# Here goes Ruby code for migration backward\n"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def exec_migration(dir, is_up)
|
19
|
+
script = "#{dir}/#{is_up ? "up" : "down"}.#{@ext}"
|
20
|
+
Log.info("Executing #{script}...")
|
21
|
+
`ruby #{script}`
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Migrate
|
2
|
+
module Lang
|
3
|
+
class Sql < Lang
|
4
|
+
def initialize(db)
|
5
|
+
@db = db
|
6
|
+
@ext = "sql"
|
7
|
+
end
|
8
|
+
|
9
|
+
def create_migration(dir)
|
10
|
+
File.open("#{dir}/up.#{@ext}", "w") do |f|
|
11
|
+
f.puts "-- Here goes SQL for migration forward\n"
|
12
|
+
end
|
13
|
+
|
14
|
+
File.open("#{dir}/down.#{@ext}", "w") do |f|
|
15
|
+
f.puts "-- Here goes SQL for migration backward\n"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def exec_migration(dir, is_up)
|
20
|
+
script = "#{dir}/#{is_up ? "up" : "down"}.#{@ext}"
|
21
|
+
Log.info("Executing #{script}...")
|
22
|
+
@db.exec_sql(File.read(script))
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require "colorize"
|
2
|
+
|
3
|
+
module Migrate
|
4
|
+
class Log
|
5
|
+
@@debug = true
|
6
|
+
|
7
|
+
def self.verbose(verbose)
|
8
|
+
@@debug = verbose
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.info(msg)
|
12
|
+
return if not @@debug
|
13
|
+
puts ("[INFO] " + msg).green
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.warn(msg)
|
17
|
+
return if not @@debug
|
18
|
+
puts ("[WARN] " + msg).yellow
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.error(msg, e=nil)
|
22
|
+
return if not @@debug
|
23
|
+
puts ("[ERRPR] " + msg + (e != nil ? " #{e.message}" : "")).red
|
24
|
+
|
25
|
+
if e != nil
|
26
|
+
puts e.backtrace
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.success(msg)
|
31
|
+
return if not @@debug
|
32
|
+
puts ("[SUCCESS] " + msg).blue
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.version(msg)
|
36
|
+
puts (" [VERSION] #{msg} ").colorize(:color => :white, :background => :blue)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
module Migrate
|
2
|
+
class Migrator
|
3
|
+
def initialize(config)
|
4
|
+
@config = config
|
5
|
+
@db = config.get_db
|
6
|
+
@lang = config.get_lang
|
7
|
+
|
8
|
+
if @db == nil
|
9
|
+
throw "Database connection not found."
|
10
|
+
exit
|
11
|
+
end
|
12
|
+
|
13
|
+
if @lang == nil
|
14
|
+
throw "Language not found."
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def init
|
19
|
+
@db.tx do
|
20
|
+
@db.create_tables
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def migration_dir(migration)
|
25
|
+
date = DateTime.parse(migration["created_date"].to_s)
|
26
|
+
"#{@config.root}/v#{migration["version"]}-#{date.strftime("%Y-%m-%d")}"
|
27
|
+
end
|
28
|
+
|
29
|
+
def new(desc)
|
30
|
+
@db.tx do
|
31
|
+
Log.info("Creating new migration...")
|
32
|
+
|
33
|
+
migration = @db.new_migration(desc)
|
34
|
+
migration_dir = self.migration_dir(migration)
|
35
|
+
Dir.mkdir migration_dir
|
36
|
+
@lang.create_migration(migration_dir)
|
37
|
+
|
38
|
+
Log.success("Migration for version #{migration["version"]} created.")
|
39
|
+
migration_dir
|
40
|
+
end
|
41
|
+
rescue Exception => e
|
42
|
+
Log.error("Error while creating new migration.", e)
|
43
|
+
exit
|
44
|
+
end
|
45
|
+
|
46
|
+
# will execute single migration by running up or down script
|
47
|
+
def exec_migration(migration, is_up)
|
48
|
+
migration_dir = self.migration_dir(migration)
|
49
|
+
result = @lang.exec_migration(migration_dir, is_up)
|
50
|
+
if @lang.ext != "sql"
|
51
|
+
puts result
|
52
|
+
end
|
53
|
+
|
54
|
+
Log.info("Updating current version number...")
|
55
|
+
version = migration["version"]
|
56
|
+
is_up ? @db.log_up(version) : @db.log_down(version)
|
57
|
+
end
|
58
|
+
|
59
|
+
# will execute range of migrations
|
60
|
+
def exec_migrations(is_up=true)
|
61
|
+
Log.info("Executing migrations...")
|
62
|
+
migrations = yield @db.current_version
|
63
|
+
|
64
|
+
if migrations.count == 0
|
65
|
+
Log.warn("Migrations not found")
|
66
|
+
return
|
67
|
+
end
|
68
|
+
|
69
|
+
migrations.each do |migration|
|
70
|
+
self.exec_migration(migration, is_up)
|
71
|
+
end
|
72
|
+
Log.success("Migrations executed. Current version: #{@db.current_version}")
|
73
|
+
end
|
74
|
+
|
75
|
+
def up(to_version=nil)
|
76
|
+
@db.tx do
|
77
|
+
self.exec_migrations do |last_version|
|
78
|
+
new_version = @db.next_version
|
79
|
+
if to_version == nil
|
80
|
+
to_version = new_version
|
81
|
+
end
|
82
|
+
|
83
|
+
@db.migrations_range(new_version, to_version, true)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def down(to_version=nil)
|
89
|
+
@db.tx do
|
90
|
+
self.exec_migrations(false) do |current_version|
|
91
|
+
if current_version == 0
|
92
|
+
raise VersionNotFound
|
93
|
+
end
|
94
|
+
|
95
|
+
if to_version == nil
|
96
|
+
to_version = current_version
|
97
|
+
else
|
98
|
+
to_version = to_version.to_i + 1
|
99
|
+
end
|
100
|
+
|
101
|
+
@db.migrations_range(to_version, current_version, false)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def current_version
|
107
|
+
@db.tx do
|
108
|
+
return @db.current_version
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def delete(version)
|
113
|
+
@db.tx do
|
114
|
+
Log.info("Removing migration data...")
|
115
|
+
|
116
|
+
if @db.current_version.to_i == version
|
117
|
+
return Log.error("Cannot remove current version.")
|
118
|
+
end
|
119
|
+
|
120
|
+
dir = self.migration_dir(@db.get_migration(version))
|
121
|
+
@db.delete version
|
122
|
+
|
123
|
+
if Dir.exist? dir
|
124
|
+
File.delete "#{dir}/up.sql"
|
125
|
+
File.delete "#{dir}/down.sql"
|
126
|
+
Dir.rmdir dir
|
127
|
+
end
|
128
|
+
|
129
|
+
Log.success("Migration data removed.")
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def list(select, limit)
|
134
|
+
@db.tx do
|
135
|
+
migrations = @db.list_migrations(select, limit)
|
136
|
+
if not migrations.any?
|
137
|
+
return Log.info("Migrations not found")
|
138
|
+
end
|
139
|
+
|
140
|
+
@db.print(migrations, "Migrations")
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
require 'terminal-table'
|
2
|
+
|
3
|
+
module Migrate
|
4
|
+
module Storage
|
5
|
+
class DB
|
6
|
+
attr_reader :config
|
7
|
+
|
8
|
+
def initialize(config)
|
9
|
+
@config = config
|
10
|
+
end
|
11
|
+
|
12
|
+
def type
|
13
|
+
@config.storage
|
14
|
+
end
|
15
|
+
|
16
|
+
def new_migration(description="")
|
17
|
+
self.exec_sql <<-eos
|
18
|
+
INSERT INTO #{@config.version_info} (description, created_date)
|
19
|
+
VALUES('#{description}', now())
|
20
|
+
eos
|
21
|
+
|
22
|
+
res = self.exec_sql <<-eos
|
23
|
+
SELECT * FROM #{@config.version_info} ORDER BY version DESC LIMIT 1
|
24
|
+
eos
|
25
|
+
res[0]
|
26
|
+
end
|
27
|
+
|
28
|
+
def list_migrations(selects, limit)
|
29
|
+
self.exec_sql <<-eos
|
30
|
+
SELECT #{(selects == nil ? "*" : selects)} FROM #{@config.version_info}
|
31
|
+
ORDER BY last_up, version
|
32
|
+
#{limit != nil ? "LIMIT #{limit}" : ""}
|
33
|
+
eos
|
34
|
+
end
|
35
|
+
|
36
|
+
def migrations_range(from, to, is_up)
|
37
|
+
self.exec_sql <<-eos
|
38
|
+
SELECT * FROM #{@config.version_info}
|
39
|
+
WHERE version >= #{from} AND version <= #{to}
|
40
|
+
ORDER BY version #{!is_up ? "DESC" : ""}
|
41
|
+
eos
|
42
|
+
end
|
43
|
+
|
44
|
+
def extract_version(results)
|
45
|
+
if results && results.count > 0
|
46
|
+
results[0]["version"]
|
47
|
+
else
|
48
|
+
raise VersionNotFound
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def lowest_version
|
53
|
+
self.extract_version self.exec_sql <<-eos
|
54
|
+
SELECT version FROM #{@config.version_info}
|
55
|
+
ORDER BY version
|
56
|
+
LIMIT 1
|
57
|
+
eos
|
58
|
+
end
|
59
|
+
|
60
|
+
def next_version
|
61
|
+
self.extract_version self.exec_sql <<-eos
|
62
|
+
SELECT version FROM #{@config.version_info}
|
63
|
+
WHERE version > (SELECT version FROM #{@config.version_number} LIMIT 1)
|
64
|
+
ORDER BY version
|
65
|
+
LIMIT 1
|
66
|
+
eos
|
67
|
+
end
|
68
|
+
|
69
|
+
def current_version
|
70
|
+
self.extract_version self.exec_sql <<-eos
|
71
|
+
SELECT * FROM #{config.version_number}
|
72
|
+
LIMIT 1
|
73
|
+
eos
|
74
|
+
end
|
75
|
+
|
76
|
+
def prev_version
|
77
|
+
self.extract_version self.exec_sql <<-eos
|
78
|
+
SELECT version FROM #{@config.version_info}
|
79
|
+
WHERE version < (SELECT version FROM #{@config.version_number} LIMIT 1)
|
80
|
+
ORDER BY version DESC
|
81
|
+
LIMIT 1
|
82
|
+
eos
|
83
|
+
end
|
84
|
+
|
85
|
+
def log_up(version)
|
86
|
+
self.exec_sql "UPDATE #{@config.version_info} SET last_up=now() WHERE version=#{version}"
|
87
|
+
self.exec_sql "UPDATE #{@config.version_number} SET version=#{version}"
|
88
|
+
end
|
89
|
+
|
90
|
+
def log_down(version)
|
91
|
+
self.exec_sql "UPDATE #{@config.version_info} SET last_down=now() WHERE version=#{version}"
|
92
|
+
|
93
|
+
lowest_version = self.lowest_version
|
94
|
+
version_to_save = lowest_version.to_i < version.to_i ? self.prev_version().to_i : 0
|
95
|
+
self.exec_sql "UPDATE #{@config.version_number} SET version=#{version_to_save}"
|
96
|
+
end
|
97
|
+
|
98
|
+
def get_migration(version)
|
99
|
+
res = self.exec_sql "SELECT * FROM #{@config.version_info} WHERE version=#{version}"
|
100
|
+
if res && res.count > 0
|
101
|
+
res[0]
|
102
|
+
else
|
103
|
+
raise VersionNotFound
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def delete(version)
|
108
|
+
self.exec_sql "DELETE FROM #{@config.version_info} WHERE version=#{version}"
|
109
|
+
end
|
110
|
+
|
111
|
+
def print(results, title="")
|
112
|
+
rows = []
|
113
|
+
headings = results[0].keys
|
114
|
+
|
115
|
+
results.each do |result|
|
116
|
+
row = []
|
117
|
+
result.each do |column, value|
|
118
|
+
if column == "description"
|
119
|
+
if value.length > 70
|
120
|
+
value = value.scan(/.{1,70}/).join("\n")
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
row << value
|
125
|
+
end
|
126
|
+
rows << row
|
127
|
+
end
|
128
|
+
|
129
|
+
table = Terminal::Table.new :headings => headings, :rows => rows
|
130
|
+
table.title = title
|
131
|
+
puts table
|
132
|
+
end
|
133
|
+
|
134
|
+
# Will create database model used by tool
|
135
|
+
def create_tables
|
136
|
+
raise "Implementation for creating tables not found"
|
137
|
+
end
|
138
|
+
|
139
|
+
# Executes SQL
|
140
|
+
def exec_sql(sql)
|
141
|
+
raise "Implementation for executing SQL script not found"
|
142
|
+
end
|
143
|
+
|
144
|
+
# Creates new transaction. Should accept block.
|
145
|
+
def tx
|
146
|
+
raise "Implementation for starting new transaction not found"
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|