pg_migrate 0.1.7 → 0.1.11
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/.gitignore +34 -34
- data/.gitmodules +3 -3
- data/Gemfile +9 -9
- data/Gemfile.lock +39 -39
- data/LICENSE +21 -21
- data/README.md +33 -33
- data/Rakefile +2 -2
- data/bin/pg_migrate +6 -6
- data/lib/pg_migrate/builder.rb +214 -214
- data/lib/pg_migrate/command_line.rb +242 -242
- data/lib/pg_migrate/config_parser.rb +48 -48
- data/lib/pg_migrate/manifest_reader.rb +102 -102
- data/lib/pg_migrate/migration.rb +11 -11
- data/lib/pg_migrate/migrator.rb +94 -94
- data/lib/pg_migrate/package.rb +152 -152
- data/lib/pg_migrate/package_templates/Gemfile.erb +3 -3
- data/lib/pg_migrate/package_templates/bin/migrate.rb +9 -9
- data/lib/pg_migrate/package_templates/gemspec.erb +21 -21
- data/lib/pg_migrate/package_templates/lib/gem/version.rb +3 -3
- data/lib/pg_migrate/package_templates/lib/gem.rb +12 -12
- data/lib/pg_migrate/props.rb +19 -19
- data/lib/pg_migrate/sql_reader.rb +51 -51
- data/lib/pg_migrate/templates/bootstrap.erb +175 -175
- data/lib/pg_migrate/templates/up.erb +30 -30
- data/lib/pg_migrate/util.rb +73 -73
- data/lib/pg_migrate/version.rb +3 -3
- data/lib/pg_migrate.rb +40 -40
- data/pg_migrate.gemspec +29 -28
- data/spec/database.yml +8 -8
- data/spec/pg_migrate/builder_spec.rb +113 -113
- data/spec/pg_migrate/command_line_spec.rb +53 -53
- data/spec/pg_migrate/config_parser_spec.rb +18 -18
- data/spec/pg_migrate/db_utility.rb +73 -73
- data/spec/pg_migrate/input_manifests/single_manifest/manifest +3 -3
- data/spec/pg_migrate/input_manifests/single_manifest/up/single1.sql +29 -29
- data/spec/pg_migrate/manifest_reader_spec.rb +19 -19
- data/spec/pg_migrate/migrator_spec.rb +68 -68
- data/spec/pg_migrate/package_spec.rb +38 -38
- data/spec/pg_migrate/sql_reader_spec.rb +21 -21
- data/spec/spec_helper.rb +15 -15
- metadata +5 -5
@@ -1,103 +1,103 @@
|
|
1
|
-
require 'pathname'
|
2
|
-
|
3
|
-
module PgMigrate
|
4
|
-
class ManifestReader
|
5
|
-
|
6
|
-
def initialize
|
7
|
-
@log = Logging.logger[self]
|
8
|
-
end
|
9
|
-
|
10
|
-
# returns array of migration paths
|
11
|
-
def load_input_manifest(manifest_path)
|
12
|
-
manifest, version = load_manifest(manifest_path, false)
|
13
|
-
return manifest
|
14
|
-
end
|
15
|
-
|
16
|
-
# returns [array of migration paths, version]
|
17
|
-
def load_output_manifest(manifest_path)
|
18
|
-
return load_manifest(manifest_path, true)
|
19
|
-
end
|
20
|
-
|
21
|
-
|
22
|
-
# verify that the migration files exist
|
23
|
-
def validate_migration_paths(manifest_path, manifest)
|
24
|
-
# each item in the manifest should be a valid file
|
25
|
-
manifest.each do |item|
|
26
|
-
item_path = build_migration_path(manifest_path, item.name)
|
27
|
-
if !Pathname.new(item_path).exist?
|
28
|
-
raise "manifest reference #{item.name} does not exist at path #{item_path}"
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
# construct a migration file path location based on the manifest basedir and the name of the migration
|
34
|
-
def build_migration_path(manifest_path, migration_name)
|
35
|
-
File.join(manifest_path, UP_DIRNAME, "#{migration_name}")
|
36
|
-
end
|
37
|
-
|
38
|
-
def hash_loaded_manifest(loaded_manifest)
|
39
|
-
hash = {}
|
40
|
-
loaded_manifest.each do |manifest|
|
41
|
-
hash[manifest.name] = manifest
|
42
|
-
end
|
43
|
-
return hash
|
44
|
-
end
|
45
|
-
|
46
|
-
# read in the manifest, saving each migration declaration in order as they are found
|
47
|
-
private
|
48
|
-
def load_manifest(manifest_path, is_output)
|
49
|
-
|
50
|
-
manifest = []
|
51
|
-
version = nil
|
52
|
-
|
53
|
-
manifest_filepath = File.join(manifest_path, MANIFEST_FILENAME)
|
54
|
-
|
55
|
-
@log.debug "loading manifest from #{manifest_filepath}"
|
56
|
-
|
57
|
-
# there should be a file called 'manifest' at this location
|
58
|
-
if !FileTest::exist?(manifest_filepath)
|
59
|
-
raise "ManifestReader: code=unloadable_manifest: manifest not found at #{manifest_filepath}"
|
60
|
-
end
|
61
|
-
|
62
|
-
manifest_lines = IO.readlines(manifest_filepath)
|
63
|
-
|
64
|
-
ordinal = 0
|
65
|
-
manifest_lines.each_with_index do |line, index|
|
66
|
-
migration_name = line.strip
|
67
|
-
|
68
|
-
@log.debug "processing line:#{index} #{line}"
|
69
|
-
|
70
|
-
# output files must have a version header as 1st line o file
|
71
|
-
if is_output
|
72
|
-
if index == 0
|
73
|
-
# the first line must be the version comment. if not, error out.
|
74
|
-
if migration_name.index(BUILDER_VERSION_HEADER) == 0 && migration_name.length > BUILDER_VERSION_HEADER.length
|
75
|
-
version = migration_name[BUILDER_VERSION_HEADER.length..-1]
|
76
|
-
@log.debug "manifest has builder_version #{version}"
|
77
|
-
else
|
78
|
-
raise "manifest invalid: missing/malformed version. expecting '# pg_migrate-VERSION' to begin first line '#{line}' of manifest file: '#{manifest_filepath}'"
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
# ignore comments
|
84
|
-
if migration_name.empty? or migration_name.start_with?('#')
|
85
|
-
# ignored!
|
86
|
-
else
|
87
|
-
@log.debug "adding migration #{migration_name} with ordinal #{ordinal}"
|
88
|
-
manifest.push(Migration.new(migration_name, ordinal, build_migration_path(manifest_path, migration_name)))
|
89
|
-
ordinal += 1
|
90
|
-
end
|
91
|
-
|
92
|
-
# the logic above wouldn't get upset with an empty manifest
|
93
|
-
if is_output
|
94
|
-
if version.nil?
|
95
|
-
raise "manifest invalid: empty"
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|
99
|
-
return manifest, version
|
100
|
-
end
|
101
|
-
|
102
|
-
end
|
1
|
+
require 'pathname'
|
2
|
+
|
3
|
+
module PgMigrate
|
4
|
+
class ManifestReader
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@log = Logging.logger[self]
|
8
|
+
end
|
9
|
+
|
10
|
+
# returns array of migration paths
|
11
|
+
def load_input_manifest(manifest_path)
|
12
|
+
manifest, version = load_manifest(manifest_path, false)
|
13
|
+
return manifest
|
14
|
+
end
|
15
|
+
|
16
|
+
# returns [array of migration paths, version]
|
17
|
+
def load_output_manifest(manifest_path)
|
18
|
+
return load_manifest(manifest_path, true)
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
# verify that the migration files exist
|
23
|
+
def validate_migration_paths(manifest_path, manifest)
|
24
|
+
# each item in the manifest should be a valid file
|
25
|
+
manifest.each do |item|
|
26
|
+
item_path = build_migration_path(manifest_path, item.name)
|
27
|
+
if !Pathname.new(item_path).exist?
|
28
|
+
raise "manifest reference #{item.name} does not exist at path #{item_path}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# construct a migration file path location based on the manifest basedir and the name of the migration
|
34
|
+
def build_migration_path(manifest_path, migration_name)
|
35
|
+
File.join(manifest_path, UP_DIRNAME, "#{migration_name}")
|
36
|
+
end
|
37
|
+
|
38
|
+
def hash_loaded_manifest(loaded_manifest)
|
39
|
+
hash = {}
|
40
|
+
loaded_manifest.each do |manifest|
|
41
|
+
hash[manifest.name] = manifest
|
42
|
+
end
|
43
|
+
return hash
|
44
|
+
end
|
45
|
+
|
46
|
+
# read in the manifest, saving each migration declaration in order as they are found
|
47
|
+
private
|
48
|
+
def load_manifest(manifest_path, is_output)
|
49
|
+
|
50
|
+
manifest = []
|
51
|
+
version = nil
|
52
|
+
|
53
|
+
manifest_filepath = File.join(manifest_path, MANIFEST_FILENAME)
|
54
|
+
|
55
|
+
@log.debug "loading manifest from #{manifest_filepath}"
|
56
|
+
|
57
|
+
# there should be a file called 'manifest' at this location
|
58
|
+
if !FileTest::exist?(manifest_filepath)
|
59
|
+
raise "ManifestReader: code=unloadable_manifest: manifest not found at #{manifest_filepath}"
|
60
|
+
end
|
61
|
+
|
62
|
+
manifest_lines = IO.readlines(manifest_filepath)
|
63
|
+
|
64
|
+
ordinal = 0
|
65
|
+
manifest_lines.each_with_index do |line, index|
|
66
|
+
migration_name = line.strip
|
67
|
+
|
68
|
+
@log.debug "processing line:#{index} #{line}"
|
69
|
+
|
70
|
+
# output files must have a version header as 1st line o file
|
71
|
+
if is_output
|
72
|
+
if index == 0
|
73
|
+
# the first line must be the version comment. if not, error out.
|
74
|
+
if migration_name.index(BUILDER_VERSION_HEADER) == 0 && migration_name.length > BUILDER_VERSION_HEADER.length
|
75
|
+
version = migration_name[BUILDER_VERSION_HEADER.length..-1]
|
76
|
+
@log.debug "manifest has builder_version #{version}"
|
77
|
+
else
|
78
|
+
raise "manifest invalid: missing/malformed version. expecting '# pg_migrate-VERSION' to begin first line '#{line}' of manifest file: '#{manifest_filepath}'"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# ignore comments
|
84
|
+
if migration_name.empty? or migration_name.start_with?('#')
|
85
|
+
# ignored!
|
86
|
+
else
|
87
|
+
@log.debug "adding migration #{migration_name} with ordinal #{ordinal}"
|
88
|
+
manifest.push(Migration.new(migration_name, ordinal, build_migration_path(manifest_path, migration_name)))
|
89
|
+
ordinal += 1
|
90
|
+
end
|
91
|
+
|
92
|
+
# the logic above wouldn't get upset with an empty manifest
|
93
|
+
if is_output
|
94
|
+
if version.nil?
|
95
|
+
raise "manifest invalid: empty"
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
return manifest, version
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
103
|
end
|
data/lib/pg_migrate/migration.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
|
-
module PgMigrate
|
2
|
-
class Migration
|
3
|
-
attr_accessor :name, :ordinal, :created, :production, :filepath
|
4
|
-
|
5
|
-
def initialize(name, ordinal, filepath)
|
6
|
-
@name = name
|
7
|
-
@ordinal = ordinal
|
8
|
-
@filepath = filepath
|
9
|
-
end
|
10
|
-
|
11
|
-
end
|
1
|
+
module PgMigrate
|
2
|
+
class Migration
|
3
|
+
attr_accessor :name, :ordinal, :created, :production, :filepath
|
4
|
+
|
5
|
+
def initialize(name, ordinal, filepath)
|
6
|
+
@name = name
|
7
|
+
@ordinal = ordinal
|
8
|
+
@filepath = filepath
|
9
|
+
end
|
10
|
+
|
11
|
+
end
|
12
12
|
end
|
data/lib/pg_migrate/migrator.rb
CHANGED
@@ -1,94 +1,94 @@
|
|
1
|
-
module PgMigrate
|
2
|
-
|
3
|
-
class Migrator
|
4
|
-
|
5
|
-
attr_accessor :conn, :connection_hash, :manifest_path, :manifest, :manifest_reader, :sql_reader
|
6
|
-
|
7
|
-
# options = gem 'pg' connection_hash options, or connstring => dbname=test port=5432, or pgconn => PG::Connection object
|
8
|
-
def initialize(manifest_reader, sql_reader, options = {})
|
9
|
-
@log = Logging.logger[self]
|
10
|
-
@connection_hash = options
|
11
|
-
@manifest = nil
|
12
|
-
@builder_version = nil
|
13
|
-
@manifest_reader = manifest_reader
|
14
|
-
@sql_reader = sql_reader
|
15
|
-
end
|
16
|
-
|
17
|
-
# 'migrate' attempt to migrate your database based on the contents of your built manifest
|
18
|
-
# The manifest_path argument should point to your manifest
|
19
|
-
# manifest_path = the directory containing your 'manifest' file, 'up' directory, 'down' directory, 'test' directory
|
20
|
-
# this method will throw an exception if anything goes wrong (such as bad SQL in the migrations themselves)
|
21
|
-
def migrate(manifest_path)
|
22
|
-
@manifest_path = manifest_path
|
23
|
-
|
24
|
-
@conn = Util::get_conn(@connection_hash)
|
25
|
-
|
26
|
-
# this is used to record the version of the 'migrator' in the pg_migrate table
|
27
|
-
@conn.exec("SET application_name = 'pg_migrate_ruby-#{PgMigrate::VERSION}'")
|
28
|
-
|
29
|
-
# load the manifest, and version of the builder that made it
|
30
|
-
process_manifest()
|
31
|
-
|
32
|
-
# execute the migrations
|
33
|
-
run_migrations(@manifest)
|
34
|
-
end
|
35
|
-
|
36
|
-
|
37
|
-
# load the manifest's migration declarations, and validate that each migration points to a real file
|
38
|
-
def process_manifest
|
39
|
-
@manifest, @builder_version = @manifest_reader.load_output_manifest(@manifest_path)
|
40
|
-
@manifest_reader.validate_migration_paths(@manifest_path, @manifest)
|
41
|
-
end
|
42
|
-
|
43
|
-
# run all necessary migrations
|
44
|
-
def run_migrations(manifest)
|
45
|
-
|
46
|
-
# run bootstrap before user migrations to prepare database
|
47
|
-
run_bootstrap
|
48
|
-
|
49
|
-
# loop through the manifest, executing migrations in turn
|
50
|
-
manifest.each_with_index do |migration, index|
|
51
|
-
execute_migration(migration.name, migration.filepath)
|
52
|
-
end
|
53
|
-
|
54
|
-
end
|
55
|
-
|
56
|
-
# executes the bootstrap method
|
57
|
-
def run_bootstrap
|
58
|
-
bootstrap = File.join(@manifest_path, UP_DIRNAME, BOOTSTRAP_FILENAME)
|
59
|
-
execute_migration('bootstrap.sql', bootstrap)
|
60
|
-
end
|
61
|
-
|
62
|
-
# execute a single migration by loading it's statements from file, and then executing each
|
63
|
-
def execute_migration(name, filepath)
|
64
|
-
@log.info "executing migration #{filepath}"
|
65
|
-
|
66
|
-
statements = @sql_reader.load_migration(filepath)
|
67
|
-
if statements.length == 0
|
68
|
-
raise 'no statements found in migration #{filepath}'
|
69
|
-
end
|
70
|
-
run_migration(name, statements)
|
71
|
-
end
|
72
|
-
|
73
|
-
# execute all the statements of a single migration
|
74
|
-
def run_migration(name, statements)
|
75
|
-
|
76
|
-
begin
|
77
|
-
statements.each do |statement|
|
78
|
-
conn.exec(statement).clear
|
79
|
-
end
|
80
|
-
rescue Exception => e
|
81
|
-
# we make a special allowance for one exception; it just means this migration
|
82
|
-
# has already occurred, and we should just treat it like a continue
|
83
|
-
if e.message.index('pg_migrate: code=migration_exists').nil?
|
84
|
-
conn.exec("ROLLBACK")
|
85
|
-
raise e
|
86
|
-
else
|
87
|
-
conn.exec("ROLLBACK")
|
88
|
-
@log.info "migration #{name} already run"
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
1
|
+
module PgMigrate
|
2
|
+
|
3
|
+
class Migrator
|
4
|
+
|
5
|
+
attr_accessor :conn, :connection_hash, :manifest_path, :manifest, :manifest_reader, :sql_reader
|
6
|
+
|
7
|
+
# options = gem 'pg' connection_hash options, or connstring => dbname=test port=5432, or pgconn => PG::Connection object
|
8
|
+
def initialize(manifest_reader, sql_reader, options = {})
|
9
|
+
@log = Logging.logger[self]
|
10
|
+
@connection_hash = options
|
11
|
+
@manifest = nil
|
12
|
+
@builder_version = nil
|
13
|
+
@manifest_reader = manifest_reader
|
14
|
+
@sql_reader = sql_reader
|
15
|
+
end
|
16
|
+
|
17
|
+
# 'migrate' attempt to migrate your database based on the contents of your built manifest
|
18
|
+
# The manifest_path argument should point to your manifest
|
19
|
+
# manifest_path = the directory containing your 'manifest' file, 'up' directory, 'down' directory, 'test' directory
|
20
|
+
# this method will throw an exception if anything goes wrong (such as bad SQL in the migrations themselves)
|
21
|
+
def migrate(manifest_path)
|
22
|
+
@manifest_path = manifest_path
|
23
|
+
|
24
|
+
@conn = Util::get_conn(@connection_hash)
|
25
|
+
|
26
|
+
# this is used to record the version of the 'migrator' in the pg_migrate table
|
27
|
+
@conn.exec("SET application_name = 'pg_migrate_ruby-#{PgMigrate::VERSION}'")
|
28
|
+
|
29
|
+
# load the manifest, and version of the builder that made it
|
30
|
+
process_manifest()
|
31
|
+
|
32
|
+
# execute the migrations
|
33
|
+
run_migrations(@manifest)
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
# load the manifest's migration declarations, and validate that each migration points to a real file
|
38
|
+
def process_manifest
|
39
|
+
@manifest, @builder_version = @manifest_reader.load_output_manifest(@manifest_path)
|
40
|
+
@manifest_reader.validate_migration_paths(@manifest_path, @manifest)
|
41
|
+
end
|
42
|
+
|
43
|
+
# run all necessary migrations
|
44
|
+
def run_migrations(manifest)
|
45
|
+
|
46
|
+
# run bootstrap before user migrations to prepare database
|
47
|
+
run_bootstrap
|
48
|
+
|
49
|
+
# loop through the manifest, executing migrations in turn
|
50
|
+
manifest.each_with_index do |migration, index|
|
51
|
+
execute_migration(migration.name, migration.filepath)
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
# executes the bootstrap method
|
57
|
+
def run_bootstrap
|
58
|
+
bootstrap = File.join(@manifest_path, UP_DIRNAME, BOOTSTRAP_FILENAME)
|
59
|
+
execute_migration('bootstrap.sql', bootstrap)
|
60
|
+
end
|
61
|
+
|
62
|
+
# execute a single migration by loading it's statements from file, and then executing each
|
63
|
+
def execute_migration(name, filepath)
|
64
|
+
@log.info "executing migration #{filepath}"
|
65
|
+
|
66
|
+
statements = @sql_reader.load_migration(filepath)
|
67
|
+
if statements.length == 0
|
68
|
+
raise 'no statements found in migration #{filepath}'
|
69
|
+
end
|
70
|
+
run_migration(name, statements)
|
71
|
+
end
|
72
|
+
|
73
|
+
# execute all the statements of a single migration
|
74
|
+
def run_migration(name, statements)
|
75
|
+
|
76
|
+
begin
|
77
|
+
statements.each do |statement|
|
78
|
+
conn.exec(statement).clear
|
79
|
+
end
|
80
|
+
rescue Exception => e
|
81
|
+
# we make a special allowance for one exception; it just means this migration
|
82
|
+
# has already occurred, and we should just treat it like a continue
|
83
|
+
if e.message.index('pg_migrate: code=migration_exists').nil?
|
84
|
+
conn.exec("ROLLBACK")
|
85
|
+
raise e
|
86
|
+
else
|
87
|
+
conn.exec("ROLLBACK")
|
88
|
+
@log.info "migration #{name} already run"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|