physique 0.3.10 → 0.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.
Files changed (109) hide show
  1. checksums.yaml +4 -4
  2. data/FLUENT_MIGRATOR.md +300 -300
  3. data/Gemfile +3 -3
  4. data/Gemfile.lock +51 -11
  5. data/Guardfile +34 -0
  6. data/README.md +368 -368
  7. data/RUBY_SETUP.md +14 -14
  8. data/Rakefile +1 -1
  9. data/lib/physique.rb +22 -22
  10. data/lib/physique/config.rb +23 -23
  11. data/lib/physique/dsl.rb +46 -46
  12. data/lib/physique/project_path_resolver.rb +45 -45
  13. data/lib/physique/solution.rb +72 -72
  14. data/lib/physique/task_builders/build.rb +76 -69
  15. data/lib/physique/task_builders/default.rb +19 -19
  16. data/lib/physique/task_builders/fluent_migrator.rb +302 -299
  17. data/lib/physique/task_builders/nuget.rb +18 -6
  18. data/lib/physique/task_builders/octopus.rb +4 -2
  19. data/lib/physique/task_builders/publish_nugets.rb +5 -3
  20. data/lib/physique/task_builders/test.rb +4 -4
  21. data/lib/physique/tasks/fluent_migrator.rb +158 -158
  22. data/lib/physique/tasks/nugets_pack.rb +12 -12
  23. data/lib/physique/tasks/octopus_pack.rb +1 -1
  24. data/lib/physique/tasks/sqlcmd.rb +109 -109
  25. data/lib/physique/tasks/versionizer.rb +112 -112
  26. data/lib/physique/tasks_builder.rb +59 -59
  27. data/lib/physique/tool_locator.rb +50 -50
  28. data/lib/physique/version.rb +3 -3
  29. data/physique.gemspec +29 -28
  30. data/spec/fluent_migrator_config_spec.rb +39 -39
  31. data/spec/project_spec.rb +42 -42
  32. data/spec/publish_nugets_spec.rb +40 -40
  33. data/spec/solution_spec.rb +11 -11
  34. data/spec/sqlcmd_spec.rb +146 -146
  35. data/spec/support/shared_contexts/rake.rb +20 -21
  36. data/spec/task_builders/build_spec.rb +25 -25
  37. data/spec/task_builders/default_spec.rb +17 -17
  38. data/spec/task_builders/fluent_migrator_spec.rb +70 -68
  39. data/spec/task_builders/nspec_spec.rb +13 -13
  40. data/spec/task_builders/nuget_spec.rb +16 -16
  41. data/spec/test_data/solutions/.nuget/NuGet.Config +5 -5
  42. data/spec/test_data/solutions/.nuget/NuGet.exe +0 -0
  43. data/spec/test_data/solutions/.nuget/NuGet.targets +144 -144
  44. data/spec/test_data/solutions/.nuget/packages.config +3 -3
  45. data/spec/test_data/solutions/basic/.semver +6 -6
  46. data/spec/test_data/solutions/basic/Basic.Tests/Basic.Tests.csproj +58 -58
  47. data/spec/test_data/solutions/basic/Basic.Tests/Class1.cs +14 -14
  48. data/spec/test_data/solutions/basic/Basic.Tests/Properties/AssemblyInfo.cs +36 -36
  49. data/spec/test_data/solutions/basic/Basic.Tests/packages.config +3 -3
  50. data/spec/test_data/solutions/basic/Basic.sln +28 -28
  51. data/spec/test_data/solutions/basic/Basic/Basic.csproj +52 -52
  52. data/spec/test_data/solutions/basic/Basic/Class1.cs +12 -12
  53. data/spec/test_data/solutions/basic/Basic/Properties/AssemblyInfo.cs +36 -36
  54. data/spec/test_data/solutions/basic/Rakefile.rb +10 -10
  55. data/spec/test_data/solutions/basic/packages.config +3 -3
  56. data/spec/test_data/solutions/fluent-migrator/.semver +6 -6
  57. data/spec/test_data/solutions/fluent-migrator/Basic.Migrations/Migrations/.gitkeep +0 -0
  58. data/spec/test_data/solutions/fluent-migrator/Basic.Migrations/Properties/AssemblyInfo.cs +36 -36
  59. data/spec/test_data/solutions/fluent-migrator/Basic.Migrations/TestMigration.cs +19 -19
  60. data/spec/test_data/solutions/fluent-migrator/Basic.Migrations/_Scripts/create.sql +1 -1
  61. data/spec/test_data/solutions/fluent-migrator/Basic.Migrations/_Scripts/drop.sql +4 -4
  62. data/spec/test_data/solutions/fluent-migrator/Basic.Migrations/_Scripts/seed.sql +1 -1
  63. data/spec/test_data/solutions/fluent-migrator/Basic.Migrations/packages.config +3 -3
  64. data/spec/test_data/solutions/fluent-migrator/Basic.Specs/Basic.Specs.csproj +59 -59
  65. data/spec/test_data/solutions/fluent-migrator/Basic.Specs/Class1.cs +12 -12
  66. data/spec/test_data/solutions/fluent-migrator/Basic.Specs/DebuggerShim.cs +42 -42
  67. data/spec/test_data/solutions/fluent-migrator/Basic.Specs/Properties/AssemblyInfo.cs +36 -36
  68. data/spec/test_data/solutions/fluent-migrator/Basic.Specs/packages.config +3 -3
  69. data/spec/test_data/solutions/fluent-migrator/Basic.sln +39 -39
  70. data/spec/test_data/solutions/fluent-migrator/Basic/Basic.csproj +55 -55
  71. data/spec/test_data/solutions/fluent-migrator/Basic/Class1.cs +12 -12
  72. data/spec/test_data/solutions/fluent-migrator/Basic/Properties/AssemblyInfo.cs +36 -36
  73. data/spec/test_data/solutions/fluent-migrator/Basic/packages.config +2 -2
  74. data/spec/test_data/solutions/fluent-migrator/Rakefile.rb +20 -20
  75. data/spec/test_data/solutions/multiple-fluent-migrator/.semver +6 -6
  76. data/spec/test_data/solutions/multiple-fluent-migrator/Basic.Migrations1/Basic.Migrations1.csproj +52 -52
  77. data/spec/test_data/solutions/multiple-fluent-migrator/Basic.Migrations1/Class1.cs +12 -12
  78. data/spec/test_data/solutions/multiple-fluent-migrator/Basic.Migrations1/Properties/AssemblyInfo.cs +36 -36
  79. data/spec/test_data/solutions/multiple-fluent-migrator/Basic.Migrations2/Basic.Migrations2.csproj +52 -52
  80. data/spec/test_data/solutions/multiple-fluent-migrator/Basic.Migrations2/Class1.cs +12 -12
  81. data/spec/test_data/solutions/multiple-fluent-migrator/Basic.Migrations2/Properties/AssemblyInfo.cs +36 -36
  82. data/spec/test_data/solutions/multiple-fluent-migrator/Basic.Specs/Basic.Specs.csproj +59 -59
  83. data/spec/test_data/solutions/multiple-fluent-migrator/Basic.Specs/Class1.cs +12 -12
  84. data/spec/test_data/solutions/multiple-fluent-migrator/Basic.Specs/DebuggerShim.cs +42 -42
  85. data/spec/test_data/solutions/multiple-fluent-migrator/Basic.Specs/Properties/AssemblyInfo.cs +36 -36
  86. data/spec/test_data/solutions/multiple-fluent-migrator/Basic.Specs/packages.config +3 -3
  87. data/spec/test_data/solutions/multiple-fluent-migrator/Basic.sln +45 -45
  88. data/spec/test_data/solutions/multiple-fluent-migrator/Basic/Basic.csproj +55 -55
  89. data/spec/test_data/solutions/multiple-fluent-migrator/Basic/Class1.cs +12 -12
  90. data/spec/test_data/solutions/multiple-fluent-migrator/Basic/Properties/AssemblyInfo.cs +36 -36
  91. data/spec/test_data/solutions/multiple-fluent-migrator/Basic/packages.config +2 -2
  92. data/spec/test_data/solutions/multiple-fluent-migrator/Rakefile.rb +28 -28
  93. data/spec/test_data/solutions/nspec/.semver +6 -6
  94. data/spec/test_data/solutions/nspec/Basic.Specs/Basic.Specs.csproj +59 -59
  95. data/spec/test_data/solutions/nspec/Basic.Specs/Class1.cs +12 -12
  96. data/spec/test_data/solutions/nspec/Basic.Specs/DebuggerShim.cs +42 -42
  97. data/spec/test_data/solutions/nspec/Basic.Specs/Properties/AssemblyInfo.cs +36 -36
  98. data/spec/test_data/solutions/nspec/Basic.Specs/packages.config +3 -3
  99. data/spec/test_data/solutions/nspec/Basic.sln +28 -28
  100. data/spec/test_data/solutions/nspec/Basic/Basic.csproj +55 -55
  101. data/spec/test_data/solutions/nspec/Basic/Class1.cs +12 -12
  102. data/spec/test_data/solutions/nspec/Basic/Properties/AssemblyInfo.cs +36 -36
  103. data/spec/test_data/solutions/nspec/Basic/packages.config +2 -2
  104. data/spec/test_data/solutions/nspec/Rakefile.rb +14 -14
  105. data/spec/test_data/tool_locator/Program Files/Microsoft SQL Server/110/Tools/Binn/SQLCMD.exe.txt b/data/spec/test_data/tool_locator/Program Files/Microsoft SQL → Server/110/Tools/Binn/SQLCMD.exe.txt +0 -0
  106. data/spec/test_data/tool_locator/Windows/Microsoft.NET/Framework/v3.5/MSBuild.exe.txt +0 -0
  107. data/spec/test_data/tool_locator/Windows/Microsoft.NET/Framework/v4.0.30319/MSBuild.exe.txt +0 -0
  108. data/spec/tool_locator_spec.rb +27 -29
  109. metadata +33 -16
