migration_bundler 1.3.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 +7 -0
- data/.bundle/config +2 -0
- data/.gitignore +3 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +49 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +122 -0
- data/Guardfile +8 -0
- data/LICENSE +202 -0
- data/README.md +219 -0
- data/Rakefile +16 -0
- data/bin/mb +6 -0
- data/lib/migration_bundler/actions.rb +38 -0
- data/lib/migration_bundler/cli.rb +355 -0
- data/lib/migration_bundler/databases/abstract_database.rb +54 -0
- data/lib/migration_bundler/databases/cassandra_database.rb +88 -0
- data/lib/migration_bundler/databases/sqlite_database.rb +116 -0
- data/lib/migration_bundler/migrations.rb +52 -0
- data/lib/migration_bundler/project.rb +90 -0
- data/lib/migration_bundler/targets/base.rb +85 -0
- data/lib/migration_bundler/targets/cassandra/cassandra_target.rb +73 -0
- data/lib/migration_bundler/targets/cassandra/create_schema_migrations.cql.erb +16 -0
- data/lib/migration_bundler/targets/cassandra/migration.cql.erb +0 -0
- data/lib/migration_bundler/targets/cocoapods/cocoapods_target.rb +43 -0
- data/lib/migration_bundler/targets/cocoapods/podspec.erb +12 -0
- data/lib/migration_bundler/targets/maven/maven_target.rb +62 -0
- data/lib/migration_bundler/targets/maven/project/.gitignore +6 -0
- data/lib/migration_bundler/targets/maven/project/MonkeyButler.iml +19 -0
- data/lib/migration_bundler/targets/maven/project/build.gradle +54 -0
- data/lib/migration_bundler/targets/sqlite/create_migration_bundler_tables.sql.erb +15 -0
- data/lib/migration_bundler/targets/sqlite/migration.sql.erb +11 -0
- data/lib/migration_bundler/targets/sqlite/sqlite_target.rb +92 -0
- data/lib/migration_bundler/templates/Gemfile.erb +4 -0
- data/lib/migration_bundler/templates/gitignore.erb +1 -0
- data/lib/migration_bundler/util.rb +71 -0
- data/lib/migration_bundler/version.rb +3 -0
- data/lib/migration_bundler.rb +1 -0
- data/migration_bundler.gemspec +33 -0
- data/spec/cli_spec.rb +700 -0
- data/spec/databases/cassandra_database_spec.rb +260 -0
- data/spec/databases/sqlite_database_spec.rb +198 -0
- data/spec/migrations_spec.rb +4 -0
- data/spec/project_spec.rb +128 -0
- data/spec/sandbox/cassandra/.gitignore +2 -0
- data/spec/sandbox/cassandra/.migration_bundler.yml +9 -0
- data/spec/sandbox/cassandra/migrations/20140523123443021_create_sandbox.cql.sql +14 -0
- data/spec/sandbox/cassandra/sandbox.cql +0 -0
- data/spec/sandbox/sqlite/.gitignore +2 -0
- data/spec/sandbox/sqlite/.migration_bundler.yml +9 -0
- data/spec/sandbox/sqlite/migrations/20140523123443021_create_sandbox.sql +14 -0
- data/spec/sandbox/sqlite/sandbox.sql +0 -0
- data/spec/spec_helper.rb +103 -0
- data/spec/targets/cassandra_target_spec.rb +191 -0
- data/spec/targets/cocoapods_target_spec.rb +197 -0
- data/spec/targets/maven_target_spec.rb +156 -0
- data/spec/targets/sqlite_target_spec.rb +103 -0
- data/spec/util_spec.rb +13 -0
- metadata +260 -0
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
Bundler.setup
|
3
|
+
|
4
|
+
require 'migration_bundler'
|
5
|
+
|
6
|
+
require 'rspec/core/shared_context'
|
7
|
+
require 'tempfile'
|
8
|
+
require 'digest'
|
9
|
+
require 'byebug'
|
10
|
+
|
11
|
+
module GlobalContext
|
12
|
+
extend RSpec::Core::SharedContext
|
13
|
+
|
14
|
+
# Assume that most specs will describe a Thor subclass
|
15
|
+
let(:thor_class) { subject.class }
|
16
|
+
end
|
17
|
+
|
18
|
+
RSpec.configure do |config|
|
19
|
+
config.before do
|
20
|
+
ARGV.replace []
|
21
|
+
MigrationBundler::Project.clear
|
22
|
+
end
|
23
|
+
|
24
|
+
config.include GlobalContext
|
25
|
+
|
26
|
+
def capture(stream)
|
27
|
+
begin
|
28
|
+
stream = stream.to_s
|
29
|
+
eval "$#{stream} = StringIO.new"
|
30
|
+
yield
|
31
|
+
result = eval("$#{stream}").string
|
32
|
+
ensure
|
33
|
+
eval("$#{stream} = #{stream.upcase}")
|
34
|
+
end
|
35
|
+
|
36
|
+
result
|
37
|
+
end
|
38
|
+
|
39
|
+
def capture_output(proc, &block)
|
40
|
+
error = nil
|
41
|
+
content = capture(:stdout) do
|
42
|
+
error = capture(:stderr) do
|
43
|
+
proc.call
|
44
|
+
end
|
45
|
+
end
|
46
|
+
yield content, error if block_given?
|
47
|
+
{ stdout: content, stderr: error }
|
48
|
+
end
|
49
|
+
|
50
|
+
# def source_root
|
51
|
+
# File.join(File.dirname(__FILE__), "fixtures")
|
52
|
+
# end
|
53
|
+
|
54
|
+
def sandbox_root
|
55
|
+
Pathname.new File.join(File.dirname(__FILE__), "sandbox")
|
56
|
+
end
|
57
|
+
|
58
|
+
def clone_temp_sandbox(database = :sqlite)
|
59
|
+
Dir.mktmpdir.tap do |path|
|
60
|
+
FileUtils.cp_r Dir.glob(sandbox_root + "#{database}/."), path
|
61
|
+
Dir.chdir(path) do
|
62
|
+
system("git init -q .")
|
63
|
+
system("git remote add origin git@github.com:layerhq/migration_bundler_sandbox.git")
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def random_migration_name
|
69
|
+
timestamp = MigrationBundler::Util.migration_timestamp + rand(1..1000)
|
70
|
+
MigrationBundler::Util.migration_named(Digest::SHA256.hexdigest(Time.now.to_s), timestamp)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Requires `thor_class` and `project_root`
|
74
|
+
def invoke!(args = [], options = {:capture => true})
|
75
|
+
output = nil
|
76
|
+
# Some commands work with a directory that doesn't yet exist
|
77
|
+
dir = File.exists?(project_root) ? project_root : '.'
|
78
|
+
Dir.chdir(dir) do
|
79
|
+
if options[:capture]
|
80
|
+
output = capture_output(proc { thor_class.start(args) })
|
81
|
+
else
|
82
|
+
thor_class.start(args)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def invoke_target!(command, options = {})
|
88
|
+
output = nil
|
89
|
+
# Some commands work with a directory that doesn't yet exist
|
90
|
+
dir = File.exists?(project_root) ? project_root : '.'
|
91
|
+
Dir.chdir(dir) do
|
92
|
+
if options.delete(:capture)
|
93
|
+
output = capture_output proc do
|
94
|
+
target = thor_class.new([], options)
|
95
|
+
target.invoke(command)
|
96
|
+
end
|
97
|
+
else
|
98
|
+
target = thor_class.new([], options)
|
99
|
+
target.invoke(command)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,191 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'migration_bundler/targets/cassandra/cassandra_target'
|
3
|
+
require 'migration_bundler/databases/cassandra_database'
|
4
|
+
|
5
|
+
describe MigrationBundler::Targets::CassandraTarget do
|
6
|
+
let(:thor_class) { MigrationBundler::CLI }
|
7
|
+
let!(:project_root) { clone_temp_sandbox(:cassandra) }
|
8
|
+
let(:project) { MigrationBundler::Project.load(project_root) }
|
9
|
+
let(:database) { MigrationBundler::Databases::CassandraDatabase.new(project.database_url) }
|
10
|
+
|
11
|
+
before(:each) do
|
12
|
+
database.drop
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "#init" do
|
16
|
+
let(:path) do
|
17
|
+
Dir.mktmpdir.tap { |path| FileUtils.remove_entry(path) }
|
18
|
+
end
|
19
|
+
|
20
|
+
it "generates a new migration with the given name" do
|
21
|
+
invoke!(["init", "-d", "cassandra://localhost:9170/sandbox", path])
|
22
|
+
migration = Dir.entries(File.join(path, 'migrations')).detect { |f| f =~ /\d{15}_create_.+\.cql/ }
|
23
|
+
migration.should_not be_nil
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "#new" do
|
28
|
+
it "generates a new migration with the given name" do
|
29
|
+
invoke!(%w{new add_column_to_table})
|
30
|
+
migration = Dir.entries(File.join(project_root, 'migrations')).detect { |f| f =~ /add_column_to_table\.cql/ }
|
31
|
+
migration.should_not be_nil
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "#dump" do
|
36
|
+
before(:each) do
|
37
|
+
database = MigrationBundler::Databases::CassandraDatabase.new(project.database_url)
|
38
|
+
database.create_migrations_table
|
39
|
+
database.insert_version(123)
|
40
|
+
end
|
41
|
+
|
42
|
+
it "dumps the database" do
|
43
|
+
invoke!(%w{dump})
|
44
|
+
content = File.read(File.join(project_root, project.schema_path))
|
45
|
+
content.should =~ /CREATE KEYSPACE sandbox/
|
46
|
+
end
|
47
|
+
|
48
|
+
it "dumps the schema_migrations rows" do
|
49
|
+
invoke!(%w{dump})
|
50
|
+
content = File.read(File.join(project_root, project.schema_path))
|
51
|
+
content.should =~ /INSERT INTO schema_migrations \(partition_key, version\) VALUES \(0, 123\);/
|
52
|
+
end
|
53
|
+
|
54
|
+
context "when there are extra keyspaces specified" do
|
55
|
+
before(:each) do
|
56
|
+
database.client.execute "CREATE KEYSPACE IF NOT EXISTS extra1 WITH replication = {'class' : 'SimpleStrategy', 'replication_factor' : 1};"
|
57
|
+
database.client.execute "CREATE KEYSPACE IF NOT EXISTS extra2 WITH replication = {'class' : 'SimpleStrategy', 'replication_factor' : 1};"
|
58
|
+
end
|
59
|
+
|
60
|
+
it "dumps the additional keyspaces" do
|
61
|
+
project.config['cassandra.keyspaces'] = %w{extra1 extra2}
|
62
|
+
project.save!(project_root)
|
63
|
+
invoke!(%w{dump})
|
64
|
+
content = File.read(File.join(project_root, project.schema_path))
|
65
|
+
content.should =~ /CREATE KEYSPACE extra1/
|
66
|
+
content.should =~ /CREATE KEYSPACE extra2/
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe "#load" do
|
72
|
+
before(:each) do
|
73
|
+
@database = MigrationBundler::Databases::CassandraDatabase.new(project.database_url)
|
74
|
+
@database.create_migrations_table
|
75
|
+
invoke!(%w{dump})
|
76
|
+
@database.drop
|
77
|
+
end
|
78
|
+
|
79
|
+
it "loads the database" do
|
80
|
+
expect(@database.migrations_table?).to be_false
|
81
|
+
invoke!(%w{load})
|
82
|
+
expect(@database.migrations_table?).to be_true
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe "#status" do
|
87
|
+
context "when the database is empty" do
|
88
|
+
before(:each) do
|
89
|
+
database.drop
|
90
|
+
end
|
91
|
+
|
92
|
+
it "shows status" do
|
93
|
+
output = invoke!(%w{status})
|
94
|
+
output[:stdout].should =~ /New database/
|
95
|
+
output[:stdout].should =~ /The database at 'cassandra:\/\/localhost:9042\/sandbox' does not have a 'schema_migrations' table./
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
context "when the database is up to date" do
|
100
|
+
before(:each) do
|
101
|
+
database.create_migrations_table
|
102
|
+
database.insert_version(12345)
|
103
|
+
end
|
104
|
+
|
105
|
+
it "tells the user the database is up to date" do
|
106
|
+
output = invoke!(%w{status})
|
107
|
+
output[:stdout].should =~ /Current version: 12345/
|
108
|
+
output[:stdout].should =~ /Database is up to date./
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
context "when there are migrations to apply" do
|
113
|
+
before(:each) do
|
114
|
+
File.open(File.join(project_root, 'migrations/20140523123443021_add_another_table.cql'), 'w') do |f|
|
115
|
+
f << "CREATE TABLE IF NOT EXISTS sandbox.another_table (some_column VARINT, PRIMARY KEY (some_column));"
|
116
|
+
end
|
117
|
+
database.create_migrations_table
|
118
|
+
database.insert_version(12345)
|
119
|
+
end
|
120
|
+
|
121
|
+
it "tells the user there is a migration to apply" do
|
122
|
+
output = invoke!(%w{status})
|
123
|
+
output[:stdout].should =~ /The database at 'cassandra:\/\/localhost:9042\/sandbox' is 1 version behind 20140523123443021/
|
124
|
+
output[:stdout].should =~ /pending migration: migrations\/20140523123443021_add_another_table.cql/
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
describe "#migrate" do
|
130
|
+
context "when the database is empty" do
|
131
|
+
before(:each) do
|
132
|
+
database.drop
|
133
|
+
end
|
134
|
+
|
135
|
+
it "shows migrate" do
|
136
|
+
output = invoke!(%w{migrate})
|
137
|
+
output[:stdout].should =~ /Database is up to date./
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
context "when the database is up to date" do
|
142
|
+
before(:each) do
|
143
|
+
database.create_migrations_table
|
144
|
+
database.insert_version(12345)
|
145
|
+
end
|
146
|
+
|
147
|
+
it "tells the user the database is up to date" do
|
148
|
+
output = invoke!(%w{migrate})
|
149
|
+
output[:stdout].should =~ /Database is up to date./
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
context "when there are migrations to apply" do
|
154
|
+
before(:each) do
|
155
|
+
File.open(File.join(project_root, 'migrations/20140523123443021_add_another_table.cql'), 'w') do |f|
|
156
|
+
f << "CREATE TABLE IF NOT EXISTS sandbox.another_table (some_column VARINT, PRIMARY KEY (some_column));"
|
157
|
+
end
|
158
|
+
database.create_migrations_table
|
159
|
+
database.insert_version(12345)
|
160
|
+
end
|
161
|
+
|
162
|
+
it "tells the user there is a migration to apply" do
|
163
|
+
output = invoke!(%w{migrate})
|
164
|
+
output[:stdout].should =~ /applying migration: migrations\/20140523123443021_add_another_table.cql/
|
165
|
+
output[:stdout].should =~ /Migration to version 20140523123443021 complete./
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
describe "#drop" do
|
171
|
+
before(:each) do
|
172
|
+
database.create_migrations_table
|
173
|
+
database.insert_version(123)
|
174
|
+
database.client.execute "CREATE KEYSPACE IF NOT EXISTS extra1 WITH replication = {'class' : 'SimpleStrategy', 'replication_factor' : 1};"
|
175
|
+
database.client.execute "CREATE KEYSPACE IF NOT EXISTS extra2 WITH replication = {'class' : 'SimpleStrategy', 'replication_factor' : 1};"
|
176
|
+
project.config['cassandra.keyspaces'] = %w{extra1 extra2}
|
177
|
+
project.save!(project_root)
|
178
|
+
end
|
179
|
+
|
180
|
+
it "drops all keyspaces" do
|
181
|
+
invoke!(%w{drop})
|
182
|
+
database.client.use(:system)
|
183
|
+
rows = database.client.execute('SELECT keyspace_name FROM schema_columnfamilies')
|
184
|
+
keyspaces = []
|
185
|
+
rows.each { |row| keyspaces << row['keyspace_name'] }
|
186
|
+
keyspaces.should_not include('sandbox')
|
187
|
+
keyspaces.should_not include('extra1')
|
188
|
+
keyspaces.should_not include('extra2')
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
@@ -0,0 +1,197 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'migration_bundler/targets/cocoapods/cocoapods_target'
|
3
|
+
|
4
|
+
module Pod
|
5
|
+
class Spec
|
6
|
+
attr_accessor :name, :version, :summary, :homepage, :author, :source, :license, :resource_bundles, :requires_arc
|
7
|
+
|
8
|
+
def initialize(hash = {})
|
9
|
+
yield self if block_given?
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe MigrationBundler::Targets::CocoapodsTarget do
|
15
|
+
let(:thor_class) { MigrationBundler::Targets::CocoapodsTarget }
|
16
|
+
let!(:project_root) { clone_temp_sandbox }
|
17
|
+
|
18
|
+
before(:each) do
|
19
|
+
Dir.chdir(project_root) do
|
20
|
+
system("git config user.email developer@layer.com")
|
21
|
+
system("git config user.name 'Layer Developer'")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "#init" do
|
26
|
+
it "asks for the name of the cocoapods repository" do
|
27
|
+
remove_cocoapods_repo_from_config
|
28
|
+
expect(Thor::LineEditor).to receive(:readline).with("What is the name of your Cocoapods specs repo? ", {}).and_return("layerhq")
|
29
|
+
invoke!(['init'])
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe '#generate' do
|
34
|
+
let!(:podspec) do
|
35
|
+
invoke!(['generate'])
|
36
|
+
eval(File.read(File.join(project_root, 'sandbox.podspec')))
|
37
|
+
end
|
38
|
+
|
39
|
+
it "has name" do
|
40
|
+
expect(podspec.name).to eq('sandbox')
|
41
|
+
end
|
42
|
+
|
43
|
+
it "has version" do
|
44
|
+
expect(podspec.version).to eq('20140523123443021')
|
45
|
+
end
|
46
|
+
|
47
|
+
it "has summary" do
|
48
|
+
expect(podspec.summary).to eq('Packages the database schema and migrations for sandbox')
|
49
|
+
end
|
50
|
+
|
51
|
+
it "has homepage" do
|
52
|
+
expect(podspec.homepage).to eq('http://github.com/layerhq')
|
53
|
+
end
|
54
|
+
|
55
|
+
it "has author" do
|
56
|
+
expect(podspec.author).to eq({"Layer Developer"=>"developer@layer.com"})
|
57
|
+
end
|
58
|
+
|
59
|
+
it "has source" do
|
60
|
+
expect(podspec.source).to eq({:git=>"git@github.com:layerhq/migration_bundler_sandbox.git", :tag=>"20140523123443021"})
|
61
|
+
end
|
62
|
+
|
63
|
+
it "has license" do
|
64
|
+
expect(podspec.license).to eq('Commercial')
|
65
|
+
end
|
66
|
+
|
67
|
+
it "has resource_bundles" do
|
68
|
+
expect(podspec.resource_bundles).to eq({"sandbox"=>["migrations/*", "sandbox.sql"]})
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe "#push" do
|
73
|
+
context "when cocoapods.repo is not configured" do
|
74
|
+
it "fails with an error" do
|
75
|
+
remove_cocoapods_repo_from_config
|
76
|
+
output = invoke!(['push'])
|
77
|
+
output[:stderr].should =~ /Invalid configuration: cocoapods.repo is not configured./
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
context "when cocoapods.repo is configured" do
|
82
|
+
it "invokes pod push" do
|
83
|
+
output = invoke!(['push', '--pretend'])
|
84
|
+
output[:stdout].should =~ /run\s+pod repo push --allow-warnings example_specs_repo sandbox.podspec/
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe "#validate" do
|
90
|
+
context "when the cocoapods repo is not configured" do
|
91
|
+
before(:each) do
|
92
|
+
remove_cocoapods_repo_from_config
|
93
|
+
end
|
94
|
+
|
95
|
+
it "fails with error" do
|
96
|
+
output = invoke!(['validate'])
|
97
|
+
output[:stderr].should =~ /Invalid configuration: cocoapods.repo is not configured./
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
describe "#push" do
|
103
|
+
context "when there is no repo configured" do
|
104
|
+
before(:each) do
|
105
|
+
remove_cocoapods_repo_from_config
|
106
|
+
end
|
107
|
+
|
108
|
+
it "fails validation" do
|
109
|
+
output = invoke!(['push'])
|
110
|
+
output[:stderr].should =~ /Invalid configuration: cocoapods.repo is not configured./
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
context "when there is a repo configured" do
|
115
|
+
def with_temporary_cocoapods_repo
|
116
|
+
path = Dir.mktmpdir
|
117
|
+
temp_specs_repo_at_path(File.join(path, 'master'))
|
118
|
+
temp_specs_repo_at_path(File.join(path, 'example_specs_repo'))
|
119
|
+
|
120
|
+
ENV['CP_REPOS_DIR'] = path
|
121
|
+
yield path
|
122
|
+
ENV.delete('CP_REPOS_DIR')
|
123
|
+
end
|
124
|
+
|
125
|
+
it "pushes to cocoapods" do
|
126
|
+
git_repo_path = path_for_temp_bare_git_repo
|
127
|
+
Dir.chdir(project_root) do
|
128
|
+
`git remote set-url origin file://#{git_repo_path}`
|
129
|
+
end
|
130
|
+
|
131
|
+
# Generate the podspec
|
132
|
+
invoke!(['generate'])
|
133
|
+
|
134
|
+
Dir.chdir(project_root) do
|
135
|
+
`git remote set-url origin file://#{git_repo_path}`
|
136
|
+
`git add .`
|
137
|
+
`git commit --no-status -m 'Adding files' .`
|
138
|
+
`git tag 20140523123443021`
|
139
|
+
`git push -q origin master --tags`
|
140
|
+
end
|
141
|
+
|
142
|
+
with_temporary_cocoapods_repo do |repo_path|
|
143
|
+
output = invoke!(%w{push --quiet})
|
144
|
+
|
145
|
+
Dir.chdir(repo_path) do
|
146
|
+
pushed_podspec_path = File.join(repo_path, 'example_specs_repo', 'sandbox', '20140523123443021', 'sandbox.podspec')
|
147
|
+
File.exists?(pushed_podspec_path).should be_true
|
148
|
+
content = File.read(pushed_podspec_path)
|
149
|
+
content.should =~ /20140523123443021/
|
150
|
+
content.should =~ /resource_bundles/
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def remove_cocoapods_repo_from_config
|
158
|
+
yaml_path = File.join(project_root, '.migration_bundler.yml')
|
159
|
+
project = YAML.load(File.read(yaml_path))
|
160
|
+
project['config'].delete 'cocoapods.repo'
|
161
|
+
File.open(yaml_path, 'w') { |f| f << YAML.dump(project) }
|
162
|
+
end
|
163
|
+
|
164
|
+
def path_for_temp_bare_git_repo
|
165
|
+
git_repo_path = Dir.mktmpdir
|
166
|
+
Dir.chdir(git_repo_path) do
|
167
|
+
`git init --bare`
|
168
|
+
end
|
169
|
+
git_repo_path
|
170
|
+
end
|
171
|
+
|
172
|
+
def temp_specs_repo_at_path(path)
|
173
|
+
FileUtils.mkdir_p path
|
174
|
+
Dir.chdir(path) do
|
175
|
+
run("git init -q .")
|
176
|
+
raise "Failed to init git repo" unless $?.exitstatus.zero?
|
177
|
+
run("echo '' > README.md")
|
178
|
+
run("git add README.md")
|
179
|
+
run("git commit --no-status -m 'Add README.md' .")
|
180
|
+
raise "Failed to touch README.md" unless $?.exitstatus.zero?
|
181
|
+
remote_url = "file://#{path_for_temp_bare_git_repo}"
|
182
|
+
run("git remote add origin #{remote_url}")
|
183
|
+
raise "Failed to set origin remote url" unless $?.exitstatus.zero?
|
184
|
+
run("git push -q -u origin master")
|
185
|
+
raise "Failed to push to temp repo" unless $?.exitstatus.zero?
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
def run(command, echo = false)
|
190
|
+
if echo
|
191
|
+
puts "Executing `#{command}`"
|
192
|
+
system(command)
|
193
|
+
else
|
194
|
+
`#{command}`
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
@@ -0,0 +1,156 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'migration_bundler/targets/base'
|
3
|
+
require 'migration_bundler/targets/maven/maven_target'
|
4
|
+
|
5
|
+
describe MigrationBundler::Targets::MavenTarget do
|
6
|
+
let(:thor_class) { MigrationBundler::Targets::MavenTarget }
|
7
|
+
let!(:project_root) { clone_temp_sandbox }
|
8
|
+
|
9
|
+
describe "#init" do
|
10
|
+
it "asks for Maven Java repository URL and credentials" do
|
11
|
+
remove_maven_repo_from_config
|
12
|
+
remove_maven_username_from_config
|
13
|
+
remove_maven_password_from_config
|
14
|
+
expect(Thor::LineEditor).to receive(:readline).with("What is the URL of your Java Maven repo? ", {}).and_return(File.join(project_root, "maven"))
|
15
|
+
expect(Thor::LineEditor).to receive(:readline).with("What is the username for your Java Maven repo? ", {}).and_return("none")
|
16
|
+
expect(Thor::LineEditor).to receive(:readline).with("What is the password for your Java Maven repo? ", {}).and_return("none")
|
17
|
+
invoke!(['init'])
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '#generate' do
|
22
|
+
before(:all) do
|
23
|
+
# Put config into the YAML
|
24
|
+
yaml_path = File.join(project_root, '.migration_bundler.yml')
|
25
|
+
project = YAML.load(File.read(yaml_path))
|
26
|
+
project['config']['maven.url'] = 'maven'
|
27
|
+
project['config']['maven.username'] = 'none'
|
28
|
+
project['config']['maven.password'] = 'none'
|
29
|
+
File.open(yaml_path, 'w') { |f| f << YAML.dump(project) }
|
30
|
+
|
31
|
+
invoke!(['generate', '--quiet'])
|
32
|
+
end
|
33
|
+
|
34
|
+
# Shadow the let! declarations so we can use before(:all)
|
35
|
+
def thor_class
|
36
|
+
MigrationBundler::Targets::MavenTarget
|
37
|
+
end
|
38
|
+
|
39
|
+
def project_root
|
40
|
+
@project_root ||= clone_temp_sandbox
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should have project/build.gradle" do
|
44
|
+
expect(File.directory?(File.join(project_root, 'project'))).to eq(true)
|
45
|
+
expect(File.file?(File.join(project_root, 'project/build.gradle'))).to eq(true)
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should have a schema file in resources/schema" do
|
49
|
+
expect(File.directory?(File.join(project_root, 'project/src/main/resources/schema'))).to eq(true)
|
50
|
+
expect(File.file?(File.join(project_root, 'project/src/main/resources/schema/schema.sql'))).to eq(true)
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should have migration files in resources/migrations" do
|
54
|
+
expect(File.directory?(File.join(project_root, 'project/src/main/resources/migrations'))).to eq(true)
|
55
|
+
expect(Dir.entries(File.join(project_root, 'project/src/main/resources/migrations')).size).not_to eq(2)
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should have project/build/libs/MigrationBundler-[version].jar files" do
|
59
|
+
expect(File.file?(built_jar_path)).to eq(true)
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should have a schema packaged in the JAR" do
|
63
|
+
jar_schema = `jar tf #{built_jar_path} | grep -e schema/.*[.]sql`.strip
|
64
|
+
expect(jar_schema).to eq("schema/schema.sql")
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should have all migrations packaged in the JAR" do
|
68
|
+
# Create a hash of all expected migrations
|
69
|
+
expected_migrations = Hash.new
|
70
|
+
Dir.foreach(File.join(project_root, "migrations")) do |entry|
|
71
|
+
if !entry.end_with? ".sql" then
|
72
|
+
next
|
73
|
+
end
|
74
|
+
expected_migrations[entry] = true
|
75
|
+
end
|
76
|
+
|
77
|
+
# Create a array of all migrations packaged in the JAR
|
78
|
+
jar_migrations = `jar tf #{built_jar_path} | grep -e migrations/.*[.]sql`
|
79
|
+
jar_migration_array = jar_migrations.split(/\r?\n/)
|
80
|
+
|
81
|
+
# Verify that the expected and actual have the same size
|
82
|
+
expect(jar_migration_array.length).to eq(expected_migrations.length)
|
83
|
+
|
84
|
+
# Verify that all JAR'd migrations were expected
|
85
|
+
jar_migration_array.each { |jar_migration|
|
86
|
+
length = jar_migration.length - "migrations/".length
|
87
|
+
migration = jar_migration[-length, length]
|
88
|
+
expect(expected_migrations[migration]).to eq(true)
|
89
|
+
}
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should clear java project between `generate` invocations" do
|
93
|
+
# Create a random file in the project directory.
|
94
|
+
yaml_path = File.join(project_root, '.migration_bundler.yml')
|
95
|
+
project = YAML.load(File.read(yaml_path))
|
96
|
+
File.open(File.join(project_root, 'project/deleteme.txt'), 'w') { |f| f << YAML.dump(project) }
|
97
|
+
expect(File.file?(File.join(project_root, 'project/deleteme.txt'))).to eq(true)
|
98
|
+
invoke!(['generate', '--quiet'])
|
99
|
+
expect(File.file?(File.join(project_root, 'project/deleteme.txt'))).to eq(false)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
describe '#push' do
|
104
|
+
before(:each) do
|
105
|
+
set_config
|
106
|
+
invoke!(%w{generate --quiet})
|
107
|
+
invoke!(%w{push --quiet})
|
108
|
+
end
|
109
|
+
|
110
|
+
it "should have MigrationBundler-[version].jar file in the local Maven" do
|
111
|
+
expect(File.file?(maven_jar_path)).to eq(true)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
private
|
116
|
+
|
117
|
+
def remove_maven_repo_from_config
|
118
|
+
yaml_path = File.join(project_root, '.migration_bundler.yml')
|
119
|
+
project = YAML.load(File.read(yaml_path))
|
120
|
+
project['config'].delete 'maven.url'
|
121
|
+
File.open(yaml_path, 'w') { |f| f << YAML.dump(project) }
|
122
|
+
end
|
123
|
+
|
124
|
+
def remove_maven_username_from_config
|
125
|
+
yaml_path = File.join(project_root, '.migration_bundler.yml')
|
126
|
+
project = YAML.load(File.read(yaml_path))
|
127
|
+
project['config'].delete 'maven.username'
|
128
|
+
File.open(yaml_path, 'w') { |f| f << YAML.dump(project) }
|
129
|
+
end
|
130
|
+
|
131
|
+
def remove_maven_password_from_config
|
132
|
+
yaml_path = File.join(project_root, '.migration_bundler.yml')
|
133
|
+
project = YAML.load(File.read(yaml_path))
|
134
|
+
project['config'].delete 'maven.password'
|
135
|
+
File.open(yaml_path, 'w') { |f| f << YAML.dump(project) }
|
136
|
+
end
|
137
|
+
|
138
|
+
def set_config
|
139
|
+
remove_maven_repo_from_config
|
140
|
+
remove_maven_username_from_config
|
141
|
+
remove_maven_password_from_config
|
142
|
+
expect(Thor::LineEditor).to receive(:readline).with("What is the URL of your Java Maven repo? ", {}).and_return(File.join(project_root, "maven"))
|
143
|
+
expect(Thor::LineEditor).to receive(:readline).with("What is the username for your Java Maven repo? ", {}).and_return("none")
|
144
|
+
expect(Thor::LineEditor).to receive(:readline).with("What is the password for your Java Maven repo? ", {}).and_return("none")
|
145
|
+
invoke!(['init'])
|
146
|
+
end
|
147
|
+
|
148
|
+
def maven_jar_path
|
149
|
+
File.join(project_root, "maven/com/layer/MigrationBundler/20140523123443021/MigrationBundler-20140523123443021.jar")
|
150
|
+
end
|
151
|
+
|
152
|
+
def built_jar_path
|
153
|
+
File.join(project_root, "project/build/libs/MigrationBundler-20140523123443021.jar")
|
154
|
+
end
|
155
|
+
|
156
|
+
end
|