migsql 1.0.4
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/bin/migsql +4 -0
- data/lib/migsql/app.rb +84 -0
- data/lib/migsql/migration.rb +140 -0
- data/lib/migsql/sqlserver.rb +81 -0
- data/lib/migsql.rb +3 -0
- data/sql/create_migration_table.sql +14 -0
- data/sql/create_test_table.sql +5 -0
- data/sql/populate_test_table.sql +2 -0
- metadata +80 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 6dce65cec7f13004fde63a68b7b963d0378ef6ce
|
4
|
+
data.tar.gz: 314daeaf69aaeafc08238b5253efa6213ee0aba3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0470c77ea747a8aa646afec4ff314ecdc2c9f859bccc1c89821276a8101c65f73993dfb0ba1ebfc033f98abef24848b057319a0d02713243d9d9bae77b0e91d3
|
7
|
+
data.tar.gz: 5e441b168abd9ad87ea51124dfe3bd51fb5a6a573acee0c2a866b127507c264e7405219f35ea5e974e63196b314ef4044f4ce7b11572381208ef1aef436ed0e8
|
data/bin/migsql
ADDED
data/lib/migsql/app.rb
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'colorize'
|
2
|
+
class MigSql
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
@migration = Migration.new './db/config.yml'
|
6
|
+
@migration.load
|
7
|
+
end
|
8
|
+
|
9
|
+
def handle_argv(argv)
|
10
|
+
if argv.length == 0
|
11
|
+
puts 'Usage: migsql {init|create-migration|migrate}'.white
|
12
|
+
else
|
13
|
+
case argv[0]
|
14
|
+
when 'init'
|
15
|
+
handle_init
|
16
|
+
when 'create-migration'
|
17
|
+
handle_create_migration argv
|
18
|
+
when 'migrate'
|
19
|
+
handle_migrate argv
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def handle_init
|
25
|
+
if File.directory?('./db')
|
26
|
+
puts 'Error: the ./db directory already exists'.red
|
27
|
+
else
|
28
|
+
@migration.create_server 'example_database', '127.0.0.1', 'dbname', 'username', 'password'
|
29
|
+
@migration.save
|
30
|
+
puts 'Default configuration created in ./db/config.yml'.green
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def handle_create_migration(argv)
|
35
|
+
migration_name = argv[1]
|
36
|
+
migration_server = get_migration_server(argv[2])
|
37
|
+
if !migration_server.nil?
|
38
|
+
@migration.create_migration migration_server, migration_name
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def handle_migrate(argv)
|
43
|
+
if argv[1] == 'to'
|
44
|
+
migration_server = @migration.get_first_server_name
|
45
|
+
migration_target = argv[2]
|
46
|
+
else
|
47
|
+
migration_server = get_migration_server(argv[1])
|
48
|
+
migration_target = argv[3]
|
49
|
+
end
|
50
|
+
o_target = migration_target
|
51
|
+
if !migration_server.nil?
|
52
|
+
if migration_target.nil?
|
53
|
+
migration_target = @migration.get_latest_migration(migration_server)
|
54
|
+
else
|
55
|
+
migration_target = @migration.get_migration_by_name(migration_server, migration_target)
|
56
|
+
end
|
57
|
+
if !migration_target.nil?
|
58
|
+
from = @migration.get_migration_status(migration_server)
|
59
|
+
plan = @migration.get_migration_plan(migration_server, migration_target, from)
|
60
|
+
if !plan.nil?
|
61
|
+
@migration.apply_migration_plan(migration_server, plan, migration_target)
|
62
|
+
end
|
63
|
+
else
|
64
|
+
puts "Error: No migration found with name: #{o_target}".red
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def get_migration_server(server_name)
|
70
|
+
if @migration.count_servers == 0
|
71
|
+
puts 'Error: Please run migsql init first'.red
|
72
|
+
elsif @migration.count_servers > 1 && server_name.nil?
|
73
|
+
puts 'Error: Your config has multiple servers, please specify which server to create the migration on'.red
|
74
|
+
elsif !server_name.nil? && @migration.get_server(server_name).nil?
|
75
|
+
puts "Error: No server named #{server_name} found in your config".red
|
76
|
+
nil
|
77
|
+
elsif @migration.count_servers == 1
|
78
|
+
@migration.get_first_server_name
|
79
|
+
else
|
80
|
+
server_name
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'yaml'
|
4
|
+
require 'tiny_tds'
|
5
|
+
require 'colorize'
|
6
|
+
require_relative 'sqlserver'
|
7
|
+
|
8
|
+
class Migration
|
9
|
+
def initialize(path)
|
10
|
+
@path = path
|
11
|
+
@root = File.dirname(@path)
|
12
|
+
@servers = Hash.new
|
13
|
+
end
|
14
|
+
|
15
|
+
def create_server(name, address, database, username, password)
|
16
|
+
@servers[name] = SqlServer.new name, address, database, username, password
|
17
|
+
end
|
18
|
+
|
19
|
+
def get_server(name)
|
20
|
+
@servers[name]
|
21
|
+
end
|
22
|
+
|
23
|
+
def get_first_server_name
|
24
|
+
@servers.keys.first
|
25
|
+
end
|
26
|
+
|
27
|
+
def save
|
28
|
+
FileUtils::mkdir_p @root
|
29
|
+
File.open(@path, 'w') { |f| f.write @servers.to_yaml }
|
30
|
+
end
|
31
|
+
|
32
|
+
def load
|
33
|
+
if File.file?(@path)
|
34
|
+
@servers = YAML.load_file(@path)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def count_servers
|
39
|
+
@servers.length
|
40
|
+
end
|
41
|
+
|
42
|
+
def create_migration(server_name, migration_name)
|
43
|
+
if get_migration_by_name(server_name, migration_name).nil?
|
44
|
+
migration_name = "#{DateTime.now.strftime('%Q')}_#{migration_name}"
|
45
|
+
server_root = "#{@root}/#{server_name}"
|
46
|
+
up = "#{server_root}/#{migration_name}_up.sql"
|
47
|
+
down = "#{server_root}/#{migration_name}_down.sql"
|
48
|
+
FileUtils::mkdir_p server_root
|
49
|
+
FileUtils::touch up
|
50
|
+
FileUtils::touch down
|
51
|
+
|
52
|
+
puts "Migration '#{migration_name}' created.".green
|
53
|
+
puts "Up: #{up}".white
|
54
|
+
puts "Down: #{down}".white
|
55
|
+
return true
|
56
|
+
else
|
57
|
+
puts 'Error: migration name already in use'.red
|
58
|
+
return false
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def get_latest_migration(server_name)
|
63
|
+
server_root = "#{@root}/#{server_name}"
|
64
|
+
latest_mig = File.basename(Dir["#{server_root}/*_up.sql"].sort.reverse[0])
|
65
|
+
/([0-9]+_.*)(_up|_down)\.sql/.match(latest_mig).captures[0]
|
66
|
+
end
|
67
|
+
|
68
|
+
def get_migration_plan(server_name, to, from)
|
69
|
+
to = to || get_latest_migration(server_name)
|
70
|
+
to_i = /([0-9]+)_?.*/.match(to).captures[0]
|
71
|
+
|
72
|
+
from_i = /([0-9]+)_?.*/.match(from).captures[0]
|
73
|
+
|
74
|
+
if to_i > from_i
|
75
|
+
plan = get_up_plan server_name, to_i, from_i
|
76
|
+
elsif to_i < from_i
|
77
|
+
plan = get_down_plan server_name, to_i, from_i
|
78
|
+
else
|
79
|
+
puts 'No migration needed, database already at current level'.green
|
80
|
+
end
|
81
|
+
return plan
|
82
|
+
end
|
83
|
+
|
84
|
+
def get_up_plan(server_name, to, from)
|
85
|
+
plan = Array.new
|
86
|
+
server_root = "#{@root}/#{server_name}"
|
87
|
+
Dir["#{server_root}/*_up.sql"].sort.each {|migration|
|
88
|
+
current_item = /([0-9]+)_?.*/.match(migration).captures[0]
|
89
|
+
if current_item > from && current_item <= to
|
90
|
+
plan.push(migration)
|
91
|
+
end
|
92
|
+
}
|
93
|
+
plan
|
94
|
+
end
|
95
|
+
|
96
|
+
def get_down_plan(server_name, to, from)
|
97
|
+
plan = Array.new
|
98
|
+
server_root = "#{@root}/#{server_name}"
|
99
|
+
Dir["#{server_root}/*_down.sql"].sort.reverse.each {|migration|
|
100
|
+
current_item = /([0-9]+)_?.*/.match(migration).captures[0]
|
101
|
+
if current_item > to && current_item <= from
|
102
|
+
plan.push(migration)
|
103
|
+
end
|
104
|
+
}
|
105
|
+
plan
|
106
|
+
end
|
107
|
+
|
108
|
+
def get_migration_by_name(server_name, name)
|
109
|
+
server_root = "#{@root}/#{server_name}"
|
110
|
+
migration = Dir["#{server_root}/*#{name}_up.sql"][0]
|
111
|
+
if !migration.nil?
|
112
|
+
migration = /([0-9]+_.*)(_up|_down)\.sql/.match(migration).captures[0]
|
113
|
+
end
|
114
|
+
return migration
|
115
|
+
end
|
116
|
+
|
117
|
+
def apply_migration_plan(server_name, migration_plan, final_state)
|
118
|
+
puts "Applying migration to: #{server_name}".yellow
|
119
|
+
migration_plan.each {|migration|
|
120
|
+
apply_migration server_name, migration
|
121
|
+
}
|
122
|
+
server = get_server(server_name)
|
123
|
+
server.set_migration_status(final_state)
|
124
|
+
puts "Migration Complete, current state: #{final_state}".green
|
125
|
+
end
|
126
|
+
|
127
|
+
def apply_migration(server_name, migration)
|
128
|
+
migration_name, updown = /([0-9]+.*)_(.*)\.sql/.match(migration).captures
|
129
|
+
puts " - #{migration_name} #{updown}".white
|
130
|
+
server = get_server(server_name)
|
131
|
+
server.apply_migration(migration)
|
132
|
+
end
|
133
|
+
|
134
|
+
def get_migration_status(server_name)
|
135
|
+
get_server(server_name).get_migration_status
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|
139
|
+
|
140
|
+
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'yaml'
|
4
|
+
require 'tiny_tds'
|
5
|
+
require 'colorize'
|
6
|
+
|
7
|
+
class SqlServer
|
8
|
+
def initialize(name, address, database, username, password)
|
9
|
+
@name = name
|
10
|
+
@address = address
|
11
|
+
@database = database
|
12
|
+
@username = username
|
13
|
+
@password = password
|
14
|
+
end
|
15
|
+
|
16
|
+
def name
|
17
|
+
return @name
|
18
|
+
end
|
19
|
+
def address
|
20
|
+
return @address
|
21
|
+
end
|
22
|
+
def database
|
23
|
+
return @database
|
24
|
+
end
|
25
|
+
def username
|
26
|
+
return @username
|
27
|
+
end
|
28
|
+
def password
|
29
|
+
return @password
|
30
|
+
end
|
31
|
+
|
32
|
+
def get_sql(name)
|
33
|
+
File.read("#{File.dirname(__FILE__)}/../../sql/#{name}.sql")
|
34
|
+
end
|
35
|
+
|
36
|
+
def get_client
|
37
|
+
TinyTds::Client.new(
|
38
|
+
:username => username,
|
39
|
+
:password => password,
|
40
|
+
:host => address,
|
41
|
+
:database => database
|
42
|
+
)
|
43
|
+
end
|
44
|
+
|
45
|
+
def get_migration_status
|
46
|
+
client = get_client
|
47
|
+
begin
|
48
|
+
results = client.execute("SELECT migration FROM _migration")
|
49
|
+
result = results.each(:first => true)[0]['migration']
|
50
|
+
if result.length == 0
|
51
|
+
return '0'
|
52
|
+
else
|
53
|
+
return result
|
54
|
+
end
|
55
|
+
rescue Exception => ex
|
56
|
+
return '0'
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def set_migration_status(to)
|
61
|
+
client = get_client
|
62
|
+
sql = [
|
63
|
+
get_sql('create_migration_table'),
|
64
|
+
"UPDATE _migration SET migration = '#{to}'"
|
65
|
+
].join(' ')
|
66
|
+
client.execute(sql).each
|
67
|
+
end
|
68
|
+
|
69
|
+
def remove_migration
|
70
|
+
client = get_client
|
71
|
+
client.execute('DROP TABLE _migration')
|
72
|
+
end
|
73
|
+
|
74
|
+
def apply_migration(path)
|
75
|
+
client = get_client
|
76
|
+
sql = File.read(path)
|
77
|
+
client.execute(sql)
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
data/lib/migsql.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
IF NOT EXISTS(SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[_migration]') AND TYPE IN (N'U'))
|
2
|
+
BEGIN
|
3
|
+
CREATE TABLE [dbo].[_migration](
|
4
|
+
[migration] [varchar](100) NOT NULL,
|
5
|
+
[date_applied] [datetime] NOT NULL
|
6
|
+
) ON [PRIMARY];
|
7
|
+
|
8
|
+
INSERT INTO [dbo].[_migration](
|
9
|
+
migration,
|
10
|
+
date_applied
|
11
|
+
) VALUES(
|
12
|
+
'0', '1990-01-01'
|
13
|
+
)
|
14
|
+
END
|
metadata
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: migsql
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.4
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Karl Stoney
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-06-03 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: tiny_tds
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: colorize
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
description: Simple lightweight up/down MSSQL migrations
|
42
|
+
email: karl@jambr.co.uk
|
43
|
+
executables:
|
44
|
+
- migsql
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- lib/migsql/migration.rb
|
49
|
+
- lib/migsql/app.rb
|
50
|
+
- lib/migsql/sqlserver.rb
|
51
|
+
- lib/migsql.rb
|
52
|
+
- bin/migsql
|
53
|
+
- sql/create_migration_table.sql
|
54
|
+
- sql/populate_test_table.sql
|
55
|
+
- sql/create_test_table.sql
|
56
|
+
homepage: https://github.com/Stono/migsql
|
57
|
+
licenses:
|
58
|
+
- MIT
|
59
|
+
metadata: {}
|
60
|
+
post_install_message:
|
61
|
+
rdoc_options: []
|
62
|
+
require_paths:
|
63
|
+
- lib
|
64
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - '>='
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: '0'
|
74
|
+
requirements: []
|
75
|
+
rubyforge_project:
|
76
|
+
rubygems_version: 2.1.11
|
77
|
+
signing_key:
|
78
|
+
specification_version: 4
|
79
|
+
summary: Simple lightweight up/down MSSQL migrations
|
80
|
+
test_files: []
|