@@ -1,69 +1,76 @@
1
- module Physique
2
- class CompileConfig
3
- attr_writer :default_targets, # Default build targets for compile task
4
- :configuration, # Build configuration (Release, Debug, etc.)
5
- :logging # MSBuild Logging level (normal, verbose, etc.)
6
-
7
- def initialize
8
- @default_targets = %w(Clean Rebuild)
9
- @targets = %w(Clean Build Rebuild)
10
- end
11
-
12
- def clear_targets
13
- @targets.clear
14
- end
15
-
16
- def add_target(val)
17
- @targets << val
18
- end
19
-
20
- def opts
21
- raise ArgumentError, 'You must specify the default targets' if @default_targets.blank?
22
-
23
- Map.new({
24
- default_targets: @default_targets,
25
- configuration: @configuration,
26
- logging: @logging,
27
- targets: @targets
28
- }).apply(
29
- configuration: 'Release',
30
- logging: 'minimal'
31
- )
32
- end
33
- end
34
-
35
- class BuildTasksBuilder < TasksBuilder
36
- def build_tasks
37
- add_version_task
38
- add_compile_tasks
39
- end
40
-
41
- private
42
-
43
- def add_version_task
44
- require 'albacore/tasks/versionizer'
45
- Albacore::Tasks::Versionizer.new :versionizer
46
- end
47
-
48
- def add_compile_tasks
49
- block = lambda &method(:configure_build)
50
-
51
- desc 'Builds the solution'
52
- build :compile => [ :restore ], &block.curry.(solution.compile.default_targets)
53
-
54
- namespace :compile do
55
- solution.compile.targets.each do |t|
56
- desc "Builds the solution using the #{t} target"
57
- build t.downcase, &block.curry.(t)
58
- end
59
- end
60
- end
61
-
62
- def configure_build(target, config)
63
- config.sln = solution.file
64
- config.prop 'Configuration', solution.compile.configuration
65
- config.logging = solution.compile.logging
66
- config.target = target
67
- end
68
- end
69
- end
1
+ module Physique
2
+ class CompileConfig
3
+ attr_writer :default_targets, # Default build targets for compile task
4
+ :configuration, # Build configuration (Release, Debug, etc.)
5
+ :logging # MSBuild Logging level (normal, verbose, etc.)
6
+
7
+ def initialize
8
+ @default_targets = %w(Rebuild)
9
+ @targets = %w(Clean Build Rebuild)
10
+ end
11
+
12
+ def disable_versioning
13
+ @disable_versioning = true
14
+ end
15
+
16
+ def clear_targets
17
+ @targets.clear
18
+ end
19
+
20
+ def add_target(val)
21
+ @targets << val
22
+ end
23
+
24
+ def opts
25
+ raise ArgumentError, 'You must specify the default targets' if @default_targets.blank?
26
+
27
+ Map.new({
28
+ default_targets: @default_targets,
29
+ configuration: @configuration,
30
+ logging: @logging,
31
+ targets: @targets,
32
+ disable_versioning: !!@disable_versioning
33
+ }).apply(
34
+ configuration: 'Release',
35
+ logging: 'minimal'
36
+ )
37
+ end
38
+ end
39
+
40
+ class BuildTasksBuilder < TasksBuilder
41
+ def build_tasks
42
+ add_version_task
43
+ add_compile_tasks
44
+ end
45
+
46
+ private
47
+
48
+ def add_version_task
49
+ return if solution.compile.disable_versioning
50
+
51
+ require 'albacore/tasks/versionizer'
52
+ Albacore::Tasks::Versionizer.new :versionizer
53
+ end
54
+
55
+ def add_compile_tasks
56
+ block = lambda(&method(:configure_build))
57
+
58
+ desc 'Builds the solution'
59
+ build :compile => [ :restore ], &block.curry.(solution.compile.default_targets)
60
+
61
+ namespace :compile do
62
+ solution.compile.targets.each do |t|
63
+ desc "Builds the solution using the #{t} target"
64
+ build t.downcase, &block.curry.(t)
65
+ end
66
+ end
67
+ end
68
+
69
+ def configure_build(target, config)
70
+ config.sln = solution.file
71
+ config.prop 'Configuration', solution.compile.configuration
72
+ config.logging = solution.compile.logging
73
+ config.target = target
74
+ end
75
+ end
76
+ end
@@ -1,19 +1,19 @@
1
- module Physique
2
- class DefaultTasksBuilder < TasksBuilder
3
-
4
- def build_phases
5
- task :version
6
- task :restore => [ :version ]
7
- task :compile => [ :restore ]
8
- task :test => [ :compile ]
9
- task :package => [ :test ]
10
- task :publish => [ :package ]
11
- task :default => [ :test ]
12
- end
13
-
14
- def build_tasks
15
- task :default => [ :test ]
16
- end
17
- end
18
- end
19
-
1
+ module Physique
2
+ class DefaultTasksBuilder < TasksBuilder
3
+
4
+ def build_phases
5
+ task :version
6
+ task :restore => [ :version ]
7
+ task :compile => [ :restore ]
8
+ task :test => [ :compile ]
9
+ task :package => [ :test ]
10
+ task :publish => [ :package ]
11
+ task :default => [ :test ]
12
+ end
13
+
14
+ def build_tasks
15
+ task :default => [ :test ]
16
+ end
17
+ end
18
+ end
19
+
@@ -1,299 +1,302 @@
1
- require 'active_support/core_ext/object/blank'
2
- require 'physique/project_path_resolver'
3
-
4
- module Physique
5
- class FluentMigratorConfig
6
- self.extend Albacore::ConfigDSL
7
- include Albacore::Logging
8
-
9
- # Project name or path
10
- attr_path :project
11
-
12
- attr_writer :lang, # Programming language of the db project
13
- :instance, # Server instance name
14
- :name, # Database name
15
- :scripts_dir, # Scripts folder to examine to create tasks
16
- :dialect, # Dialect to use for generating SQL
17
- :task_alias # Alias used to construct rake task names
18
-
19
- def initialize
20
- @lang = :cs
21
- @scripts_dir = '_Scripts'
22
- end
23
-
24
- def opts
25
- validate_config
26
-
27
- Map.new({
28
- lang: @lang,
29
- instance: @instance,
30
- name: @name,
31
- scripts_dir: @scripts_dir,
32
- dialect: @dialect,
33
- project_file: Physique::ProjectPathResolver.resolve(@project, @lang),
34
- task_alias: (@task_alias || @name),
35
- })
36
- end
37
-
38
- private
39
-
40
- def validate_config
41
- raise ArgumentError, 'You must specify a database instance' if @instance.blank?
42
- raise ArgumentError, 'You must specify a database name' if @name.blank?
43
- raise ArgumentError, 'You must specify the FluentMigrator project' if @project.blank?
44
- raise ArgumentError, 'You must specify a language' if @lang.blank?
45
- raise ArgumentError, 'You must specify a scripts_dir' if @scripts_dir.blank?
46
- end
47
- end
48
-
49
- class FluentMigratorTasksBuilder < TasksBuilder
50
- def build_tasks
51
- dbs = solution.fluent_migrator_dbs
52
- return if dbs.blank?
53
-
54
- dbs.each do |db|
55
- expand_project_config db
56
- task_namespace = db_task_name(db)
57
-
58
- namespace :db do
59
- namespace task_namespace do
60
- # First look at the scripts_dir and add a task for every sql file that you find
61
- defaults = default_tasks(db.name)
62
- add_script_tasks db, defaults
63
-
64
- # Then add the default minimum required tasks in case the scripts_dir didn't contain them
65
- add_default_db_tasks db, defaults
66
-
67
- # Add the migrate and rollback tasks
68
- add_migrator_tasks db
69
-
70
- # Add the tasks to create the db from scratch
71
- add_create_tasks
72
-
73
- # Add a task to create a new migration in the db project
74
- add_new_migration_task db
75
- end
76
- end
77
-
78
- # Rebuild the databases when running tests
79
- task :test => "db:#{task_namespace}:rebuild"
80
- end
81
-
82
- alias_default_tasks
83
- end
84
-
85
- private
86
-
87
- def expand_project_config(db)
88
- project = Albacore::Project.new(db.project_file)
89
- db[:project_namespace] = project.namespace
90
- db[:project_dir] = project.proj_path_base
91
- db[:scripts_dir] = "#{project.proj_path_base}/#{db.scripts_dir}"
92
-
93
- build_conf = solution.compile.configuration
94
- db[:output_path] = project.output_path build_conf
95
- db[:output_dll] = File.expand_path("#{db.project_dir}/#{project.output_dll(build_conf)}")
96
- end
97
-
98
- def add_script_tasks(db, defaults)
99
- FileList["#{db.scripts_dir}/*.sql"].each do |f|
100
- task_name = File.basename(f, '.*')
101
-
102
- desc get_script_task_description(defaults, task_name, db)
103
- sqlcmd task_name do |s|
104
- s.file = f
105
- s.server_name = db.instance
106
- s.set_variable 'DATABASE_NAME', db.name
107
- end
108
- end
109
- end
110
-
111
- def get_script_task_description(defaults, task_name, db)
112
- default_task = defaults[task_name.to_sym]
113
- default_task ? default_task[:description] : "Executes #{task_name}.sql on #{db.name} in the #{db.scripts_dir} folder."
114
- end
115
-
116
- def add_default_db_tasks(db, defaults)
117
- defaults.each do |task_name,task_details|
118
- unless Rake::Task.task_defined? "db:#{db_task_name(db)}:#{task_name.to_s}"
119
- desc task_details[:description]
120
- sqlcmd task_name do |s|
121
- s.command = task_details[:command]
122
- s.server_name = db.instance
123
- s.set_variable 'DATABASE_NAME', db.name
124
- end
125
- end
126
- end
127
- end
128
-
129
- def default_tasks(database)
130
- { create: { description: 'Create the database', command: "CREATE DATABASE #{database}" },
131
- drop: { description: 'Drop the database', command: "DROP DATABASE #{database}"},
132
- seed: { description: 'Seed the database with test data', command: 'SELECT 1' } } # This is a no-op
133
- end
134
-
135
- def add_migrator_tasks(db)
136
- require 'physique/tasks/fluent_migrator'
137
-
138
- # Compile just the database project.
139
- # This task is registered as a dependency of the migration
140
- # tasks to ensure the latest code is available.
141
- build :compile_db => [ :restore ] do |b|
142
- b.target = [ 'Build' ]
143
- b.file = db.project_file
144
- b.prop 'Configuration', solution.compile.configuration
145
- b.logging = solution.compile.logging
146
- end
147
-
148
- block = lambda &method(:configure_migration)
149
-
150
- # Migrate up
151
- desc 'Migrate database to the latest version'
152
- fluent_migrator :migrate => [ :compile_db ], &block.curry.(db, 'migrate:up')
153
-
154
- # Migrate down
155
- desc 'Rollback the database to the previous version'
156
- fluent_migrator :rollback => [ :compile_db ], &block.curry.(db, 'rollback')
157
-
158
- # Try the migration
159
- desc 'Migrate and then immediately rollback'
160
- task :try => [ :migrate, :rollback ]
161
- end
162
-
163
- def configure_migration(db, task, config)
164
- config.instance = db.instance
165
- config.database = db.name
166
- config.task = task
167
- config.dll = %{"#{db.output_dll}"}
168
- config.exe = locate_tool(tool_in_output_folder(db) || tool_in_nuget_package)
169
- config.output_to_file
170
- end
171
-
172
- def add_create_tasks
173
- # Setup the database from nothing
174
- desc 'Create the database and run all migrations'
175
- task :setup => [ :create, :migrate, :seed ]
176
-
177
- # Drop and recreate the database
178
- desc 'Drop and recreate the database'
179
- task :rebuild => [ :drop, :setup ]
180
- end
181
-
182
- def tool_in_output_folder(db)
183
- existing_path "#{db.output_path}/Migrate.exe"
184
- end
185
-
186
- def tool_in_nuget_package
187
- existing_path "#{solution.nuget.restore_location}/FluentMigrator.*/tools/Migrate.exe"
188
- end
189
-
190
- def existing_path(path)
191
- return path if FileList[path].any? { |p| File.exists? p }
192
- nil
193
- end
194
-
195
- def db_task_name(db)
196
- db.task_alias.downcase
197
- end
198
-
199
- def add_new_migration_task(db)
200
- desc 'Create a new migration file with the specified name'
201
- task :new_migration, :name, :description do |t, args|
202
- name, description = args[:name], args[:description]
203
-
204
- unless name
205
- abort [
206
- %Q{Usage: rake "#{t.name}[name[,description]]"},
207
- desc,
208
- ].join "\n\n"
209
- end
210
-
211
- # Save the new migration file
212
- version = migration_version
213
- migration_file_name = "#{version}_#{name}.cs"
214
- migration_content = migration_template(version, name, description, db.project_namespace)
215
- save_file migration_content, "#{db.project_dir}/Migrations/#{migration_file_name}"
216
-
217
- # Add the new migration file to the project
218
- Albacore::Project.new(db.project_file).tap do |p|
219
- p.add_compile_node :Migrations, migration_file_name
220
- p.save
221
- end
222
- end
223
- end
224
-
225
- def migration_version
226
- Time.now.utc.strftime('%Y%m%d%H%M%S')
227
- end
228
-
229
- def migration_template(version, name, description, project_namespace)
230
- description = ", \"#{description}\"" unless description.nil?
231
- return <<TEMPLATE
232
- using FluentMigrator;
233
-
234
- namespace #{project_namespace}.Migrations
235
- {
236
- [Migration(#{version}#{description})]
237
- public class #{name} : Migration
238
- {
239
- public override void Up()
240
- {
241
- // Add migration code here
242
- }
243
-
244
- public override void Down()
245
- {
246
- // Add migration rollback code here
247
- }
248
- }
249
- }
250
- TEMPLATE
251
- end
252
-
253
- def save_file(content, file_path)
254
- raise "#{file_path} already exists, cancelling" if File.exists? file_path
255
- File.open(file_path, 'w') { |f| f.write(content) }
256
- end
257
-
258
- def alias_default_tasks
259
- Rake.application.tasks
260
- .select {|t| should_alias_db_task?(t)}
261
- .group_by {|t| db_command(t) }
262
- .each do |command,tasks|
263
- desc global_task_description(command,tasks)
264
- task "db:#{command}", tasks[0].arg_names => tasks.map {|t| t.name }
265
- end
266
- end
267
-
268
- def db_command(task)
269
- task.name.split(':').last.to_sym
270
- end
271
-
272
- def should_alias_db_task?(task)
273
- task.name.starts_with?('db') &&
274
- (only_one_db_configured? ||
275
- GLOBAL_TASKS.has_key?(db_command(task)))
276
- end
277
-
278
- def global_task_description(command, tasks)
279
- return GLOBAL_TASKS[command] unless only_one_db_configured?
280
-
281
- # Blank out the comment to hide the task in the list by default
282
- description = tasks[0].comment
283
- tasks[0].clear_comments
284
- description
285
- end
286
-
287
- def only_one_db_configured?
288
- solution.fluent_migrator_dbs.length == 1
289
- end
290
-
291
- GLOBAL_TASKS = {
292
- create: 'Create all databases',
293
- drop: 'Drop all databases',
294
- seed: 'Seed all databases with test data',
295
- setup: 'Build all databases and migrate them to the latest version',
296
- rebuild: 'Drop and recreate all databases',
297
- migrate: 'Migrates all databases to the latest version' }
298
- end
299
- end
1
+ require 'active_support/core_ext/object/blank'
2
+ require 'physique/project_path_resolver'
3
+
4
+ module Physique
5
+ class FluentMigratorConfig
6
+ self.extend Albacore::ConfigDSL
7
+ include Albacore::Logging
8
+
9
+ # Project name or path
10
+ attr_path :project
11
+
12
+ attr_writer :lang, # Programming language of the db project
13
+ :instance, # Server instance name
14
+ :name, # Database name
15
+ :scripts_dir, # Scripts folder to examine to create tasks
16
+ :dialect, # Dialect to use for generating SQL
17
+ :task_alias # Alias used to construct rake task names
18
+
19
+ def initialize
20
+ @lang = :cs
21
+ @scripts_dir = '_Scripts'
22
+ end
23
+
24
+ def opts
25
+ validate_config
26
+
27
+ Map.new({
28
+ lang: @lang,
29
+ instance: @instance,
30
+ name: @name,
31
+ scripts_dir: @scripts_dir,
32
+ dialect: @dialect,
33
+ project_file: Physique::ProjectPathResolver.resolve(@project, @lang),
34
+ task_alias: (@task_alias || @name),
35
+ })
36
+ end
37
+
38
+ private
39
+
40
+ def validate_config
41
+ raise ArgumentError, 'You must specify a database instance' if @instance.blank?
42
+ raise ArgumentError, 'You must specify a database name' if @name.blank?
43
+ raise ArgumentError, 'You must specify the FluentMigrator project' if @project.blank?
44
+ raise ArgumentError, 'You must specify a language' if @lang.blank?
45
+ raise ArgumentError, 'You must specify a scripts_dir' if @scripts_dir.blank?
46
+ end
47
+ end
48
+
49
+ class FluentMigratorTasksBuilder < TasksBuilder
50
+ def build_tasks
51
+ dbs = solution.fluent_migrator_dbs
52
+ return if dbs.blank?
53
+
54
+ dbs.each do |db|
55
+ expand_project_config db
56
+ task_namespace = db_task_name(db)
57
+
58
+ namespace :db do
59
+ namespace task_namespace do
60
+ # First look at the scripts_dir and add a task for every sql file that you find
61
+ defaults = default_tasks(db.name)
62
+ add_script_tasks db, defaults
63
+
64
+ # Then add the default minimum required tasks in case the scripts_dir didn't contain them
65
+ add_default_db_tasks db, defaults
66
+
67
+ # Add the migrate and rollback tasks
68
+ add_migrator_tasks db
69
+
70
+ # Add the tasks to create the db from scratch
71
+ add_create_tasks
72
+
73
+ # Add a task to create a new migration in the db project
74
+ add_new_migration_task db
75
+ end
76
+ end
77
+
78
+ # Rebuild the databases when running tests
79
+ task :test => "db:#{task_namespace}:rebuild"
80
+ end
81
+
82
+ alias_default_tasks
83
+ end
84
+
85
+ private
86
+
87
+ def expand_project_config(db)
88
+ project = Albacore::Project.new(db.project_file)
89
+ db[:project_namespace] = project.namespace
90
+ db[:project_dir] = project.proj_path_base
91
+ db[:scripts_dir] = "#{db[:project_dir]}\\#{db.scripts_dir}"
92
+
93
+ build_conf = solution.compile.configuration
94
+ db[:output_path] = project.output_path build_conf
95
+ db[:output_dll] = File.expand_path("#{db.project_dir}\\#{project.output_dll(build_conf)}")
96
+ end
97
+
98
+ def add_script_tasks(db, defaults)
99
+ # Need to keep the forward slashes for FileList.
100
+ scripts_dir = db.scripts_dir.gsub('\\', '/')
101
+ FileList["#{scripts_dir}/*.sql"].each do |f|
102
+ task_name = File.basename(f, '.*')
103
+
104
+ desc get_script_task_description(defaults, task_name, db)
105
+ sqlcmd task_name do |s|
106
+ s.file = f
107
+ s.server_name = db.instance
108
+ s.set_variable 'DATABASE_NAME', db.name
109
+ end
110
+ end
111
+ end
112
+
113
+ def get_script_task_description(defaults, task_name, db)
114
+ default_task = defaults[task_name.to_sym]
115
+ default_task ? default_task[:description] : "Executes #{task_name}.sql on #{db.name} in the #{db.scripts_dir} folder."
116
+ end
117
+
118
+ def add_default_db_tasks(db, defaults)
119
+ defaults.each do |task_name,task_details|
120
+ unless Rake::Task.task_defined? "db:#{db_task_name(db)}:#{task_name.to_s}"
121
+ desc task_details[:description]
122
+ sqlcmd task_name do |s|
123
+ s.command = task_details[:command]
124
+ s.server_name = db.instance
125
+ s.set_variable 'DATABASE_NAME', db.name
126
+ end
127
+ end
128
+ end
129
+ end
130
+
131
+ def default_tasks(database)
132
+ { create: { description: 'Create the database', command: "CREATE DATABASE #{database}" },
133
+ drop: { description: 'Drop the database', command: "DROP DATABASE #{database}"},
134
+ seed: { description: 'Seed the database with test data', command: "SELECT 'No seed script found'" } }
135
+ end
136
+
137
+ def add_migrator_tasks(db)
138
+ require 'physique/tasks/fluent_migrator'
139
+
140
+ # Compile just the database project.
141
+ # This task is registered as a dependency of the migration
142
+ # tasks to ensure the latest code is available.
143
+ build :compile_db => [ :restore ] do |b|
144
+ b.target = [ 'Build' ]
145
+ b.file = db.project_file
146
+ b.prop 'Configuration', solution.compile.configuration
147
+ b.logging = solution.compile.logging
148
+ end
149
+
150
+ block = lambda &method(:configure_migration)
151
+
152
+ # Migrate up
153
+ desc 'Migrate database to the latest version'
154
+ fluent_migrator :migrate => [ :compile_db ], &block.curry.(db, 'migrate:up')
155
+
156
+ # Migrate down
157
+ desc 'Rollback the database to the previous version'
158
+ fluent_migrator :rollback => [ :compile_db ], &block.curry.(db, 'rollback')
159
+
160
+ # Try the migration
161
+ desc 'Migrate and then immediately rollback'
162
+ task :try => [ :migrate, :rollback ]
163
+ end
164
+
165
+ def configure_migration(db, task, config)
166
+ config.instance = db.instance
167
+ config.database = db.name
168
+ config.task = task
169
+ config.dll = %{"#{db.output_dll}"}
170
+ config.exe = locate_tool(tool_in_output_folder(db) || tool_in_nuget_package)
171
+ config.output_to_file
172
+ end
173
+
174
+ def add_create_tasks
175
+ # Setup the database from nothing
176
+ desc 'Create the database and run all migrations'
177
+ task :setup => [ :create, :migrate, :seed ]
178
+
179
+ # Drop and recreate the database
180
+ desc 'Drop and recreate the database'
181
+ task :rebuild => [ :drop, :setup ]
182
+ end
183
+
184
+ def tool_in_output_folder(db)
185
+ existing_path "#{db.output_path}/Migrate.exe"
186
+ end
187
+
188
+ def tool_in_nuget_package
189
+ existing_path "#{solution.nuget.restore_location}/FluentMigrator*/tools/Migrate.exe"
190
+ end
191
+
192
+ def existing_path(path)
193
+ path = path.gsub('\\', '/')
194
+ return path if FileList[path].any? { |p| File.exists? p }
195
+ nil
196
+ end
197
+
198
+ def db_task_name(db)
199
+ db.task_alias.downcase
200
+ end
201
+
202
+ def add_new_migration_task(db)
203
+ desc 'Create a new migration file with the specified name'
204
+ task :new_migration, :name, :description do |t, args|
205
+ name, description = args[:name], args[:description]
206
+
207
+ unless name
208
+ abort [
209
+ %Q{Usage: rake "#{t.name}[name[,description]]"},
210
+ desc,
211
+ ].join "\n\n"
212
+ end
213
+
214
+ # Save the new migration file
215
+ version = migration_version
216
+ migration_file_name = "#{version}_#{name}.cs"
217
+ migration_content = migration_template(version, name, description, db.project_namespace)
218
+ save_file migration_content, "#{db.project_dir}/Migrations/#{migration_file_name}"
219
+
220
+ # Add the new migration file to the project
221
+ Albacore::Project.new(db.project_file).tap do |p|
222
+ p.add_compile_node :Migrations, migration_file_name
223
+ p.save
224
+ end
225
+ end
226
+ end
227
+
228
+ def migration_version
229
+ Time.now.utc.strftime('%Y%m%d%H%M%S')
230
+ end
231
+
232
+ def migration_template(version, name, description, project_namespace)
233
+ description = ", \"#{description}\"" unless description.nil?
234
+ return <<TEMPLATE
235
+ using FluentMigrator;
236
+
237
+ namespace #{project_namespace}.Migrations
238
+ {
239
+ [Migration(#{version}#{description})]
240
+ public class #{name} : Migration
241
+ {
242
+ public override void Up()
243
+ {
244
+ // Add migration code here
245
+ }
246
+
247
+ public override void Down()
248
+ {
249
+ // Add migration rollback code here
250
+ }
251
+ }
252
+ }
253
+ TEMPLATE
254
+ end
255
+
256
+ def save_file(content, file_path)
257
+ raise "#{file_path} already exists, cancelling" if File.exists? file_path
258
+ File.open(file_path, 'w') { |f| f.write(content) }
259
+ end
260
+
261
+ def alias_default_tasks
262
+ Rake.application.tasks
263
+ .select {|t| should_alias_db_task?(t)}
264
+ .group_by {|t| db_command(t) }
265
+ .each do |command,tasks|
266
+ desc global_task_description(command,tasks)
267
+ task "db:#{command}", tasks[0].arg_names => tasks.map {|t| t.name }
268
+ end
269
+ end
270
+
271
+ def db_command(task)
272
+ task.name.split(':').last.to_sym
273
+ end
274
+
275
+ def should_alias_db_task?(task)
276
+ task.name.starts_with?('db') &&
277
+ (only_one_db_configured? ||
278
+ GLOBAL_TASKS.has_key?(db_command(task)))
279
+ end
280
+
281
+ def global_task_description(command, tasks)
282
+ return GLOBAL_TASKS[command] unless only_one_db_configured?
283
+
284
+ # Blank out the comment to hide the task in the list by default
285
+ description = tasks[0].comment
286
+ tasks[0].clear_comments
287
+ description
288
+ end
289
+
290
+ def only_one_db_configured?
291
+ solution.fluent_migrator_dbs.length == 1
292
+ end
293
+
294
+ GLOBAL_TASKS = {
295
+ create: 'Create all databases',
296
+ drop: 'Drop all databases',
297
+ seed: 'Seed all databases with test data',
298
+ setup: 'Build all databases and migrate them to the latest version',
299
+ rebuild: 'Drop and recreate all databases',
300
+ migrate: 'Migrates all databases to the latest version' }
301
+ end
302
+ end