xmigra 1.3.1 → 1.4.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/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:
|