xmigra 1.3.1 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/xmigra/access_artifact.rb +14 -3
- data/lib/xmigra/access_artifact_collection.rb +14 -1
- data/lib/xmigra/branch_upgrade.rb +12 -1
- data/lib/xmigra/console.rb +115 -0
- data/lib/xmigra/db_support/mssql.rb +10 -0
- data/lib/xmigra/index.rb +8 -1
- data/lib/xmigra/index_collection.rb +13 -0
- data/lib/xmigra/migration.rb +12 -1
- data/lib/xmigra/permission_script_writer.rb +4 -1
- data/lib/xmigra/plugin.rb +148 -0
- data/lib/xmigra/program.rb +44 -1
- data/lib/xmigra/reversion_script_building.rb +8 -2
- data/lib/xmigra/schema_manipulator.rb +11 -1
- data/lib/xmigra/schema_updater.rb +8 -1
- data/lib/xmigra/source_tree_initializer.rb +106 -0
- data/lib/xmigra/vcs_support/git.rb +107 -0
- data/lib/xmigra/vcs_support/svn.rb +27 -1
- data/lib/xmigra/version.rb +1 -1
- data/lib/xmigra.rb +3 -0
- data/test/reversions.rb +1 -30
- data/test/runner.rb +1 -34
- data/test/utils.rb +78 -0
- data/xmigra.gemspec +1 -1
- metadata +7 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 87e678fcb7c1e98cff100798bd30948628becbcb
|
4
|
+
data.tar.gz: d89d555130e4e53e28f449f1cfdcd5ded31bf942
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e35d004c20dc96dfd27418281ce19ded3b1c639d8aa7cb3591ba713c29b3c1246a4389ef63801a270b6409cfef22968f6d0eaa97f71ca2db574b35eedf99b933
|
7
|
+
data.tar.gz: ade9c348aacb41f7a81aa669c60511076fbf20b5ea0464f129473e2a859b0dfd87a558b979910eb88104550d0b32e9f8c3cc9ff683f11055bf282ed8006674f8
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'xmigra/plugin'
|
1
2
|
|
2
3
|
module XMigra
|
3
4
|
class AccessArtifact
|
@@ -26,10 +27,20 @@ module XMigra
|
|
26
27
|
end
|
27
28
|
|
28
29
|
def creation_sql
|
29
|
-
|
30
|
-
|
30
|
+
raw = begin
|
31
|
+
if metavar = filename_metavariable
|
32
|
+
@definition.gsub(metavar) {|m| self.name}
|
33
|
+
else
|
34
|
+
@definition
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
if Plugin.active
|
39
|
+
raw.dup.tap do |sql|
|
40
|
+
Plugin.active.amend_source_sql(sql)
|
41
|
+
end
|
31
42
|
else
|
32
|
-
|
43
|
+
raw
|
33
44
|
end
|
34
45
|
end
|
35
46
|
|
@@ -1,5 +1,6 @@
|
|
1
1
|
|
2
2
|
require "tsort"
|
3
|
+
require 'xmigra/plugin'
|
3
4
|
|
4
5
|
module XMigra
|
5
6
|
class AccessArtifactCollection
|
@@ -10,9 +11,21 @@ module XMigra
|
|
10
11
|
filename_metavariable = filename_metavariable.dup.freeze if filename_metavariable
|
11
12
|
|
12
13
|
XMigra.each_access_artifact(path) do |artifact|
|
13
|
-
@items[artifact.name] = artifact
|
14
14
|
artifact.extend(db_specifics) if db_specifics
|
15
15
|
artifact.filename_metavariable = filename_metavariable
|
16
|
+
|
17
|
+
if Plugin.active
|
18
|
+
next unless Plugin.active.include_access_artifact?(artifact)
|
19
|
+
Plugin.active.amend_access_artifact(artifact)
|
20
|
+
end
|
21
|
+
|
22
|
+
@items[artifact.name] = artifact
|
23
|
+
end
|
24
|
+
|
25
|
+
if Plugin.active
|
26
|
+
Plugin.active.each_additional_access_artifact(db_specifics) do |artifact|
|
27
|
+
@items[artifact.name] = artifact
|
28
|
+
end
|
16
29
|
end
|
17
30
|
end
|
18
31
|
|
@@ -1,5 +1,6 @@
|
|
1
1
|
|
2
2
|
require 'xmigra/migration'
|
3
|
+
require 'xmigra/plugin'
|
3
4
|
|
4
5
|
module XMigra
|
5
6
|
class BranchUpgrade
|
@@ -27,7 +28,7 @@ module XMigra
|
|
27
28
|
@sql = verinc_info['sql']
|
28
29
|
end
|
29
30
|
|
30
|
-
attr_reader :file_path, :base_migration, :target_branch, :migration_completed
|
31
|
+
attr_reader :file_path, :base_migration, :target_branch, :migration_completed
|
31
32
|
|
32
33
|
def found?
|
33
34
|
@found
|
@@ -48,6 +49,16 @@ module XMigra
|
|
48
49
|
@warnings.dup
|
49
50
|
end
|
50
51
|
|
52
|
+
def sql
|
53
|
+
if Plugin.active
|
54
|
+
@sql.dup.tap do |result|
|
55
|
+
Plugin.active.amend_source_sql(result)
|
56
|
+
end
|
57
|
+
else
|
58
|
+
@sql
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
51
62
|
def migration_completed_id
|
52
63
|
Migration.id_from_filename(XMigra.yaml_path(migration_completed))
|
53
64
|
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
module XMigra
|
2
|
+
module Console
|
3
|
+
class Menu
|
4
|
+
def initialize(title, options, prompt, opts={})
|
5
|
+
@title = title
|
6
|
+
@prompt = prompt
|
7
|
+
@title_width = opts[:title_width] || 40
|
8
|
+
@title_rule = opts[:title_rule] || '='
|
9
|
+
@trailing_newlines = opts[:trailing_newlines] || 3
|
10
|
+
get_name = opts[:get_name] || lambda {|o| o.to_s}
|
11
|
+
@name_map = {}
|
12
|
+
@menu_map = {}
|
13
|
+
options.each_with_index do |opt, i|
|
14
|
+
opt_name = get_name[opt]
|
15
|
+
@name_map[opt_name] = opt
|
16
|
+
@menu_map[i + 1] = opt_name
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
attr :title, :prompt, :title_width, :title_rule, :trailing_newlines
|
21
|
+
|
22
|
+
def show_once
|
23
|
+
Console.output_section(
|
24
|
+
title,
|
25
|
+
:trailing_newlines => @trailing_newlines
|
26
|
+
) do
|
27
|
+
@menu_map.each_pair do |item_num, name|
|
28
|
+
puts "#{item_num.to_s.rjust(4)}. #{name}"
|
29
|
+
end
|
30
|
+
puts
|
31
|
+
print prompt + ': '
|
32
|
+
|
33
|
+
user_choice = $stdin.gets.strip
|
34
|
+
@menu_map[user_choice.to_i].tap do |mapped|
|
35
|
+
return mapped unless mapped.nil?
|
36
|
+
end
|
37
|
+
return user_choice if @menu_map.values.include? user_choice
|
38
|
+
by_prefix = @menu_map.values.select {|e| e.start_with? user_choice}
|
39
|
+
return by_prefix[0] if by_prefix.length == 1
|
40
|
+
end
|
41
|
+
return nil
|
42
|
+
end
|
43
|
+
|
44
|
+
def get_selection
|
45
|
+
loop do
|
46
|
+
selection = show_once
|
47
|
+
break unless selection.nil?
|
48
|
+
puts "That input did not uniquely identify one of the available options."
|
49
|
+
puts
|
50
|
+
end
|
51
|
+
@trailing_newlines.times {puts}
|
52
|
+
return @name_map[selection]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
class InvalidInput < Exception
|
57
|
+
def initialize(msg=nil)
|
58
|
+
super(msg)
|
59
|
+
@explicit_message = !msg.nil?
|
60
|
+
end
|
61
|
+
|
62
|
+
def explicit_message?
|
63
|
+
@explicit_message
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class <<self
|
68
|
+
def output_section(title=nil, opts={})
|
69
|
+
trailing_newlines = opts[:trailing_newlines] || 3
|
70
|
+
|
71
|
+
if title
|
72
|
+
puts " #{title} ".center(40, '=')
|
73
|
+
puts
|
74
|
+
end
|
75
|
+
|
76
|
+
(yield).tap do
|
77
|
+
trailing_newlines.times {puts}
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def validated_input(prompt)
|
82
|
+
loop do
|
83
|
+
print prompt + ": "
|
84
|
+
input_value = $stdin.gets.strip
|
85
|
+
|
86
|
+
result = begin
|
87
|
+
yield input_value
|
88
|
+
rescue InvalidInput => e
|
89
|
+
puts e.message if e.explicit_message?
|
90
|
+
next
|
91
|
+
end
|
92
|
+
|
93
|
+
return result unless result.nil?
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def yes_no(prompt, default_value)
|
98
|
+
input_options = ""
|
99
|
+
input_options << (default_value == :yes ? "Y" : "y")
|
100
|
+
input_options << (default_value == :no ? "N" : "n")
|
101
|
+
|
102
|
+
validated_input("#{prompt} [#{input_options}]") do |input_value|
|
103
|
+
case input_value
|
104
|
+
when /^y(es)?$/io
|
105
|
+
true
|
106
|
+
when /^n(o)?$/io
|
107
|
+
false
|
108
|
+
when ''
|
109
|
+
{:yes => true, :no => false}[default_value]
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'xmigra/console'
|
1
2
|
|
2
3
|
module XMigra
|
3
4
|
module MSSQLSpecifics
|
@@ -1166,6 +1167,15 @@ INSERT INTO [xmigra].[branch_upgrade] ([Current]) VALUES (#{branch_id_literal});
|
|
1166
1167
|
def string_literal(s)
|
1167
1168
|
"N'#{s.gsub("'","''")}'"
|
1168
1169
|
end
|
1170
|
+
|
1171
|
+
def init_schema(schema_config)
|
1172
|
+
Console.output_section "Microsoft SQL Server Specifics" do
|
1173
|
+
if Console.yes_no("Use more verbose syntax compatible with SQL Server 2005", :no)
|
1174
|
+
schema_config.dbinfo["MSSQL 2005 compatible"] = true
|
1175
|
+
puts "Configured for SQL Server 2005 compatibility mode."
|
1176
|
+
end
|
1177
|
+
end
|
1178
|
+
end
|
1169
1179
|
end
|
1170
1180
|
end
|
1171
1181
|
end
|
data/lib/xmigra/index.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'xmigra/plugin'
|
1
2
|
|
2
3
|
module XMigra
|
3
4
|
class Index
|
@@ -15,7 +16,13 @@ module XMigra
|
|
15
16
|
end
|
16
17
|
|
17
18
|
def definition_sql
|
18
|
-
|
19
|
+
if Plugin.active
|
20
|
+
@definition.dup.tap do |sql|
|
21
|
+
Plugin.active.amend_source_sql(sql)
|
22
|
+
end
|
23
|
+
else
|
24
|
+
@definition
|
25
|
+
end
|
19
26
|
end
|
20
27
|
end
|
21
28
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
|
2
2
|
require 'xmigra/index'
|
3
|
+
require 'xmigra/plugin'
|
3
4
|
|
4
5
|
module XMigra
|
5
6
|
class IndexCollection
|
@@ -12,8 +13,20 @@ module XMigra
|
|
12
13
|
index = Index.new(info)
|
13
14
|
index.extend(db_specifics) if db_specifics
|
14
15
|
index.file_path = File.expand_path(fpath)
|
16
|
+
|
17
|
+
if Plugin.active
|
18
|
+
next unless Plugin.active.include_index?(index)
|
19
|
+
Plugin.active.amend_index(index)
|
20
|
+
end
|
21
|
+
|
15
22
|
@items[index.name] = index
|
16
23
|
end
|
24
|
+
|
25
|
+
if Plugin.active
|
26
|
+
Plugin.active.each_additional_index(db_specifics) do |index|
|
27
|
+
@items[index.name] = index
|
28
|
+
end
|
29
|
+
end
|
17
30
|
end
|
18
31
|
|
19
32
|
def [](name)
|
data/lib/xmigra/migration.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'pathname'
|
2
|
+
require 'xmigra/plugin'
|
2
3
|
require 'xmigra/revert_file'
|
3
4
|
|
4
5
|
module XMigra
|
@@ -17,13 +18,23 @@ module XMigra
|
|
17
18
|
@changes.each {|c| c.freeze}
|
18
19
|
end
|
19
20
|
|
20
|
-
attr_reader :id, :follows, :
|
21
|
+
attr_reader :id, :follows, :description, :changes
|
21
22
|
attr_accessor :file_path
|
22
23
|
|
23
24
|
def schema_dir
|
24
25
|
Pathname(file_path).dirname.join('..')
|
25
26
|
end
|
26
27
|
|
28
|
+
def sql
|
29
|
+
if Plugin.active
|
30
|
+
@sql.dup.tap do |result|
|
31
|
+
Plugin.active.amend_source_sql(result)
|
32
|
+
end
|
33
|
+
else
|
34
|
+
@sql
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
27
38
|
def reversion
|
28
39
|
result = RevertFile.new(self)
|
29
40
|
return result if result.exist?
|
@@ -1,4 +1,5 @@
|
|
1
1
|
|
2
|
+
require 'xmigra/plugin'
|
2
3
|
require 'xmigra/schema_manipulator'
|
3
4
|
|
4
5
|
module XMigra
|
@@ -35,7 +36,9 @@ module XMigra
|
|
35
36
|
# Grant the permissions indicated in the source file
|
36
37
|
grant_specified_permissions_sql,
|
37
38
|
|
38
|
-
].flatten.compact.join(ddl_block_separator)
|
39
|
+
].flatten.compact.join(ddl_block_separator).tap do |result|
|
40
|
+
Plugin.active.amend_composed_sql(result) if Plugin.active
|
41
|
+
end
|
39
42
|
end
|
40
43
|
end
|
41
44
|
|
@@ -0,0 +1,148 @@
|
|
1
|
+
module XMigra
|
2
|
+
|
3
|
+
# Base class for XMigra plugins.
|
4
|
+
#
|
5
|
+
# Derive a class from this class, then call XMigra::Plugin.activate! with
|
6
|
+
# a block that instantiates your class.
|
7
|
+
#
|
8
|
+
# require "xmigra/plugin"
|
9
|
+
#
|
10
|
+
# class YearTemplatePlugin < XMigra::Plugin
|
11
|
+
# def amend_composed_sql(sql)
|
12
|
+
# sql.gsub! '[{year}]', Date.today.year.to_s
|
13
|
+
# end
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# XMigra::Plugin.activate! {YearTemplatePlugin.new}
|
17
|
+
#
|
18
|
+
# The last call to XMigra::Plugin.activate! will determine which block will
|
19
|
+
# be executed to return the active plugin, so make sure to +require+ any
|
20
|
+
# plugins to be aggregated before activating your own.
|
21
|
+
|
22
|
+
class Plugin
|
23
|
+
class LoadingError < ::LoadError; end
|
24
|
+
|
25
|
+
class <<self
|
26
|
+
attr_reader :active
|
27
|
+
|
28
|
+
def loading?
|
29
|
+
!!@load_depth
|
30
|
+
end
|
31
|
+
|
32
|
+
def load!(name)
|
33
|
+
previous_depth, @load_depth = @load_depth, (@load_depth || 0) + 1
|
34
|
+
@activation = nil if previous_depth.nil?
|
35
|
+
begin
|
36
|
+
require name
|
37
|
+
rescue ::LoadError => error
|
38
|
+
if previous_depth.nil? && error.path == name
|
39
|
+
raise LoadingError, "The XMigra plugin #{name.inspect} is not installed (Kernel#require failed)."
|
40
|
+
else
|
41
|
+
raise
|
42
|
+
end
|
43
|
+
ensure
|
44
|
+
@load_depth = previous_depth
|
45
|
+
end
|
46
|
+
|
47
|
+
if previous_depth.nil? && @activation
|
48
|
+
@active = @activation.call
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def activate!(&blk)
|
53
|
+
@activation = blk
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
# Amend SQL coming from source documents. The String object passed to
|
59
|
+
# this method will be included in the commands to run, and any
|
60
|
+
# modifications made to this object will be reflected in the script.
|
61
|
+
# XMigra only calls this method to amend SQL read in from source files.
|
62
|
+
#
|
63
|
+
# The default implementation does nothing.
|
64
|
+
|
65
|
+
def amend_source_sql(sql)
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
# Amend SQL for script after all parts are combined. This method will
|
70
|
+
# have an opportunity to amend all of the SQL in the output script,
|
71
|
+
# including SQL generated by XMigra.
|
72
|
+
#
|
73
|
+
# XMigra only calls this method one time for the entire output script
|
74
|
+
# _prior_ to any transformation necessary for script transactionality
|
75
|
+
# (e.g. splitting the script into batches and encoding as string
|
76
|
+
# literals). The one exception to this is for any branch upgrade SQL,
|
77
|
+
# which will already be encoded in one or more string literals.
|
78
|
+
#
|
79
|
+
# The default implementation does nothing.
|
80
|
+
|
81
|
+
def amend_composed_sql(sql)
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
# Indicate if the access artifact (stored procedure, user defined function,
|
86
|
+
# or view) should be included in the logical schema.
|
87
|
+
#
|
88
|
+
# The default implementation always returns +true+.
|
89
|
+
|
90
|
+
def include_access_artifact?(artifact)
|
91
|
+
true
|
92
|
+
end
|
93
|
+
|
94
|
+
|
95
|
+
# Amend each included access artifact.
|
96
|
+
#
|
97
|
+
# _artifact_ - an XMigra::StoredProcedure, XMigra::Function, or XMigra::View
|
98
|
+
#
|
99
|
+
# The default implementation does nothing.
|
100
|
+
|
101
|
+
def amend_access_artifact(artifact)
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
# Yields additional access artifacts to include in the logical schema.
|
106
|
+
#
|
107
|
+
# _db_specifics_ - A module providing methods useful for building SQL
|
108
|
+
# specific to the target RDBMS.
|
109
|
+
#
|
110
|
+
# The yielded artifact objects must respond to +name+, +depends_on+ and
|
111
|
+
# +definition_sql+.
|
112
|
+
#
|
113
|
+
# The default implementation does not yield any objects.
|
114
|
+
|
115
|
+
def each_additional_access_artifact(db_specifics=nil) # :yields: artifact
|
116
|
+
end
|
117
|
+
|
118
|
+
|
119
|
+
# Indicate if the index should be included in the logical schema.
|
120
|
+
#
|
121
|
+
# The default implementation always return +true+.
|
122
|
+
|
123
|
+
def include_index?(index)
|
124
|
+
true
|
125
|
+
end
|
126
|
+
|
127
|
+
|
128
|
+
# Amend each included index.
|
129
|
+
#
|
130
|
+
# The default implementation does nothing.
|
131
|
+
|
132
|
+
def amend_index(index)
|
133
|
+
end
|
134
|
+
|
135
|
+
|
136
|
+
# Yields additional indexes to include in the logical schema.
|
137
|
+
#
|
138
|
+
# _db_specifics_ - A module providing methods useful for building SQL
|
139
|
+
# specific to the target RDBMS.
|
140
|
+
#
|
141
|
+
# The yielded index objects must respond to +name+ and +definition_sql+.
|
142
|
+
#
|
143
|
+
# The default implementation does not yield any objects.
|
144
|
+
|
145
|
+
def each_additional_index(db_specifics=nil) # :yields: index
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
data/lib/xmigra/program.rb
CHANGED
@@ -42,6 +42,9 @@ module XMigra
|
|
42
42
|
end
|
43
43
|
rescue TerminatingOption => stop
|
44
44
|
return stop
|
45
|
+
rescue Plugin::LoadingError => error
|
46
|
+
$stderr.puts error.message
|
47
|
+
exit 1
|
45
48
|
end
|
46
49
|
ensure
|
47
50
|
@active_subcommand = prev_subcommand
|
@@ -446,7 +449,7 @@ END_SECTION
|
|
446
449
|
begin; section['The "SCHEMA/database.yaml" File', <<END_SECTION]
|
447
450
|
|
448
451
|
The SCHEMA/database.yaml file consists of several sections that provide general
|
449
|
-
information about the database schema. The following
|
452
|
+
information about the database schema. The following subsections detail some
|
450
453
|
contents that may be included in this file.
|
451
454
|
|
452
455
|
system
|
@@ -491,6 +494,25 @@ metavariable that is used for access object definitions. The default value
|
|
491
494
|
is "[{filename}]" (excluding the quotation marks). If that string is required
|
492
495
|
in one or more access object definitions, this section allows the schema to
|
493
496
|
dictate another value.
|
497
|
+
|
498
|
+
XMigra plugin
|
499
|
+
-------------
|
500
|
+
|
501
|
+
If given, this section/entry provides a name to require into the XMigra
|
502
|
+
program (see documentation on Ruby's Kernel#require), with the intention that
|
503
|
+
the required file will define and activate an instance of a subclass of
|
504
|
+
XMigra::Plugin (see the documentation for XMigra::Plugin or
|
505
|
+
lib/xmigra/plugin.rb). Only one plugin may be specified, though that one
|
506
|
+
plugin may aggregate the functionality of other plugins.
|
507
|
+
|
508
|
+
Plugins are an advanced feature that can defeat many of the measures %program_name
|
509
|
+
takes to guarantee that a database generated from scratch will go through the
|
510
|
+
same sequence of changes as the production database(s) has/have. This can
|
511
|
+
happen even unintentionally, for instance by upgrading the gem that provides
|
512
|
+
the plugin. While the resulting script will still (if possible) be transacted,
|
513
|
+
the incompatibility may not be discovered until the script is run against a
|
514
|
+
production database, requiring cancellation of deployment. Use this feature
|
515
|
+
with extreme caution.
|
494
516
|
END_SECTION
|
495
517
|
end
|
496
518
|
begin; section['Script Generation Modes', <<END_SECTION]
|
@@ -626,6 +648,22 @@ END_SECTION
|
|
626
648
|
puts
|
627
649
|
end
|
628
650
|
|
651
|
+
subcommand 'init', "Interactively set up a source filesystem subtree" do |argv|
|
652
|
+
args, options = command_line(argv, {:edit=>true},
|
653
|
+
:help=> <<END_OF_HELP)
|
654
|
+
This command interactively asks for and records the information needed to set
|
655
|
+
up a filesystem subtree as a source for generating scripts.
|
656
|
+
END_OF_HELP
|
657
|
+
|
658
|
+
tool = SourceTreeInitializer.new(options.source_dir).extend(WarnToStderr)
|
659
|
+
|
660
|
+
file_paths = tool.create_files!
|
661
|
+
|
662
|
+
if options.edit
|
663
|
+
file_paths.each {|fpath| edit(fpath)}
|
664
|
+
end
|
665
|
+
end
|
666
|
+
|
629
667
|
subcommand 'new', "Create a new migration file" do |argv|
|
630
668
|
args, options = command_line(argv, {:edit=>true},
|
631
669
|
:argument_desc=>"MIGRATION_SUMMARY",
|
@@ -665,6 +703,7 @@ END_OF_HELP
|
|
665
703
|
"'%prog %cmd' does not take any arguments.")
|
666
704
|
|
667
705
|
sql_gen = SchemaUpdater.new(options.source_dir).extend(WarnToStderr)
|
706
|
+
sql_gen.load_plugin!
|
668
707
|
sql_gen.production = options.production
|
669
708
|
|
670
709
|
output_to(options.outfile) do |out_stream|
|
@@ -696,6 +735,7 @@ END_OF_HELP
|
|
696
735
|
"'%prog %cmd' does not take any arguments.")
|
697
736
|
|
698
737
|
sql_gen = SchemaUpdater.new(options.source_dir).extend(WarnToStderr)
|
738
|
+
sql_gen.load_plugin!
|
699
739
|
|
700
740
|
output_to(options.outfile) do |out_stream|
|
701
741
|
out_stream.print(sql_gen.reversion_script)
|
@@ -717,6 +757,7 @@ END_OF_HELP
|
|
717
757
|
"'%prog %cmd' must target an existing access object definition.")
|
718
758
|
|
719
759
|
sql_gen = SchemaUpdater.new(options.source_dir).extend(WarnToStderr)
|
760
|
+
sql_gen.load_plugin!
|
720
761
|
|
721
762
|
artifact = sql_gen.access_artifacts[args[0]] || sql_gen.access_artifacts.at_path(args[0])
|
722
763
|
output_to(options.outfile) do |out_stream|
|
@@ -895,6 +936,7 @@ END_OF_HELP
|
|
895
936
|
end
|
896
937
|
|
897
938
|
tool = SchemaUpdater.new(options.source_dir).extend(WarnToStderr)
|
939
|
+
tool.load_plugin!
|
898
940
|
|
899
941
|
output_to(options.outfile) do |out_stream|
|
900
942
|
tool.migrations.each do |migration|
|
@@ -949,6 +991,7 @@ END_OF_HELP
|
|
949
991
|
"'%prog %cmd' does not take any arguments.")
|
950
992
|
|
951
993
|
sql_gen = PermissionScriptWriter.new(options.source_dir).extend(WarnToStderr)
|
994
|
+
sql_gen.load_plugin!
|
952
995
|
|
953
996
|
output_to(options.outfile) do |out_stream|
|
954
997
|
out_stream.print(sql_gen.permissions_sql)
|
@@ -1,4 +1,6 @@
|
|
1
1
|
|
2
|
+
require 'xmigra/plugin'
|
3
|
+
|
2
4
|
module XMigra
|
3
5
|
module ReversionScriptBuilding
|
4
6
|
# This module is intended to be included into XMigra::SchemaUpdater
|
@@ -33,8 +35,12 @@ module XMigra
|
|
33
35
|
"database.\n",
|
34
36
|
].collect {|l| '-- ' + l + "\n"}.join('')
|
35
37
|
|
36
|
-
|
37
|
-
|
38
|
+
"".tap do |result|
|
39
|
+
result << usage_note + "========================================\n"
|
40
|
+
result << reversions.join("-- ================================== --\n")
|
41
|
+
|
42
|
+
Plugin.active.amend_composed_sql(result) if Plugin.active
|
43
|
+
end
|
38
44
|
end
|
39
45
|
end
|
40
46
|
end
|
@@ -8,6 +8,8 @@ module XMigra
|
|
8
8
|
STRUCTURE_SUBDIR = 'structure'
|
9
9
|
VERINC_FILE = 'branch-upgrade.yaml'
|
10
10
|
|
11
|
+
PLUGIN_KEY = 'XMigra plugin'
|
12
|
+
|
11
13
|
def initialize(path)
|
12
14
|
@path = Pathname.new(path)
|
13
15
|
@db_info = YAML.load_file(@path + DBINFO_FILE)
|
@@ -28,12 +30,20 @@ module XMigra
|
|
28
30
|
m.manages(path)
|
29
31
|
} || NoSpecifics
|
30
32
|
)
|
33
|
+
|
34
|
+
if @db_info.has_key? PLUGIN_KEY
|
35
|
+
@plugin = @db_info[PLUGIN_KEY]
|
36
|
+
end
|
31
37
|
end
|
32
38
|
|
33
|
-
attr_reader :path
|
39
|
+
attr_reader :path, :plugin
|
34
40
|
|
35
41
|
def branch_upgrade_file
|
36
42
|
@path.join(STRUCTURE_SUBDIR, VERINC_FILE)
|
37
43
|
end
|
44
|
+
|
45
|
+
def load_plugin!
|
46
|
+
Plugin.load! plugin if plugin
|
47
|
+
end
|
38
48
|
end
|
39
49
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
|
2
|
+
require 'xmigra/plugin'
|
2
3
|
require 'xmigra/schema_manipulator'
|
3
4
|
require 'xmigra/reversion_script_building'
|
4
5
|
|
@@ -77,6 +78,10 @@ RUNNING THIS SCRIPT ON A PRODUCTION DATABASE WILL FAIL.
|
|
77
78
|
check_working_copy!
|
78
79
|
|
79
80
|
intro_comment = @db_info.fetch('script comment', '')
|
81
|
+
if Plugin.active
|
82
|
+
intro_comment = intro_comment.dup
|
83
|
+
Plugin.active.amend_source_sql(intro_comment)
|
84
|
+
end
|
80
85
|
intro_comment << if production
|
81
86
|
sql_comment_block(vcs_information || "")
|
82
87
|
else
|
@@ -140,7 +145,9 @@ RUNNING THIS SCRIPT ON A PRODUCTION DATABASE WILL FAIL.
|
|
140
145
|
|
141
146
|
amend_script_parts(script_parts)
|
142
147
|
|
143
|
-
script_parts.map {|mn| self.send(mn)}.flatten.compact.join(ddl_block_separator)
|
148
|
+
script_parts.map {|mn| self.send(mn)}.flatten.compact.join(ddl_block_separator).tap do |result|
|
149
|
+
Plugin.active.amend_composed_sql(result) if Plugin.active
|
150
|
+
end
|
144
151
|
end
|
145
152
|
end
|
146
153
|
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'xmigra/console'
|
3
|
+
require 'xmigra/schema_manipulator'
|
4
|
+
|
5
|
+
module XMigra
|
6
|
+
class SourceTreeInitializer
|
7
|
+
class ConfigInfo
|
8
|
+
def initialize(root_path)
|
9
|
+
@root_path = Pathname(root_path)
|
10
|
+
@dbinfo = {}
|
11
|
+
@steps_after_dbinfo_creation = []
|
12
|
+
@created_files = []
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_reader :root_path, :dbinfo
|
16
|
+
|
17
|
+
def created_files
|
18
|
+
@created_files.dup
|
19
|
+
end
|
20
|
+
|
21
|
+
def created_file!(fpath)
|
22
|
+
@created_files << Pathname(fpath)
|
23
|
+
end
|
24
|
+
|
25
|
+
def after_dbinfo_creation(&blk)
|
26
|
+
@steps_after_dbinfo_creation << blk
|
27
|
+
end
|
28
|
+
|
29
|
+
def run_steps_after_dbinfo_creation!
|
30
|
+
@steps_after_dbinfo_creation.each do |step|
|
31
|
+
step.call
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def initialize(root_path)
|
37
|
+
@root_path = Pathname.new(root_path)
|
38
|
+
end
|
39
|
+
|
40
|
+
def dbinfo_path
|
41
|
+
@root_path + SchemaManipulator::DBINFO_FILE
|
42
|
+
end
|
43
|
+
|
44
|
+
def get_user_input_block(input_type)
|
45
|
+
puts "Input ends on a line containing nothing but a single '.'."
|
46
|
+
"".tap do |result|
|
47
|
+
while (line = $stdin.gets).strip != '.'
|
48
|
+
result << line
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def vcs_system
|
54
|
+
@vcs_system ||= VersionControlSupportModules.find do |m|
|
55
|
+
m.manages(@root_path)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def create_files!
|
60
|
+
schema_config = ConfigInfo.new(@root_path)
|
61
|
+
|
62
|
+
if vcs_system.nil?
|
63
|
+
puts "The indicated folder is not under version control. Some features"
|
64
|
+
puts "of this system require version control for full functionality. If"
|
65
|
+
puts "you later decide to use version control, you will need to configure"
|
66
|
+
puts "it without assistance from this script."
|
67
|
+
puts
|
68
|
+
unless Console.yes_no("Continue configuring schema management", :no)
|
69
|
+
return
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
db_system = Console::Menu.new(
|
74
|
+
"Supported Database Systems",
|
75
|
+
DatabaseSupportModules,
|
76
|
+
"Target system",
|
77
|
+
:get_name => lambda {|m| m::SYSTEM_NAME}
|
78
|
+
).get_selection
|
79
|
+
|
80
|
+
schema_config.dbinfo['system'] = db_system::SYSTEM_NAME
|
81
|
+
if db_system.respond_to? :init_schema
|
82
|
+
db_system.init_schema(schema_config)
|
83
|
+
end
|
84
|
+
|
85
|
+
if vcs_system.respond_to? :init_schema
|
86
|
+
vcs_system.init_schema(schema_config)
|
87
|
+
end
|
88
|
+
|
89
|
+
puts "Enter a script comment. This comment will be prepended to each"
|
90
|
+
puts "generated script exactly as given here."
|
91
|
+
script_comment = get_user_input_block('script comment').extend(LiteralYamlStyle)
|
92
|
+
schema_config.dbinfo['script comment'] = script_comment if script_comment != ''
|
93
|
+
|
94
|
+
schema_config.root_path.mkpath
|
95
|
+
|
96
|
+
dbinfo_path.open('w') do |dbinfo_io|
|
97
|
+
$xmigra_yamler.dump(schema_config.dbinfo, dbinfo_io)
|
98
|
+
end
|
99
|
+
schema_config.created_file! dbinfo_path
|
100
|
+
|
101
|
+
schema_config.run_steps_after_dbinfo_creation!
|
102
|
+
|
103
|
+
return schema_config.created_files
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'xmigra/console'
|
1
2
|
|
2
3
|
module XMigra
|
3
4
|
module GitSpecifics
|
@@ -6,6 +7,57 @@ module XMigra
|
|
6
7
|
MASTER_HEAD_ATTRIBUTE = 'xmigra-master'
|
7
8
|
MASTER_BRANCH_SUBDIR = 'xmigra-master'
|
8
9
|
|
10
|
+
class AttributesFile
|
11
|
+
def initialize(effect_root, access=:shared)
|
12
|
+
@effect_root = Pathname(effect_root)
|
13
|
+
@access = access
|
14
|
+
end
|
15
|
+
|
16
|
+
attr_reader :effect_root, :access
|
17
|
+
|
18
|
+
def file_relative_path
|
19
|
+
case @access
|
20
|
+
when :local
|
21
|
+
Pathname('.git/info/attributes')
|
22
|
+
else
|
23
|
+
Pathname('.gitattributes')
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def file_path
|
28
|
+
@effect_root + file_relative_path
|
29
|
+
end
|
30
|
+
|
31
|
+
def path_from(path)
|
32
|
+
file_path.relative_path_from(Pathname(path))
|
33
|
+
end
|
34
|
+
|
35
|
+
def description
|
36
|
+
"".tap do |result|
|
37
|
+
result << "#{path_from(Pathname.pwd)}"
|
38
|
+
|
39
|
+
chars = []
|
40
|
+
|
41
|
+
if file_path.exist?
|
42
|
+
chars << "exists"
|
43
|
+
end
|
44
|
+
|
45
|
+
case access
|
46
|
+
when :local
|
47
|
+
chars << "local"
|
48
|
+
end
|
49
|
+
|
50
|
+
unless chars.empty?
|
51
|
+
result << " (#{chars.join(', ')})"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def open(*args, &blk)
|
57
|
+
file_path.open(*args, &blk)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
9
61
|
class << self
|
10
62
|
def manages(path)
|
11
63
|
run_git(:status, :check_exit=>true, :quiet=>true)
|
@@ -50,6 +102,61 @@ module XMigra
|
|
50
102
|
end
|
51
103
|
return value_list[0]
|
52
104
|
end
|
105
|
+
|
106
|
+
def attributes_file_paths(path)
|
107
|
+
wdroot = Dir.chdir path do
|
108
|
+
Pathname(run_git('rev-parse', '--show-toplevel').strip).realpath
|
109
|
+
end
|
110
|
+
pwd = Pathname.pwd
|
111
|
+
|
112
|
+
[].tap do |result|
|
113
|
+
path.realpath.ascend do |dirpath|
|
114
|
+
result << AttributesFile.new(dirpath)
|
115
|
+
break if (wdroot <=> dirpath) >= 0
|
116
|
+
end
|
117
|
+
|
118
|
+
result << AttributesFile.new(wdroot, :local)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def get_master_url
|
123
|
+
print "Master repository URL (empty for none): "
|
124
|
+
master_repo = $stdin.gets.strip
|
125
|
+
return nil if master_repo.empty?
|
126
|
+
|
127
|
+
Console.validated_input "Master branch name" do |master_branch|
|
128
|
+
if master_branch.empty?
|
129
|
+
raise Console::InvalidInput.new(
|
130
|
+
"Master branch name required to set 'xmigra-master' attribute --"
|
131
|
+
)
|
132
|
+
end
|
133
|
+
"#{master_repo}##{master_branch}"
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def init_schema(schema_config)
|
138
|
+
Console.output_section "Git Integration" do
|
139
|
+
if master_url = get_master_url
|
140
|
+
# Select locations for .gitattributes or .git/info/attributes
|
141
|
+
attribs_file = Console::Menu.new(
|
142
|
+
"Git Attributes Files",
|
143
|
+
attributes_file_paths(schema_config.root_path),
|
144
|
+
"File for storing 'xmigra-master' attribute",
|
145
|
+
:get_name => lambda {|af| af.description}
|
146
|
+
).get_selection
|
147
|
+
|
148
|
+
dbinfo_path = schema_config.root_path + SchemaManipulator::DBINFO_FILE
|
149
|
+
attribute_pattern = "/#{dbinfo_path.relative_path_from(attribs_file.effect_root)}"
|
150
|
+
|
151
|
+
schema_config.after_dbinfo_creation do
|
152
|
+
attribs_file.open('a') do |attribs_io|
|
153
|
+
attribs_io.puts "#{attribute_pattern} xmigra-master=#{master_url}"
|
154
|
+
end
|
155
|
+
schema_config.created_file! attribs_file.file_path
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
53
160
|
end
|
54
161
|
|
55
162
|
def git(*args)
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'xmigra/console'
|
1
2
|
|
2
3
|
module XMigra
|
3
4
|
module SubversionSpecifics
|
@@ -26,7 +27,7 @@ module XMigra
|
|
26
27
|
cmd_parts = ["svn", subcmd.to_s]
|
27
28
|
cmd_parts << "--xml" unless no_result || raw_result
|
28
29
|
cmd_parts.concat(
|
29
|
-
args.collect {|a| '""'.insert(1, a)}
|
30
|
+
args.collect {|a| '""'.insert(1, a.to_s)}
|
30
31
|
)
|
31
32
|
cmd_str = cmd_parts.join(' ')
|
32
33
|
|
@@ -35,6 +36,31 @@ module XMigra
|
|
35
36
|
return output if raw_result && !no_result
|
36
37
|
return REXML::Document.new(output) unless no_result
|
37
38
|
end
|
39
|
+
|
40
|
+
def init_schema(schema_config)
|
41
|
+
Console.output_section "Subversion Integration" do
|
42
|
+
puts "Establishing a \"production pattern,\" a regular expression for"
|
43
|
+
puts "recognizing branch identifiers of branches used for production"
|
44
|
+
puts "script generation, simplifies the process of resolving conflicts"
|
45
|
+
puts "in the migration chain, should any arise."
|
46
|
+
puts
|
47
|
+
puts "No escaping (either shell or Ruby) of the regular expression is"
|
48
|
+
puts "necessary when entered here."
|
49
|
+
puts
|
50
|
+
puts "Common choices are:"
|
51
|
+
puts " ^trunk/"
|
52
|
+
puts " ^version/"
|
53
|
+
puts
|
54
|
+
print "Production pattern (empty to skip): "
|
55
|
+
|
56
|
+
production_pattern = $stdin.gets.chomp
|
57
|
+
return if production_pattern.empty?
|
58
|
+
schema_config.after_dbinfo_creation do
|
59
|
+
tool = SchemaManipulator.new(schema_config.root_path).extend(WarnToStderr)
|
60
|
+
tool.production_pattern = production_pattern
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
38
64
|
end
|
39
65
|
|
40
66
|
def subversion(*args)
|
data/lib/xmigra/version.rb
CHANGED
data/lib/xmigra.rb
CHANGED
@@ -319,6 +319,8 @@ require 'xmigra/stored_procedure'
|
|
319
319
|
require 'xmigra/view'
|
320
320
|
require 'xmigra/function'
|
321
321
|
|
322
|
+
require 'xmigra/plugin'
|
323
|
+
|
322
324
|
require 'xmigra/access_artifact_collection'
|
323
325
|
require 'xmigra/index'
|
324
326
|
require 'xmigra/index_collection'
|
@@ -330,6 +332,7 @@ require 'xmigra/schema_manipulator'
|
|
330
332
|
require 'xmigra/schema_updater'
|
331
333
|
require 'xmigra/new_migration_adder'
|
332
334
|
require 'xmigra/permission_script_writer'
|
335
|
+
require 'xmigra/source_tree_initializer'
|
333
336
|
|
334
337
|
require 'xmigra/program'
|
335
338
|
|
data/test/reversions.rb
CHANGED
@@ -1,33 +1,4 @@
|
|
1
1
|
|
2
|
-
def in_xmigra_schema
|
3
|
-
1.temp_dirs do |schema|
|
4
|
-
Dir.chdir(schema) do
|
5
|
-
initialize_xmigra_schema
|
6
|
-
yield
|
7
|
-
end
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
|
-
def add_migration(migration_name, reversion_sql=nil)
|
12
|
-
tool = XMigra::NewMigrationAdder.new('.')
|
13
|
-
mig_path = tool.add_migration migration_name
|
14
|
-
mig_chain = XMigra::MigrationChain.new('structure')
|
15
|
-
migration = mig_chain[-1]
|
16
|
-
unless reversion_sql.nil?
|
17
|
-
class <<migration
|
18
|
-
def reversion_tracking_sql
|
19
|
-
'-- TRACK REVERSION OF MIGRATION --'
|
20
|
-
end
|
21
|
-
end
|
22
|
-
rev_file = XMigra::RevertFile.new(migration)
|
23
|
-
rev_file.path.dirname.mkpath
|
24
|
-
rev_file.path.open('w') do |rev_stream|
|
25
|
-
rev_stream.puts reversion_sql
|
26
|
-
end
|
27
|
-
end
|
28
|
-
return migration
|
29
|
-
end
|
30
|
-
|
31
2
|
def add_migration_reversion_pair(migration_name, reversion_sql)
|
32
3
|
return [add_migration(migration_name, reversion_sql), reversion_sql]
|
33
4
|
end
|
@@ -78,7 +49,7 @@ run_test "Generated revisions script for one migration removes application recor
|
|
78
49
|
assert("Reversions script does not remove migration application record") {
|
79
50
|
XMigra::Program.run(['reversions'])
|
80
51
|
script = test_output
|
81
|
-
script =~ /DELETE\s+FROM\s+.?xmigra.?\..?applied.?\s+WHERE\s+.?MigrationID.?\s*=\s*'#{migration.id}'\s*;/
|
52
|
+
script =~ /DELETE\s+FROM\s+.?xmigra.?\..?applied.?\s+WHERE\s+.?MigrationID.?\s*=\s*'#{Regexp.escape migration.id}'\s*;/
|
82
53
|
}
|
83
54
|
end
|
84
55
|
end
|
data/test/runner.rb
CHANGED
@@ -13,6 +13,7 @@ TESTS = %w[
|
|
13
13
|
$:.unshift Pathname(__FILE__).expand_path.dirname.dirname + 'lib'
|
14
14
|
$:.unshift Pathname(__FILE__).expand_path.dirname.dirname
|
15
15
|
require 'xmigra'
|
16
|
+
require 'test/utils'
|
16
17
|
|
17
18
|
$test_count = 0
|
18
19
|
$test_successes = 0
|
@@ -68,40 +69,6 @@ def run_test(name, &block)
|
|
68
69
|
end
|
69
70
|
end
|
70
71
|
|
71
|
-
class Integer
|
72
|
-
def temp_dirs(prefix='')
|
73
|
-
tmpdirs = []
|
74
|
-
begin
|
75
|
-
(1..self).each do |i|
|
76
|
-
tmpdirs << Pathname(Dir.mktmpdir([prefix, ".#{i}"]))
|
77
|
-
end
|
78
|
-
|
79
|
-
yield(*tmpdirs)
|
80
|
-
ensure
|
81
|
-
tmpdirs.each do |dp|
|
82
|
-
begin
|
83
|
-
FileUtils.remove_entry dp
|
84
|
-
rescue
|
85
|
-
# Skip failure
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
def do_or_die(command, message=nil, exc_type=Exception)
|
93
|
-
output = `#{command}`
|
94
|
-
$?.success? || raise(exc_type, message || ("Unable to " + command + "\n" + output))
|
95
|
-
end
|
96
|
-
|
97
|
-
def initialize_xmigra_schema(path='.', options={})
|
98
|
-
(Pathname(path) + XMigra::SchemaManipulator::DBINFO_FILE).open('w') do |f|
|
99
|
-
YAML.dump({
|
100
|
-
'system' => $xmigra_test_system,
|
101
|
-
}.merge(options[:db_info] || {}), f)
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
72
|
def test_output
|
106
73
|
return nil unless $stdout.kind_of? StringIO
|
107
74
|
return $stdout.string
|
data/test/utils.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'ostruct'
|
3
|
+
require 'pathname'
|
4
|
+
require 'stringio'
|
5
|
+
require 'tmpdir'
|
6
|
+
|
7
|
+
class Integer
|
8
|
+
def temp_dirs(prefix='')
|
9
|
+
tmpdirs = []
|
10
|
+
begin
|
11
|
+
(1..self).each do |i|
|
12
|
+
tmpdirs << Pathname(Dir.mktmpdir([prefix, ".#{i}"]))
|
13
|
+
end
|
14
|
+
|
15
|
+
yield(*tmpdirs)
|
16
|
+
ensure
|
17
|
+
tmpdirs.each do |dp|
|
18
|
+
begin
|
19
|
+
FileUtils.remove_entry dp
|
20
|
+
rescue
|
21
|
+
# Skip failure
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def do_or_die(command, message=nil, exc_type=Exception)
|
29
|
+
output = `#{command}`
|
30
|
+
$?.success? || raise(exc_type, message || ("Unable to " + command + "\n" + output))
|
31
|
+
end
|
32
|
+
|
33
|
+
def initialize_xmigra_schema(path='.', options={})
|
34
|
+
(Pathname(path) + XMigra::SchemaManipulator::DBINFO_FILE).open('w') do |f|
|
35
|
+
YAML.dump({
|
36
|
+
'system' => $xmigra_test_system,
|
37
|
+
}.merge(options[:db_info] || {}), f)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def in_xmigra_schema
|
42
|
+
1.temp_dirs do |schema|
|
43
|
+
Dir.chdir(schema) do
|
44
|
+
initialize_xmigra_schema
|
45
|
+
yield
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def add_migration(migration_name, reversion_sql=nil)
|
51
|
+
tool = XMigra::NewMigrationAdder.new('.')
|
52
|
+
mig_path = tool.add_migration migration_name
|
53
|
+
mig_chain = XMigra::MigrationChain.new('structure')
|
54
|
+
migration = mig_chain[-1]
|
55
|
+
unless reversion_sql.nil?
|
56
|
+
class <<migration
|
57
|
+
def reversion_tracking_sql
|
58
|
+
'-- TRACK REVERSION OF MIGRATION --'
|
59
|
+
end
|
60
|
+
end
|
61
|
+
rev_file = XMigra::RevertFile.new(migration)
|
62
|
+
rev_file.path.dirname.mkpath
|
63
|
+
rev_file.path.open('w') do |rev_stream|
|
64
|
+
rev_stream.puts reversion_sql
|
65
|
+
end
|
66
|
+
end
|
67
|
+
return migration
|
68
|
+
end
|
69
|
+
|
70
|
+
def capture_stdout
|
71
|
+
old_stdout, $stdout = $stdout, StringIO.new
|
72
|
+
begin
|
73
|
+
yield
|
74
|
+
return $stdout.string
|
75
|
+
ensure
|
76
|
+
$stdout = old_stdout
|
77
|
+
end
|
78
|
+
end
|
data/xmigra.gemspec
CHANGED
@@ -13,7 +13,7 @@ Gem::Specification.new do |spec|
|
|
13
13
|
XMigra is a suite of tools for managing database schema evolution with
|
14
14
|
version controlled files. All database manipulations are written in
|
15
15
|
SQL (specific to the target database). Works with Git or Subversion.
|
16
|
-
Currently supports Microsoft SQL Server.
|
16
|
+
Currently supports Microsoft SQL Server and PostgreSQL.
|
17
17
|
END
|
18
18
|
spec.homepage = "https://github.com/rtweeks/xmigra"
|
19
19
|
spec.license = "CC-BY-SA 4.0 Itnl."
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: xmigra
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Next IT Corporation
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2015-
|
12
|
+
date: 2015-05-25 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -43,7 +43,7 @@ description: |2
|
|
43
43
|
XMigra is a suite of tools for managing database schema evolution with
|
44
44
|
version controlled files. All database manipulations are written in
|
45
45
|
SQL (specific to the target database). Works with Git or Subversion.
|
46
|
-
Currently supports Microsoft SQL Server.
|
46
|
+
Currently supports Microsoft SQL Server and PostgreSQL.
|
47
47
|
email:
|
48
48
|
- rtweeks21@gmail.com
|
49
49
|
executables:
|
@@ -61,6 +61,7 @@ files:
|
|
61
61
|
- lib/xmigra/access_artifact.rb
|
62
62
|
- lib/xmigra/access_artifact_collection.rb
|
63
63
|
- lib/xmigra/branch_upgrade.rb
|
64
|
+
- lib/xmigra/console.rb
|
64
65
|
- lib/xmigra/db_support/mssql.rb
|
65
66
|
- lib/xmigra/db_support/psql.rb
|
66
67
|
- lib/xmigra/function.rb
|
@@ -72,11 +73,13 @@ files:
|
|
72
73
|
- lib/xmigra/new_file.rb
|
73
74
|
- lib/xmigra/new_migration_adder.rb
|
74
75
|
- lib/xmigra/permission_script_writer.rb
|
76
|
+
- lib/xmigra/plugin.rb
|
75
77
|
- lib/xmigra/program.rb
|
76
78
|
- lib/xmigra/reversion_script_building.rb
|
77
79
|
- lib/xmigra/revert_file.rb
|
78
80
|
- lib/xmigra/schema_manipulator.rb
|
79
81
|
- lib/xmigra/schema_updater.rb
|
82
|
+
- lib/xmigra/source_tree_initializer.rb
|
80
83
|
- lib/xmigra/stored_procedure.rb
|
81
84
|
- lib/xmigra/utils.rb
|
82
85
|
- lib/xmigra/vcs_support/git.rb
|
@@ -86,6 +89,7 @@ files:
|
|
86
89
|
- test/git_vcs.rb
|
87
90
|
- test/reversions.rb
|
88
91
|
- test/runner.rb
|
92
|
+
- test/utils.rb
|
89
93
|
- xmigra.gemspec
|
90
94
|
homepage: https://github.com/rtweeks/xmigra
|
91
95
|
licenses:
|